bee 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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