bee 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -25,4 +25,4 @@ or go to URL http://www.apache.org/licenses/LICENSE-2.0).
25
25
 
26
26
  = Copyright
27
27
 
28
- bee version 0.3.1 (C) Michel Casabianca - 2006
28
+ bee version 0.4.0 (C) Michel Casabianca & Contributors - 2006-2007
data/bin/bee CHANGED
@@ -1,5 +1,21 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ # Copyright 2006-2007 Michel Casabianca <michel.casabianca@gmail.com>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ # Launching script for bee.
18
+
3
19
  $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
20
  require 'bee_console'
5
21
 
@@ -0,0 +1,32 @@
1
+ @echo off
2
+ if not "%~f0" == "~f0" goto WinNT
3
+ ruby -Sx "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
4
+ goto endofruby
5
+ :WinNT
6
+ "%~d0%~p0ruby" -x "%~f0" %*
7
+ goto endofruby
8
+ #!/usr/bin/env ruby
9
+
10
+ # Copyright 2006-2007 Michel Casabianca <michel.casabianca@gmail.com>
11
+ #
12
+ # Licensed under the Apache License, Version 2.0 (the "License");
13
+ # you may not use this file except in compliance with the License.
14
+ # You may obtain a copy of the License at
15
+ #
16
+ # http://www.apache.org/licenses/LICENSE-2.0
17
+ #
18
+ # Unless required by applicable law or agreed to in writing, software
19
+ # distributed under the License is distributed on an "AS IS" BASIS,
20
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ # See the License for the specific language governing permissions and
22
+ # limitations under the License.
23
+
24
+ # Launching script for bee.
25
+
26
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
27
+ require 'bee_console'
28
+
29
+ Bee::Console.start_command_line
30
+
31
+ __END__
32
+ :endofruby
data/lib/bee.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2006 Michel Casabianca <casa@sweetohm.net>
1
+ # Copyright 2006-2007 Michel Casabianca <michel.casabianca@gmail.com>
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,125 +12,139 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- # Module for Bee stuff.
16
- module Bee
17
-
18
- require 'yaml'
19
-
20
- #########################################################################
21
- # BUILD ERROR #
22
- #########################################################################
23
-
24
- class BuildError < RuntimeError; end
25
-
26
- module BuildErrorMixin
27
-
28
- # Convenient method to raise a BuildError.
29
- # - message: error message.
30
- def error(message)
31
- raise BuildError.new(message)
32
- end
15
+ require 'yaml'
16
+ require 'bee_util'
33
17
 
34
- end
35
-
36
- #########################################################################
37
- # BUILD CLASS #
38
- #########################################################################
18
+ # Module for Bee core classes.
19
+ module Bee
39
20
 
40
- # Class for a given build.
21
+ # Class for a build. This class is built from an object resulting from YAML
22
+ # build file parsing.
41
23
  class Build
42
24
 
43
- include BuildErrorMixin
25
+ include Bee::Util::BuildErrorMixin
26
+ include Bee::Util::HashCheckerMixin
44
27
 
45
28
  # Build file.
46
29
  attr_reader :file
47
- # Base directory.
30
+ # Base directory (directory where lives the build file). All file paths
31
+ # are relative to this directory.
48
32
  attr_reader :base
49
33
  # Build name.
50
34
  attr_reader :name
51
- # Default target.
35
+ # Default target (bee runs target passed on the command line, default
36
+ # target or first one in build file).
52
37
  attr_reader :default
53
38
  # Build description.
54
39
  attr_reader :description
55
- # Properties hash (used for project help).
56
- attr_reader :properties
57
40
  # Hash for targets, indexed by target name.
58
41
  attr_reader :targets
59
42
  # Context for Ruby scripts and properties.
60
43
  attr_reader :context
61
- # Loaded tasks.
62
- attr_reader :tasks
63
- # Build listener.
44
+ # Package manager (for task invocation).
45
+ attr_reader :package_manager
46
+ # Build listener (responsible for displaying build status).
64
47
  attr_reader :listener
48
+
49
+ # Load a build from a YAML build file.
50
+ # - file: YAML build file.
51
+ # - recursive: tells if we look for build file recursively (defaults to
52
+ # nil).
53
+ def self.load(file, recursive=nil)
54
+ if recursive
55
+ begin
56
+ file = Bee::Util::find(file)
57
+ rescue
58
+ raise Bee::Util::BuildError.new("Build file '#{file}' " +
59
+ "not found recursively")
60
+ end
61
+ end
62
+ raise Bee::Util::BuildError.new("Build file '#{file}' not found") if
63
+ not File.exists?(file)
64
+ raise Bee::Util::BuildError.new("Build file '#{file}' is not a file") if
65
+ not File.file?(file)
66
+ raise Bee::Util::BuildError.new("Build file '#{file}' is not readable") if
67
+ not File.readable?(file)
68
+ yaml = File.read(file)
69
+ begin
70
+ object = YAML::load(yaml)
71
+ rescue
72
+ raise Bee::Util::BuildError.
73
+ new("YAML syntax error in build file: #{$!}")
74
+ end
75
+ return Build.new(object, file)
76
+ end
65
77
 
66
78
  # Constructor:
67
- # - file: build file to load.
68
- def initialize(file)
79
+ # - object: build description as an object (resulting from YAML loading).
80
+ # - file: build file (nil if none).
81
+ def initialize(object, file)
82
+ require 'bee_task'
69
83
  @file = file
84
+ if file
85
+ @base = File.expand_path(File.dirname(file))
86
+ else
87
+ @base = File.expand_path(Dir.pwd)
88
+ end
89
+ error "Build must be a list" unless object.kind_of?(Array)
70
90
  @properties = {}
71
91
  @targets = {}
72
- @tasks = {}
73
92
  @context = Context.new
74
- default_context_file = File.join(File.dirname(__FILE__),
75
- 'bee_default_context.rb')
76
- source = File.read(default_context_file)
77
- load_context(source)
78
- @base = File.expand_path(File.dirname(@file))
79
- @context.set('base', @base)
80
- @properties['base'] = @base
81
- # load build file
82
- begin
83
- source = File.read(@file)
84
- build = YAML::load(source)
85
- # parse object entries
86
- for entry in build
87
- if entry.key?('build')
88
- # build info entry
89
- error "Duplicate build info" if @name
90
- @name = entry['build']
91
- @default = entry['default']
92
- @description = entry['description']
93
- # load context files if any
94
- if entry['context']
95
- for script in entry['context']
96
- begin
97
- based_script = File.join(@base, script)
98
- evaluated_script = @context.evaluate_object(based_script)
99
- source = File.read(evaluated_script)
100
- load_context(source)
101
- rescue
102
- error "Error loading context '#{script}': #{$!}"
103
- end
104
- end
105
- end
106
- elsif entry.key?('properties')
107
- # properties entry
108
- for property in entry['properties']
93
+ @context.set_property(:base, @base)
94
+ @properties[:base] = @base
95
+ @package_manager = Bee::Task::PackageManager.new(self)
96
+ # parse object entries
97
+ for entry in object
98
+ if entry.key?('build')
99
+ # build info entry
100
+ begin
101
+ check_hash(entry,
102
+ {'build' => :mandatory,
103
+ 'default' => :optional,
104
+ 'description' => :optional,
105
+ 'context' => :optional})
106
+ rescue
107
+ error "Error parsing build entry: #{$!}"
108
+ end
109
+ error "Duplicate build info" if @name
110
+ @name = entry['build']
111
+ @default = entry['default']
112
+ @description = entry['description']
113
+ # load context files if any
114
+ if entry['context']
115
+ for script in entry['context']
109
116
  begin
110
- name = property.keys[0]
111
- value = @context.evaluate_object(property[name])
112
- error "Duplicate property '#{name}' definition" if
113
- @properties[name]
114
- @properties[name] = value
115
- @context.set(name, value)
116
- rescue
117
- error "Error evaluating property '#{name}': #{$!}"
117
+ based_script = File.join(@base, script)
118
+ evaluated_script = @context.evaluate_object(based_script)
119
+ source = File.read(evaluated_script)
120
+ @context.evaluate_script(source)
121
+ rescue Exception
122
+ error "Error loading context '#{script}': #{$!}"
118
123
  end
119
124
  end
120
- elsif entry.key?('target')
121
- # target entry
125
+ end
126
+ elsif entry.key?('properties')
127
+ # properties entry
128
+ for property in entry['properties']
129
+ name = property.keys[0]
130
+ value = @context.evaluate_object(property[name])
131
+ @context.set_property(name, value, false)
132
+ end
133
+ elsif entry.key?('target')
134
+ # target entry
135
+ begin
122
136
  target = Target.new(entry, self)
123
- @default = target.name if not @default and @targets.keys.length == 0
124
- error "Duplicate target definition: '#{target.name}'" if
125
- @targets[target.name]
126
- @targets[target.name] = target
127
- else
128
- # unknown entry
129
- error "Unknown entry:\n#{YAML::dump(entry)}"
137
+ rescue
138
+ error "Error parsing target '#{entry['target']}': #{$!}"
130
139
  end
140
+ @default = target.name if !@default and @targets.keys.empty?
141
+ error "Duplicate target definition: '#{target.name}'" if
142
+ @targets[target.name]
143
+ @targets[target.name] = target
144
+ else
145
+ # unknown entry
146
+ error "Unknown entry:\n#{YAML::dump(entry)}"
131
147
  end
132
- rescue
133
- error "Error loading build file '#{@file}': #{$!}"
134
148
  end
135
149
  end
136
150
 
@@ -148,8 +162,8 @@ module Bee
148
162
  for target in targets
149
163
  run_target(target)
150
164
  end
151
- @listener.build_finished(self)
152
- rescue BuildError => e
165
+ @listener.build_finished(self) if @listener
166
+ rescue Bee::Util::BuildError => e
153
167
  if listener
154
168
  @listener.error(e)
155
169
  else
@@ -168,31 +182,14 @@ module Bee
168
182
  @targets[target].run
169
183
  end
170
184
 
171
- private
172
-
173
- # Load a given context and add last expression evaluated in context to
174
- # extension tasks mapping.
175
- # - source: the context source.
176
- def load_context(source)
177
- new_tasks = @context.evaluate_script(source)
178
- if new_tasks.kind_of?(Hash)
179
- for task in new_tasks.keys
180
- error "Duplicate definition for task '#{task}'" if @tasks[task]
181
- end
182
- @tasks.merge!(new_tasks)
183
- end
184
- end
185
-
186
185
  end
187
186
 
188
- #########################################################################
189
- # TARGET CLASS #
190
- #########################################################################
191
-
192
- # Class for a given target.
187
+ # Class for a target. It is built from the YAML build file and manages a
188
+ # target, in particular, tasks execution.
193
189
  class Target
194
190
 
195
- include BuildErrorMixin
191
+ include Bee::Util::BuildErrorMixin
192
+ include Bee::Util::HashCheckerMixin
196
193
 
197
194
  # Build that encapsulates target.
198
195
  attr_reader :build
@@ -206,14 +203,19 @@ module Bee
206
203
  attr_reader :script
207
204
 
208
205
  # Constructor.
209
- # - target: target for target, resulting from YAML parsing.
206
+ # - object: object for target, resulting from YAML parsing.
210
207
  # - build: build that encapsulate this target.
211
- def initialize(target, build)
208
+ def initialize(object, build)
209
+ check_hash(object,
210
+ {'target' => :mandatory,
211
+ 'depends' => :optional,
212
+ 'description' => :optional,
213
+ 'script' => :optional})
212
214
  @build = build
213
- @name = target['target']
214
- @depends = target['depends']||[]
215
- @description = target['description']
216
- @script = target['script']
215
+ @name = object['target']
216
+ @depends = object['depends']||[]
217
+ @description = object['description']
218
+ @script = object['script']
217
219
  end
218
220
 
219
221
  # Run target.
@@ -260,7 +262,7 @@ module Bee
260
262
  run_ruby(script)
261
263
  else
262
264
  # must be a task
263
- run_extension(task)
265
+ run_bee_task(task)
264
266
  end
265
267
  end
266
268
  end
@@ -280,37 +282,26 @@ module Bee
280
282
  @listener.task(script) if @listener
281
283
  begin
282
284
  @build.context.evaluate_script(script)
283
- rescue BuildError
285
+ rescue Exception
284
286
  error "Error running Ruby script: #{$!}"
285
287
  end
286
288
  end
287
289
 
288
- # Run a given task.
290
+ # Run a given bee task.
289
291
  # - task: task to run as a Hash.
290
- def run_extension(task)
292
+ def run_bee_task(task)
293
+ require 'bee_task'
291
294
  @listener.task(task) if @listener
292
- name = task.keys[0]
293
- error "Unknown task '#{name}'" if
294
- not @build.tasks.key?(name)
295
- parameters = task[name]
296
- script = "#{name}(#{parameters.inspect})"
297
- begin
298
- @build.context.evaluate_script(script)
299
- rescue BuildError
300
- error "Error running task #{name}: #{$!}"
301
- end
295
+ @build.package_manager.run_task(task)
302
296
  end
303
-
297
+
304
298
  end
305
299
 
306
- #########################################################################
307
- # CONTEXT CLASS #
308
- #########################################################################
309
-
310
- # Class for Ruby scripts context.
300
+ # Class for Ruby scripts context. All embedded Ruby scripts run in this
301
+ # context where build properties are defined as Ruby variables.
311
302
  class Context
312
303
 
313
- include BuildErrorMixin
304
+ include Bee::Util::BuildErrorMixin
314
305
 
315
306
  # The binding of this context.
316
307
  attr_reader :context_binding
@@ -320,26 +311,37 @@ module Bee
320
311
  @context_binding = get_binding
321
312
  end
322
313
 
323
- # Set a given value in context.
324
- # - name: the variable name to set.
325
- # - value: the variable value.
326
- def set(name, value)
314
+ # Set a given property in context.
315
+ # - name: the property name.
316
+ # - value: the property value.
317
+ # - override: tells if we can overwrite an existing value.
318
+ def set_property(name, value, overwrite=true)
319
+ error "Property '#{name}' was already defined" if
320
+ !overwrite and properties.include?(name.to_s)
327
321
  begin
328
322
  eval("#{name} = #{value.inspect}", @context_binding)
329
- rescue
323
+ rescue Exception
330
324
  error "Error setting property '#{name} = #{value.inspect}': #{$!}"
331
325
  end
332
326
  end
333
327
 
334
- # Get a given value in context.
335
- # - name: the variable name.
336
- def get(name)
328
+ # Get a given property in context.
329
+ # - name: the property name.
330
+ # - strict: raise an error if given property was not set.
331
+ def get_property(name, strict=false)
332
+ error "Property '#{name}' was not set" if
333
+ strict and !properties.include?(name.to_s)
337
334
  begin
338
335
  eval("#{name}", @context_binding)
339
- rescue
336
+ rescue Exception
340
337
  error "Error getting property '#{name}': #{$!}"
341
338
  end
342
339
  end
340
+
341
+ # Return list of properties (as local variables of binding).
342
+ def properties
343
+ return eval('local_variables', @context_binding)
344
+ end
343
345
 
344
346
  # Evaluate a script in context.
345
347
  # - script: script to evaluate.
@@ -361,38 +363,31 @@ module Bee
361
363
  # string: replace property references
362
364
  object = object.gsub(/#\{.+?\}/) do |match|
363
365
  property = match[2..-2]
364
- value = get(property)
365
- error ("Property '#{property}' was not defined") unless value
366
+ value = get_property(property)
367
+ error "Property '#{property}' was not defined" unless value
366
368
  value
367
369
  end
368
370
  return object
369
371
  when Symbol
370
372
  # symbol: return property object
371
373
  property = object.to_s
372
- value = get(property)
373
- error ("Property '#{property}' was not defined") unless value
374
- return value
374
+ value = get_property(property, true)
375
+ return evaluate_object(value)
376
+ when Array
377
+ # array: evaluate each element
378
+ return object.collect { |element| evaluate_object(element) }
379
+ when Hash
380
+ # hash: evaluate all keys and values
381
+ evaluated = {}
382
+ object.each_pair do |key, value|
383
+ evaluated[evaluate_object(key)] = evaluate_object(value)
384
+ end
385
+ return evaluated
375
386
  else
376
387
  return object
377
388
  end
378
389
  end
379
390
 
380
- # Check a task parameters. Raise a RuntimeError with explanation message if
381
- # a mandatory parameter is missing or an unknown parameter was found.
382
- # - params: task parameters as a Hash.
383
- # - description: parameters description as a Hash associating a parameter
384
- # name with symbol :mandatory or :optional.
385
- def check_task_parameters(params, description)
386
- error "Parameters must be a Hash" unless params.kind_of?(Hash)
387
- for param in description.keys
388
- error "Missing mandatory parameter '#{param}'" unless
389
- params[param] or description[param] == :optional
390
- end
391
- for param in params.keys
392
- error "Unknown parameter '#{param}'" if not description.key?(param)
393
- end
394
- end
395
-
396
391
  private
397
392
 
398
393
  # Get a binding as script context.