bee 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.9.0 (C) Michel Casabianca & Contributors - 2006-2010
28
+ bee version 0.10.0 (C) Michel Casabianca & Contributors - 2006-2010
@@ -3,8 +3,8 @@
3
3
  description: "Generate a sample Ruby application project"
4
4
 
5
5
  - properties:
6
- - name: application
7
- - description: |
6
+ name: application
7
+ description: |
8
8
  This script will create a sample Ruby application project. Build file
9
9
  can run unit tests, generate API documentation, generate GEM archive
10
10
  and build a ZIP distribution file.
@@ -5,30 +5,30 @@
5
5
 
6
6
  # Build properties
7
7
  - properties:
8
- - name: "<%= name %>"
9
- - version: ~
10
- - author: ~
11
- - years: ~
12
- - platform: "Gem::Platform::RUBY"
13
- - summary: ~
14
- - email: ~
15
- - homepage: ~
16
- - rubyforge: ~
17
- - dependencies:
8
+ name: "<%= name %>"
9
+ version: ~
10
+ author: ~
11
+ years: ~
12
+ platform: "Gem::Platform::RUBY"
13
+ summary: ~
14
+ email: ~
15
+ homepage: ~
16
+ rubyforge: ~
17
+ dependencies:
18
18
  bee: [">= 0.7.0"]
19
- - lib_dir: "lib"
20
- - test_dir: "test"
21
- - build_dir: "build"
22
- - api_dir: "#{build_dir}/api"
23
- - gem_spec: "gem.spec"
24
- - gem_spec_erb: "gem.spec.erb"
25
- - gem_package: "#{name}-#{version}.gem"
26
- - rdoc_files: ["#{lib_dir}/*.rb"]
27
- - zip_excludes: ["**/CVS", "**/.DS_Store", "**/*~"]
28
- - zip_prefix: "#{name}-#{version}"
29
- - zip_archive: "#{build_dir}/#{name}-#{version}.zip"
30
- - clean_dirs: [:build_dir]
31
- - clean_files: ["**/*~", "**/.#*", "**/.DS_Store", :gem_spec]
19
+ lib_dir: "lib"
20
+ test_dir: "test"
21
+ build_dir: "build"
22
+ api_dir: "#{build_dir}/api"
23
+ gem_spec: "gem.spec"
24
+ gem_spec_erb: "gem.spec.erb"
25
+ gem_package: "#{name}-#{version}.gem"
26
+ rdoc_files: ["#{lib_dir}/*.rb"]
27
+ zip_excludes: ["**/CVS", "**/.DS_Store", "**/*~"]
28
+ zip_prefix: "#{name}-#{version}"
29
+ zip_archive: "#{build_dir}/#{name}-#{version}.zip"
30
+ clean_dirs: [:build_dir]
31
+ clean_files: ["**/*~", "**/.#*", "**/.DS_Store", :gem_spec]
32
32
 
33
33
  # Build targets
34
34
  - target: test
@@ -3,8 +3,8 @@
3
3
  description: "Generate a bee package project"
4
4
 
5
5
  - properties:
6
- - project_name: bee_hello
7
- - description: |
6
+ project_name: bee_hello
7
+ description: |
8
8
  This script will create a project for a bee package with sample task (in
9
9
  'lib' directory) and template (in 'egg' directory). A unit test for the
10
10
  task is generated in 'test' directory. Generated build file has a target
@@ -6,28 +6,28 @@
6
6
 
7
7
  # Build properties
8
8
  - properties:
9
- - name: <%= project_name %>
10
- - version: 0.0.1
11
- - author: ~
12
- - years: ~
13
- - platform: "Gem::Platform::RUBY"
14
- - summary: ~
15
- - email: ~
16
- - homepage: "http://<%= project_name %>.rubyforge.org"
17
- - rubyforge: ~
18
- - dependencies:
9
+ name: <%= project_name %>
10
+ version: 0.0.1
11
+ author: ~
12
+ years: ~
13
+ platform: "Gem::Platform::RUBY"
14
+ summary: ~
15
+ email: ~
16
+ homepage: "http://<%= project_name %>.rubyforge.org"
17
+ rubyforge: ~
18
+ dependencies:
19
19
  "bee": [">= 0.5.0"]
20
- - lib_dir: "lib"
21
- - test_dir: "test"
22
- - build_dir: "build"
23
- - readme_erb: "readme.erb"
24
- - readme_file: "#{build_dir}/README"
25
- - license_file: "LICENSE"
26
- - gem_spec_erb: "gem.spec.erb"
27
- - gem_spec: "#{build_dir}/gem.spec"
28
- - gem_package: "#{name}-#{version}.gem"
29
- - clean_dirs: :build_dir
30
- - clean_files: ""
20
+ lib_dir: "lib"
21
+ test_dir: "test"
22
+ build_dir: "build"
23
+ readme_erb: "readme.erb"
24
+ readme_file: "#{build_dir}/README"
25
+ license_file: "LICENSE"
26
+ gem_spec_erb: "gem.spec.erb"
27
+ gem_spec: "#{build_dir}/gem.spec"
28
+ gem_package: "#{name}-#{version}.gem"
29
+ clean_dirs: :build_dir
30
+ clean_files: ""
31
31
 
32
32
  # Build targets
33
33
  - target: test
@@ -3,8 +3,8 @@
3
3
  description: "Generate a project for a Ruby script"
4
4
 
5
5
  - properties:
6
- - project_name: hello
7
- - description: |
6
+ project_name: hello
7
+ description: |
8
8
  This script will create a project for a Ruby script that might be
9
9
  distributed as a gem. Generated build file has a single target gem
10
10
  to generate the distribution gem.
@@ -3,11 +3,11 @@
3
3
  default: all
4
4
 
5
5
  - properties:
6
- - name: <%= project_name %>
7
- - version: 0.0.1
8
- - build: build
9
- - gem_spec: "#{build}/gem_spec"
10
- - gem_erb: |
6
+ name: <%= project_name %>
7
+ version: 0.0.1
8
+ build: build
9
+ gem_spec: "#{build}/gem_spec"
10
+ gem_erb: |
11
11
  require 'rubygems'
12
12
  SPEC = Gem::Specification.new do |spec|
13
13
  spec.name = '<%= name %>'
@@ -3,8 +3,8 @@
3
3
  description: "Generate a sample Ruby script project"
4
4
 
5
5
  - properties:
6
- - name: script
7
- - description: This script will generate a sample Ruby script project.
6
+ name: script
7
+ description: This script will generate a sample Ruby script project.
8
8
 
9
9
  - target: welcome
10
10
  description: "Print information message"
@@ -5,11 +5,11 @@
5
5
 
6
6
  # Build properties
7
7
  - properties:
8
- - name: "<%= name %>"
9
- - version: "0.0.0"
10
- - build: "build"
11
- - clean_dirs: [:build]
12
- - clean_files: ["**/*~", "**/.#*", "**/.DS_Store"]
8
+ name: "<%= name %>"
9
+ version: "0.0.0"
10
+ build: "build"
11
+ clean_dirs: [:build]
12
+ clean_files: ["**/*~", "**/.#*", "**/.DS_Store"]
13
13
 
14
14
  # Build targets
15
15
  - target: zip
@@ -3,8 +3,8 @@
3
3
  description: "Generate a Sinatra project"
4
4
 
5
5
  - properties:
6
- - name: sinatra
7
- - description: |
6
+ name: sinatra
7
+ description: |
8
8
  This script will create a Sinatra project. Generated build file has a
9
9
  target 'run' to run the server, 'zip' to generate a distribution archive
10
10
  and 'clean' to delete generated files.
@@ -3,14 +3,14 @@
3
3
  default: all
4
4
 
5
5
  - properties:
6
- - name: "<%= name %>"
7
- - version: "0.0.0"
8
- - build: "build"
9
- - zip_prefix: "#{name}-#{version}"
10
- - zip_file: "#{build}/#{zip_prefix}.zip"
11
- - url: "http://localhost:4567/hello"
12
- - clean_dirs: [:build]
13
- - clean_files: ["**/*~", "**/.#*", "**/.DS_Store"]
6
+ name: "<%= name %>"
7
+ version: "0.0.0"
8
+ build: "build"
9
+ zip_prefix: "#{name}-#{version}"
10
+ zip_file: "#{build}/#{zip_prefix}.zip"
11
+ url: "http://localhost:4567/hello"
12
+ clean_dirs: [:build]
13
+ clean_files: ["**/*~", "**/.#*", "**/.DS_Store"]
14
14
 
15
15
  - target: run
16
16
  description: Run server
@@ -3,8 +3,8 @@
3
3
  description: Generate a sample Ruby source file
4
4
 
5
5
  - properties:
6
- - name: source.rb
7
- - description: This script will generate a sample Ruby source file.
6
+ name: source.rb
7
+ description: This script will generate a sample Ruby source file.
8
8
 
9
9
  - target: welcome
10
10
  description: Print information message
@@ -3,8 +3,8 @@
3
3
  description: "Generate a sample XML-RPC project"
4
4
 
5
5
  - properties:
6
- - project_name: xmlrpc
7
- - description: |
6
+ project_name: xmlrpc
7
+ description: |
8
8
  This script will create a sample XML-RPC project with a Ruby server and
9
9
  client.
10
10
 
@@ -5,12 +5,12 @@
5
5
 
6
6
  # Build properties
7
7
  - properties:
8
- - name: <%= project_name %>
9
- - version: "0.0.0"
10
- - build: "build"
11
- - zip: "#{build}/#{name}-#{version}.zip"
12
- - clean_dirs: [:build]
13
- - clean_files: ["**/*~", "**/.#*", "**/.DS_Store"]
8
+ name: <%= project_name %>
9
+ version: "0.0.0"
10
+ build: "build"
11
+ zip: "#{build}/#{name}-#{version}.zip"
12
+ clean_dirs: [:build]
13
+ clean_files: ["**/*~", "**/.#*", "**/.DS_Store"]
14
14
 
15
15
  # Build targets
16
16
  - target: server
data/lib/bee.rb CHANGED
@@ -15,7 +15,10 @@
15
15
  require 'rubygems'
16
16
  require 'yaml'
17
17
  require 'bee_task'
18
+ require 'bee_target'
18
19
  require 'bee_util'
20
+ require 'bee_context'
21
+ require 'bee_properties'
19
22
 
20
23
  # Module for Bee core classes.
21
24
  module Bee
@@ -36,7 +39,6 @@ module Bee
36
39
  'description' => :optional,
37
40
  'context' => :optional,
38
41
  'extends' => :optional,
39
- 'includes' => :optional,
40
42
  'abstract' => :optional
41
43
  }
42
44
 
@@ -50,8 +52,6 @@ module Bee
50
52
  attr_reader :name
51
53
  # Parent build.
52
54
  attr_reader :extends
53
- # Included builds
54
- attr_reader :includes
55
55
  # Abstractness.
56
56
  attr_reader :abstract
57
57
  # Default target, specified with default entry or fist target in build file.
@@ -108,8 +108,8 @@ module Bee
108
108
  @file = file
109
109
  @base = get_base(@file)
110
110
  @here = here
111
- @context = Context.new
112
111
  @properties = Properties.new(self)
112
+ @context = Context.new(@properties)
113
113
  @properties.evaluate(@context)
114
114
  @targets = Targets.new(self)
115
115
  parse(object)
@@ -172,16 +172,11 @@ module Bee
172
172
  error "Unknown entry:\n#{YAML::dump(entry)}"
173
173
  end
174
174
  end
175
- # manage parent build
176
- if @parent
177
- @properties.extend(@parent.properties)
178
- @targets.extend(@parent.targets)
179
- end
180
- # manage included builds
181
- if @included
182
- for include in @included.reverse
183
- @properties.include(include.properties)
184
- @targets.include(include.targets)
175
+ # manage extended builds
176
+ if @extends
177
+ for parent in @extends
178
+ @properties.extend(parent.properties)
179
+ @targets.extend(parent.targets)
185
180
  end
186
181
  end
187
182
  end
@@ -199,33 +194,46 @@ module Bee
199
194
  @default = entry['default']
200
195
  @targets.default = @default
201
196
  @description = entry['description']
202
- @extends = entry['extends']
203
- @includes = Array(entry['includes'])
204
197
  @abstract = entry['abstract']
205
- # load parent build if any
206
- if @extends
207
- absolute_path = Bee::Util::absolute_path(@extends, @base)
208
- begin
209
- @parent = Bee::Build::load(absolute_path)
210
- rescue Exception
211
- error "Error loading parent build file '#{@extends}': #{$!}"
212
- end
213
- @default = @default || @parent.default
214
- @targets.default = @default
215
- @description = @description || @parent.description
216
- end
217
- # load included build files if any
218
- if @includes
219
- @included = []
220
- for include in @includes
221
- absolute_path = Bee::Util::absolute_path(include, @base)
198
+ # load parents build if any
199
+ parents = Array(entry['extends'])
200
+ if parents.length > 0
201
+ @extends = []
202
+ for extended in parents
203
+ absolute_path = Bee::Util::absolute_path(extended, @base)
222
204
  begin
223
- @included << Bee::Build::load(absolute_path)
205
+ @extends << Bee::Build::load(absolute_path)
224
206
  rescue Exception
225
- error "Error loading included build file '#{include}': #{$!}"
207
+ error "Error loading parent build file '#{extended}': #{$!}"
226
208
  end
227
209
  end
228
210
  end
211
+ # check that there are no property and target collisions in parents
212
+ if @extends
213
+ properties = []
214
+ collisions = []
215
+ for parent in @extends
216
+ parent_properties = parent.properties.expressions.keys
217
+ collisions += properties & parent_properties
218
+ properties += parent_properties
219
+ end
220
+ collisions = collisions - Properties::SYSTEM_PROPERTIES
221
+ collisions = collisions.uniq.map { |e| e.to_s }.sort
222
+ if collisions.length > 0
223
+ error "Properties in parents are colliding: #{collisions.join(', ')}"
224
+ end
225
+ targets = []
226
+ collisions = []
227
+ for parent in @extends
228
+ parent_targets = parent.targets.hash.keys
229
+ collisions += targets & parent_targets
230
+ targets += parent_targets
231
+ end
232
+ collisions = collisions.uniq.map { |e| e.to_s }.sort
233
+ if collisions.length > 0
234
+ error "Targets in parents are colliding: #{collisions.join(', ')}"
235
+ end
236
+ end
229
237
  # load context files if any
230
238
  context = entry['context']
231
239
  if context
@@ -258,146 +266,6 @@ module Bee
258
266
 
259
267
  end
260
268
 
261
- # Class to manage properties.
262
- class Properties
263
-
264
- include Bee::Util::BuildErrorMixin
265
-
266
- # Key for properties entry.
267
- KEY = 'properties'
268
-
269
- # List of system properties
270
- SYSTEM_PROPERTIES = [:base, :here]
271
-
272
- # List of properties in order of evaluation.
273
- attr_reader :list
274
- # Hash of properties by name.
275
- attr_reader :hash
276
-
277
- # Constructor.
278
- # - build: build object.
279
- def initialize(build)
280
- @list = []
281
- @hash = {}
282
- @build = build
283
- defaults
284
- end
285
-
286
- # Write properties. Will raise an error on duplicate definitions.
287
- # - object: object tree resulting from YAML build file parsing or string
288
- # for a properties file to load.
289
- def write(object)
290
- properties = object[Properties::KEY]
291
- if properties.kind_of?(String)
292
- begin
293
- source = Bee::Util::get_file(properties, @build.base)
294
- list = YAML::load(source)
295
- set_properties(list, false)
296
- rescue Exception
297
- error "Error loading properties file '#{properties}': #{$!}"
298
- end
299
- else
300
- set_properties(properties, false)
301
- end
302
- end
303
-
304
- # Overwrite properties. Will raise an error if properties don't already
305
- # exist.
306
- # - object: object tree resulting from YAML build file parsing.
307
- def overwrite(object)
308
- set_properties(object, true)
309
- end
310
-
311
- # Extend with properties of parent build.
312
- # - properties: properties of parent build.
313
- def extend(properties)
314
- current_list = @list
315
- current_hash = @hash
316
- @list = properties.list
317
- @hash = properties.hash
318
- index = 0
319
- 0.upto(current_list.length - 1) do |current|
320
- name = current_list[current]
321
- pos = @list.index(name)
322
- if pos
323
- sub = current_list[index .. current]
324
- @list[pos .. pos] = sub
325
- for name in sub
326
- @hash[name] = current_hash[name]
327
- end
328
- index = current + 1
329
- end
330
- end
331
- tail = current_list[index .. current_list.length() - 1]
332
- @list += tail
333
- for name in tail
334
- @hash[name] = current_hash[name]
335
- end
336
- end
337
-
338
- # Include properties of included build.
339
- # - properties: properties of parent build.
340
- def include(properties)
341
- extend(properties)
342
- end
343
-
344
- # Evaluate properties in context.
345
- # - context: build context where properties must be evaluated.
346
- def evaluate(context)
347
- for name in @list
348
- begin
349
- value = context.evaluate_object(@hash[name])
350
- rescue Bee::Util::BuildError
351
- error "Error evaluating property '#{name}': #{$!}"
352
- end
353
- context.set_property(name, value, true)
354
- end
355
- end
356
-
357
- private
358
-
359
- # Set default properties.
360
- def defaults
361
- set(:base, @build.base, false)
362
- set(:here, @build.here, false)
363
- end
364
-
365
- # Set properties.
366
- # - properties: properties as a list of hash if overwrite = false
367
- # or a hash if overwrite = true.
368
- # - overwrite: tells if we can overwrite existing properties.
369
- def set_properties(properties, overwrite)
370
- if overwrite
371
- raise "Properties must be a hash" if not properties.kind_of?(Hash)
372
- for name in properties.keys
373
- expression = properties[name]
374
- set(name, expression, overwrite)
375
- end
376
- else
377
- raise "Properties must be a list" if not properties.kind_of?(Array)
378
- for property in properties
379
- name = property.keys[0]
380
- expression = property[name]
381
- set(name, expression, overwrite)
382
- end
383
- end
384
- end
385
-
386
- # Set a property.
387
- # - name: property name (as a string or symbol).
388
- # - expression: property expression (as a string) to be evaluated in
389
- # context to get value.
390
- # - overwrite: tells if we can overwrite existing properties.
391
- def set(name, expression, overwrite)
392
- name = name.to_sym
393
- error "Duplicate property definition: '#{name}'" if
394
- not overwrite and @hash.has_key?(name)
395
- @list << name
396
- @hash[name] = expression
397
- end
398
-
399
- end
400
-
401
269
  # Class to manage targets in a build.
402
270
  class Targets
403
271
 
@@ -470,25 +338,6 @@ module Bee
470
338
  @default = @default || parent.default
471
339
  end
472
340
 
473
- # Include included build file targets.
474
- # - included: included build file targets.
475
- def include(included)
476
- # set appropriate targets for targets of parent
477
- for targets in included.hash.values
478
- for target in targets
479
- target.targets = self
480
- end
481
- end
482
- # look for targets already defined
483
- duplicates = @hash.keys & included.hash.keys
484
- error "Included target(s) '#{duplicates.join('\', \'')}' already defined" if
485
- duplicates.length > 0
486
- # insert included targets
487
- for name in included.hash.keys
488
- @hash[name] = included.hash[name]
489
- end
490
- end
491
-
492
341
  # Run targets.
493
342
  # - targets: list of target names to run.
494
343
  def run(targets, dry)
@@ -543,380 +392,6 @@ module Bee
543
392
  return description
544
393
  end
545
394
 
546
- end
547
-
548
- # Class for a target. It is built from the YAML build file and manages a
549
- # target, in particular, tasks execution.
550
- class Target
551
-
552
- include Bee::Util::BuildErrorMixin
553
- include Bee::Util::HashCheckerMixin
554
-
555
- # Target key.
556
- KEY = 'target'
557
- # Target entry description.
558
- DESCRIPTION = {
559
- 'target' => :mandatory,
560
- 'depends' => :optional,
561
- 'description' => :optional,
562
- 'script' => :optional}
563
-
564
- # Targets for build.
565
- attr_accessor :targets
566
- # Name of the target.
567
- attr_reader :name
568
- # Target dependencies.
569
- attr_accessor :depends
570
- # Target description.
571
- attr_accessor :description
572
- # Script that run in the target.
573
- attr_reader :script
574
-
575
- # Constructor.
576
- # - object: object for target, resulting from YAML parsing.
577
- # - targets: build targets.
578
- def initialize(object, targets)
579
- check_hash(object, Target::DESCRIPTION)
580
- @targets = targets
581
- @name = object[Target::KEY]
582
- error "Target name cannot be 'null'" if @name == nil
583
- @depends = object['depends']||[]
584
- @depends = Array(@depends)
585
- @description = object['description']
586
- @script = object['script']
587
- @script = [@script] if @script.kind_of?(String)
588
- @script = [] if not @script
589
- end
590
-
591
- # Run target.
592
- # - dry: tells if we run in dry mode. Defaults to false.
593
- def run(dry=false)
594
- current_dir = Dir.pwd
595
- begin
596
- for depend in @depends
597
- @targets.run_target(depend, dry)
598
- end
599
- @targets.build.listener.target(self) if
600
- @targets.build.listener and @targets.is_last(self)
601
- run_block(@script, dry)
602
- ensure
603
- Dir.chdir(current_dir)
604
- end
605
- end
606
-
607
- private
608
-
609
- # Run a task.
610
- # - task: the task to run.
611
- # - dry: whether to just print.
612
- def run_task(task, dry=false)
613
- @targets.build.listener.task(task) if @targets.build.listener
614
- case task
615
- when String
616
- # shell script
617
- run_shell(task, dry)
618
- when Hash
619
- if task.keys.length > 1
620
- # construct
621
- run_construct(task, dry)
622
- else
623
- if task.key?('rb')
624
- # ruby script
625
- script = task['rb']
626
- run_ruby(script, dry)
627
- elsif task.key?('sh')
628
- # shell script
629
- script = task['sh']
630
- run_shell(script, dry)
631
- elsif task.key?('super')
632
- # call super target
633
- targets.call_super(self, dry)
634
- else
635
- # must be a task
636
- run_bee_task(task, dry)
637
- end
638
- end
639
- end
640
- end
641
-
642
- # Run a given shell script.
643
- # - script: the scrip to run.
644
- def run_shell(script, dry=false)
645
- @listener.task(script) if @listener
646
- return if dry
647
- evaluated_script = @targets.build.context.evaluate_object(script)
648
- if evaluated_script != ''
649
- system(evaluated_script)
650
- error "Script exited with value #{$?}" if $? != 0
651
- end
652
- end
653
-
654
- # Run a given shell script.
655
- # - script: the scrip to run.
656
- # - dry: tells if we run in dry mode.
657
- def run_ruby(script, dry=false)
658
- @listener.task(script) if @listener
659
- return if dry
660
- begin
661
- @targets.build.context.evaluate_script(script)
662
- rescue Exception
663
- error "Error running Ruby script: #{$!}"
664
- end
665
- end
666
-
667
- # Run a given bee task.
668
- # - task: task to run as a Hash.
669
- def run_bee_task(task, dry=false)
670
- @listener.task(task) if @listener
671
- return if dry
672
- @targets.build.package_manager.run_task(task)
673
- end
674
-
675
- # Run a given construct.
676
- # - construct: construct to run as a Hash.
677
- def run_construct(construct, dry=false)
678
- @listener.task(construct) if @listener
679
- return if dry
680
- # if construct
681
- if construct.keys.include?('if')
682
- construct_if(construct, dry)
683
- # while construct
684
- elsif construct.keys.include?('while')
685
- construct_while(construct, dry)
686
- # for construct
687
- elsif construct.keys.include?('for')
688
- construct_for(construct, dry)
689
- # try construct
690
- elsif construct.keys.include?('try')
691
- construct_try(construct, dry)
692
- else
693
- error "Unknown construct '#{construct.keys.join('-')}'"
694
- end
695
- end
696
-
697
- # Run if construct.
698
- # - task: the construct as a hash.
699
- # - dry: tells if we run in dry mode.
700
- def construct_if(task, dry)
701
- # test entries
702
- error "If-then-else construct must include 'then' entry" if
703
- not task.keys.include?('then')
704
- unknown_keys = task.keys - ['if', 'then', 'else']
705
- error "If-then-else construct may only include 'if', 'then' and 'else' entries" if
706
- unknown_keys.length > 0
707
- error "If entry in if-then-else construct must be a string" if
708
- not task['if'].kind_of?(String)
709
- error "Then entry in if-then-else construct must be a list" if
710
- not task['then'].kind_of?(Array)
711
- error "Else entry in if-then-else construct must be a list" if
712
- task['else'] and not task['else'].kind_of?(Array)
713
- # evaluate condition in the build context
714
- if evaluate(task['if'])
715
- run_block(task['then'], dry)
716
- else
717
- run_block(task['else'], dry) if task['else']
718
- end
719
- end
720
-
721
- # Run while construct.
722
- # - task: the construct as a hash.
723
- # - dry: tells if we run in dry mode.
724
- def construct_while(task, dry)
725
- # test entries
726
- error "While-do construct must include 'do' entry" if
727
- not task.keys.include?('do')
728
- unknown_keys = task.keys - ['while', 'do']
729
- error "While-do construct may only include 'while' and 'do' entries" if
730
- unknown_keys.length > 0
731
- error "While entry in while-do construct must be a string" if
732
- not task['while'].kind_of?(String)
733
- error "Do entry in while-do construct must be a list" if
734
- not task['do'].kind_of?(Array)
735
- # evaluate condition in the build context
736
- while evaluate(task['while'])
737
- run_block(task['do'], dry)
738
- end
739
- end
740
-
741
- # Run for construct.
742
- # - task: the construct as a hash.
743
- # - dry: tells if we run in dry mode.
744
- def construct_for(task, dry)
745
- # test entries
746
- error "For-in-do construct must include 'in' and 'do' entries" if
747
- not task.keys.include?('in') or not task.keys.include?('do')
748
- unknown_keys = task.keys - ['for', 'in', 'do']
749
- error "For-in-do construct may only include 'for', 'in' and 'do' entries" if
750
- unknown_keys.length > 0
751
- error "For entry in for-in-do construct must be a string" if
752
- not task['for'].kind_of?(String)
753
- error "In entry in for-in-do construct must be a list or a string" if
754
- not task['in'].kind_of?(Enumerable) and not task['in'].kind_of?(String)
755
- error "Do entry in for-in-do construct must be a list" if
756
- not task['do'].kind_of?(Array)
757
- # iterate over list
758
- if task['in'].kind_of?(String)
759
- enumerable = evaluate(@targets.build.context.
760
- evaluate_object(task['in']))
761
- else
762
- enumerable = task['in']
763
- end
764
- for element in enumerable
765
- begin
766
- @targets.build.context.set_property(task['for'], element)
767
- rescue
768
- error "Error setting property '#{task['for']}'"
769
- end
770
- run_block(task['do'], dry)
771
- end
772
- end
773
-
774
- # Run try-catch construct.
775
- # - task: the construct as a hash.
776
- # - dry: tells if we run in dry mode.
777
- def construct_try(task, dry)
778
- # test entries
779
- error "Try-catch construct must include 'catch' entry" if
780
- not task.keys.include?('catch')
781
- unknown_keys = task.keys - ['try', 'catch']
782
- error "Try-catch construct may only include 'try' and 'catch' entries" if
783
- unknown_keys.length > 0
784
- error "Try entry in try-catch construct must be a list" if
785
- not task['try'].kind_of?(Array)
786
- error "Catch entry in try-catch construct must be a list" if
787
- not task['catch'].kind_of?(Array)
788
- # try and catch errors
789
- begin
790
- run_block(task['try'], dry)
791
- rescue
792
- @targets.build.listener.recover if @targets.build.listener
793
- run_block(task['catch'], dry)
794
- end
795
- end
796
-
797
- # Run a block, that is a list of tasks.
798
- # - block: the block to run as a list of tasks.
799
- # - dry: tells if we run in dry mode.
800
- def run_block(block, dry)
801
- for task in block
802
- run_task(task, dry)
803
- end
804
- end
805
-
806
- # Evaluate a given expression and raise a BuildError if an error happens.
807
- def evaluate(expression)
808
- begin
809
- return @targets.build.context.evaluate_script(expression)
810
- rescue
811
- error "Error evaluating expression: #{$!}"
812
- end
813
- end
814
-
815
- end
816
-
817
- # Class for Ruby scripts context. All embedded Ruby scripts run in this
818
- # context where build properties are defined as Ruby variables.
819
- class Context
820
-
821
- include Bee::Util::BuildErrorMixin
822
-
823
- # The binding of this context.
824
- attr_reader :context_binding
825
-
826
- # Constructor.
827
- def initialize
828
- @context_binding = get_binding
829
- end
830
-
831
- # Set a given property in context.
832
- # - name: the property name.
833
- # - value: the property value.
834
- # - override: tells if we can overwrite an existing value.
835
- def set_property(name, value, overwrite=true)
836
- error "Property '#{name}' was already defined" if
837
- !overwrite and properties.include?(name.to_s)
838
- begin
839
- eval("#{name} = #{value.inspect}", @context_binding)
840
- rescue Exception
841
- error "Error setting property '#{name} = #{value.inspect}': #{$!}"
842
- end
843
- end
844
-
845
- # Get a given property in context.
846
- # - name: the property name.
847
- # - strict: raise an error if given property was not set.
848
- def get_property(name, strict=false)
849
- error "Property '#{name}' was not set" if
850
- strict and !properties.include?(name.to_s)
851
- begin
852
- eval("#{name}", @context_binding)
853
- rescue NameError
854
- error "Property '#{name}' was not set"
855
- rescue Exception
856
- error "Error getting property '#{name}': #{$!}"
857
- end
858
- end
859
-
860
- # Return list of properties (as local variables of binding).
861
- def properties
862
- return eval('local_variables', @context_binding).map{ |var| var.to_s }
863
- end
864
-
865
- # Evaluate a script in context.
866
- # - script: source of the script to evaluate.
867
- def evaluate_script(script)
868
- eval(script, @context_binding)
869
- end
870
-
871
- # Process a given object, replacing properties references with their
872
- # string value, symbol with their raw value. Property references have
873
- # same form than variable references in ruby strings: '#{variable}'
874
- # will be replaced with variable string value.
875
- # - object: object to process.
876
- def evaluate_object(object)
877
- case object
878
- when NilClass
879
- # nil: return nil
880
- return nil
881
- when String
882
- # string: replace embedded Ruby expressions
883
- object = object.gsub(/#\{.+?\}/) do |match|
884
- expression = match[2..-2]
885
- begin
886
- value = evaluate_script(expression)
887
- rescue
888
- error "Error evaluating expression '#{expression}': #{$!}"
889
- end
890
- value
891
- end
892
- return object
893
- when Symbol
894
- # symbol: return property object
895
- property = object.to_s
896
- value = get_property(property, true)
897
- return evaluate_object(value)
898
- when Array
899
- # array: evaluate each element
900
- return object.collect { |element| evaluate_object(element) }
901
- when Hash
902
- # hash: evaluate all keys and values
903
- evaluated = {}
904
- object.each_pair do |key, value|
905
- evaluated[evaluate_object(key)] = evaluate_object(value)
906
- end
907
- return evaluated
908
- else
909
- return object
910
- end
911
- end
912
-
913
- private
914
-
915
- # Get a binding as script context.
916
- def get_binding
917
- return binding
918
- end
919
-
920
- end
395
+ end
921
396
 
922
397
  end