Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
---|---|---|---|---|
lib/bee_task_default.rb | 1828 | 1017 | 98.74%
|
97.74%
|
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
1 # Copyright 2006-2012 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 'rubygems' |
16 require 'bee_build' |
17 require 'bee_task_package' |
18 require 'bee_util' |
19 require 'bee_version_dependant' |
20 require 'erb' |
21 require 'fileutils' |
22 require 'net/smtp' |
23 require 'highline/import' |
24 require 'net/ftp' |
25 |
26 module Bee |
27 |
28 module Task |
29 |
30 # Package for default tasks (tasks with no package). |
31 class Default < Package |
32 |
33 ###################################################################### |
34 # MISCELLANEOUS TASKS # |
35 ###################################################################### |
36 |
37 # Print a message on console. If message is not a string, this task |
38 # outputs the inspected value of the object. |
39 # |
40 # - message: message to print. |
41 # |
42 # Example |
43 # |
44 # - echo: "Hello World!" |
45 def echo(message) |
46 message = '' if message == nil |
47 case message |
48 when String |
49 puts message |
50 else |
51 puts message.inspect |
52 end |
53 end |
54 |
55 # Alias for echo. |
56 alias :print :echo |
57 |
58 # Wait for a given amount of time. |
59 # |
60 # - time: time to wait, in seconds, as an integer or float. |
61 # |
62 # Example |
63 # |
64 # - sleep: 3.5 |
65 def sleep(time) |
66 error "sleep parameter must be a float or a integer" if |
67 not time.kind_of?(Numeric) |
68 seconds = time.to_f |
69 puts "Waiting #{time} seconds..." |
70 Kernel.sleep seconds |
71 end |
72 |
73 # Alias for sleep. |
74 alias :wait :sleep |
75 |
76 # Prompt the user for the value of a given property matching a pattern. |
77 # |
78 # - message: message to print at prompt. Should include a description |
79 # of the expected pattern. |
80 # - property: the name of the property to set. |
81 # - default: default value if user doesn't type anything. Written |
82 # into square brakets after prompt message. Optional. |
83 # - pattern: a Ruby pattern for prompted value. If this pattern is not |
84 # matched, this task will prompt again. Optional, if no pattern is |
85 # given, any value is accepted. |
86 # - error: the error message to print when pattern is not matched. |
87 # - attempts: number of allowed attempts. Optional, defaults to 0, which |
88 # means an unlimited number of attempts. |
89 # - echo: the character to echo while typing. Useful for passwords, |
90 # echoing '*' for instance. |
91 # |
92 # Example |
93 # |
94 # - prompt: |
95 # message: "Enter your age" |
96 # property: "age" |
97 # default: "18" |
98 # pattern: "^\\d+$" |
99 # error: "Age must be a positive integer" |
100 def prompt(params) |
101 params_desc = { |
102 :message => { :mandatory => true, :type => :string }, |
103 :property => { :mandatory => true, :type => :string }, |
104 :default => { :mandatory => false, :type => :string }, |
105 :pattern => { :mandatory => false, :type => :string }, |
106 :error => { :mandatory => false, :type => :string }, |
107 :attempts => { :mandatory => false, :type => :integer, |
108 :default => 0 }, |
109 :echo => { :mandatory => false, :type => :string, |
110 :default => false } |
111 } |
112 check_parameters(params, params_desc) |
113 message = params[:message] |
114 property = params[:property] |
115 default = params[:default] |
116 pattern = params[:pattern] |
117 error = params[:error] |
118 attempts = params[:attempts] |
119 echo_char = params[:echo] |
120 message << " [#{default}]" if default |
121 message << ': ' |
122 ok = false |
123 nb_attempts = 1 |
124 while not (ok or (nb_attempts > attempts and attempts != 0)) |
125 if echo_char |
126 value = ask(message) {|q| q.echo=echo_char} |
127 else |
128 value = ask(message) |
129 end |
130 value = default if default and value.length == 0 |
131 if pattern |
132 if value =~ /#{pattern}/ |
133 ok = true |
134 elsif error |
135 puts error |
136 end |
137 else |
138 ok = true |
139 end |
140 nb_attempts += 1 |
141 end |
142 error "Failed to obtain a matching prompt" if not ok |
143 @build.context.set_property(property, value) |
144 end |
145 |
146 # Throw a build error with a given message. |
147 # |
148 # - message: the error message. Will be printed on the console as the |
149 # build failure reason. |
150 # |
151 # Example |
152 # |
153 # - if: "not File.exists?('/etc/config')" |
154 # then: |
155 # - throw: "No /etc/config file found!" |
156 def throw(message) |
157 error "throw parameter must be a string" if not message.kind_of?(String) |
158 error message |
159 end |
160 |
161 # Alias for throw. |
162 alias :raise :throw |
163 |
164 # Get a given URL and store its content in a given file. Parameters |
165 # is a Hash with following entries: |
166 # |
167 # - url: the URL to get. |
168 # - dest: destination file. Optional, defaults to retrieved file name |
169 # in current directory. If destination is a directory, file is saved |
170 # in destination directory with the name of the retrieved file. |
171 # - prop: Property to set with content of the response body. Optional |
172 # defaults to output in a file. |
173 # - limit: the redirections limit. Optional, defaults to 10. |
174 # - username: username for HTTP basic authentication. Optional. |
175 # - password: password for HTTP basic authentication. Optional. |
176 # |
177 # Example |
178 # |
179 # - get: |
180 # url: http://rubyforge.org/frs/download.php/22185/bee-0.4.0.zip |
181 def http_get(parameters) |
182 params_desc = { |
183 :url => { :mandatory => true, :type => :string }, |
184 :dest => { :mandatory => false, :type => :string }, |
185 :prop => { :mandatory => false, :type => :string }, |
186 :limit => { :mandatory => false, :type => :integer, |
187 :default => 10 }, |
188 :username => { :mandatory => false, :type => :string }, |
189 :password => { :mandatory => false, :type => :string }, |
190 } |
191 check_parameters(parameters, params_desc) |
192 url = parameters[:url] |
193 dest = parameters[:dest] |
194 prop = parameters[:prop] |
195 username = parameters[:username] |
196 password = parameters[:password] |
197 if not dest and not prop |
198 destination = File.basename(url) |
199 elsif dest and File.directory?(dest) |
200 destination = File.join(dest, File.basename(url)) |
201 elsif dest |
202 destination = dest |
203 else |
204 destination = nil |
205 end |
206 limit = parameters[:limit] |
207 puts "Getting URL '#{url}'..." |
208 begin |
209 content = Util::fetch(url, limit, username, password) |
210 rescue Exception |
211 error "Error getting URL: #{$!}" |
212 end |
213 if destination |
214 todir = File.dirname(destination) |
215 begin |
216 FileUtils.makedirs(todir) if not File.exists?(todir) |
217 File.open(destination, 'w') { |file| file.write(content) } |
218 rescue Exception |
219 error "Error saving file: #{$!}" |
220 end |
221 end |
222 if prop |
223 @build.context.set_property(prop, content) |
224 end |
225 end |
226 |
227 # Send an email using SMTP. |
228 # |
229 # - from: The sender of the email. |
230 # - to: Recipient of the email. This may be a list of recipients. |
231 # - subject: The subject of the email. |
232 # - message: The body of the email. |
233 # - smtp: The address of the SMTP server. |
234 # - encoding: The message encoding. Defaults to ASCII. |
235 # |
236 # Example |
237 # |
238 # - mail: |
239 # from: "foo@bee.com" |
240 # to: "bar@bee.com" |
241 # subject: "Bee Release 0.6.2" |
242 # message: "Hi! There is a new Bee release!" |
243 # smtp: "smtp.bee.com" |
244 def mail(parameters) |
245 params_desc = { |
246 :from => { :mandatory => true, :type => :string }, |
247 :to => { :mandatory => true, :type => :string_or_array }, |
248 :subject => { :mandatory => true, :type => :string }, |
249 :message => { :mandatory => true, :type => :string }, |
250 :smtp => { :mandatory => true, :type => :string }, |
251 :encoding => { :mandatory => false, :type => :string, |
252 :default => 'UTF-8'} |
253 } |
254 check_parameters(parameters, params_desc) |
255 from = parameters[:from] |
256 to = Array(parameters[:to]) |
257 subject = parameters[:subject] |
258 message = parameters[:message] |
259 smtp = parameters[:smtp] |
260 encoding = parameters[:encoding] |
261 body = <<EOF |
262 MIME-Version: 1.0 |
263 Content-Type: text/plain; charset=#{encoding} |
264 From: #{from} |
265 To: #{to.join(', ')} |
266 Subject: #{subject} |
267 |
268 #{message} |
269 EOF |
270 puts "Sending email about '#{subject}'..." |
271 begin |
272 Net::SMTP.start(smtp) do |smtp_server| |
273 smtp_server.send_message(body, from, to) |
274 end |
275 rescue Exception |
276 error "Error sending email: #{$!}" |
277 end |
278 end |
279 |
280 ###################################################################### |
281 # FILE RELATED TASKS # |
282 ###################################################################### |
283 |
284 # Print contents of a given file on the console. Parameter is the name |
285 # of the file to output, as a String. |
286 # |
287 # Example |
288 # |
289 # - cat: "doc/welcome-message.txt" |
290 def cat(file) |
291 error "Parameter must be a string" unless file.kind_of?(String) |
292 error "File '#{file}' not a regular file or not readable" unless |
293 File.file?(file) and File.readable?(file) |
294 puts File.read(file).strip |
295 end |
296 |
297 # Change working directory. This change will persist for all tasks in |
298 # the current target. Entering a new target, working directory will |
299 # recover its default value, which is the directory of the build file |
300 # (or property 'base'). Parameter is a String with directory to change |
301 # to. |
302 # |
303 # Example |
304 # |
305 # - cd: "build" |
306 def cd(dir) |
307 error "cd parameter must be a string" unless dir.kind_of?(String) |
308 error "cd parameter must be a readable existing directory" unless |
309 File.directory?(dir) and File.executable?(dir) |
310 puts "Changing directory to '#{dir}'" |
311 Dir.chdir(dir) |
312 end |
313 |
314 # Put working directory in a given property. Parameter is the name of |
315 # the property to write current directory into. |
316 # |
317 # Example |
318 # |
319 # - pwd: current_dir |
320 def pwd(property) |
321 error "pwd parameter must be a string" unless property.kind_of?(String) |
322 pwd = FileUtils.pwd |
323 @build.context.set_property(property, pwd) |
324 end |
325 |
326 # Make a symbolic link from a source file to a destination one. |
327 # Parameter is a Hash with following entries: |
328 # |
329 # - old: source of the link, as a glob. If there are more than one |
330 # file to link, this task will make links 'new/file' for each file |
331 # of the glob. |
332 # - new: destination of the link. |
333 # |
334 # Example |
335 # |
336 # - ln: |
337 # old: /usr/local |
338 # new: /opt |
339 # |
340 # Note: |
341 # |
342 # This task is not implemented under Windows. |
343 def ln(parameters) |
344 params_desc = { |
345 :old => { :mandatory => true, :type => :string }, |
346 :new => { :mandatory => true, :type => :string } |
347 } |
348 check_parameters(parameters, params_desc) |
349 old = parameters[:old] |
350 new = parameters[:new] |
351 files = Dir.glob(old) |
352 files = files.first if files.length == 1 |
353 puts "Linking #{files.length} file(s) to '#{new}'" |
354 begin |
355 FileUtils.ln_s(files, new) |
356 rescue Exception |
357 error "Error making the link: #{$!}" |
358 end |
359 end |
360 |
361 # Alias for ln. |
362 alias :link :ln |
363 |
364 # Change permissions for a set of files. Parameters is a Hash with |
365 # following entries: |
366 # |
367 # - files: files to change permissions for, as a glob. |
368 # - mode: permissons as an Unix integer (such as 0644 or 0755). Note that |
369 # numbers starting with 0 are considered octal, with 0x, they are |
370 # supposed to be hexa and in base 10 otherwise. |
371 # - recursive: tells if should process directories recursively. |
372 # Optional, defaults to 'false'. |
373 # |
374 # Example: |
375 # |
376 # - chmod: |
377 # files: /usr/local/bin/* |
378 # mode: 0755 |
379 # |
380 # Note: |
381 # |
382 # This task is not implemented under Windows. |
383 def chmod(parameters) |
384 params_desc = { |
385 :files => { :mandatory => true, :type => :string_or_array }, |
386 :mode => { :mandatory => true, :type => :integer }, |
387 :recursive => { :mandatory => false, :type => :boolean, |
388 :default => false } |
389 } |
390 check_parameters(parameters, params_desc) |
391 files = parameters[:files] |
392 mode = parameters[:mode] |
393 recursive = parameters[:recursive] |
394 files = Dir.glob(files) |
395 if files.length > 0 |
396 puts "Changing permissions for #{files.length} file(s) to '#{mode}'" |
397 begin |
398 if recursive |
399 FileUtils.chmod_R(mode, files) |
400 else |
401 FileUtils.chmod(mode, files) |
402 end |
403 rescue Exception |
404 error "Error changing permissions: #{$!}" |
405 end |
406 end |
407 end |
408 |
409 # Change owner and group for a set of files. Parameters is a Hash with |
410 # following entries: |
411 # |
412 # - files: files to change owner for, as a glob. |
413 # - user: the user to change for, may be a name or an ID (integer). If |
414 # not set, the user is not changed. |
415 # - group: the group to change for, may be a name or an ID (integer). If |
416 # not set, the group is not changed. |
417 # - recursive: tells if should process directories recursively. |
418 # Optional, defaults to 'false'. |
419 # |
420 # Example: |
421 # |
422 # - chown: |
423 # files: /home/casa |
424 # user: casa |
425 # group: staff |
426 # recursive: true |
427 # |
428 # Note: |
429 # |
430 # This task is not implemented under Windows. |
431 def chown(parameters) |
432 params_desc = { |
433 :files => { :mandatory => true, :type => :string_or_array }, |
434 :user => { :mandatory => false, :type => :string_or_integer }, |
435 :group => { :mandatory => false, :type => :string_or_integer }, |
436 :recursive => { :mandatory => false, :type => :boolean, |
437 :default => false } |
438 } |
439 check_parameters(parameters, params_desc) |
440 files = parameters['files'] |
441 user = parameters['user'] |
442 group = parameters['group'] |
443 recursive = parameters['recursive'] |
444 files = Dir.glob(files) |
445 if files.length > 0 |
446 puts "Changing owner of #{files.length} file(s) to '#{user}/#{group}'" |
447 begin |
448 if recursive |
449 FileUtils.chown_R(user, group, files) |
450 else |
451 FileUtils.chown(user, group, files) |
452 end |
453 rescue Exception |
454 error "Error changing owner: #{$!}" |
455 end |
456 end |
457 end |
458 |
459 # Make a directory, and parent directories if necessary. Doesn't |
460 # complain if directory already exists. Parameter is directory to |
461 # create as a String or a list of directories as an Array of Strings. |
462 # |
463 # Example |
464 # |
465 # - mkdir: "foo/bar" |
466 def mkdir(dirs) |
467 error "mkdir parameter must a String or an array of Strings" unless |
468 dirs.kind_of?(String) or dirs.kind_of?(Array) |
469 dirs = Array(dirs) |
470 for dir in dirs |
471 error "mkdir parameter must a String or an array of Strings" unless |
472 dir.kind_of?(String) |
473 puts "Creating directory '#{dir}'" |
474 begin |
475 FileUtils.makedirs(dir) |
476 rescue Exception |
477 error "Error creating directory '#{dir}': #{$!}" |
478 end |
479 end |
480 end |
481 |
482 # Copy files or directories to destination file or directory. Parameter |
483 # is a Hash with following entries: |
484 # |
485 # - src: glob or list of globs for source files or directories to copy. |
486 # Included source directories are copied recursively. |
487 # - dest: destination file or directory. |
488 # |
489 # Example |
490 # |
491 # - cp: |
492 # src: "img/*" |
493 # dest: :doc |
494 def cp(params) |
495 params_desc = { |
496 :src => { :mandatory => true, :type => :string_or_array }, |
497 :dest => { :mandatory => true, :type => :string } |
498 } |
499 check_parameters(params, params_desc) |
500 src = params['src'] |
501 dest = params['dest'] |
502 src = Array(src).map { |s| Dir.glob(s) }.flatten.uniq |
503 src = src.first if src.length == 1 |
504 if src.kind_of?(Array) |
505 nb_copies = src.length |
506 else |
507 nb_copies = 1 |
508 end |
509 puts "Copying #{nb_copies} file(s) to '#{dest}'" |
510 begin |
511 FileUtils.cp_r(src, dest) |
512 rescue Exception |
513 error "Error copying file(s): #{$!}" |
514 end |
515 end |
516 |
517 # Moves files or directories to destination file or directory. Parameter |
518 # is a Hash with following entries: |
519 # |
520 # - src: glob or list of globs for source files or directories to move. |
521 # Included source directories are moved recursively. |
522 # - dest: destination file or directory. |
523 # |
524 # Example |
525 # |
526 # - mv: |
527 # src: "**/*~" |
528 # dest: :trash |
529 def mv(params) |
530 params_desc = { |
531 :src => { :mandatory => true, :type => :string_or_array }, |
532 :dest => { :mandatory => true, :type => :string } |
533 } |
534 check_parameters(params, params_desc) |
535 src = params['src'] |
536 dest = params['dest'] |
537 src = Array(src).map { |s| Dir.glob(s) }.flatten.uniq |
538 src = src.first if src.length == 1 |
539 if src.kind_of?(Array) |
540 nb_moves = src.length |
541 else |
542 nb_moves = 1 |
543 end |
544 puts "Moving #{nb_moves} file(s) to '#{dest}'" |
545 begin |
546 FileUtils.mv(src, dest) |
547 rescue Exception |
548 error "Error moving file(s): #{$!}" |
549 end |
550 end |
551 |
552 # Copy filtered files. Parameter is a hash with following entries: |
553 # |
554 # - root: root directory for files to copy. Optional, defaults to current |
555 # directory. |
556 # - includes: list of globs for files to copy. Optional, defaults to |
557 # '**/*' to include all files recursively. |
558 # - excludes: list of globs for files to exclude from copy. Optional, |
559 # default to nil to exclude no file. |
560 # - dotmatch: tells if joker matches dot files. Optional, defaults to |
561 # false. |
562 # - flatten: tells if included files should be copied in destination |
563 # directory, ignoring their subdirectory. Optional, defaults to false. |
564 # - dest: destination directory for the copy, must be an existing |
565 # directory. |
566 # - lenient: tells if copy is lenient, which will silently succeed on |
567 # errors (for instance if root or destination directory don't exist). |
568 # Optional, defaults to false. |
569 # |
570 # Example: |
571 # |
572 # To copy all files from directory 'src', except those living in 'CVS' |
573 # directories, into directory 'destination', you could write: |
574 # |
575 # - copy: |
576 # root: src |
577 # includes: **/* |
578 # excludes: **/CVS/**/* |
579 # dest: destination |
580 # |
581 # Note: this task only deals with files. Thus, 'includes' and 'excludes' |
582 # globs should be ones for files. |
583 def copy(params) |
584 # check parameters and set default values |
585 params_desc = { |
586 :root => { :mandatory => false, :type => :string }, |
587 :includes => { :mandatory => false, :type => :string_or_array }, |
588 :excludes => { :mandatory => false, :type => :string_or_array }, |
589 :dotmatch => { :mandatory => false, :type => :boolean }, |
590 :dest => { :mandatory => true, :type => :string }, |
591 :flatten => { :mandatory => false, :type => :boolean, |
592 :default => false }, |
593 :lenient => { :mandatory => false, :type => :boolean, |
594 :default => false } |
595 } |
596 check_parameters(params, params_desc) |
597 root = params[:root] |
598 includes = params[:includes] |
599 excludes = params[:excludes] |
600 dotmatch = params[:dotmatch] |
601 dest = params[:dest] |
602 flatten = params[:flatten] |
603 lenient = params[:lenient] |
604 # check that destination is an existing directory |
605 if not (File.exists?(dest) and File.directory?(dest)) |
606 if lenient |
607 return |
608 else |
609 error "copy 'dest' parameter must be an existing directory" |
610 end |
611 end |
612 root = '.' if root == nil |
613 dotmatch = false if dotmatch == nil |
614 if not (File.exists?(root) and File.directory?(root)) |
615 if lenient |
616 return |
617 else |
618 error "copy 'root' parameter must be an existing directory" |
619 end |
620 end |
621 files = filter_files(root, includes, excludes, dotmatch) |
622 copy_files(root, files, dest, flatten) |
623 end |
624 |
625 # Move filtered files. Parameter is a hash with following entries: |
626 # |
627 # - root: root directory for files to move. Optional, defaults to |
628 # current directory. |
629 # - includes: list of globs for files to move. Optional, defaults to |
630 # '**/*' to include all files recursively. |
631 # - excludes: list of globs for files to exclude from move. Optional, |
632 # default to nil to exclude no file. |
633 # - flatten: tells if included files should be moved to destination |
634 # directory, ignoring their subdirectory. Optional, defaults to false. |
635 # - dotmatch: tells if joker matches dot files. Optional, defaults to |
636 # false. |
637 # - lenient: tells if move is lenient, which will silently succeed on |
638 # errors (for instance if root or destination directory don't exist). |
639 # Optional, defaults to false. |
640 # |
641 # Example: |
642 # |
643 # To move all files from directory 'src', except those living in 'CVS' |
644 # directories, into directory 'destination', you could write: |
645 # |
646 # - move: |
647 # root: src |
648 # includes: **/* |
649 # excludes: **/CVS/**/* |
650 # dest: destination |
651 # |
652 # Note: this task only deals with files. Thus, 'includes' and 'excludes' |
653 # globs should be ones for files and directories from root will not |
654 # be affected by this task. |
655 def move(params) |
656 # check parameters and set default values |
657 params_desc = { |
658 :root => { :mandatory => false, :type => :string, |
659 :default => '.' }, |
660 :includes => { :mandatory => false, :type => :string_or_array }, |
661 :excludes => { :mandatory => false, :type => :string_or_array }, |
662 :dest => { :mandatory => true, :type => :string }, |
663 :flatten => { :mandatory => false, :type => :boolean, |
664 :default => false }, |
665 :dotmatch => { :mandatory => false, :type => :boolean, |
666 :default => false }, |
667 :lenient => { :mandatory => false, :type => :boolean, |
668 :default => false } |
669 } |
670 check_parameters(params, params_desc) |
671 root = params[:root] |
672 includes = params[:includes] |
673 excludes = params[:excludes] |
674 dest = params[:dest] |
675 flatten = params[:flatten] |
676 dotmatch = params[:dotmatch] |
677 lenient = params[:lenient] |
678 # check that root and dest are existing directories |
679 if not (File.exists?(root) and File.directory?(root)) |
680 if lenient |
681 return |
682 else |
683 error "move 'root' parameter must be an existing directory" |
684 end |
685 end |
686 if not (File.exists?(dest) and File.directory?(dest)) |
687 if lenient |
688 return |
689 else |
690 error "move 'dest' parameter must be an existing directory" |
691 end |
692 end |
693 # select files and make move |
694 files = filter_files(root, includes, excludes, dotmatch) |
695 puts "Moving #{files.length} file(s) to '#{dest}'" |
696 for file in files |
697 from_file = File.join(root, file) |
698 if flatten |
699 to_file = File.join(dest, File.basename(file)) |
700 else |
701 to_file = File.join(dest, file) |
702 end |
703 to_dir = File.dirname(to_file) |
704 FileUtils.makedirs(to_dir) if not File.exists?(to_dir) |
705 FileUtils.mv(from_file, to_file) |
706 end |
707 end |
708 |
709 # Delete files for a given glob or list of globs. Parameter is a glob or |
710 # list of globs for files to delete. This task will raise an error if |
711 # told to delete a directory. Use task 'rmrf' to do so. |
712 # |
713 # Example |
714 # |
715 # - rm: ["**/*~", "**/.DS_Store"] |
716 def rm(globs) |
717 error "rm parameter is a String or Array of Strings" unless |
718 globs.kind_of?(String) or globs.kind_of?(Array) |
719 globs = Array(globs) |
720 for glob in globs |
721 error "rm parameter is a String or Array of Strings" unless |
722 glob.kind_of?(String) |
723 files = Dir.glob(glob) |
724 size = (files.kind_of?(Array) ? files.size : 1) |
725 puts "Deleting #{size} file(s)" if files.length > 0 |
726 for file in files |
727 begin |
728 FileUtils.rm(file) |
729 rescue Exception |
730 error "Error deleting files: #{$!}" |
731 end |
732 end |
733 end |
734 end |
735 |
736 # Delete files and directories recursively. Parameter is a glob or list |
737 # of globs for files and directories to delete. |
738 # |
739 # Example |
740 # |
741 # - rmrf: :build |
742 def rmrf(globs) |
743 error "rmrf parameter is a String or an Array of Strings" unless |
744 globs.kind_of?(String) or globs.kind_of?(Array) |
745 globs = Array(globs) |
746 for glob in globs |
747 error "rmrf parameter is a String or an Array of Strings" unless |
748 glob.kind_of?(String) |
749 dirs = Dir.glob(glob) |
750 size = (dirs.kind_of?(Array) ? dirs.size : 1) |
751 puts "Deleting #{size} directory(ies)" if dirs.length > 0 |
752 for dir in dirs |
753 begin |
754 FileUtils.rm_rf(dir) |
755 rescue Exception |
756 error "Error deleting directory(ies): #{$!}" |
757 end |
758 end |
759 end |
760 end |
761 |
762 # Alias for rmrf. |
763 alias :rmdir :rmrf |
764 |
765 # Update modification time and access time of files in a list. Files |
766 # are created if they don't exist. Parameter is a glob or list of |
767 # globs for files to touch. |
768 # |
769 # Example |
770 # |
771 # - touch: '#{target}/classes/**/*.class' |
772 def touch(globs) |
773 error "touch parameter is a String or an Array of Strings" unless |
774 globs.kind_of?(String) or globs.kind_of?(Array) |
775 globs = Array(globs) |
776 files = [] |
777 for glob in globs |
778 error "touch parameter is a String or an Array of Strings" unless |
779 glob.kind_of?(String) |
780 new_files = Dir.glob(glob) |
781 if new_files.length == 0 |
782 files << glob |
783 else |
784 files += new_files |
785 end |
786 end |
787 files.uniq! |
788 size = (files.kind_of?(Array) ? files.size : 1) |
789 puts "Touching #{size} file(s)" if size > 0 |
790 begin |
791 FileUtils.touch(files) |
792 rescue Exception |
793 error "Error touching file(s): #{$!}" |
794 end |
795 end |
796 |
797 # Find files for a glob or list of globs and store list in a property. |
798 # Parameter is a Hash with entries: |
799 # |
800 # - root: root directory for file search. Defaults to '.' (current |
801 # directory). |
802 # - includes: glob or list of globs for files to look for. Defaults to |
803 # '**/*' to include all files recursively. |
804 # - excludes: glob or list of globs for files to exclude from search. |
805 # Defaults to nil to exclude no file. |
806 # - dotmatch: tells if joker matches dot files. Optional, defaults to |
807 # false. |
808 # - property: name of the property to set. |
809 # - join: a character used to join the list in a string. Defaults |
810 # to nil so that list is not joined. |
811 # |
812 # Example |
813 # |
814 # To find all PNG in files in 'img' directory, and store the list in |
815 # property image_files, one could write: |
816 # |
817 # - find: |
818 # root: "img" |
819 # includes: "**/*.png" |
820 # property: "image_files" |
821 def find(params) |
822 params_desc = { |
823 :root => { :mandatory => false, :type => :string, |
824 :default => '.' }, |
825 :includes => { :mandatory => false, :type => :string_or_array }, |
826 :excludes => { :mandatory => false, :type => :string_or_array }, |
827 :property => { :mandatory => true, :type => :string }, |
828 :dotmatch => { :mandatory => false, :type => :boolean, |
829 :default => false }, |
830 :join => { :mandatory => false, :type => :string } |
831 } |
832 check_parameters(params, params_desc) |
833 root = params[:root] |
834 includes = params[:includes] |
835 excludes = params[:excludes] |
836 property = params[:property] |
837 dotmatch = params[:dotmatch] |
838 join = params[:join] |
839 files = filter_files(root, includes, excludes, dotmatch) |
840 if join |
841 files = files.join(join) |
842 end |
843 @build.context.set_property(property, files) |
844 end |
845 |
846 # Load a YAML file in a given property. |
847 # |
848 # - file: the YAML file name to load. |
849 # - prop: the property name to set with YAML parsed content. |
850 # |
851 # Example |
852 # |
853 # - yaml_load: |
854 # file: "my_list.yml" |
855 # prop: "my_list" |
856 def yaml_load(params) |
857 params_desc = { |
858 :file => { :mandatory => true, :type => :string }, |
859 :prop => { :mandatory => true, :type => :string }, |
860 } |
861 check_parameters(params, params_desc) |
862 file = params[:file] |
863 prop = params[:prop] |
864 error "YAML file '#{file}' not found" if not File.exists?(file) |
865 script = "#{prop} = YAML.load(File.read('#{file}'))" |
866 begin |
867 @build.context.evaluate_script(script) |
868 rescue Exception |
869 error "Error loading YAML file '#{file}': #{$!}" |
870 end |
871 end |
872 |
873 # Dump the content of a given property into a YAML file. |
874 # |
875 # - prop: the property to dump. |
876 # - file: the YAML file name to dump into. |
877 # |
878 # Example |
879 # |
880 # - yaml_dump: |
881 # prop: "my_list" |
882 # file: "my_list.yml" |
883 def yaml_dump(params) |
884 params_desc = { |
885 :prop => { :mandatory => true, :type => :string }, |
886 :file => { :mandatory => true, :type => :string } |
887 } |
888 check_parameters(params, params_desc) |
889 prop = params[:prop] |
890 file = params[:file] |
891 script = "File.open('#{file}', 'w') {|f| f.write(YAML.dump(#{prop}))}" |
892 begin |
893 @build.context.evaluate_script(script) |
894 rescue Exception |
895 error "Error dumping YAML file '#{file}': #{$!}" |
896 end |
897 end |
898 |
899 ###################################################################### |
900 # RUBY RELATED TASKS # |
901 ###################################################################### |
902 |
903 # Tests a required library and prints an error message if import |
904 # fails. Parameter is a Hash with entries: |
905 # |
906 # - library: required library (as in require call). |
907 # - message: error message to print if require fails. |
908 # |
909 # Example |
910 # |
911 # - required: |
912 # library: foo |
913 # message: > |
914 # Library foo must be installed (gem install foo) to run |
915 # task bar. |
916 def required(params) |
917 require 'rubygems' |
918 require 'rubygems/gem_runner' |
919 params_desc = { |
920 :library => { :mandatory => true, :type => :string }, |
921 :message => { :mandatory => true, :type => :string } |
922 } |
923 check_parameters(params, params_desc) |
924 library = params[:library] |
925 message = params[:message] |
926 available = Bee::VersionDependant::gem_available?(library) |
927 error message if not available |
928 end |
929 |
930 # Run Ruby unit tests listed as a glob or list of globs in a given |
931 # directory (that defaults to current one). Parameter is a Hash with |
932 # following entries: |
933 # |
934 # - root: root directory for files to include. Defaults to current |
935 # directory. |
936 # - includes: glob or list of globs for unit test files to run. |
937 # Defaults to '**/*' to include all files recursively. |
938 # - excludes: glob or list of globs for unit test files to exclude. |
939 # Defaults to nil to exclude no file. |
940 # - dotmatch: tells if joker matches dot files. Optional, defaults to |
941 # false. |
942 # - dir: directory where to run unit tests. |
943 # |
944 # Example |
945 # |
946 # - find: |
947 # root: :test |
948 # includes: "**/tc_*.rb" |
949 # dir: "test" |
950 # |
951 # Notes |
952 # |
953 # For ruby 1.9 and later, you must install gem 'test-unit' to run this |
954 # task. |
955 def test(params) |
956 require 'test/unit' |
957 params_desc = { |
958 :root => { :mandatory => false, :type => :string, |
959 :default => '.' }, |
960 :includes => { :mandatory => true, :type => :string }, |
961 :excludes => { :mandatory => false, :type => :string }, |
962 :dotmatch => { :mandatory => false, :type => :boolean, |
963 :default => false }, |
964 :dir => { :mandatory => false, :type => :string, |
965 :default => '.' } |
966 } |
967 check_parameters(params, params_desc) |
968 root = params[:root] |
969 includes = params[:includes] |
970 excludes = params[:excludes] |
971 dotmatch = params[:dotmatch] |
972 dir = params[:dir] |
973 error "Test directory '#{dir}' not found" if |
974 not (File.exists?(dir) and File.directory?(dir)) |
975 files = filter_files(root, includes, excludes, dotmatch) |
976 files.map! { |file| File.expand_path(File.join(root, file)) } |
977 size = (files.kind_of?(Array) ? files.size : 1) |
978 puts "Running #{size} unit test(s)" |
979 for file in files |
980 load file |
981 end |
982 old_dir = Dir.pwd |
983 begin |
984 Dir.chdir(dir) |
985 if Bee::VersionDependant::ruby_lower_than('1.9.2') |
986 runner = Test::Unit::AutoRunner.new(false) |
987 else |
988 runner = MiniTest::Unit.new() |
989 end |
990 ok = runner.run |
991 error "Test failure" if not ok |
992 ensure |
993 Dir.chdir(old_dir) |
994 end |
995 end |
996 |
997 # Run an ERB file or source in bee context and store result in a file or |
998 # property. Parameter is a Hash with following entries: |
999 # |
1000 # - source: ERB source text (if no 'src'). |
1001 # - src: ERB file name (if no 'source'). |
1002 # - dest: file where to store result (if no 'property'). |
1003 # - property: property name where to store result (if no 'dest'). |
1004 # - options: ERB options, a String containing one or more of the |
1005 # following modifiers: |
1006 # % enables Ruby code processing for lines beginning with % |
1007 # <> omit newline for lines starting with <% and ending in %> |
1008 # > omit newline for lines ending in %> |
1009 # |
1010 # For more information ebout ERB syntax, please see documentation at: |
1011 # http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/. |
1012 # |
1013 # Example |
1014 # |
1015 # - erb: { src: "gem.spec.erb", dest: "gem.spec" } |
1016 # |
1017 # Notes |
1018 # |
1019 # In these ERB files, you can access a property _foo_ writing: |
1020 # |
1021 # <p>Hello <%= foo %>!</p> |
1022 def erb(params) |
1023 params_desc = { |
1024 :source => { :mandatory => false, :type => :string }, |
1025 :src => { :mandatory => false, :type => :string }, |
1026 :dest => { :mandatory => false, :type => :string }, |
1027 :property => { :mandatory => false, :type => :string }, |
1028 :options => { :mandatory => false, :type => :string } |
1029 } |
1030 check_parameters(params, params_desc) |
1031 source = params[:source] |
1032 src = params[:src] |
1033 dest = params[:dest] |
1034 property = params[:property] |
1035 options = params[:options] |
1036 error "Must pass one of 'source' or 'src' parameters to erb task" if |
1037 not source and not src |
1038 error "Must pass one of 'dest' or 'property' parameters to erb task" if |
1039 not dest and not property |
1040 error "erb src file '#{src}' not found" if src and |
1041 (not File.exists?(src) or not File.file?(src) or |
1042 not File.readable?(src)) |
1043 # load ERB source |
1044 erb_source = source||File.open(src, 'r') {|f| f.read} |
1045 if options |
1046 template = ERB.new(erb_source, 0, options) |
1047 else |
1048 template = ERB.new(erb_source) |
1049 end |
1050 if src |
1051 puts "Processing ERB '#{src}'" |
1052 else |
1053 puts "Processing ERB" |
1054 end |
1055 begin |
1056 result = template.result(@build.context.context_binding) |
1057 rescue Exception |
1058 error "Error processing ERB: #{$!}" |
1059 end |
1060 # write result in file or set property |
1061 if dest |
1062 begin |
1063 File.open(dest, 'w') { |file| file.write(result) } |
1064 rescue Exception |
1065 error "Error writing ERB result in file: #{$!}" |
1066 end |
1067 else |
1068 @build.context.set_property(property, result) |
1069 end |
1070 end |
1071 |
1072 # Generate RDoc documentation for a given list of globs to include or |
1073 # exclude and a destination directory. Parameter is a Hash with following |
1074 # entries: |
1075 # |
1076 # - root: root directory for files to include. Defaults to current |
1077 # directory. |
1078 # - includes: glob or list of globs for files or directories to document. |
1079 # Defaults to '**/*' to include all files. |
1080 # - excludes: glob or list of globs for files or directories that should |
1081 # not be documented. Defaults to nil to exclude no file. |
1082 # - dotmatch: tells if joker matches dot files. Optional, defaults to |
1083 # false. |
1084 # - dest: destination directory for generated documentation. |
1085 # - options: additional options as a string or list of strings. |
1086 # |
1087 # Example |
1088 # |
1089 # - rdoc: |
1090 # includes: ["README", "LICENSE", "#{src}/**/*"] |
1091 # dest: :api |
1092 def rdoc(params) |
1093 require 'rdoc/rdoc' |
1094 params_desc= { |
1095 :root => { :mandatory => false, :type => :string }, |
1096 :includes => { :mandatory => true, :type => :string_or_array }, |
1097 :excludes => { :mandatory => false, :type => :string_or_array }, |
1098 :dotmatch => { :mandatory => false, :type => :boolean, |
1099 :default => false }, |
1100 :dest => { :mandatory => true, :type => :string }, |
1101 :options => { :mandatory => false, :type => :string_or_array } |
1102 } |
1103 check_parameters(params, params_desc) |
1104 root = params[:root] |
1105 includes = params[:includes] |
1106 excludes = params[:excludes] |
1107 dotmatch = params[:dotmatch] |
1108 dest = params[:dest] |
1109 options = params[:options] |
1110 files = filter_files(root, includes, excludes, dotmatch) |
1111 command_line = ['-S', '-o', dest] |
1112 command_line += options if options |
1113 command_line += files |
1114 begin |
1115 rdoc = RDoc::RDoc.new |
1116 rdoc.document(command_line) |
1117 rescue Exception |
1118 error "Error generating RDoc: #{$!}" |
1119 end |
1120 end |
1121 |
1122 # Generate a Gem package in current directory, named after the Gem name |
1123 # and version. Parameter is the name of the Gem description file. |
1124 # |
1125 # Example |
1126 # |
1127 # - gem: :gem_spec |
1128 def gem(description) |
1129 require 'rubygems' |
1130 require 'rubygems/gem_runner' |
1131 error "gem parameter must be an existing file" if |
1132 not description.kind_of?(String) or not File.exists?(description) |
1133 arguments = ['build', description] |
1134 begin |
1135 Gem::GemRunner.new.run(arguments) |
1136 rescue Exception |
1137 error "Error generating Gem: #{$!}" |
1138 end |
1139 end |
1140 |
1141 # Run another bee build file. |
1142 # |
1143 # - file: the build file to run, relative to current build file. |
1144 # Optional, defaults to 'build.yml'. |
1145 # - target: target or list of targets to run (default target if omitted). |
1146 # - properties: boolean (true or false) that tells if properties of |
1147 # current build file should be sent and overwrite those of target |
1148 # build. Properties modified in child build don't change in parent |
1149 # one. Defaults to false. |
1150 # |
1151 # Example |
1152 # |
1153 # - bee: |
1154 # file: "doc/build.yml" |
1155 # target: "pdf" |
1156 # properties: true |
1157 def bee(parameters) |
1158 # parse parameters |
1159 params_desc = { |
1160 :file => { :mandatory => false, :type => :string, :default => 'build.yml' }, |
1161 :target => { :mandatory => false, :type => :string_or_array, :default => '' }, |
1162 :properties => { :mandatory => false, :type => :boolean, :default => false } |
1163 } |
1164 check_parameters(parameters, params_desc) |
1165 file = parameters[:file] |
1166 target = parameters[:target] |
1167 properties = parameters[:properties] |
1168 # run target build |
1169 props = {} |
1170 if properties |
1171 for name in @build.context.properties |
1172 props[name] = @build.context.get_property(name) |
1173 end |
1174 end |
1175 begin |
1176 build = Bee::Build.load(file, false, props) |
1177 build.run(target, @build.listener.clone) |
1178 rescue Exception |
1179 error "Error invoking build file '#{file}': #{$!}" |
1180 end |
1181 end |
1182 |
1183 ###################################################################### |
1184 # ARCHIVE TASKS # |
1185 ###################################################################### |
1186 |
1187 # Generate a ZIP archive. Parameter is a Hash with following entries: |
1188 # |
1189 # - root: root directory for files to include in the archive. Defaults |
1190 # to '.' for current directory. |
1191 # - includes: glob or list of globs for files to select for the archive. |
1192 # Defaults to '**/*' to include all files recursively. |
1193 # - excludes: glob or list of globs for files to exclude from the archive. |
1194 # Defaults to nil to exclude no file. |
1195 # - dotmatch: tells if joker matches dot files. Optional, defaults to |
1196 # false. |
1197 # - dest: the archive file to generate. |
1198 # - prefix: prefix for archive entries (default to nil). |
1199 # |
1200 # Example |
1201 # |
1202 # - zip: |
1203 # excludes: ["build/**/*", "**/*~"] |
1204 # dest: :zip_archive |
1205 # |
1206 # Note |
1207 # |
1208 # If archive already exists, files are added to the archive. |
1209 def zip(parameters) |
1210 require 'zip/zip' |
1211 params_desc = { |
1212 :root => { :mandatory => false, :type => :string }, |
1213 :includes => { :mandatory => false, :type => :string_or_array, |
1214 :default => "**/*"}, |
1215 :excludes => { :mandatory => false, :type => :string_or_array, |
1216 :default => nil }, |
1217 :dotmatch => { :mandatory => false, :type => :boolean, |
1218 :default => false }, |
1219 :dest => { :mandatory => true, :type => :string }, |
1220 :prefix => { :mandatory => false, :type => :string } |
1221 } |
1222 check_parameters(parameters, params_desc) |
1223 root = parameters[:root] |
1224 includes = parameters[:includes] |
1225 excludes = parameters[:excludes] |
1226 dotmatch = parameters[:dotmatch] |
1227 dest = parameters[:dest] |
1228 prefix = parameters[:prefix] |
1229 files = filter_files(root, includes, excludes, dotmatch) |
1230 # build the archive |
1231 puts "Building ZIP archive '#{dest}'" |
1232 begin |
1233 Zip::ZipFile.open(dest, Zip::ZipFile::CREATE) do |zip| |
1234 for file in files |
1235 path = (root == nil ? file : File.join(root, file)) |
1236 entry = prefix ? File.join(prefix, file) : file |
1237 puts "Adding '#{entry}'" if @verbose |
1238 zip.add(entry, path) |
1239 end |
1240 zip.close |
1241 end |
1242 rescue Exception |
1243 error "Error building ZIP archive: #{$!}" |
1244 end |
1245 end |
1246 |
1247 # Extract ZIP archive to a destination directory. Existing extracted |
1248 # files are not overwritten and result in an error. Parameter is a Hash |
1249 # with following entries: |
1250 # |
1251 # - src: archive to extract. |
1252 # - dest: destination directory for extracted files. Optional, defaults |
1253 # to current directory. |
1254 # |
1255 # Example |
1256 # |
1257 # - unzip: |
1258 # src: myarchive.zip |
1259 # dest: mydir |
1260 def unzip(parameters) |
1261 require 'zip/zip' |
1262 params_desc = { |
1263 :src => { :mandatory => true, :type => :string }, |
1264 :dest => { :mandatory => false, :type => :string, :default => '.' } |
1265 } |
1266 check_parameters(parameters, params_desc) |
1267 src = parameters[:src] |
1268 dest = parameters[:dest] |
1269 error "unzip 'src' parameter must be an readable ZIP archive" unless |
1270 File.exists?(src) and File.readable?(src) |
1271 FileUtils.makedirs(dest) if not File.exists?(dest) |
1272 puts "Extracting ZIP file '#{src}' to '#{dest}'" |
1273 begin |
1274 Zip::ZipFile.foreach(src) do |entry| |
1275 puts "Writing '#{entry}'" if @verbose |
1276 tofile = File.join(dest, entry.name) |
1277 if entry.file? |
1278 dir = File.dirname(tofile) |
1279 FileUtils.makedirs(dir) if not File.exists?(dir) |
1280 entry.extract(tofile) |
1281 elsif entry.directory? |
1282 FileUtils.makedirs(tofile) |
1283 end |
1284 end |
1285 rescue Exception |
1286 error "Error extracting ZIP archive: #{$!}" |
1287 end |
1288 end |
1289 |
1290 # Generate a TAR archive. Parameter is a Hash with following entries: |
1291 # |
1292 # - root: root directory for files to include. Defaults to current |
1293 # directory. |
1294 # - includes: glob or list of globs for files to select for the archive. |
1295 # Defaults to '**/*' to include all files recursively. |
1296 # - excludes: glob or list of globs for files to exclude from the archive. |
1297 # Defaults to nil to exclude no file. |
1298 # - dotmatch: tells if joker matches dot files. Optional, defaults to |
1299 # false. |
1300 # - dest: the archive file to generate. |
1301 # |
1302 # Example |
1303 # |
1304 # - tar: |
1305 # includes: "**/*" |
1306 # excludes: ["build", "build/**/*", "**/*~"] |
1307 # dest: :tar_archive |
1308 # |
1309 # Note |
1310 # |
1311 # If archive already exists, it's overwritten. |
1312 def tar(parameters) |
1313 require 'archive/tar/minitar' |
1314 # parse parameters |
1315 params_desc = { |
1316 :root => { :mandatory => false, :type => :string }, |
1317 :includes => { :mandatory => true, :type => :string_or_array }, |
1318 :excludes => { :mandatory => false, :type => :string_or_array, |
1319 :default => nil }, |
1320 :dotmatch => { :mandatory => false, :type => :boolean, |
1321 :default => false }, |
1322 :dest => { :mandatory => true, :type => :string } |
1323 } |
1324 check_parameters(parameters, params_desc) |
1325 root = parameters[:root] |
1326 includes = parameters[:includes] |
1327 excludes = parameters[:excludes] |
1328 dotmatch = parameters[:dotmatch] |
1329 dest = parameters[:dest] |
1330 files = filter_files(root, includes, excludes, dotmatch) |
1331 # build the archive |
1332 puts "Processing TAR archive '#{dest}'" |
1333 begin |
1334 current_dir = Dir.pwd |
1335 abs_dest = File.expand_path(dest) |
1336 Dir.chdir(root) if root |
1337 Archive::Tar::Minitar::Output.open(abs_dest) do |tarfile| |
1338 for file in files |
1339 puts "Adding '#{file}'" if @verbose |
1340 Archive::Tar::Minitar.pack_file(file, tarfile) |
1341 end |
1342 end |
1343 rescue Exception |
1344 error "Error generating TAR archive: #{$!}" |
1345 ensure |
1346 Dir.chdir(current_dir) |
1347 end |
1348 end |
1349 |
1350 # Generate a GZIP archive for a given file. Parameter is a Hash with |
1351 # following entries: |
1352 # |
1353 # - src: source file to generate GZIP for. |
1354 # - dest: GZIP file to generate. Defaults to the src file with '.gz' |
1355 # extension added. |
1356 # |
1357 # Example |
1358 # |
1359 # - gzip: |
1360 # src: "dist.tar" |
1361 # dest: "dist.tar.gz" |
1362 def gzip(parameters) |
1363 require 'zlib' |
1364 # parse parameters |
1365 params_desc = { |
1366 :src => { :mandatory => true, :type => :string }, |
1367 :dest => { :mandatory => false, :type => :string } |
1368 } |
1369 check_parameters(parameters, params_desc) |
1370 src = parameters[:src] |
1371 dest = parameters[:dest] |
1372 dest = src + '.gz' if not dest |
1373 # compress file |
1374 puts "Processing GZIP archive '#{dest}'" |
1375 begin |
1376 File.open(src) do |input| |
1377 output = Zlib::GzipWriter.new(File.open(dest, 'wb')) |
1378 output.write(input.read) |
1379 output.close |
1380 end |
1381 rescue Exception |
1382 error "Error generating GZIP archive: #{$!}" |
1383 end |
1384 end |
1385 |
1386 # Expand a GZIP archive for a given file. Parameter is a Hash with |
1387 # following entries: |
1388 # |
1389 # - src: GZIP file to expand. |
1390 # - dest: destination for expanded file. Destination file can be guessed |
1391 # (and thus omitted) for src files '.gz', '.gzip' and '.tgz'; |
1392 # corresponding dest for latest will be '.tar'. |
1393 # |
1394 # Example |
1395 # |
1396 # - gunzip: |
1397 # src: "dist.tar.gz" |
1398 # dest: "dist.tar" |
1399 def gunzip(parameters) |
1400 require 'zlib' |
1401 # parse parameters |
1402 params_desc = { |
1403 :src => { :mandatory => true, :type => :string }, |
1404 :dest => { :mandatory => false, :type => :string } |
1405 } |
1406 check_parameters(parameters, params_desc) |
1407 src = parameters[:src] |
1408 dest = parameters[:dest] |
1409 error "gunzip 'src' parameter must be an readable GZIP archive" unless |
1410 File.exists?(src) and File.readable?(src) |
1411 if not dest |
1412 if src =~ /.*\.gz$/ |
1413 dest = src[0..-4] |
1414 elsif src =~ /.*\.gzip$/ |
1415 dest = src[0..-6] |
1416 elsif src =~ /.*\.tgz/ |
1417 dest = src[0..-5]+'.tar' |
1418 else |
1419 error "gunzip can't guess 'dest' parameter from 'src' file name" |
1420 end |
1421 end |
1422 # expand file |
1423 puts "Expanding GZIP archive '#{dest}'" |
1424 begin |
1425 Zlib::GzipReader.open(src) do |input| |
1426 output = File.open(dest, 'wb') |
1427 output.write(input.read) |
1428 output.close |
1429 end |
1430 rescue Exception |
1431 error "Error expanding GZIP archive: #{$!}" |
1432 end |
1433 end |
1434 |
1435 # Generate a TAR.GZ archive. Parameter is a Hash with following entries: |
1436 # |
1437 # - root: root directory for files to include. Defaults to current |
1438 # directory. |
1439 # - includes: glob or list of globs for files to select for the archive. |
1440 # Defaults to '**/*' to include all files recursively. |
1441 # - excludes: glob or list of globs for files to exclude from the archive. |
1442 # Defaults to nil to exclude no file. |
1443 # - dotmatch: tells if joker matches dot files. Optional, defaults to |
1444 # false. |
1445 # - dest: the archive file to generate. |
1446 # |
1447 # Example |
1448 # |
1449 # - targz: |
1450 # excludes: ["build/**/*", "**/*~"] |
1451 # dest: :targz_archive |
1452 # |
1453 # Note |
1454 # |
1455 # If archive already exists, it's overwritten. |
1456 def targz(parameters) |
1457 require 'archive/tar/minitar' |
1458 require 'zlib' |
1459 # parse parameters |
1460 params_desc = { |
1461 :root => { :mandatory => false, :type => :string, |
1462 :default => '.' }, |
1463 :includes => { :mandatory => false, :type => :string_or_array, |
1464 :default => '**/*' }, |
1465 :excludes => { :mandatory => false, :type => :string_or_array, |
1466 :default => nil }, |
1467 :dotmatch => { :mandatory => false, :type => :boolean, |
1468 :default => false }, |
1469 :dest => { :mandatory => true, :type => :string } |
1470 } |
1471 check_parameters(parameters, params_desc) |
1472 root = parameters[:root] |
1473 includes = parameters[:includes] |
1474 excludes = parameters[:excludes] |
1475 dotmatch = parameters[:dotmatch] |
1476 dest = parameters[:dest] |
1477 files = filter_files(root, includes, excludes, dotmatch) |
1478 # build the archive |
1479 puts "Building TARGZ archive '#{dest}'" |
1480 begin |
1481 current_dir = Dir.pwd |
1482 abs_dest = File.expand_path(dest) |
1483 Dir.chdir(root) if root |
1484 Archive::Tar::Minitar::Output. |
1485 open(Zlib::GzipWriter.new(File.open(abs_dest, 'wb'))) do |tgz| |
1486 for file in files |
1487 puts "Adding '#{file}'" if @verbose |
1488 Archive::Tar::Minitar.pack_file(file, tgz) |
1489 end |
1490 end |
1491 rescue Exception |
1492 error "Error generating TARGZ archive: #{$!}" |
1493 ensure |
1494 Dir.chdir(current_dir) |
1495 end |
1496 end |
1497 |
1498 # Extract TAR archive to a destination directory. Gziped archives are |
1499 # managed if their extension is '.tgz' or '.tar.gz'. Extracted files |
1500 # are overwritten if they already exist. Parameter is a Hash with |
1501 # following entries: |
1502 # |
1503 # - src: archive to extract. |
1504 # - dest: destination directory for extracted files. Optional, defaults |
1505 # to current directory. |
1506 # |
1507 # Example |
1508 # |
1509 # - untar: |
1510 # src: myarchive.tar.gz |
1511 # dest: mydir |
1512 def untar(parameters) |
1513 require 'archive/tar/minitar' |
1514 require 'zlib' |
1515 params_desc = { |
1516 :src => { :mandatory => true, :type => :string }, |
1517 :dest => { :mandatory => false, :type => :string, :default => '.' } |
1518 } |
1519 check_parameters(parameters, params_desc) |
1520 src = parameters[:src] |
1521 dest = parameters[:dest] |
1522 error "untar 'src' parameter must be an readable TAR archive" unless |
1523 File.exists?(src) and File.readable?(src) |
1524 FileUtils.makedirs(dest) if not File.exists?(dest) |
1525 puts "Extracting TAR file '#{src}' to '#{dest}'" |
1526 begin |
1527 if src =~ /\.tar\.gz$/ or src =~ /\.tgz$/ |
1528 tgz = Zlib::GzipReader.new(File.open(src, 'rb')) |
1529 Archive::Tar::Minitar.unpack(tgz, dest) |
1530 else |
1531 Archive::Tar::Minitar.unpack(src, dest) |
1532 end |
1533 rescue Exception |
1534 error "Error extracting TAR archive: #{$!}" |
1535 end |
1536 end |
1537 |
1538 ###################################################################### |
1539 # FTP TASKS # |
1540 ###################################################################### |
1541 |
1542 # Login to a remote FTP site. Useful to test a connection. Raises a |
1543 # build error if connection fails. Parameter is a hash with following |
1544 # entries: |
1545 # |
1546 # - username: the username to connect to FTP. Defaults to anonymous. |
1547 # - password: the password to connect to FTP. Defaults to no password. |
1548 # - host: the hostname to connect to. |
1549 # |
1550 # Example |
1551 # |
1552 # - ftp_login: |
1553 # username: foo |
1554 # password: bar |
1555 # host: example.com |
1556 def ftp_login(params) |
1557 params_desc = { |
1558 :username => { :mandatory => false, :type => :string }, |
1559 :password => { :mandatory => false, :type => :string }, |
1560 :host => { :mandatory => true, :type => :string } |
1561 } |
1562 check_parameters(params, params_desc) |
1563 username = params[:username] |
1564 password = params[:password] |
1565 host = params[:host] |
1566 begin |
1567 Net::FTP.open(host) do |ftp| |
1568 ftp.login(username, password) |
1569 ftp.close |
1570 end |
1571 puts "Connection to FTP host '#{host}' sucessful" |
1572 rescue Exception |
1573 error "Error connecting to FTP host: #{$!}" |
1574 end |
1575 end |
1576 |
1577 # Get a file from a remote FTP site. Raises a build error this operation |
1578 # fails. Parameter is a hash with following entries: |
1579 # |
1580 # - username: the username to connect to FTP. Defaults to anonymous. |
1581 # - password: the password to connect to FTP. Defaults to no password. |
1582 # - host: the hostname to connect to. |
1583 # - file: the FTP path to remote file to get. |
1584 # - output: the local path to file to write. Defaults to same file name |
1585 # in current directory. |
1586 # - binary: sets the binary mode for download. Defaults to true. |
1587 # |
1588 # Example: |
1589 # |
1590 # - ftp_get: |
1591 # username: foo |
1592 # password: bar |
1593 # host: foo |
1594 # file: test.txt |
1595 def ftp_get(params) |
1596 params_desc = { |
1597 :username => { :mandatory => false, :type => :string }, |
1598 :password => { :mandatory => false, :type => :string }, |
1599 :host => { :mandatory => true, :type => :string }, |
1600 :file => { :mandatory => true, :type => :string }, |
1601 :output => { :mandatory => false, :type => :string }, |
1602 :binary => { :mandatory => false, :type => :boolean, |
1603 :default => true } |
1604 } |
1605 check_parameters(params, params_desc) |
1606 username = params[:username] |
1607 password = params[:password] |
1608 host = params[:host] |
1609 file = params[:file] |
1610 output = params[:output]||File.basename(file) |
1611 binary = params[:binary] |
1612 basename = File.basename(file) |
1613 puts "Getting file '#{basename}'..." |
1614 begin |
1615 Net::FTP.open(host) do |ftp| |
1616 ftp.login(username, password) |
1617 if binary |
1618 ftp.getbinaryfile(file, output) |
1619 else |
1620 ftp.gettextfile(file, output) |
1621 end |
1622 ftp.close |
1623 end |
1624 rescue Exception |
1625 error "Error getting file '#{basename}': #{$!}" |
1626 end |
1627 end |
1628 |
1629 # Put a file to a remote FTP site. Raises a build error this operation |
1630 # fails. Parameter is a hash with following entries: |
1631 # |
1632 # - username: the username to connect to FTP. Defaults to anonymous. |
1633 # - password: the password to connect to FTP. Defaults to no password. |
1634 # - host: the hostname to connect to. |
1635 # - file: locale file to send. |
1636 # - tofile: remote file to write on remote server. Defaults to base name |
1637 # of local file. |
1638 # - binary: sets the binary mode for upload. Defaults to true. |
1639 # |
1640 # Example: |
1641 # |
1642 # - ftp_put: |
1643 # username: foo |
1644 # password: bar |
1645 # host: foo |
1646 # file: test.txt |
1647 def ftp_put(params) |
1648 params_desc = { |
1649 :username => { :mandatory => false, :type => :string }, |
1650 :password => { :mandatory => false, :type => :string }, |
1651 :host => { :mandatory => true, :type => :string }, |
1652 :file => { :mandatory => true, :type => :string }, |
1653 :tofile => { :mandatory => false, :type => :string }, |
1654 :binary => { :mandatory => false, :type => :boolean, |
1655 :default => true } |
1656 } |
1657 check_parameters(params, params_desc) |
1658 username = params[:username] |
1659 password = params[:password] |
1660 host = params[:host] |
1661 file = params[:file] |
1662 tofile = params[:tofile] |
1663 binary = params[:binary] |
1664 basename = File.basename(file) |
1665 puts "Putting file '#{basename}'..." |
1666 begin |
1667 Net::FTP.open(host) do |ftp| |
1668 ftp.login(username, password) |
1669 if binary |
1670 ftp.putbinaryfile(file, tofile) |
1671 else |
1672 ftp.puttextfile(file, tofile) |
1673 end |
1674 ftp.close |
1675 end |
1676 rescue Exception |
1677 error "Error putting file '#{basename}': #{$!}" |
1678 end |
1679 end |
1680 |
1681 # Make a directory on a remote FTP site. Raises a build error this |
1682 # operation fails. Parameter is a hash with following entries: |
1683 # |
1684 # - username: the username to connect to FTP. Defaults to anonymous. |
1685 # - password: the password to connect to FTP. Defaults to no password. |
1686 # - host: the hostname to connect to. |
1687 # - dir: the path of the remote directory to create. |
1688 # |
1689 # Example: |
1690 # |
1691 # - ftp_mkdir: |
1692 # username: foo |
1693 # password: bar |
1694 # host: foo |
1695 # dir: test |
1696 def ftp_mkdir(params) |
1697 params_desc = { |
1698 :username => { :mandatory => false, :type => :string }, |
1699 :password => { :mandatory => false, :type => :string }, |
1700 :host => { :mandatory => true, :type => :string }, |
1701 :dir => { :mandatory => true, :type => :string } |
1702 } |
1703 check_parameters(params, params_desc) |
1704 username = params[:username] |
1705 password = params[:password] |
1706 host = params[:host] |
1707 dir = params[:dir] |
1708 basename = File.basename(dir) |
1709 puts "Making directory '#{basename}'..." |
1710 begin |
1711 Net::FTP.open(host) do |ftp| |
1712 ftp.login(username, password) |
1713 ftp.mkdir(dir) |
1714 ftp.close |
1715 end |
1716 rescue Exception |
1717 error "Error making directory '#{basename}': #{$!}" |
1718 end |
1719 end |
1720 |
1721 ###################################################################### |
1722 # CONSTRUCTS # |
1723 ###################################################################### |
1724 |
1725 # If construct will evaluate the expression in the 'if' entry and run |
1726 # block in the 'then' entry or 'else' entry accordingly. |
1727 # |
1728 # - if: the condition to evaluate. This is a Ruby expression (thus a |
1729 # string) evaluated in the build context, a symbol that refers to a |
1730 # property or a boolean. |
1731 # - then: block that is evaluated if condition in if is true. |
1732 # - else: block that is evaluated if condition in if is false. |
1733 # |
1734 # Example |
1735 # |
1736 # - if: RUBY_PLATFORM =~ /darwin/ |
1737 # then: |
1738 # - print: Hello, I'm a Mac |
1739 # else: |
1740 # - print: Hello, I'm a PC |
1741 def if |
1742 end |
1743 |
1744 # While construct will run the block in the 'do' entry while the |
1745 # condition in the 'while' entry is true. |
1746 # |
1747 # - while: the condition to evaluate. This is a Ruby expression evaluated |
1748 # in the build context. |
1749 # - do: the block to run while the condition is true. |
1750 # |
1751 # Example: |
1752 # |
1753 # - while: i > 0 |
1754 # do: |
1755 # - print: :i |
1756 # - rb: i -= 1 |
1757 def while |
1758 end |
1759 |
1760 # For construct iterates on a list in the 'in' entry, putting values in |
1761 # a property which name is in the 'for' entry and running the block in |
1762 # the 'do' entry for each value. |
1763 # |
1764 # - for: the name of the property which receives values of the iteration, |
1765 # as a string. |
1766 # - in: a list on which to iterate. This can be a list, a ruby expression |
1767 # to evaluate in the context of the build to obtain the Enumerable on |
1768 # which to iterate or a symbol that refers to a property that is a list. |
1769 # - do: the block to run at each iteration. |
1770 # |
1771 # Example |
1772 # |
1773 # - for: file |
1774 # in: [foo, bar] |
1775 # do: |
1776 # - print: "Creating #{file}..." |
1777 # - touch: :file |
1778 # |
1779 # The same using a reference to a property that is a list: |
1780 # |
1781 # - properties: |
1782 # list: ['foo', 'bar'] |
1783 # |
1784 # - target: test |
1785 # script: |
1786 # - for: name |
1787 # in: :list |
1788 # do: |
1789 # - print: "Hi #{name}!" |
1790 # |
1791 # To iterate five times, we could write (using a Ruby Range): |
1792 # |
1793 # - for: i |
1794 # in: (1..5) |
1795 # do: |
1796 # - print: :i |
1797 # |
1798 # To iterate on files in current directory, we could write: |
1799 # |
1800 # - for: file |
1801 # in: "Dir.glob('*')" |
1802 # do: |
1803 # - print: :file |
1804 def for |
1805 end |
1806 |
1807 # Try construct will run the block in the 'try' entry and will switch to |
1808 # block in the 'catch' entry if an error occurs. |
1809 # |
1810 # - try: the block to run. |
1811 # - catch: the block to switch to if an error occurs. |
1812 # |
1813 # Example: |
1814 # |
1815 # - try: |
1816 # - print: "In the try block" |
1817 # - throw: "Something went terribly wrong!" |
1818 # catch: |
1819 # - print: "An error occured" |
1820 def try |
1821 end |
1822 |
1823 end |
1824 |
1825 end |
1826 |
1827 end |
1828 |
Generated on Fri Oct 09 02:07:49 +0200 2015 with rcov 1.0.0