bee 0.4.0 → 0.5.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-2007 Michel Casabianca <michel.casabianca@gmail.com>
1
+ # Copyright 2006-2008 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.
@@ -15,6 +15,8 @@
15
15
  require 'bee'
16
16
  require 'bee_task'
17
17
  require 'bee_util'
18
+ require 'erb'
19
+ require 'fileutils'
18
20
 
19
21
  module Bee
20
22
 
@@ -23,18 +25,19 @@ module Bee
23
25
  # Package for default tasks (tasks with no package).
24
26
  class Default < Package
25
27
 
26
- include Bee::Util::FileSelector
27
-
28
- # Print a message on console. If message is a string, it is printed
29
- # on standard output, if message is another Ruby object, this task
30
- # outputs its inspected value instead.
28
+ ######################################################################
29
+ # MISCELLANEOUS TASKS #
30
+ ######################################################################
31
+
32
+ # Print a message on console. If message is not a string, this task
33
+ # outputs the inspected value of the object.
31
34
  #
32
35
  # - message: message to print.
33
36
  #
34
37
  # Example
35
38
  #
36
- # - print: "Hello World!"
37
- def print(message)
39
+ # - echo: "Hello World!"
40
+ def echo(message)
38
41
  case message
39
42
  when String
40
43
  puts message
@@ -42,7 +45,138 @@ module Bee
42
45
  puts message.inspect
43
46
  end
44
47
  end
45
-
48
+
49
+ # Alias for echo.
50
+ alias :print :echo
51
+
52
+ # Wait for a given amount of time.
53
+ #
54
+ # - time: time to wait, in seconds, as an integer or float.
55
+ #
56
+ # Example
57
+ #
58
+ # - sleep: 3.5
59
+ def sleep(time)
60
+ error "sleep parameter must be a float or a integer" if
61
+ not time.kind_of?(Numeric)
62
+ seconds = time.to_f
63
+ puts "Waiting #{time} seconds..."
64
+ Kernel.sleep seconds
65
+ end
66
+
67
+ # Alias for sleep.
68
+ alias :wait :sleep
69
+
70
+ # Prompt the user for the value of a given property matching a pattern.
71
+ #
72
+ # - message: message to print at prompt. Should include a description
73
+ # of the expected pattern.
74
+ # - property: the name of the property to set.
75
+ # - default: default value if user doesn't type anything. Written
76
+ # into square brakets after prompt message. Optional.
77
+ # - pattern: a Ruby pattern for prompted value. If this pattern is not
78
+ # matched, this task will prompt again. Optional, if no pattern is
79
+ # given, any value is accepted.
80
+ # - error: the error message to print when pattern is not matched.
81
+ # - attempts: number of allowed attempts. Optional, defaults to 0, which
82
+ # means an unlimited number of attempts.
83
+ #
84
+ # Example
85
+ #
86
+ # - prompt:
87
+ # message: "Enter your age"
88
+ # property: "age"
89
+ # default: 18
90
+ # pattern: "^\\d+$"
91
+ # error: "Age must be a positive integer"
92
+ def prompt(params)
93
+ params_desc = {
94
+ :message => { :mandatory => true, :type => :string },
95
+ :property => { :mandatory => true, :type => :string },
96
+ :default => { :mandatory => false, :type => :string },
97
+ :pattern => { :mandatory => false, :type => :string },
98
+ :error => { :mandatory => false, :type => :string },
99
+ :attempts => { :mandatory => false, :type => :integer, :default => 0 }
100
+ }
101
+ check_parameters(params, params_desc)
102
+ message = params[:message]
103
+ property = params[:property]
104
+ default = params[:default]
105
+ pattern = params[:pattern]
106
+ error = params[:error]
107
+ attempts = params[:attempts]
108
+ message << " [#{default}]" if default
109
+ message << ':'
110
+ ok = false
111
+ nb_attempts = 1
112
+ while not (ok or (nb_attempts > attempts and attempts != 0))
113
+ puts message
114
+ value = gets.strip
115
+ value = default if default and value.length == 0
116
+ if pattern
117
+ if value =~ /#{pattern}/
118
+ ok = true
119
+ elsif error
120
+ puts error
121
+ end
122
+ else
123
+ ok = true
124
+ end
125
+ nb_attempts += 1
126
+ end
127
+ error "Failed to obtain a matching prompt" if not ok
128
+ @build.context.set_property(property, value)
129
+ end
130
+
131
+ # Get a given URL and store its content in a given file. Parameters
132
+ # is a Hash with following entries:
133
+ #
134
+ # - url: the URL to get.
135
+ # - dest: destination file. Optional, defaults to retrieved file name
136
+ # in current directory. If destination is a directory, file is saved
137
+ # in destination directory with the name of the retrieved file.
138
+ # - limit: the redirections limit. Optional, defaults to 10.
139
+ #
140
+ # Example
141
+ #
142
+ # - get:
143
+ # url: http://rubyforge.org/frs/download.php/22185/bee-0.4.0.zip
144
+ def get(parameters)
145
+ params_desc = {
146
+ :url => { :mandatory => true, :type => :string },
147
+ :dest => { :mandatory => false, :type => :string },
148
+ :limit => { :mandatory => false, :type => :integer, :default => 10 }
149
+ }
150
+ check_parameters(parameters, params_desc)
151
+ url = parameters[:url]
152
+ dest = parameters[:dest]
153
+ if not dest
154
+ destination = File.basename(url)
155
+ elsif File.directory?(dest)
156
+ destination = File.join(dest, File.basename(url))
157
+ else
158
+ destination = dest
159
+ end
160
+ limit = parameters[:limit]
161
+ puts "Getting URL '#{url}'..."
162
+ begin
163
+ content = Util::fetch(url, limit)
164
+ rescue Exception
165
+ error "Error getting URL: #{$!}"
166
+ end
167
+ todir = File.dirname(destination)
168
+ begin
169
+ FileUtils.makedirs(todir) if not File.exists?(todir)
170
+ File.open(destination, 'w') { |file| file.write(content) }
171
+ rescue Exception
172
+ error "Error saving file: #{$!}"
173
+ end
174
+ end
175
+
176
+ ######################################################################
177
+ # FILE RELATED TASKS #
178
+ ######################################################################
179
+
46
180
  # Print contents of a given file on the console. Parameter is the name
47
181
  # of the file to output, as a String.
48
182
  #
@@ -50,203 +184,609 @@ module Bee
50
184
  #
51
185
  # - cat: "doc/welcome-message.txt"
52
186
  def cat(file)
53
- file = file
54
- error "Parameter must be a String" unless file.kind_of?(String)
55
- error "File '#{file}' not found" unless
56
- File.exists?(file) or File.directory?(file)
187
+ error "Parameter must be a string" unless file.kind_of?(String)
188
+ error "File '#{file}' not a regular file or not readable" unless
189
+ File.file?(file) and File.readable?(file)
57
190
  puts File.read(file).strip
58
191
  end
59
-
192
+
60
193
  # Change working directory. This change will persist for all tasks in
61
- # the target but not in other targets. Parameter is a String with
62
- # directory to change to.
194
+ # the current target. Entering a new target, working directory will
195
+ # recover its default value, which is the directory of the build file
196
+ # (or property 'base'). Parameter is a String with directory to change
197
+ # to.
63
198
  #
64
199
  # Example
65
200
  #
66
201
  # - cd: "build"
67
202
  def cd(dir)
68
- dir = dir
69
- error "cd parameter is a String" unless dir.kind_of?(String)
203
+ error "cd parameter must be a string" unless dir.kind_of?(String)
204
+ error "cd parameter must be a readable existing directory" unless
205
+ File.directory?(dir) and File.executable?(dir)
206
+ puts "Changing directory to '#{dir}'"
70
207
  Dir.chdir(dir)
71
208
  end
72
-
73
- # Make a directory and parent directories if necessary. Doesn't complain
74
- # if directory already exists. Parameter is directory to create as a
75
- # String or a list of directories as Strings.
209
+
210
+ # Put working directory in a given property. Parameter is the name of
211
+ # the property to write current directory into.
212
+ #
213
+ # Example
214
+ #
215
+ # - pwd: current_dir
216
+ def pwd(property)
217
+ error "pwd parameter must be a string" unless property.kind_of?(String)
218
+ pwd = FileUtils.pwd
219
+ @build.context.set_property(property, pwd)
220
+ end
221
+
222
+ # Make a symbolic link from a source file to a destination one.
223
+ # Parameter is a Hash with following entries:
224
+ #
225
+ # - old: source of the link, as a glob. If there are more than one
226
+ # file to link, this task will make links 'new/file' for each file
227
+ # of the glob.
228
+ # - new: destination of the link.
229
+ #
230
+ # Example
231
+ #
232
+ # - ln:
233
+ # old: /usr/local
234
+ # new: /opt
235
+ #
236
+ # Note:
237
+ #
238
+ # This task is not implemented under Windows.
239
+ def ln(parameters)
240
+ params_desc = {
241
+ :old => { :mandatory => true, :type => :string },
242
+ :new => { :mandatory => true, :type => :string }
243
+ }
244
+ check_parameters(parameters, params_desc)
245
+ old = parameters[:old]
246
+ new = parameters[:new]
247
+ files = Dir.glob(old)
248
+ files = files.first if files.length == 1
249
+ puts "Linking #{files.length} file(s) to '#{new}'"
250
+ begin
251
+ FileUtils.ln_s(files, new)
252
+ rescue
253
+ error "Error making the link: #{$!}"
254
+ end
255
+ end
256
+
257
+ # Alias for ln.
258
+ alias :link :ln
259
+
260
+ # Change permissions for a set of files. Parameters is a Hash with
261
+ # following entries:
262
+ #
263
+ # - files: files to change permissions for, as a glob.
264
+ # - mode: permissons as an Unix integer (such as 0644 or 0755). Note that
265
+ # numbers starting with 0 are considered octal, with 0x, they are
266
+ # supposed to be hexa and in base 10 otherwise.
267
+ # - recursive: tells if should process directories recursively.
268
+ # Optional, defaults to 'false'.
269
+ #
270
+ # Example:
271
+ #
272
+ # - chmod:
273
+ # files: /usr/local/bin/*
274
+ # mode: 0755
275
+ #
276
+ # Note:
277
+ #
278
+ # This task is not implemented under Windows.
279
+ def chmod(parameters)
280
+ params_desc = {
281
+ :files => { :mandatory => true, :type => :string_or_array },
282
+ :mode => { :mandatory => true, :type => :integer },
283
+ :recursive => { :mandatory => false, :type => :boolean, :default => false }
284
+ }
285
+ check_parameters(parameters, params_desc)
286
+ files = parameters[:files]
287
+ mode = parameters[:mode]
288
+ recursive = parameters[:recursive]
289
+ files = Dir.glob(files)
290
+ if files.length > 0
291
+ puts "Changing permissions for #{files.length} file(s) to '#{mode}'"
292
+ begin
293
+ if recursive
294
+ FileUtils.chmod_R(mode, files)
295
+ else
296
+ FileUtils.chmod(mode, files)
297
+ end
298
+ rescue
299
+ error "Error changing permissions: #{$!}"
300
+ end
301
+ end
302
+ end
303
+
304
+ # Change owner and group for a set of files. Parameters is a Hash with
305
+ # following entries:
306
+ #
307
+ # - files: files to change owner for, as a glob.
308
+ # - user: the user to change for, may be a name or an ID (integer). If
309
+ # not set, the user is not changed.
310
+ # - group: the group to change for, may be a name or an ID (integer). If
311
+ # not set, the group is not changed.
312
+ # - recursive: tells if should process directories recursively.
313
+ # Optional, defaults to 'false'.
314
+ #
315
+ # Example:
316
+ #
317
+ # - chown:
318
+ # files: /home/casa
319
+ # user: casa
320
+ # group: staff
321
+ # recursive: true
322
+ #
323
+ # Note:
324
+ #
325
+ # This task is not implemented under Windows.
326
+ def chown(parameters)
327
+ params_desc = {
328
+ :files => { :mandatory => true, :type => :string_or_array },
329
+ :user => { :mandatory => false, :type => :string_or_integer },
330
+ :group => { :mandatory => false, :type => :string_or_integer },
331
+ :recursive => { :mandatory => false, :type => :boolean, :default => false }
332
+ }
333
+ check_parameters(parameters, params_desc)
334
+ files = parameters['files']
335
+ user = parameters['user']
336
+ group = parameters['group']
337
+ recursive = parameters['recursive']
338
+ files = Dir.glob(files)
339
+ if files.length > 0
340
+ puts "Changing owner of #{files.length} file(s) to '#{user}/#{group}'"
341
+ begin
342
+ if recursive
343
+ FileUtils.chown_R(user, group, files)
344
+ else
345
+ FileUtils.chown(user, group, files)
346
+ end
347
+ rescue
348
+ error "Error changing owner: #{$!}"
349
+ end
350
+ end
351
+ end
352
+
353
+ # Make a directory, and parent directories if necessary. Doesn't
354
+ # complain if directory already exists. Parameter is directory to
355
+ # create as a String or a list of directories as an Array of Strings.
76
356
  #
77
357
  # Example
78
358
  #
79
359
  # - mkdir: "foo/bar"
80
- # - mkdir: ["foo", "bar"]
81
360
  def mkdir(dirs)
82
- require 'fileutils'
83
- dirs = dirs
84
361
  error "mkdir parameter must a String or an array of Strings" unless
85
- dirs.kind_of?(String) or dirs.kind_of?(Array)
362
+ dirs.kind_of?(String) or dirs.kind_of?(Array)
86
363
  for dir in dirs
87
364
  error "mkdir parameter must a String or an array of Strings" unless
88
- dir.kind_of?(String)
89
- FileUtils.makedirs(dir)
365
+ dir.kind_of?(String)
366
+ puts "Creating directory '#{dir}'"
367
+ begin
368
+ FileUtils.makedirs(dir)
369
+ rescue
370
+ error "Error creating directory '#{dir}': #{$!}"
371
+ end
90
372
  end
91
373
  end
92
-
374
+
93
375
  # Copy files or directories to destination file or directory. Parameter
94
376
  # is a Hash with following entries:
95
377
  #
96
- # - includes: glob or list of globs for source files or directories to
97
- # include.
98
- # - excludes: glob or list of globs for source files or directories to
99
- # exclude.
378
+ # - src: glob or list of globs for source files or directories to copy.
379
+ # Included source directories are copied recursively.
100
380
  # - dest: destination file or directory.
101
381
  #
102
382
  # Example
103
383
  #
104
384
  # - cp:
105
- # includes: "doc/img/**/*"
106
- # excludes: ["**/*.bmp", "**/*.psd"]
385
+ # src: "img/*"
107
386
  # dest: :doc
108
387
  def cp(params)
109
- require 'fileutils'
110
388
  params_desc = {
111
- 'includes' => :mandatory,
112
- 'excludes' => :optional,
113
- 'dest' => :mandatory
389
+ :src => { :mandatory => true, :type => :string_or_array },
390
+ :dest => { :mandatory => true, :type => :string }
114
391
  }
115
- check_task_parameters(params, params_desc)
116
- includes = params['includes']
117
- excludes = params['excludes']
392
+ check_parameters(params, params_desc)
393
+ src = params['src']
118
394
  dest = params['dest']
119
- error "cp 'dest' parameter must a String" unless dest.kind_of?(String)
120
- files = select_files(includes, excludes)
121
- files = files[0] if files.length == 1
122
- FileUtils.cp_r(files, dest)
395
+ src = Array(src).map { |s| Dir.glob(s) }.flatten.uniq
396
+ src = src.first if src.length == 1
397
+ if src.kind_of?(Array)
398
+ nb_copies = src.length
399
+ else
400
+ nb_copies = 1
401
+ end
402
+ puts "Copying #{nb_copies} file(s) to '#{dest}'"
403
+ begin
404
+ FileUtils.cp_r(src, dest)
405
+ rescue
406
+ error "Error copying file(s): #{$!}"
407
+ end
123
408
  end
124
-
125
- # Move source file(s) or directory(ies) to dest file or directory.
126
- # Parameter is a Hash with following entries:
409
+
410
+ # Moves files or directories to destination file or directory. Parameter
411
+ # is a Hash with following entries:
127
412
  #
128
- # - includes: glob or list of globs for files or directories to move.
129
- # - excludes: glob or list of globs for files or directories that should
130
- # not move.
131
- # - dest: file or directory to move file(s) to.
413
+ # - src: glob or list of globs for source files or directories to move.
414
+ # Included source directories are moved recursively.
415
+ # - dest: destination file or directory.
132
416
  #
133
417
  # Example
134
418
  #
135
- # - mv: { includes: "**/*~", dest: "trash" }
419
+ # - mv:
420
+ # src: "**/*~"
421
+ # dest: :trash
136
422
  def mv(params)
137
- require 'fileutils'
138
423
  params_desc = {
139
- 'includes' => :mandatory,
140
- 'excludes' => :optional,
141
- 'dest' => :mandatory
424
+ :src => { :mandatory => true, :type => :string_or_array },
425
+ :dest => { :mandatory => true, :type => :string }
142
426
  }
143
- check_task_parameters(params, params_desc)
144
- includes = params['includes']
145
- excludes = params['excludes']
427
+ check_parameters(params, params_desc)
428
+ src = params['src']
146
429
  dest = params['dest']
147
- error "mv 'dest' parameter must a String" unless dest.kind_of?(String)
148
- files = select_files(includes, excludes)
149
- files = files[0] if files.length == 1
150
- FileUtils.mv(files, dest)
430
+ src = Array(src).map { |s| Dir.glob(s) }.flatten.uniq
431
+ src = src.first if src.length == 1
432
+ if src.kind_of?(Array)
433
+ nb_moves = src.length
434
+ else
435
+ nb_moves = 1
436
+ end
437
+ puts "Moving #{nb_moves} file(s) to '#{dest}'"
438
+ begin
439
+ FileUtils.mv(src, dest)
440
+ rescue
441
+ error "Error moving file(s): #{$!}"
442
+ end
151
443
  end
152
-
444
+
445
+ # Copy filtered files. Parameter is a hash with following entries:
446
+ #
447
+ # - root: root directory for files to copy. Optional, defaults to current
448
+ # directory.
449
+ # - includes: list of globs for files to copy. Optional, defaults to
450
+ # '**/*' to include all files recursively.
451
+ # - excludes: list of globs for files to exclude from copy. Optional,
452
+ # default to nil to exclude no file.
453
+ # - dotmatch: tells if joker matches dot files. Optional, defaults to
454
+ # false.
455
+ # - flatten: tells if included files should be copied in destination
456
+ # directory, ignoring their subdirectory. Optional, defaults to false.
457
+ #
458
+ # Example:
459
+ #
460
+ # To copy all files from directory 'src', except those living in 'CVS'
461
+ # directories, into directory 'destination', you could write:
462
+ #
463
+ # - copy:
464
+ # root: src
465
+ # includes: **/*
466
+ # excludes: **/CVS/**/*
467
+ # dest: destination
468
+ #
469
+ # Note: this task only deals with files. Thus, 'includes' and 'excludes'
470
+ # globs should be ones for files.
471
+ def copy(params)
472
+ # check parameters and set default values
473
+ params_desc = {
474
+ :root => { :mandatory => false, :type => :string, :default => '.' },
475
+ :includes => { :mandatory => false, :type => :string_or_array },
476
+ :excludes => { :mandatory => false, :type => :string_or_array },
477
+ :dest => { :mandatory => true, :type => :string },
478
+ :flatten => { :mandatory => false, :type => :boolean, :default => false },
479
+ :dotmatch => { :mandatory => false, :type => :boolean, :default => false }
480
+ }
481
+ check_parameters(params, params_desc)
482
+ root = params[:root]
483
+ includes = params[:includes]
484
+ excludes = params[:excludes]
485
+ dest = params[:dest]
486
+ flatten = params[:flatten]
487
+ dotmatch = params[:dotmatch]
488
+ error "copy 'root' parameter must be an existing directory" unless
489
+ File.exists?(root) and File.directory?(root)
490
+ error "copy 'dest' parameter must be an existing directory" unless
491
+ File.exists?(dest) and File.directory?(dest)
492
+ # select files and copy
493
+ files = filter_files(includes, excludes, root, dotmatch)
494
+ puts "Copying #{files.length} file(s) to '#{dest}'"
495
+ for file in files
496
+ from_file = File.join(root, file)
497
+ if flatten
498
+ to_file = File.join(dest, File.basename(file))
499
+ else
500
+ to_file = File.join(dest, file)
501
+ end
502
+ to_dir = File.dirname(to_file)
503
+ FileUtils.makedirs(to_dir) if not File.exists?(to_dir)
504
+ FileUtils.cp(from_file, to_file)
505
+ end
506
+ end
507
+
508
+ # Move filtered files. Parameter is a hash with following entries:
509
+ #
510
+ # - root: root directory for files to move. Optional, defaults to
511
+ # current directory.
512
+ # - includes: list of globs for files to move. Optional, defaults to
513
+ # '**/*' to include all files recursively.
514
+ # - excludes: list of globs for files to exclude from move. Optional,
515
+ # default to nil to exclude no file.
516
+ # - flatten: tells if included files should be moved to destination
517
+ # directory, ignoring their subdirectory. Optional, defaults to false.
518
+ # - dotmatch: tells if joker matches dot files. Optional, defaults to
519
+ # false.
520
+ #
521
+ # Example:
522
+ #
523
+ # To move all files from directory 'src', except those living in 'CVS'
524
+ # directories, into directory 'destination', you could write:
525
+ #
526
+ # - move:
527
+ # root: src
528
+ # includes: **/*
529
+ # excludes: **/CVS/**/*
530
+ # dest: destination
531
+ #
532
+ # Note: this task only deals with files. Thus, 'includes' and 'excludes'
533
+ # globs should be ones for files and directories from root will not
534
+ # be affected by this task.
535
+ def move(params)
536
+ # check parameters and set default values
537
+ params_desc = {
538
+ :root => { :mandatory => false, :type => :string, :default => '.' },
539
+ :includes => { :mandatory => false, :type => :string_or_array },
540
+ :excludes => { :mandatory => false, :type => :string_or_array },
541
+ :dest => { :mandatory => true, :type => :string },
542
+ :flatten => { :mandatory => false, :type => :boolean, :default => false },
543
+ :dotmatch => { :mandatory => false, :type => :boolean, :default => false }
544
+ }
545
+ check_parameters(params, params_desc)
546
+ root = params[:root]
547
+ includes = params[:includes]
548
+ excludes = params[:excludes]
549
+ dest = params[:dest]
550
+ flatten = params[:flatten]
551
+ dotmatch = params[:dotmatch]
552
+ error "move 'root' parameter must be an existing directory" unless
553
+ File.exists?(root) and File.directory?(root)
554
+ error "move 'dest' parameter must be an existing directory" unless
555
+ File.exists?(dest) and File.directory?(dest)
556
+ # select files and make move
557
+ files = filter_files(includes, excludes, root, dotmatch)
558
+ puts "Moving #{files.length} file(s) to '#{dest}'"
559
+ for file in files
560
+ from_file = File.join(root, file)
561
+ if flatten
562
+ to_file = File.join(dest, File.basename(file))
563
+ else
564
+ to_file = File.join(dest, file)
565
+ end
566
+ to_dir = File.dirname(to_file)
567
+ FileUtils.makedirs(to_dir) if not File.exists?(to_dir)
568
+ FileUtils.mv(from_file, to_file)
569
+ end
570
+ end
571
+
153
572
  # Delete files for a given glob or list of globs. Parameter is a glob or
154
- # list of globs for files to delete.
573
+ # list of globs for files to delete. This task will raise an error if
574
+ # told to delete a directory. Use task 'rmrf' to do so.
155
575
  #
156
576
  # Example
157
577
  #
158
- # - rm: "**/*.bak"
159
578
  # - rm: ["**/*~", "**/.DS_Store"]
160
579
  def rm(globs)
161
- require 'fileutils'
162
- globs = globs
163
580
  error "rm parameter is a String or Array of Strings" unless
164
581
  globs.kind_of?(String) or globs.kind_of?(Array)
165
582
  for glob in globs
166
583
  error "rm parameter is a String or Array of Strings" unless
167
584
  glob.kind_of?(String)
168
585
  files = Dir.glob(glob)
586
+ size = (files.kind_of?(Array) ? files.size : 1)
587
+ puts "Deleting #{size} file(s)" if files.length > 0
169
588
  for file in files
170
- FileUtils.rm_f(file)
589
+ begin
590
+ FileUtils.rm(file)
591
+ rescue
592
+ error "Error deleting files: #{$!}"
593
+ end
171
594
  end
172
595
  end
173
596
  end
174
-
175
- # Delete directories recursively. Parameter is a string for glob of
176
- # directories to delete.
597
+
598
+ # Delete files and directories recursively. Parameter is a glob or list
599
+ # of globs for files and directories to delete.
177
600
  #
178
601
  # Example
179
602
  #
180
- # - rmdir: :build
181
- def rmdir(globs)
182
- require 'fileutils'
183
- globs = globs
184
- error "rmdir parameter is a String or an Array of Strings" unless
603
+ # - rmrf: :build
604
+ def rmrf(globs)
605
+ globs = [globs] if globs.kind_of?(String)
606
+ error "rmrf parameter is a String or an Array of Strings" unless
185
607
  globs.kind_of?(String) or globs.kind_of?(Array)
186
608
  for glob in globs
187
- error "rmdir parameter is a String or an Array of Strings" unless
609
+ error "rmrf parameter is a String or an Array of Strings" unless
188
610
  glob.kind_of?(String)
189
611
  dirs = Dir.glob(glob)
612
+ size = (dirs.kind_of?(Array) ? dirs.size : 1)
613
+ puts "Deleting #{size} directory(ies)" if dirs.length > 0
190
614
  for dir in dirs
191
- FileUtils.rm_rf(dir)
615
+ begin
616
+ FileUtils.rm_rf(dir)
617
+ rescue
618
+ error "Error deleting directory(ies): #{$!}"
619
+ end
192
620
  end
193
621
  end
194
622
  end
195
-
623
+
624
+ # Alias for rmrf.
625
+ alias :rmdir :rmrf
626
+
627
+ # Update modification time and access time of files in a list. Files
628
+ # are created if they don't exist. Parameter is a glob or list of
629
+ # globs for files to touch.
630
+ #
631
+ # Example
632
+ #
633
+ # - touch: '#{target}/classes/**/*.class'
634
+ def touch(globs)
635
+ globs = [globs] if globs.kind_of?(String)
636
+ error "touch parameter is a String or an Array of Strings" unless
637
+ globs.kind_of?(String) or globs.kind_of?(Array)
638
+ files = []
639
+ for glob in globs
640
+ error "touch parameter is a String or an Array of Strings" unless
641
+ glob.kind_of?(String)
642
+ new_files = Dir.glob(glob)
643
+ if new_files.length == 0
644
+ files << glob
645
+ else
646
+ files += new_files
647
+ end
648
+ end
649
+ files.uniq!
650
+ size = (files.kind_of?(Array) ? files.size : 1)
651
+ puts "Touching #{size} file(s)" if size > 0
652
+ begin
653
+ FileUtils.touch(files)
654
+ rescue
655
+ error "Error touching file(s): #{$!}"
656
+ end
657
+ end
658
+
196
659
  # Find files for a glob or list of globs and store list in a property.
197
660
  # Parameter is a Hash with entries:
198
661
  #
199
- # - includes: glob or list of globs for files to look for.
662
+ # - root: root directory for file search. Defaults to '.' (current
663
+ # directory).
664
+ # - includes: glob or list of globs for files to look for. Defaults to
665
+ # '**/*' to include all files recursively.
200
666
  # - excludes: glob or list of globs for files to exclude from search.
667
+ # Defaults to nil to exclude no file.
668
+ # - dotmatch: tells if joker matches dot files. Optional, defaults to
669
+ # false.
201
670
  # - property: name of the property to set.
671
+ # - join: a character used to join the list in a string. Defaults
672
+ # to nil so that list is not joined.
202
673
  #
203
674
  # Example
675
+ #
676
+ # To find all PNG in files in 'img' directory, and store the list in
677
+ # property image_files, one could write:
204
678
  #
205
- # - find: { includes: "**/*.rb", property: "rb_files_to_check" }
206
679
  # - find:
207
- # - includes: "doc/img/**/*"
208
- # - excludes: "**/*.psd"
209
- # - property: "image_files"
210
- def find(parameters)
680
+ # root: "img"
681
+ # includes: "**/*.png"
682
+ # property: "image_files"
683
+ def find(params)
211
684
  params_desc = {
212
- 'includes' => :mandatory,
213
- 'excludes' => :optional,
214
- 'property' => :mandatory
685
+ :root => { :mandatory => false, :type => :string, :default => '.' },
686
+ :includes => { :mandatory => false, :type => :string_or_array },
687
+ :excludes => { :mandatory => false, :type => :string_or_array },
688
+ :property => { :mandatory => true, :type => :string },
689
+ :dotmatch => { :mandatory => false, :type => :boolean, :default => false },
690
+ :join => { :mandatory => false, :type => :string }
215
691
  }
216
- check_task_parameters(parameters, params_desc)
217
- includes = parameters['includes']
218
- excludes = parameters['excludes']
219
- property = parameters['property']
220
- files = select_files(includes, excludes)
692
+ check_parameters(params, params_desc)
693
+ root = params[:root]
694
+ includes = params[:includes]
695
+ excludes = params[:excludes]
696
+ property = params[:property]
697
+ dotmatch = params[:dotmatch]
698
+ join = params[:join]
699
+ files = filter_files(includes, excludes, root, dotmatch)
700
+ if join
701
+ files = files.join(join)
702
+ end
221
703
  @build.context.set_property(property, files)
222
704
  end
223
-
224
- # Run a set of tests selected using globs. Parameter is a glob or list of
225
- # globs for files or directories to run.
226
- #
705
+
706
+ ######################################################################
707
+ # RUBY RELATED TASKS #
708
+ ######################################################################
709
+
710
+ # Tests a required library and prints an error message if import
711
+ # fails. Parameter is a Hash with entries:
712
+ #
713
+ # - library: required library (as in require call).
714
+ # - message: error message to print if require fails.
715
+ #
227
716
  # Example
717
+ #
718
+ # - required:
719
+ # library: foo
720
+ # message: >
721
+ # Library foo must be installed (gem install foo) to run
722
+ # task bar.
723
+ def required(params)
724
+ require 'rubygems'
725
+ require 'rubygems/gem_runner'
726
+ params_desc = {
727
+ :library => { :mandatory => true, :type => :string },
728
+ :message => { :mandatory => true, :type => :string }
729
+ }
730
+ check_parameters(params, params_desc)
731
+ library = params[:library]
732
+ message = params[:message]
733
+ if Gem::RubyGemsVersion < '1.3.0'
734
+ begin
735
+ Gem::activate(library, false)
736
+ available = true
737
+ rescue LoadError
738
+ available = false
739
+ end
740
+ else
741
+ available = Gem::available?(library)
742
+ end
743
+ error message if not available
744
+ end
745
+
746
+ # Run Ruby unit tests listed as a glob or list of globs in a given
747
+ # directory (that defaults to current one). Parameter is a Hash with
748
+ # following entries:
228
749
  #
229
- # - test: "**/tc_*.rb"
750
+ # - root: root directory for files to include. Defaults to current
751
+ # directory.
752
+ # - includes: glob or list of globs for unit test files to run.
753
+ # Defaults to '**/*' to include all files recursively.
754
+ # - excludes: glob or list of globs for unit test files to exclude.
755
+ # Defaults to nil to exclude no file.
756
+ # - dotmatch: tells if joker matches dot files. Optional, defaults to
757
+ # false.
758
+ # - dir: directory where to run unit tests.
230
759
  #
231
- # FIXME
760
+ # Example
232
761
  #
233
- # if tasks runs twice, it will run twice loaded tests...
234
- def test(globs)
762
+ # - find:
763
+ # root: :test
764
+ # includes: "**/tc_*.rb"
765
+ # dir: "test"
766
+ def test(params)
235
767
  require 'test/unit'
236
768
  require 'test/unit/testresult'
237
- files = []
238
- globs = globs
239
- error "test parameter must be a String or a list of Strings" unless
240
- globs.kind_of?(String) or globs.kind_of?(Array)
241
- for glob in globs
242
- error "Parameter must be a String or a list of Strings" unless
243
- glob.kind_of?(String)
244
- files += Dir.glob(glob)
245
- end
769
+ params_desc = {
770
+ :root => { :mandatory => false, :type => :string },
771
+ :includes => { :mandatory => true, :type => :string },
772
+ :excludes => { :mandatory => false, :type => :string },
773
+ :dotmatch => { :mandatory => false, :type => :boolean, :default => false },
774
+ :dir => { :mandatory => false, :type => :string, :default => '.' }
775
+ }
776
+ check_parameters(params, params_desc)
777
+ root = params[:root]
778
+ includes = params[:includes]
779
+ excludes = params[:excludes]
780
+ dotmatch = params[:dotmatch]
781
+ dir = params[:dir]
782
+ files = filter_files(includes, excludes, root, dotmatch)
246
783
  for file in files
247
784
  load file
248
785
  end
249
- Test::Unit::AutoRunner.run
786
+ size = (files.kind_of?(Array) ? files.size : 1)
787
+ puts "Running #{size} unit test(s)"
788
+ ok = Test::Unit::AutoRunner.run(false, dir)
789
+ error "Tests failed" if not ok
250
790
  end
251
791
 
252
792
  # Run an ERB file or source in bee context and store result in a file or
@@ -267,34 +807,32 @@ module Bee
267
807
  #
268
808
  # <p>Hello <%= foo %>!</p>
269
809
  def erb(params)
270
- require 'erb'
271
- # check parameters
272
810
  params_desc = {
273
- 'source' => :optional,
274
- 'src' => :optional,
275
- 'dest' => :optional,
276
- 'property' => :optional
811
+ :source => { :mandatory => false, :type => :string },
812
+ :src => { :mandatory => false, :type => :string },
813
+ :dest => { :mandatory => false, :type => :string },
814
+ :property => { :mandatory => false, :type => :string }
277
815
  }
278
- check_task_parameters(params, params_desc)
279
- source = params['source']
280
- error "erb 'source' parameter must be a String" unless
281
- source.kind_of?(String) or source == nil
282
- src = params['src']
283
- error "erb 'src' parameter must be a String" unless
284
- src.kind_of?(String) or src == nil
285
- dest = params['dest']
286
- error "erb 'dest' parameter must be a String" unless
287
- dest.kind_of?(String) or dest == nil
288
- property = params['property']
289
- error "erb 'property' parameter must be a String" unless
290
- property.kind_of?(String) or property == nil
816
+ check_parameters(params, params_desc)
817
+ source = params[:source]
818
+ src = params[:src]
819
+ dest = params[:dest]
820
+ property = params[:property]
291
821
  error "Must pass one of 'source' or 'src' parameters to erb task" if
292
822
  not source and not src
293
823
  error "Must pass one of 'dest' or 'property' parameters to erb task" if
294
824
  not dest and not property
825
+ error "erb src file '#{src}' not found" if src and
826
+ (not File.exists?(src) or not File.file?(src) or
827
+ not File.readable?(src))
295
828
  # load ERB source
296
829
  erb_source = source||File.read(src)
297
830
  template = ERB.new(erb_source)
831
+ if src
832
+ puts "Processing ERB '#{src}'"
833
+ else
834
+ puts "Processing ERB"
835
+ end
298
836
  begin
299
837
  result = template.result(@build.context.context_binding)
300
838
  rescue
@@ -302,7 +840,11 @@ module Bee
302
840
  end
303
841
  # write result in file or set property
304
842
  if dest
305
- File.open(dest, 'w') { |file| file.write(result) }
843
+ begin
844
+ File.open(dest, 'w') { |file| file.write(result) }
845
+ rescue
846
+ error "Error writing ERB processing result in file: #{$!}"
847
+ end
306
848
  else
307
849
  @build.context.set_property(property, result)
308
850
  end
@@ -312,106 +854,226 @@ module Bee
312
854
  # exclude and a destination directory. Parameter is a Hash with following
313
855
  # entries:
314
856
  #
315
- # - includes: glob or list of globs for files to document.
316
- # - excludes: glob or list of globs for files that should not be
317
- # documented.
857
+ # - root: root directory for files to include. Defaults to current
858
+ # directory.
859
+ # - includes: glob or list of globs for files or directories to document.
860
+ # Defaults to '**/*' to include all files.
861
+ # - excludes: glob or list of globs for files or directories that should
862
+ # not be documented. Defaults to nil to exclude no file.
863
+ # - dotmatch: tells if joker matches dot files. Optional, defaults to
864
+ # false.
318
865
  # - dest: destination directory for generated documentation.
319
866
  # - options: additional options as a string or list of strings.
320
867
  #
321
868
  # Example
322
869
  #
323
870
  # - rdoc:
324
- # includes: ["README", "LICENSE", :src]
871
+ # includes: ["README", "LICENSE", "#{src}/**/*"]
325
872
  # dest: :api
326
- def rdoc(parameters)
873
+ def rdoc(params)
327
874
  require 'rdoc/rdoc'
328
- params_desc = {
329
- 'includes' => :mandatory,
330
- 'excludes' => :optional,
331
- 'dest' => :mandatory,
332
- 'options' => :optional
875
+ params_desc= {
876
+ :root => { :mandatory => false, :type => :string },
877
+ :includes => { :mandatory => true, :type => :string_or_array },
878
+ :excludes => { :mandatory => false, :type => :string_or_array },
879
+ :dotmatch => { :mandatory => false, :type => :boolean, :default => false },
880
+ :dest => { :mandatory => true, :type => :string },
881
+ :options => { :mandatory => false, :type => :string_or_array }
333
882
  }
334
- check_task_parameters(parameters, params_desc)
335
- includes = parameters['includes']
336
- excludes = parameters['excludes']
337
- dest = parameters['dest']
338
- options = parameters['options']
339
- error "rdoc 'dest' parameter must be a String" unless
340
- dest.kind_of?(String)
341
- files = select_files(includes, excludes)
883
+ check_parameters(params, params_desc)
884
+ root = params[:root]
885
+ includes = params[:includes]
886
+ excludes = params[:excludes]
887
+ dotmatch = params[:dotmatch]
888
+ dest = params[:dest]
889
+ options = params[:options]
890
+ files = filter_files(includes, excludes, root, dotmatch)
342
891
  command_line = ['-S', '-o', dest]
343
892
  command_line << options if options
344
893
  command_line += files
345
- rdoc = RDoc::RDoc.new
346
- rdoc.document(command_line)
894
+ begin
895
+ rdoc = RDoc::RDoc.new
896
+ rdoc.document(command_line)
897
+ rescue
898
+ error "Error generating RDoc: #{$!}"
899
+ end
347
900
  end
348
901
 
349
- # Generate a Gem package. Parameter is the name of the Gem description
350
- # file. Resulting Gem package is generated in current directory (as with
351
- # command line tool).
902
+ # Generate a Gem package in current directory, named after the Gem name
903
+ # and version. Parameter is the name of the Gem description file.
352
904
  #
353
905
  # Example
354
906
  #
355
- # - gembuild: :gem_spec
356
- def gembuild(description)
907
+ # - gem: :gem_spec
908
+ def gem(description)
357
909
  require 'rubygems'
358
- description = description
910
+ require 'rubygems/gem_runner'
911
+ error "gem parameter must be an existing file" if
912
+ not description.kind_of?(String) or not File.exists?(description)
359
913
  arguments = ['build', description]
360
- Gem.manage_gems
361
- Gem::GemRunner.new.run(arguments)
914
+ begin
915
+ Gem::GemRunner.new.run(arguments)
916
+ rescue Exception
917
+ error "Error generating Gem: #{$!}"
918
+ end
362
919
  end
363
920
 
921
+ # Run another bee build file.
922
+ #
923
+ # - file: the build file to run, relative to current build file.
924
+ # Optional, defaults to 'build.yml'.
925
+ # - target: target or list of targets to run (default target if omitted).
926
+ # - properties: boolean (true or false) that tells if properties of
927
+ # current build file should be sent and overwrite those of target
928
+ # build. Properties modified in child build don't change in parent
929
+ # one. Defaults to false.
930
+ #
931
+ # Example
932
+ #
933
+ # - bee:
934
+ # file: "doc/build.yml"
935
+ # target: "pdf"
936
+ # properties: true
937
+ def bee(parameters)
938
+ # parse parameters
939
+ params_desc = {
940
+ :file => { :mandatory => false, :type => :string, :default => 'build.yml' },
941
+ :target => { :mandatory => false, :type => :string_or_array, :default => '' },
942
+ :properties => { :mandatory => false, :type => :boolean, :default => false }
943
+ }
944
+ check_parameters(parameters, params_desc)
945
+ file = parameters[:file]
946
+ target = parameters[:target]
947
+ properties = parameters[:properties]
948
+ # run target build
949
+ props = {}
950
+ if properties
951
+ for name in @build.context.properties
952
+ props[name] = @build.context.get_property(name)
953
+ end
954
+ end
955
+ begin
956
+ build = Bee::Build.load(file, false, props)
957
+ build.run(target, @build.listener.clone)
958
+ rescue
959
+ error "Error invoking build file '#{file}': #{$!}"
960
+ end
961
+ end
962
+
963
+ ######################################################################
964
+ # ARCHIVE TASKS #
965
+ ######################################################################
966
+
364
967
  # Generate a ZIP archive. Parameter is a Hash with following entries:
365
968
  #
969
+ # - root: root directory for files to include in the archive. Defaults
970
+ # to '.' for current directory.
366
971
  # - includes: glob or list of globs for files to select for the archive.
972
+ # Defaults to '**/*' to include all files recursively.
367
973
  # - excludes: glob or list of globs for files to exclude from the archive.
974
+ # Defaults to nil to exclude no file.
975
+ # - dotmatch: tells if joker matches dot files. Optional, defaults to
976
+ # false.
368
977
  # - dest: the archive file to generate.
369
978
  # - prefix: prefix for archive entries (default to nil).
370
979
  #
371
980
  # Example
372
981
  #
373
982
  # - zip:
374
- # includes: "**/*"
375
- # excludes: ["build", "build/**/*", "**/*~"]
376
- # dest: :zip_archive
983
+ # excludes: ["build/**/*", "**/*~"]
984
+ # dest: :zip_archive
377
985
  #
378
986
  # Note
379
987
  #
380
988
  # If archive already exists, files are added to the archive.
381
989
  def zip(parameters)
382
990
  require 'zip/zip'
383
- # parse parameters
384
991
  params_desc = {
385
- 'includes' => :mandatory,
386
- 'excludes' => :optional,
387
- 'dest' => :mandatory,
388
- 'prefix' => :optional
992
+ :root => { :mandatory => false, :type => :string },
993
+ :includes => { :mandatory => true, :type => :string_or_array },
994
+ :excludes => { :mandatory => false, :type => :string_or_array, :default => nil },
995
+ :dotmatch => { :mandatory => false, :type => :boolean, :default => false },
996
+ :dest => { :mandatory => true, :type => :string },
997
+ :prefix => { :mandatory => false, :type => :string }
389
998
  }
390
- check_task_parameters(parameters, params_desc)
391
- includes = parameters['includes']
392
- excludes = parameters['excludes']
393
- dest = parameters['dest']
394
- error "zip 'dest' parameter must be a String" unless
395
- dest.kind_of?(String)
396
- prefix = parameters['prefix']
397
- error "zip 'prefix' parameter must be a String" unless
398
- !prefix or prefix.kind_of?(String)
399
- files = select_files(includes, excludes)
999
+ check_parameters(parameters, params_desc)
1000
+ root = parameters[:root]
1001
+ includes = parameters[:includes]
1002
+ excludes = parameters[:excludes]
1003
+ dotmatch = parameters[:dotmatch]
1004
+ dest = parameters[:dest]
1005
+ prefix = parameters[:prefix]
1006
+ files = filter_files(includes, excludes, root, dotmatch)
400
1007
  # build the archive
401
- zipfile = Zip::ZipFile.open(dest, Zip::ZipFile::CREATE) do |zip|
402
- for file in files
403
- entry = prefix ? File.join(prefix, file) : file
404
- puts "Adding '#{entry}'"
405
- zip.add(entry, file)
1008
+ puts "Building ZIP archive '#{dest}'"
1009
+ begin
1010
+ zipfile = Zip::ZipFile.open(dest, Zip::ZipFile::CREATE) do |zip|
1011
+ for file in files
1012
+ path = (root == nil ? file : File.join(root, file))
1013
+ entry = prefix ? File.join(prefix, file) : file
1014
+ puts "Adding '#{entry}'" if @build.listener.verbose
1015
+ zip.add(entry, path)
1016
+ end
1017
+ zip.close
1018
+ end
1019
+ rescue
1020
+ error "Error building ZIP archive: #{$!}"
1021
+ end
1022
+ end
1023
+
1024
+ # Extract ZIP archive to a destination directory. Existing extracted
1025
+ # files are not overwritten and result in an error. Parameter is a Hash
1026
+ # with following entries:
1027
+ #
1028
+ # - src: archive to extract.
1029
+ # - dest: destination directory for extracted files. Optional, defaults
1030
+ # to current directory.
1031
+ #
1032
+ # Example
1033
+ #
1034
+ # - unzip:
1035
+ # src: myarchive.zip
1036
+ # dest: mydir
1037
+ def unzip(parameters)
1038
+ require 'zip/zip'
1039
+ params_desc = {
1040
+ :src => { :mandatory => true, :type => :string },
1041
+ :dest => { :mandatory => false, :type => :string, :default => '.' }
1042
+ }
1043
+ check_parameters(parameters, params_desc)
1044
+ src = parameters[:src]
1045
+ dest = parameters[:dest]
1046
+ error "unzip 'src' parameter must be an readable ZIP archive" unless
1047
+ File.exists?(src) and File.readable?(src)
1048
+ FileUtils.makedirs(dest) if not File.exists?(dest)
1049
+ puts "Extracting ZIP file '#{src}' to '#{dest}'"
1050
+ begin
1051
+ Zip::ZipFile.foreach(src) do |entry|
1052
+ puts "Writing '#{entry}'" if @build.listener.verbose
1053
+ tofile = File.join(dest, entry.name)
1054
+ if entry.file?
1055
+ dir = File.dirname(tofile)
1056
+ FileUtils.makedirs(dir) if not File.exists?(dir)
1057
+ entry.extract(tofile)
1058
+ elsif entry.directory?
1059
+ FileUtils.makedirs(tofile)
1060
+ end
406
1061
  end
407
- zip.close
1062
+ rescue
1063
+ error "Error extracting ZIP archive: #{$!}"
408
1064
  end
409
1065
  end
410
1066
 
411
1067
  # Generate a TAR archive. Parameter is a Hash with following entries:
412
1068
  #
1069
+ # - root: root directory for files to include. Defaults to current
1070
+ # directory.
413
1071
  # - includes: glob or list of globs for files to select for the archive.
1072
+ # Defaults to '**/*' to include all files recursively.
414
1073
  # - excludes: glob or list of globs for files to exclude from the archive.
1074
+ # Defaults to nil to exclude no file.
1075
+ # - dotmatch: tells if joker matches dot files. Optional, defaults to
1076
+ # false.
415
1077
  # - dest: the archive file to generate.
416
1078
  #
417
1079
  # Example
@@ -423,28 +1085,40 @@ module Bee
423
1085
  #
424
1086
  # Note
425
1087
  #
426
- # If archive already exists, its is overwritten.
1088
+ # If archive already exists, it's overwritten.
427
1089
  def tar(parameters)
428
1090
  require 'archive/tar/minitar'
429
1091
  # parse parameters
430
1092
  params_desc = {
431
- 'includes' => :mandatory,
432
- 'excludes' => :optional,
433
- 'dest' => :mandatory
1093
+ :root => { :mandatory => false, :type => :string },
1094
+ :includes => { :mandatory => true, :type => :string_or_array },
1095
+ :excludes => { :mandatory => false, :type => :string_or_array, :default => nil },
1096
+ :dotmatch => { :mandatory => false, :type => :boolean, :default => false },
1097
+ :dest => { :mandatory => true, :type => :string }
434
1098
  }
435
- check_task_parameters(parameters, params_desc)
436
- includes = parameters['includes']
437
- excludes = parameters['excludes']
438
- dest = parameters['dest']
439
- error "tar 'dest' parameter must be a String" unless
440
- dest.kind_of?(String)
441
- files = select_files(includes, excludes)
1099
+ check_parameters(parameters, params_desc)
1100
+ root = parameters[:root]
1101
+ includes = parameters[:includes]
1102
+ excludes = parameters[:excludes]
1103
+ dotmatch = parameters[:dotmatch]
1104
+ dest = parameters[:dest]
1105
+ files = filter_files(includes, excludes, root, dotmatch)
442
1106
  # build the archive
443
- Archive::Tar::Minitar::Output.open(dest) do |tarfile|
444
- for file in files
445
- puts "Adding '#{file}'"
446
- Archive::Tar::Minitar.pack_file(file, tarfile)
1107
+ puts "Processing TAR archive '#{dest}'"
1108
+ begin
1109
+ current_dir = Dir.pwd
1110
+ abs_dest = File.expand_path(dest)
1111
+ Dir.chdir(root) if root
1112
+ Archive::Tar::Minitar::Output.open(abs_dest) do |tarfile|
1113
+ for file in files
1114
+ puts "Adding '#{file}'" if @build.listener.verbose
1115
+ Archive::Tar::Minitar.pack_file(file, tarfile)
1116
+ end
447
1117
  end
1118
+ rescue Exception
1119
+ error "Error generating TAR archive: #{$!}"
1120
+ ensure
1121
+ Dir.chdir(current_dir)
448
1122
  end
449
1123
  end
450
1124
 
@@ -452,7 +1126,8 @@ module Bee
452
1126
  # following entries:
453
1127
  #
454
1128
  # - src: source file to generate GZIP for.
455
- # - dest: GZIP file to generate.
1129
+ # - dest: GZIP file to generate. Defaults to the src file with '.gz'
1130
+ # extension added.
456
1131
  #
457
1132
  # Example
458
1133
  #
@@ -463,61 +1138,173 @@ module Bee
463
1138
  require 'zlib'
464
1139
  # parse parameters
465
1140
  params_desc = {
466
- 'src' => :mandatory,
467
- 'dest' => :mandatory
1141
+ :src => { :mandatory => true, :type => :string },
1142
+ :dest => { :mandatory => false, :type => :string }
468
1143
  }
469
- check_task_parameters(parameters, params_desc)
470
- src = parameters['src']
471
- dest = parameters['dest']
1144
+ check_parameters(parameters, params_desc)
1145
+ src = parameters[:src]
1146
+ dest = parameters[:dest]
1147
+ dest = src + '.gz' if not dest
472
1148
  # compress file
473
- File.open(src) do |input|
474
- output = Zlib::GzipWriter.new(File.open(dest, 'wb'))
475
- output.write(input.read)
476
- output.close
1149
+ puts "Processing GZIP archive '#{dest}'"
1150
+ begin
1151
+ File.open(src) do |input|
1152
+ output = Zlib::GzipWriter.new(File.open(dest, 'wb'))
1153
+ output.write(input.read)
1154
+ output.close
1155
+ end
1156
+ rescue
1157
+ error "Error generating GZIP archive: #{$!}"
1158
+ end
1159
+ end
1160
+
1161
+ # Expand a GZIP archive for a given file. Parameter is a Hash with
1162
+ # following entries:
1163
+ #
1164
+ # - src: GZIP file to expand.
1165
+ # - dest: destination for expanded file. Destination file can be guessed
1166
+ # (and thus omitted) for src files '.gz', '.gzip' and '.tgz';
1167
+ # corresponding dest for latest will be '.tar'.
1168
+ #
1169
+ # Example
1170
+ #
1171
+ # - gunzip:
1172
+ # src: "dist.tar.gz"
1173
+ # dest: "dist.tar"
1174
+ def gunzip(parameters)
1175
+ require 'zlib'
1176
+ # parse parameters
1177
+ params_desc = {
1178
+ :src => { :mandatory => true, :type => :string },
1179
+ :dest => { :mandatory => false, :type => :string }
1180
+ }
1181
+ check_parameters(parameters, params_desc)
1182
+ src = parameters[:src]
1183
+ dest = parameters[:dest]
1184
+ error "gunzip 'src' parameter must be an readable GZIP archive" unless
1185
+ File.exists?(src) and File.readable?(src)
1186
+ if not dest
1187
+ if src =~ /.*\.gz$/
1188
+ dest = src[0..-4]
1189
+ elsif src =~ /.*\.gzip$/
1190
+ dest = src[0..-6]
1191
+ elsif src =~ /.*\.tgz/
1192
+ dest = src[0..-5]+'.tar'
1193
+ else
1194
+ error "gunzip can't guess 'dest' parameter from 'src' file name"
1195
+ end
1196
+ end
1197
+ # expand file
1198
+ puts "Expanding GZIP archive '#{dest}'"
1199
+ begin
1200
+ Zlib::GzipReader.open(src) do |input|
1201
+ output = File.open(dest, 'wb')
1202
+ output.write(input.read)
1203
+ output.close
1204
+ end
1205
+ rescue
1206
+ error "Error expanding GZIP archive: #{$!}"
477
1207
  end
478
1208
  end
479
1209
 
480
1210
  # Generate a TAR.GZ archive. Parameter is a Hash with following entries:
481
1211
  #
1212
+ # - root: root directory for files to include. Defaults to current
1213
+ # directory.
482
1214
  # - includes: glob or list of globs for files to select for the archive.
1215
+ # Defaults to '**/*' to include all files recursively.
483
1216
  # - excludes: glob or list of globs for files to exclude from the archive.
1217
+ # Defaults to nil to exclude no file.
1218
+ # - dotmatch: tells if joker matches dot files. Optional, defaults to
1219
+ # false.
484
1220
  # - dest: the archive file to generate.
485
1221
  #
486
1222
  # Example
487
1223
  #
488
1224
  # - targz:
489
- # includes: "**/*"
490
- # excludes: ["build", "build/**/*", "**/*~"]
491
- # dest: :targz_archive
1225
+ # excludes: ["build/**/*", "**/*~"]
1226
+ # dest: :targz_archive
492
1227
  #
493
1228
  # Note
494
1229
  #
495
- # If archive already exists, its is overwritten.
1230
+ # If archive already exists, it's overwritten.
496
1231
  def targz(parameters)
497
1232
  require 'archive/tar/minitar'
498
1233
  require 'zlib'
499
1234
  # parse parameters
500
1235
  params_desc = {
501
- 'includes' => :mandatory,
502
- 'excludes' => :optional,
503
- 'dest' => :mandatory
1236
+ :root => { :mandatory => false, :type => :string },
1237
+ :includes => { :mandatory => true, :type => :string_or_array },
1238
+ :excludes => { :mandatory => false, :type => :string_or_array, :default => nil },
1239
+ :dotmatch => { :mandatory => false, :type => :boolean, :default => false },
1240
+ :dest => { :mandatory => true, :type => :string }
504
1241
  }
505
- check_task_parameters(parameters, params_desc)
506
- includes = parameters['includes']
507
- excludes = parameters['excludes']
508
- dest = parameters['dest']
509
- error "targz 'dest' parameter must be a String" unless
510
- dest.kind_of?(String)
511
- files = select_files(includes, excludes)
1242
+ check_parameters(parameters, params_desc)
1243
+ root = parameters[:root]
1244
+ includes = parameters[:includes]
1245
+ excludes = parameters[:excludes]
1246
+ dotmatch = parameters[:dotmatch]
1247
+ dest = parameters[:dest]
1248
+ files = filter_files(includes, excludes, root, dotmatch)
512
1249
  # build the archive
513
- Archive::Tar::Minitar::Output.open(Zlib::GzipWriter.new(File.open(dest, 'wb'))) do |tgz|
514
- for file in files
515
- puts "Adding '#{file}'"
516
- Archive::Tar::Minitar.pack_file(file, tgz)
1250
+ begin
1251
+ current_dir = Dir.pwd
1252
+ abs_dest = File.expand_path(dest)
1253
+ Dir.chdir(root) if root
1254
+ Archive::Tar::Minitar::Output.
1255
+ open(Zlib::GzipWriter.new(File.open(abs_dest, 'wb'))) do |tgz|
1256
+ for file in files
1257
+ puts "Adding '#{file}'" if @build.listener.verbose
1258
+ Archive::Tar::Minitar.pack_file(file, tgz)
1259
+ end
517
1260
  end
1261
+ rescue
1262
+ error "Error generating TARGZ archive: #{$!}"
1263
+ ensure
1264
+ Dir.chdir(current_dir)
518
1265
  end
519
1266
  end
520
1267
 
1268
+ # Extract TAR archive to a destination directory. Gziped archives are
1269
+ # managed if their extension is '.tgz' or '.tar.gz'. Extracted files
1270
+ # are overwritten if they already exist. Parameter is a Hash with
1271
+ # following entries:
1272
+ #
1273
+ # - src: archive to extract.
1274
+ # - dest: destination directory for extracted files. Optional, defaults
1275
+ # to current directory.
1276
+ #
1277
+ # Example
1278
+ #
1279
+ # - untar:
1280
+ # src: myarchive.tar.gz
1281
+ # dest: mydir
1282
+ def untar(parameters)
1283
+ require 'archive/tar/minitar'
1284
+ require 'zlib'
1285
+ params_desc = {
1286
+ :src => { :mandatory => true, :type => :string },
1287
+ :dest => { :mandatory => false, :type => :string, :default => '.' }
1288
+ }
1289
+ check_parameters(parameters, params_desc)
1290
+ src = parameters[:src]
1291
+ dest = parameters[:dest]
1292
+ error "untar 'src' parameter must be an readable TAR archive" unless
1293
+ File.exists?(src) and File.readable?(src)
1294
+ FileUtils.makedirs(dest) if not File.exists?(dest)
1295
+ puts "Extracting TAR file '#{src}' to '#{dest}'"
1296
+ begin
1297
+ if src =~ /\.tar\.gz$/ or src =~ /\.tgz$/
1298
+ tgz = Zlib::GzipReader.new(File.open(src, 'rb'))
1299
+ Archive::Tar::Minitar.unpack(tgz, dest)
1300
+ else
1301
+ Archive::Tar::Minitar.unpack(src, dest)
1302
+ end
1303
+ rescue
1304
+ error "Error extracting TAR archive: #{$!}"
1305
+ end
1306
+ end
1307
+
521
1308
  end
522
1309
 
523
1310
  end