bee 0.9.0 → 0.10.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.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