bee 0.3.1 → 0.4.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.
@@ -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,23 +12,28 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require 'bee'
16
+ require 'bee_task'
17
+ require 'bee_util'
18
+ require 'getoptlong'
19
+
15
20
  # Module for Bee stuff.
16
21
  module Bee
17
22
 
18
23
  module Console
19
24
 
20
- require 'bee'
21
- require 'getoptlong'
22
-
23
25
  # Command line help.
24
26
  HELP = 'Usage: bee [options] [targets]
25
27
  -h Print help about usage and exit.
26
28
  -b Print help about build and exit.
27
- -k [task] Print help about all tasks or given one.
28
- -t Write template build file on disk.
29
+ -k task Print help about tasks in a package (writing "foo.?") or a
30
+ given one (writing "foo.bar") and exit.
31
+ -t Write template build file on disk (use -f to write build file
32
+ in another one than default "build.yml").
29
33
  -v Enable verbose mode.
30
34
  -s style Define style for output (see documentation).
31
35
  -f file Build file to run (defaults to "build.yml").
36
+ -r Look for build file recursively up in file system.
32
37
  targets Targets to run (default target if omitted).'
33
38
  # Name for default build file.
34
39
  DEFAULT_BUILD_FILE = 'build.yml'
@@ -38,6 +43,10 @@ targets Targets to run (default target if omitted).'
38
43
  EXIT_BUILD_ERROR = 2
39
44
  # Exit value on unknown error
40
45
  EXIT_UNKNOWN_ERROR = 3
46
+ # Bee options environment variable.
47
+ BEE_OPT_ENV = 'BEEOPT'
48
+ # README file where copyright is extracted.
49
+ README_FILE = 'README'
41
50
 
42
51
  # Parse command line and return parsed arguments.
43
52
  def self.parse_command_line
@@ -49,19 +58,21 @@ targets Targets to run (default target if omitted).'
49
58
  verbose = false
50
59
  style = nil
51
60
  file = DEFAULT_BUILD_FILE
61
+ recursive = false
52
62
  targets = []
53
63
  # read options in BEEOPT environment variable
54
- options = ENV['BEEOPT']
64
+ options = ENV[BEE_OPT_ENV]
55
65
  options.split(' ').reverse.each { |option| ARGV.unshift(option) } if
56
66
  options
57
67
  # parse command line arguments
58
68
  opts = GetoptLong.new(['--help', '-h', GetoptLong::NO_ARGUMENT],
59
69
  ['--help-build', '-b', GetoptLong::NO_ARGUMENT],
60
- ['--help-task','-k', GetoptLong::OPTIONAL_ARGUMENT],
70
+ ['--help-task','-k', GetoptLong::REQUIRED_ARGUMENT],
61
71
  ['--template', '-t', GetoptLong::NO_ARGUMENT ],
62
72
  ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
63
73
  ['--style', '-s', GetoptLong::REQUIRED_ARGUMENT],
64
- ['--file', '-f', GetoptLong::REQUIRED_ARGUMENT])
74
+ ['--file', '-f', GetoptLong::REQUIRED_ARGUMENT],
75
+ ['--recursive', '-r', GetoptLong::NO_ARGUMENT])
65
76
  opts.each do |opt, arg|
66
77
  case opt
67
78
  when '--help'
@@ -79,11 +90,13 @@ targets Targets to run (default target if omitted).'
79
90
  style = arg
80
91
  when '--file'
81
92
  file = arg
93
+ when '--recursive'
94
+ recursive = true
82
95
  end
83
96
  end
84
97
  targets = ARGV
85
98
  return [help, help_build, help_task, task, template, verbose, style,
86
- file, targets]
99
+ file, recursive, targets]
87
100
  end
88
101
 
89
102
  # Start build from command line.
@@ -91,7 +104,7 @@ targets Targets to run (default target if omitted).'
91
104
  STDOUT.sync = true
92
105
  begin
93
106
  help, help_build, help_task, task, template, verbose, style, file,
94
- targets = parse_command_line
107
+ recursive, targets = parse_command_line
95
108
  rescue
96
109
  puts "ERROR: parsing command line (type 'bee -h' for help)"
97
110
  exit(EXIT_PARSING_CMDLINE)
@@ -99,34 +112,33 @@ targets Targets to run (default target if omitted).'
99
112
  formatter = Formatter.new(style)
100
113
  begin
101
114
  if help
102
- readme = File.join(File.dirname(__FILE__), '..', 'README')
103
- if File.exists?(readme)
115
+ readme = File.join(File.dirname(__FILE__), '..', README_FILE)
116
+ if File.exists?(readme) and File.file?(readme) and
117
+ File.readable?(readme)
104
118
  copyright = File.read(readme).strip!.split("\n")[-1]
105
119
  puts copyright
106
120
  end
107
121
  puts HELP
108
122
  elsif help_build
109
- build = Build.new(file)
123
+ build = Build.load(file, recursive)
110
124
  puts formatter.help_build(build)
111
125
  elsif help_task
112
- build = Build.new(file)
113
- puts formatter.help_task(build, task)
126
+ puts formatter.help_task(task)
114
127
  elsif template
115
128
  puts "Writing build template in file '#{file}'..."
116
- raise BuildError.new("Build file '#{file}' already exists") if
129
+ raise Bee::Util::BuildError.new("Build file '#{file}' already exists") if
117
130
  File.exists?(file)
118
131
  File.open(file, 'w') { |file| file.write(BUILD_TEMPLATE) }
119
132
  puts formatter.format_success("OK")
120
133
  else
121
- listener = ConsoleListener.new(formatter, verbose)
122
- build = Build.new(file)
134
+ listener = Listener.new(formatter, verbose)
135
+ build = Build.load(file, recursive)
123
136
  build.run(targets, listener)
124
137
  end
125
- rescue BuildError
138
+ rescue Bee::Util::BuildError => e
126
139
  puts "#{formatter.format_error('ERROR')}: #{$!}"
140
+ puts e.backtrace.join("\n") if verbose
127
141
  exit(EXIT_BUILD_ERROR)
128
- rescue SystemExit
129
- # do nothing, exit in code
130
142
  rescue Exception => e
131
143
  puts "#{formatter.format_error('ERROR')}: #{$!}"
132
144
  puts e.backtrace.join("\n")
@@ -134,16 +146,10 @@ targets Targets to run (default target if omitted).'
134
146
  end
135
147
  end
136
148
 
137
- #########################################################################
138
- # FORMATTER CLASS #
139
- #########################################################################
140
-
141
149
  # Class to format build output on console.
142
150
  class Formatter
143
151
 
144
- include BuildErrorMixin
145
-
146
- ###################### ANSI COLORS AND STYLES #########################
152
+ include Bee::Util::BuildErrorMixin
147
153
 
148
154
  # List of colors.
149
155
  COLORS = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white]
@@ -182,8 +188,6 @@ targets Targets to run (default target if omitted).'
182
188
  :hidden => 8
183
189
  }
184
190
 
185
- ############################ DEFAULT STYLE ############################
186
-
187
191
  # Default style (supposed to work on any configuration).
188
192
  DEFAULT_STYLE = {
189
193
  :line_character => '-'
@@ -205,11 +209,9 @@ targets Targets to run (default target if omitted).'
205
209
  'sb' => 'success_background',
206
210
  'es' => 'error_style',
207
211
  'ef' => 'error_foreground',
208
- 'eb' => 'error_background'
212
+ 'eb' => 'error_background'
209
213
  }
210
214
 
211
- ############################## METHODS ################################
212
-
213
215
  # Constructor.
214
216
  # - style: style as a Hash or a String.
215
217
  def initialize(style)
@@ -227,17 +229,7 @@ targets Targets to run (default target if omitted).'
227
229
  # - target: target to format.
228
230
  def format_target(target)
229
231
  name = target.name
230
- # generate title line
231
- length = @style[:line_length] || line_length || DEFAULT_LINE_LENGTH
232
- right = ' ' + @style[:line_character]*2
233
- left = @style[:line_character]*(length - (name.length + 4)) + ' '
234
- line = left + name + right
235
- # apply style
236
- formatted = style(line,
237
- @style[:target_style],
238
- @style[:target_foreground],
239
- @style[:target_background])
240
- return formatted
232
+ return format_title(name)
241
233
  end
242
234
 
243
235
  # Format a task.
@@ -247,7 +239,7 @@ targets Targets to run (default target if omitted).'
247
239
  source = task
248
240
  elsif task.kind_of?(Hash)
249
241
  if task.key?('rb')
250
- source = task['rb']
242
+ source = "rb: #{task['rb']}"
251
243
  else
252
244
  source = YAML::dump(task)
253
245
  source = source.sub(/---/, '')
@@ -285,43 +277,45 @@ targets Targets to run (default target if omitted).'
285
277
  # - build: running build.
286
278
  def help_build(build)
287
279
  help = ''
280
+ # print build name and description
288
281
  if build.name
289
282
  help << "- Build: #{build.name.inspect}\n"
290
283
  end
291
284
  if build.description
292
285
  help << format_description('Description', build.description, 2, false)
293
286
  end
294
- if build.properties.keys.length > 0
287
+ # print build properties
288
+ if build.context.properties.length > 0
295
289
  help << "- Properties:\n"
296
- for property in build.properties.keys.sort
297
- help << " - #{property}: #{build.properties[property].inspect}\n"
290
+ for property in build.context.properties.sort
291
+ help << " - #{property}: " +
292
+ "#{build.context.get_property(property, true).inspect}\n"
298
293
  end
299
294
  end
295
+ # print build targets
300
296
  if build.targets.length > 0
301
297
  help << "- Targets:\n"
302
298
  for target in build.targets.values.sort { |a, b| a.name <=> b.name }
303
299
  help << format_description(target.name, target.description, 2)
304
300
  end
305
301
  end
302
+ # print default target
306
303
  help << "- Default: #{build.default}"
307
304
  return help.strip
308
305
  end
309
306
 
310
307
  # Return help about task(s).
311
- # - build: running build.
312
308
  # - task: task to print help about (all tasks if nil).
313
- def help_task(build, task)
314
- task = nil if task.length == 0
309
+ def help_task(task)
310
+ task = '*' if task == nil or task.length == 0
311
+ package_manager = Bee::Task::PackageManager.new(nil)
312
+ methods = package_manager.help_task(task)
315
313
  help = ''
316
- if task
317
- error "Task '#{task}' not loaded" if not build.tasks[task]
318
- help << build.tasks[task]
319
- else
320
- help << "Loaded tasks: "
321
- for task in build.tasks.keys.sort
322
- help << "#{task}, "
323
- end
324
- help = help[0..-3]
314
+ for method in methods.keys
315
+ help << format_title(method)
316
+ help << "\n"
317
+ help << methods[method].strip
318
+ help << "\n"
325
319
  end
326
320
  return help
327
321
  end
@@ -353,20 +347,6 @@ targets Targets to run (default target if omitted).'
353
347
  return colorized
354
348
  end
355
349
 
356
- # Get line length calling IOCTL. Return nil if call failed.
357
- def line_length
358
- begin
359
- tiocgwinsz = 0x5413
360
- string = [0, 0, 0, 0].pack('SSSS')
361
- if $stdin.ioctl(tiocgwinsz, string) >= 0 then
362
- rows, cols, xpixels, ypixels = string.unpack('SSSS')
363
- return cols
364
- end
365
- rescue
366
- return nil
367
- end
368
- end
369
-
370
350
  # Parse style from command line. If error occurs parsing style, return
371
351
  # nil (which means default style).
372
352
  # - string: style to parse.
@@ -405,7 +385,7 @@ targets Targets to run (default target if omitted).'
405
385
  string = ' '*indent
406
386
  string << '- ' if bullet
407
387
  string << title
408
- if text
388
+ if text and !text.empty?
409
389
  string << ": "
410
390
  if text.split("\n").length > 1
411
391
  string << "\n"
@@ -415,19 +395,34 @@ targets Targets to run (default target if omitted).'
415
395
  else
416
396
  string << text.strip + "\n"
417
397
  end
398
+ else
399
+ string << "\n"
418
400
  end
419
401
  return string
420
402
  end
403
+
404
+ # Format a title.
405
+ # - title: title to format.
406
+ def format_title(title)
407
+ length = @style[:line_length] ||
408
+ Bee::Util::term_width ||
409
+ DEFAULT_LINE_LENGTH
410
+ right = ' ' + @style[:line_character]*2
411
+ left = @style[:line_character]*(length - (title.length + 4)) + ' '
412
+ line = left + title + right
413
+ # apply style
414
+ formatted = style(line,
415
+ @style[:target_style],
416
+ @style[:target_foreground],
417
+ @style[:target_background])
418
+ return formatted
419
+ end
421
420
 
422
421
  end
423
422
 
424
- #########################################################################
425
- # CONSOLE LISTENER #
426
- #########################################################################
427
-
428
423
  # Listener when running in a console. Prints messages on the console using
429
424
  # a given formatter.
430
- class ConsoleListener
425
+ class Listener
431
426
 
432
427
  # Formatter used by listener.
433
428
  attr_reader :formatter
@@ -506,13 +501,22 @@ targets Targets to run (default target if omitted).'
506
501
  message << exception.to_s
507
502
  puts "#{@formatter.format_error('ERROR')}: #{message}"
508
503
  end
504
+
505
+ # Print text on the console.
506
+ # - text: text to print.
507
+ def print(text)
508
+ Kernel.print(text)
509
+ end
510
+
511
+ # Puts text on the console.
512
+ # - text: text to puts.
513
+ def puts(text)
514
+ Kernel.puts(text)
515
+ end
509
516
 
510
517
  end
511
518
 
512
- #########################################################################
513
- # BUILD TEMPLATE #
514
- #########################################################################
515
-
519
+ # Build template.
516
520
  BUILD_TEMPLATE =
517
521
  '# Template build file
518
522
  - build: template
@@ -533,8 +537,7 @@ targets Targets to run (default target if omitted).'
533
537
  depends: capitalize
534
538
  description: Print greatings.
535
539
  script:
536
- - "echo \"Hello #{who}!\""
537
- '
540
+ - print: "Hello #{who}!"'
538
541
 
539
542
  end
540
543
 
@@ -0,0 +1,149 @@
1
+ # Copyright 2006-2007 Michel Casabianca <michel.casabianca@gmail.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'bee'
16
+ require 'bee_util'
17
+
18
+ module Bee
19
+
20
+ # Module for bee tasks.
21
+ module Task
22
+
23
+ # Base class for task package. Provides methods to access build context
24
+ # (to set and get properties and evaluate an object in this context).
25
+ class Package < Bee::Util::MethodInfoBase
26
+
27
+ include Bee::Util::BuildErrorMixin
28
+
29
+ # Constructor.
30
+ # - build: the build we are running.
31
+ def initialize(build)
32
+ @build = build
33
+ end
34
+
35
+ protected
36
+
37
+ # Check a task parameters. Raise a RuntimeError with explanation message
38
+ # if a mandatory parameter is missing or an unknown parameter was found.
39
+ # - params: task parameters as a Hash.
40
+ # - description: parameters description as a Hash associating a parameter
41
+ # name with symbol :mandatory or :optional.
42
+ def check_task_parameters(params, description)
43
+ error "Parameters must be a Hash" unless params.kind_of?(Hash)
44
+ for param in description.keys
45
+ error "Missing mandatory parameter '#{param}'" unless
46
+ params[param] or description[param] == :optional
47
+ end
48
+ for param in params.keys
49
+ error "Unknown parameter '#{param}'" if not description.key?(param)
50
+ end
51
+ end
52
+
53
+ # Print text on the console.
54
+ # - text: text to print.
55
+ def print(text)
56
+ if @build.listener
57
+ @build.listener.print(text)
58
+ else
59
+ Kernel.print(text)
60
+ end
61
+ end
62
+
63
+ # Puts text on the console.
64
+ # - text: text to puts.
65
+ def puts(text)
66
+ if @build.listener
67
+ @build.listener.puts(text)
68
+ else
69
+ Kernel.puts(text)
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ # Package manager is responsible for loading packages and calling tasks.
76
+ class PackageManager
77
+
78
+ include Bee::Util::BuildErrorMixin
79
+
80
+ # Constructor.
81
+ # - build: the build we are running.
82
+ def initialize(build)
83
+ @build = build
84
+ @packages = {}
85
+ end
86
+
87
+ # Run a given task.
88
+ # - task: YAML object for the task to run.
89
+ def run_task(task)
90
+ packaged = task.keys[0]
91
+ package, name = Bee::Util::get_package_name(packaged)
92
+ parameters = @build.context.evaluate_object(task[packaged])
93
+ if not @packages[package]
94
+ load_package(package)
95
+ end
96
+ error "Task '#{name}' not found in package '#{package}'" if
97
+ not @packages[package].respond_to?(name)
98
+ @packages[package].send(name, parameters)
99
+ end
100
+
101
+ # Get help for a given task.
102
+ # - task: YAML object for the task to run.
103
+ def help_task(task)
104
+ package, name = Bee::Util::get_package_name(task)
105
+ if not @packages[package]
106
+ load_package(package)
107
+ end
108
+ help = {}
109
+ if name == '?'
110
+ methods = @packages[package].class.public_instance_methods(false)
111
+ for method in methods
112
+ help[method] = @packages[package].class.method_info(method).comment
113
+ end
114
+ return help
115
+ else
116
+ error "Task '#{name}' not found in package '#{package}'" if
117
+ not @packages[package].respond_to?(name)
118
+ help[task] = @packages[package].class.method_info(name).comment
119
+ end
120
+ return help
121
+ end
122
+
123
+ private
124
+
125
+ # Load a given package using introspection: we try to instantiate class
126
+ # named after the package capitalized, in module Bee::Task.
127
+ # - package: the package name.
128
+ def load_package(package)
129
+ if package
130
+ package.downcase!
131
+ script = "bee_task_#{package.downcase}"
132
+ clazz = package.capitalize
133
+ else
134
+ script = 'bee_task'
135
+ clazz = 'Default'
136
+ end
137
+ begin
138
+ require script
139
+ @packages[package] = Bee::Task.const_get(clazz).new(@build)
140
+ rescue Exception
141
+ error "Task package '#{package}' not found"
142
+ end
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+
149
+ end