2
2
# tired of rebuilding the parser.rb file all the time.
3
3
class Puppet::Parser::Parser
4
4
require 'puppet/parser/functions'
6
ASTSet = Struct.new(:classes, :definitions, :nodes)
8
# Define an accessor method for each table. We hide the existence of
10
[:classes, :definitions, :nodes].each do |name|
11
define_method(name) do
5
require 'puppet/parser/files'
6
require 'puppet/parser/loaded_code'
16
9
AST = Puppet::Parser::AST
108
# Find a class definition, relative to the current namespace.
109
def findclass(namespace, name)
110
fqfind namespace, name, classes
113
# Find a component definition, relative to the current namespace.
114
def finddefine(namespace, name)
115
fqfind namespace, name, definitions
118
# This is only used when nodes are looking up the code for their
121
fqfind "", name, nodes
124
# The recursive method used to actually look these objects up.
125
def fqfind(namespace, name, table)
101
[:hostclass, :definition, :node, :nodes?].each do |method|
102
define_method(method) do |*args|
103
@loaded_code.send(method, *args)
107
def find_hostclass(namespace, name)
108
find_or_load(namespace, name, :hostclass)
111
def find_definition(namespace, name)
112
find_or_load(namespace, name, :definition)
115
def find_or_load(namespace, name, type)
116
method = "find_#{type}"
126
117
namespace = namespace.downcase
127
name = name.to_s.downcase
129
# If our classname is fully qualified or we have no namespace,
130
# just try directly for the class, and return either way.
131
if name =~ /^::/ or namespace == ""
132
classname = name.sub(/^::/, '')
133
self.load(classname) unless table[classname]
134
return table[classname]
137
# Else, build our namespace up piece by piece, checking
138
# for the class in each namespace.
139
ary = namespace.split("::")
142
newname = (ary + [name]).join("::").sub(/^::/, '')
143
if obj = table[newname] or (self.load(newname) and obj = table[newname])
147
# Delete the second to last object, which reduces our namespace by one.
151
# If we've gotten to this point without finding it, see if the name
152
# exists at the top namespace
153
if obj = table[name] or (self.load(name) and obj = table[name])
119
fullname = (namespace + "::" + name).sub(/^::/, '')
122
names_to_try = [name.sub(/^::/, '')]
124
names_to_try = [fullname]
126
# Try to load the module init file if we're a qualified name
127
names_to_try << fullname.split("::")[0] if fullname.include?("::")
129
# Otherwise try to load the bare name on its own. This
130
# is appropriate if the class we're looking for is in a
131
# module that's different from our namespace.
133
names_to_try.compact!
136
until (result = @loaded_code.send(method, namespace, name)) or names_to_try.empty? do
137
self.load(names_to_try.shift)
160
142
# Import our files.
183
166
"in file #{@lexer.file} at line #{@lexer.line}"
186
files = Puppet::Module::find_manifests(pat, :cwd => dir, :environment => @environment)
169
files = Puppet::Parser::Files.find_manifests(pat, :cwd => dir, :environment => @environment)
187
170
if files.size == 0
188
171
raise Puppet::ImportError.new("No file(s) found for import " +
192
175
files.collect { |file|
193
parser = Puppet::Parser::Parser.new(:astset => @astset, :environment => @environment)
176
parser = Puppet::Parser::Parser.new(:loaded_code => @loaded_code, :environment => @environment)
194
177
parser.files = self.files
195
178
Puppet.debug("importing '%s'" % file)
220
203
@lexer = Puppet::Parser::Lexer.new()
207
@loading.extend(MonitorMixin)
211
delete(item)[:busy].signal if self.has_key?(item) and self[item][:loader] == Thread.current
216
if !self.has_key? item
217
self[item] = { :loader => Thread.current, :busy => self.new_cond}
219
elsif self[item][:loader] == Thread.current
222
flag = self[item][:busy]
232
# Utility method factored out of load
233
def able_to_import?(classname,item,msg)
234
unless @loaded.include?(item)
236
case @loading.owner_of(item)
240
return able_to_import?(classname,item,msg)
243
Puppet.info "Autoloaded #{msg}"
246
rescue Puppet::ImportError => detail
247
# We couldn't load the item
249
@loading.done_with(item)
252
# We don't know whether we're looking for a class or definition, so we have
254
return @loaded_code.hostclass(classname) || @loaded_code.definition(classname)
225
257
# Try to load a class, since we could not find it.
226
258
def load(classname)
227
259
return false if classname == ""
228
260
filename = classname.gsub("::", File::SEPARATOR)
230
# First try to load the top-level module
231
261
mod = filename.scan(/^[\w-]+/).shift
232
unless @loaded.include?(mod)
236
Puppet.info "Autoloaded module %s" % mod
237
rescue Puppet::ImportError => detail
238
# We couldn't load the module
242
# We don't know whether we're looking for a class or definition, so we have
244
return true if classes.include?(classname) || definitions.include?(classname)
246
unless @loaded.include?(filename)
248
# Then the individual file
251
Puppet.info "Autoloaded file %s from module %s" % [filename, mod]
252
rescue Puppet::ImportError => detail
253
# We couldn't load the file
256
# We don't know whether we're looking for a class or definition, so we have
258
return classes.include?(classname) || definitions.include?(classname)
263
# First try to load the top-level module then the individual file
264
[[mod, "module %s" % mod ],
265
[filename,"file %s from module %s" % [filename, mod]]
266
].any? { |item,description| able_to_import?(classname,item,description) }
261
269
# Split an fq name into a namespace and name
323
331
args[:code] = code if code
324
332
args[:parentclass] = parent if parent
334
args[:line] = options[:line]
327
@astset.classes[name] = ast AST::HostClass, args
336
@loaded_code.add_hostclass(name, ast(AST::HostClass, args))
330
return @astset.classes[name]
339
return @loaded_code.hostclass(name)
333
342
# Create a new definition.
334
343
def newdefine(name, options = {})
335
344
name = name.downcase
336
if @astset.classes.include?(name)
345
if @loaded_code.hostclass(name)
337
346
raise Puppet::ParseError, "Cannot redefine class %s as a definition" %
340
349
# Make sure our definition doesn't already exist
341
if other = @astset.definitions[name]
350
if other = @loaded_code.definition(name)
342
351
error("%s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
365
375
names = [names] unless names.instance_of?(Array)
366
376
doc = lexer.getcomment
367
377
names.collect do |name|
368
name = name.to_s.downcase
369
if other = @astset.nodes[name]
378
name = AST::HostName.new :value => name unless name.is_a?(AST::HostName)
379
if other = @loaded_code.node_exists?(name)
370
380
error("Node %s is already defined at %s:%s; cannot redefine" % [other.name, other.file, other.line])
372
382
name = name.to_s if name.is_a?(Symbol)
387
:line => options[:line]
378
389
if options[:code]
379
390
args[:code] = options[:code]
465
476
@lexer.string = string
480
return @version if defined?(@version)
482
if Puppet[:config_version] == ""
483
@version = Time.now.to_i
487
@version = Puppet::Util.execute([Puppet[:config_version]]).strip
489
rescue Puppet::ExecutionFailure => e
490
raise Puppet::ParseError, "Unable to set config_version: #{e.message}"
468
493
# Add a new file to be checked when we're checking to see if we should be
469
494
# reparsed. This is basically only used by the TemplateWrapper to let the
470
495
# parser know about templates that should be parsed.