rake 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rake might be problematic. Click here for more details.

@@ -0,0 +1,165 @@
1
+ = Rake 0.8.2 Released
2
+
3
+ Rake version 0.8.2 is a new release of rake that includes a number of
4
+ new features and numerous bug fixes.
5
+
6
+ == Changes
7
+
8
+ === New Features in Version 0.8.2
9
+
10
+ * Switched from getoptlong to optparse (patches supplied by Edwin
11
+ Pratomo).
12
+
13
+ * The -T option will now attempt to dynamically sense the size of the
14
+ terminal. The -T output will only self-truncate if the output is a
15
+ tty. However, if RAKE_COLUMNS is explicitly set, it will be honored
16
+ in any case. (Patch provided by Gavin Stark).
17
+
18
+ * The following public methods have been added to rake task objects:
19
+
20
+ * task.clear -- Clear both the prerequisites and actions of the
21
+ target rake task.
22
+ * task.clear_prerequisites -- Clear all the existing prerequisites
23
+ from the target rake task.
24
+ * task.clear_actions -- Clear all the existing actions from the
25
+ target rake task.
26
+ * task.reenable -- Re-enable a task, allowing its actions to be
27
+ executed again if the task is invoked.
28
+
29
+ * Changed RDoc test task to have no default template. This makes it
30
+ easier for the tempate to pick up the template from the environment.
31
+
32
+ * Default values for task arguments can easily be specified with the
33
+ :with_defaults method. (Idea for default argument merging supplied
34
+ by (Adam Q. Salter)
35
+
36
+ === Bug Fixes in Version 0.8.2
37
+
38
+ * Fixed bug in package task so that it will include the subdir
39
+ directory in the package for testing. (Bug found by Adam Majer)
40
+
41
+ * Fixed filename dependency order bug in test_inspect_pending and
42
+ test_to_s_pending. (Bug found by Adam Majer)
43
+
44
+ * Fixed check for file utils options to make them immune to the
45
+ symbol/string differences. (Patch supplied by Edwin Pratomo)
46
+
47
+ * Fixed bug with rules involving multiple source, where only the first
48
+ dependency of a rule has any effect (Patch supplied by Emanuel
49
+ Inderm�hle)
50
+
51
+ * FileList#clone and FileList#dup have better sematics w.r.t. taint
52
+ and freeze.
53
+
54
+ * Changed from using Mutex to Monitor. Evidently Mutex causes thread
55
+ join errors when Ruby is compiled with -disable-pthreads. (Patch
56
+ supplied by Ittay Dror)
57
+
58
+ * Fixed bug in makefile parser that had problems with extra spaces in
59
+ file task names. (Patch supplied by Ittay Dror)
60
+
61
+ == Other changes in Version 0.8.2
62
+
63
+ * Added ENV var to rake's own Rakefile to prevent OS X from including
64
+ extended attribute junk in the rake package tar file. (Bug found by
65
+ Adam Majer)
66
+
67
+ * Added a performance patch for reading large makefile dependency
68
+ files. (Patch supplied by Ittay Dror)
69
+
70
+ == What is Rake
71
+
72
+ Rake is a build tool similar to the make program in many ways. But
73
+ instead of cryptic make recipes, Rake uses standard Ruby code to
74
+ declare tasks and dependencies. You have the full power of a modern
75
+ scripting language built right into your build tool.
76
+
77
+ == Availability
78
+
79
+ The easiest way to get and install rake is via RubyGems ...
80
+
81
+ gem install rake (you may need root/admin privileges)
82
+
83
+ Otherwise, you can get it from the more traditional places:
84
+
85
+ Home Page:: http://rake.rubyforge.org/
86
+ Download:: http://rubyforge.org/project/showfiles.php?group_id=50
87
+
88
+ == Task Argument Examples
89
+
90
+ Prior to version 0.8.0, rake was only able to handle command line
91
+ arguments of the form NAME=VALUE that were passed into Rake via the
92
+ ENV hash. Many folks had asked for some kind of simple command line
93
+ arguments, perhaps using "--" to separate regular task names from
94
+ argument values on the command line. The problem is that there was no
95
+ easy way to associate positional arguments on the command line with
96
+ different tasks. Suppose both tasks :a and :b expect a command line
97
+ argument: does the first value go with :a? What if :b is run first?
98
+ Should it then get the first command line argument.
99
+
100
+ Rake 0.8.0 solves this problem by explicitly passing values directly
101
+ to the tasks that need them. For example, if I had a release task
102
+ that required a version number, I could say:
103
+
104
+ rake release[0.8.2]
105
+
106
+ And the string "0.8.2" will be passed to the :release task. Multiple
107
+ arguments can be passed by separating them with a comma, for example:
108
+
109
+ rake name[john,doe]
110
+
111
+ Just a few words of caution. The rake task name and its arguments
112
+ need to be a single command line argument to rake. This generally
113
+ means no spaces. If spaces are needed, then the entire rake +
114
+ argument string should be quoted. Something like this:
115
+
116
+ rake "name[billy bob, smith]"
117
+
118
+ (Quoting rules vary between operating systems and shells, so make sure
119
+ you consult the proper docs for your OS/shell).
120
+
121
+ === Tasks that Expect Parameters
122
+
123
+ Parameters are only given to tasks that are setup to expect them. In
124
+ order to handle named parameters, the task declaration syntax for
125
+ tasks has been extended slightly.
126
+
127
+ For example, a task that needs a first name and last name might be
128
+ declared as:
129
+
130
+ task :name, :first_name, :last_name
131
+
132
+ The first argument is still the name of the task (:name in this case).
133
+ The next to argumements are the names of the parameters expected by
134
+ :name (:first_name and :last_name in the example).
135
+
136
+ To access the values of the paramters, the block defining the task
137
+ behaviour can now accept a second parameter:
138
+
139
+ task :name, :first_name, :last_name do |t, args|
140
+ puts "First name is #{args.first_name}"
141
+ puts "Last name is #{args.last_name}"
142
+ end
143
+
144
+ The first argument of the block "t" is always bound to the current
145
+ task object. The second argument "args" is an open-struct like object
146
+ that allows access to the task arguments. Extra command line
147
+ arguments to a task are ignored. Missing command line arguments are
148
+ given the nil value.
149
+
150
+ == Thanks
151
+
152
+ As usual, it was input from users that drove a alot of these changes. The
153
+ following people either contributed patches, made suggestions or made
154
+ otherwise helpful comments. Thanks to ...
155
+
156
+ * Edwin Pratomo
157
+ * Gavin Stark
158
+ * Adam Q. Salter
159
+ * Adam Majer
160
+ * Emanuel Inderm�hle
161
+ * Ittay Dror
162
+ * Bheeshmar Redheendran (for spending an afternoon with me debugging
163
+ windows issues)
164
+
165
+ -- Jim Weirich
@@ -29,13 +29,14 @@
29
29
  # as a library via a require statement, but it can be distributed
30
30
  # independently as an application.
31
31
 
32
- RAKEVERSION = '0.8.1'
32
+ RAKEVERSION = '0.8.2'
33
33
 
34
34
  require 'rbconfig'
35
35
  require 'getoptlong'
36
36
  require 'fileutils'
37
37
  require 'singleton'
38
- require 'thread'
38
+ require 'monitor'
39
+ require 'optparse'
39
40
  require 'ostruct'
40
41
 
41
42
  ######################################################################
@@ -239,6 +240,33 @@ end # class String
239
240
  ##############################################################################
240
241
  module Rake
241
242
 
243
+ # Errors -----------------------------------------------------------
244
+
245
+ # Error indicating an ill-formed task declaration.
246
+ class TaskArgumentError < ArgumentError
247
+ end
248
+
249
+ # Error indicating a recursion overflow error in task selection.
250
+ class RuleRecursionOverflowError < StandardError
251
+ def initialize(*args)
252
+ super
253
+ @targets = []
254
+ end
255
+
256
+ def add_target(target)
257
+ @targets << target
258
+ end
259
+
260
+ def message
261
+ super + ": [" + @targets.reverse.join(' => ') + "]"
262
+ end
263
+ end
264
+
265
+ # Error indicating a problem in locating the home directory on a
266
+ # Win32 system.
267
+ class Win32HomeError < RuntimeError
268
+ end
269
+
242
270
  # --------------------------------------------------------------------------
243
271
  # Rake module singleton methods.
244
272
  #
@@ -266,16 +294,22 @@ module Rake
266
294
  module Cloneable
267
295
  # Clone an object by making a new object and setting all the instance
268
296
  # variables to the same values.
269
- def clone
297
+ def dup
270
298
  sibling = self.class.new
271
299
  instance_variables.each do |ivar|
272
300
  value = self.instance_variable_get(ivar)
273
301
  new_value = value.clone rescue value
274
302
  sibling.instance_variable_set(ivar, new_value)
275
303
  end
304
+ sibling.taint if tainted?
305
+ sibling
306
+ end
307
+
308
+ def clone
309
+ sibling = dup
310
+ sibling.freeze if frozen?
276
311
  sibling
277
312
  end
278
- alias dup clone
279
313
  end
280
314
 
281
315
  ####################################################################
@@ -286,12 +320,15 @@ module Rake
286
320
 
287
321
  attr_reader :names
288
322
 
323
+ # Create a TaskArgument object with a list of named arguments
324
+ # (given by :names) and a set of associated values (given by
325
+ # :values). :parent is the parent argument object.
289
326
  def initialize(names, values, parent=nil)
290
327
  @names = names
291
328
  @parent = parent
292
329
  @hash = {}
293
330
  names.each_with_index { |name, i|
294
- @hash[name.to_sym] = values[i]
331
+ @hash[name.to_sym] = values[i] unless values[i].nil?
295
332
  }
296
333
  end
297
334
 
@@ -307,6 +344,13 @@ module Rake
307
344
  lookup(index.to_sym)
308
345
  end
309
346
 
347
+ # Specify a hash of default values for task arguments. Use the
348
+ # defaults only if there is no specific value for the given
349
+ # argument.
350
+ def with_defaults(defaults)
351
+ @hash = defaults.merge(@hash)
352
+ end
353
+
310
354
  def each(&block)
311
355
  @hash.each(&block)
312
356
  end
@@ -342,6 +386,8 @@ module Rake
342
386
  end
343
387
  end
344
388
 
389
+ EMPTY_TASK_ARGS = TaskArguments.new([], [])
390
+
345
391
  ####################################################################
346
392
  # InvocationChain tracks the chain of task invocations to detect
347
393
  # circular dependencies.
@@ -409,6 +455,9 @@ module Rake
409
455
  # List of prerequisites for a task.
410
456
  attr_reader :prerequisites
411
457
 
458
+ # List of actions attached to a task.
459
+ attr_reader :actions
460
+
412
461
  # Application owning this task.
413
462
  attr_accessor :application
414
463
 
@@ -446,12 +495,12 @@ module Rake
446
495
  # +enhance+ to add actions and prerequisites.
447
496
  def initialize(task_name, app)
448
497
  @name = task_name.to_s
449
- @prerequisites = FileList[]
498
+ @prerequisites = []
450
499
  @actions = []
451
500
  @already_invoked = false
452
501
  @full_comment = nil
453
502
  @comment = nil
454
- @lock = Mutex.new
503
+ @lock = Monitor.new
455
504
  @application = app
456
505
  @scope = app.current_scope
457
506
  @arg_names = nil
@@ -488,6 +537,31 @@ module Rake
488
537
  @arg_names || []
489
538
  end
490
539
 
540
+ # Reenable the task, allowing its tasks to be executed if the task
541
+ # is invoked again.
542
+ def reenable
543
+ @already_invoked = false
544
+ end
545
+
546
+ # Clear the existing prerequisites and actions of a rake task.
547
+ def clear
548
+ clear_prerequisites
549
+ clear_actions
550
+ self
551
+ end
552
+
553
+ # Clear the existing prerequisites of a rake task.
554
+ def clear_prerequisites
555
+ prerequisites.clear
556
+ self
557
+ end
558
+
559
+ # Clear the existing actions on a rake task.
560
+ def clear_actions
561
+ actions.clear
562
+ self
563
+ end
564
+
491
565
  # Invoke the task if it is needed. Prerequites are invoked first.
492
566
  def invoke(*args)
493
567
  task_args = TaskArguments.new(arg_names, args)
@@ -496,7 +570,7 @@ module Rake
496
570
 
497
571
  # Same as invoke, but explicitly pass a call chain to detect
498
572
  # circular dependencies.
499
- def invoke_with_call_chain(task_args, invocation_chain)
573
+ def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
500
574
  new_chain = InvocationChain.append(self, invocation_chain)
501
575
  @lock.synchronize do
502
576
  if application.options.trace
@@ -511,7 +585,7 @@ module Rake
511
585
  protected :invoke_with_call_chain
512
586
 
513
587
  # Invoke all the prerequisites of a task.
514
- def invoke_prerequisites(task_args, invocation_chain)
588
+ def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
515
589
  @prerequisites.each { |n|
516
590
  prereq = application[n, @scope]
517
591
  prereq_args = task_args.new_scope(prereq.arg_names)
@@ -529,7 +603,8 @@ module Rake
529
603
  private :format_trace_flags
530
604
 
531
605
  # Execute the actions associated with this task.
532
- def execute(args)
606
+ def execute(args=nil)
607
+ args ||= EMPTY_TASK_ARGS
533
608
  if application.options.dryrun
534
609
  puts "** Execute (dry run) #{name}"
535
610
  return
@@ -772,8 +847,8 @@ end
772
847
  # end
773
848
  # end
774
849
  #
775
- def file(args, &block)
776
- Rake::FileTask.define_task(args, &block)
850
+ def file(*args, &block)
851
+ Rake::FileTask.define_task(*args, &block)
777
852
  end
778
853
 
779
854
  # Declare a file creation task.
@@ -899,14 +974,38 @@ module FileUtils
899
974
  ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]"
900
975
  }
901
976
  end
977
+ if RakeFileUtils.verbose_flag == :default
978
+ options[:verbose] = false
979
+ else
980
+ options[:verbose] ||= RakeFileUtils.verbose_flag
981
+ end
982
+ options[:noop] ||= RakeFileUtils.nowrite_flag
902
983
  rake_check_options options, :noop, :verbose
903
984
  rake_output_message cmd.join(" ") if options[:verbose]
904
985
  unless options[:noop]
905
- res = system(*cmd)
986
+ res = rake_system(*cmd)
906
987
  block.call(res, $?)
907
988
  end
908
989
  end
909
990
 
991
+ def rake_system(*cmd)
992
+ if Rake.application.windows?
993
+ rake_win32_system(*cmd)
994
+ else
995
+ system(*cmd)
996
+ end
997
+ end
998
+ private :rake_system
999
+
1000
+ def rake_win32_system(*cmd)
1001
+ if cmd.size == 1
1002
+ system("call #{cmd}")
1003
+ else
1004
+ system(*cmd)
1005
+ end
1006
+ end
1007
+ private :rake_win32_system
1008
+
910
1009
  # Run a Ruby interpreter with the given arguments.
911
1010
  #
912
1011
  # Example:
@@ -961,7 +1060,7 @@ module RakeFileUtils
961
1060
  class << self
962
1061
  attr_accessor :verbose_flag, :nowrite_flag
963
1062
  end
964
- RakeFileUtils.verbose_flag = true
1063
+ RakeFileUtils.verbose_flag = :default
965
1064
  RakeFileUtils.nowrite_flag = false
966
1065
 
967
1066
  $fileutils_verbose = true
@@ -969,10 +1068,10 @@ module RakeFileUtils
969
1068
 
970
1069
  FileUtils::OPT_TABLE.each do |name, opts|
971
1070
  default_options = []
972
- if opts.include?('verbose')
1071
+ if opts.include?(:verbose) || opts.include?("verbose")
973
1072
  default_options << ':verbose => RakeFileUtils.verbose_flag'
974
1073
  end
975
- if opts.include?('noop')
1074
+ if opts.include?(:noop) || opts.include?("noop")
976
1075
  default_options << ':noop => RakeFileUtils.nowrite_flag'
977
1076
  end
978
1077
 
@@ -1094,21 +1193,6 @@ private(*RakeFileUtils.instance_methods(false))
1094
1193
  ######################################################################
1095
1194
  module Rake
1096
1195
 
1097
- class RuleRecursionOverflowError < StandardError
1098
- def initialize(*args)
1099
- super
1100
- @targets = []
1101
- end
1102
-
1103
- def add_target(target)
1104
- @targets << target
1105
- end
1106
-
1107
- def message
1108
- super + ": [" + @targets.reverse.join(' => ') + "]"
1109
- end
1110
- end
1111
-
1112
1196
  # #########################################################################
1113
1197
  # A FileList is essentially an array with a few helper methods defined to
1114
1198
  # make file manipulation a bit easier.
@@ -1642,23 +1726,65 @@ module Rake
1642
1726
  # Resolve the arguments for a task/rule. Returns a triplet of
1643
1727
  # [task_name, arg_name_list, prerequisites].
1644
1728
  def resolve_args(args)
1729
+ if args.last.is_a?(Hash)
1730
+ deps = args.pop
1731
+ resolve_args_with_dependencies(args, deps)
1732
+ else
1733
+ resolve_args_without_dependencies(args)
1734
+ end
1735
+ end
1736
+
1737
+ # Resolve task arguments for a task or rule when there are no
1738
+ # dependencies declared.
1739
+ #
1740
+ # The patterns recognized by this argument resolving function are:
1741
+ #
1742
+ # task :t
1743
+ # task :t, [:a]
1744
+ # task :t, :a (deprecated)
1745
+ #
1746
+ def resolve_args_without_dependencies(args)
1645
1747
  task_name = args.shift
1646
- arg_names = args #.map { |a| a.to_sym }
1647
- needs = []
1648
- if task_name.is_a?(Hash)
1649
- hash = task_name
1650
- task_name = hash.keys[0]
1651
- needs = hash[task_name]
1748
+ if args.size == 1 && args.first.respond_to?(:to_ary)
1749
+ arg_names = args.first.to_ary
1750
+ else
1751
+ arg_names = args
1652
1752
  end
1653
- if arg_names.last.is_a?(Hash)
1654
- hash = arg_names.pop
1655
- needs = hash[:needs]
1656
- fail "Unrecognized keys in task hash: #{hash.keys.inspect}" if hash.size > 1
1753
+ [task_name, arg_names, []]
1754
+ end
1755
+ private :resolve_args_without_dependencies
1756
+
1757
+ # Resolve task arguments for a task or rule when there are
1758
+ # dependencies declared.
1759
+ #
1760
+ # The patterns recognized by this argument resolving function are:
1761
+ #
1762
+ # task :t => [:d]
1763
+ # task :t, [a] => [:d]
1764
+ # task :t, :needs => [:d] (deprecated)
1765
+ # task :t, :a, :needs => [:d] (deprecated)
1766
+ #
1767
+ def resolve_args_with_dependencies(args, hash) # :nodoc:
1768
+ fail "Task Argument Error" if hash.size != 1
1769
+ key, value = hash.map { |k, v| [k,v] }.first
1770
+ if args.empty?
1771
+ task_name = key
1772
+ arg_names = []
1773
+ deps = value
1774
+ elsif key == :needs
1775
+ task_name = args.shift
1776
+ arg_names = args
1777
+ deps = value
1778
+ else
1779
+ task_name = args.shift
1780
+ arg_names = key
1781
+ deps = value
1657
1782
  end
1658
- needs = [needs] unless needs.respond_to?(:to_ary)
1659
- [task_name, arg_names, needs]
1783
+ deps = [deps] unless deps.respond_to?(:to_ary)
1784
+ [task_name, arg_names, deps]
1660
1785
  end
1661
-
1786
+ private :resolve_args_with_dependencies
1787
+
1662
1788
  # If a rule can be found that matches the task name, enhance the
1663
1789
  # task with the prerequisites and actions from the rule. Set the
1664
1790
  # source attribute of the task appropriately for the rule. Return
@@ -1749,15 +1875,23 @@ module Rake
1749
1875
  "_anon_#{@seed}"
1750
1876
  end
1751
1877
 
1878
+ def trace_rule(level, message)
1879
+ puts "#{" "*level}#{message}" if Rake.application.options.trace_rules
1880
+ end
1881
+
1752
1882
  # Attempt to create a rule given the list of prerequisites.
1753
1883
  def attempt_rule(task_name, extensions, block, level)
1754
1884
  sources = make_sources(task_name, extensions)
1755
1885
  prereqs = sources.collect { |source|
1886
+ trace_rule level, "Attempting Rule #{task_name} => #{source}"
1756
1887
  if File.exist?(source) || Rake::Task.task_defined?(source)
1888
+ trace_rule level, "(#{task_name} => #{source} ... EXIST)"
1757
1889
  source
1758
- elsif parent = enhance_with_matching_rule(sources.first, level+1)
1890
+ elsif parent = enhance_with_matching_rule(source, level+1)
1891
+ trace_rule level, "(#{task_name} => #{source} ... ENHANCE)"
1759
1892
  parent.name
1760
1893
  else
1894
+ trace_rule level, "(#{task_name} => #{source} ... FAIL)"
1761
1895
  return nil
1762
1896
  end
1763
1897
  }
@@ -1814,41 +1948,6 @@ module Rake
1814
1948
 
1815
1949
  DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
1816
1950
 
1817
- OPTIONS = [ # :nodoc:
1818
- ['--classic-namespace', '-C', GetoptLong::NO_ARGUMENT,
1819
- "Put Task and FileTask in the top level namespace"],
1820
- ['--describe', '-D', GetoptLong::OPTIONAL_ARGUMENT,
1821
- "Describe the tasks (matching optional PATTERN), then exit."],
1822
- ['--rakefile', '-f', GetoptLong::OPTIONAL_ARGUMENT,
1823
- "Use FILE as the rakefile."],
1824
- ['--help', '-h', '-H', GetoptLong::NO_ARGUMENT,
1825
- "Display this help message."],
1826
- ['--libdir', '-I', GetoptLong::REQUIRED_ARGUMENT,
1827
- "Include LIBDIR in the search path for required modules."],
1828
- ['--dry-run', '-n', GetoptLong::NO_ARGUMENT,
1829
- "Do a dry run without executing actions."],
1830
- ['--nosearch', '-N', GetoptLong::NO_ARGUMENT,
1831
- "Do not search parent directories for the Rakefile."],
1832
- ['--prereqs', '-P', GetoptLong::NO_ARGUMENT,
1833
- "Display the tasks and dependencies, then exit."],
1834
- ['--quiet', '-q', GetoptLong::NO_ARGUMENT,
1835
- "Do not log messages to standard output."],
1836
- ['--require', '-r', GetoptLong::REQUIRED_ARGUMENT,
1837
- "Require MODULE before executing rakefile."],
1838
- ['--rakelibdir', '-R', GetoptLong::REQUIRED_ARGUMENT,
1839
- "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')"],
1840
- ['--silent', '-s', GetoptLong::NO_ARGUMENT,
1841
- "Like --quiet, but also suppresses the 'in directory' announcement."],
1842
- ['--tasks', '-T', GetoptLong::OPTIONAL_ARGUMENT,
1843
- "Display the tasks (matching optional PATTERN) with descriptions, then exit."],
1844
- ['--trace', '-t', GetoptLong::NO_ARGUMENT,
1845
- "Turn on invoke/execute tracing, enable full backtrace."],
1846
- ['--verbose', '-v', GetoptLong::NO_ARGUMENT,
1847
- "Log message to standard output (default)."],
1848
- ['--version', '-V', GetoptLong::NO_ARGUMENT,
1849
- "Display the program version."],
1850
- ]
1851
-
1852
1951
  # Initialize a Rake::Application object.
1853
1952
  def initialize
1854
1953
  super
@@ -1861,8 +1960,10 @@ module Rake
1861
1960
  @default_loader = Rake::DefaultLoader.new
1862
1961
  @original_dir = Dir.pwd
1863
1962
  @top_level_tasks = []
1963
+ add_loader('rb', DefaultLoader.new)
1864
1964
  add_loader('rf', DefaultLoader.new)
1865
1965
  add_loader('rake', DefaultLoader.new)
1966
+ @tty_output = STDOUT.tty?
1866
1967
  end
1867
1968
 
1868
1969
  # Run the Rake application. The run method performs the following three steps:
@@ -1886,8 +1987,7 @@ module Rake
1886
1987
  def init(app_name='rake')
1887
1988
  standard_exception_handling do
1888
1989
  @name = app_name
1889
- handle_options
1890
- collect_tasks
1990
+ collect_tasks handle_options
1891
1991
  end
1892
1992
  end
1893
1993
 
@@ -1949,7 +2049,7 @@ module Rake
1949
2049
  rescue SystemExit => ex
1950
2050
  # Exit silently with current status
1951
2051
  exit(ex.status)
1952
- rescue SystemExit, GetoptLong::InvalidOption => ex
2052
+ rescue SystemExit, OptionParser::InvalidOption => ex
1953
2053
  # Exit silently
1954
2054
  exit(1)
1955
2055
  rescue Exception => ex
@@ -1971,28 +2071,26 @@ module Rake
1971
2071
  def have_rakefile
1972
2072
  @rakefiles.each do |fn|
1973
2073
  if File.exist?(fn) || fn == ''
1974
- @rakefile = fn
1975
- return true
2074
+ return fn
1976
2075
  end
1977
2076
  end
1978
- return false
2077
+ return nil
1979
2078
  end
1980
2079
 
1981
- # Display the rake command line help.
1982
- def help
1983
- puts "rake [-f rakefile] {options} targets..."
1984
- puts
1985
- puts "Options are ..."
1986
- puts
1987
- OPTIONS.sort.each do |long, short, mode, desc|
1988
- if mode == GetoptLong::REQUIRED_ARGUMENT
1989
- if desc =~ /\b([A-Z]{2,})\b/
1990
- long = long + "=#{$1}"
1991
- end
1992
- end
1993
- printf " %-20s (%s)\n", long, short
1994
- printf " %s\n", desc
1995
- end
2080
+ # True if we are outputting to TTY, false otherwise
2081
+ def tty_output?
2082
+ @tty_output
2083
+ end
2084
+
2085
+ # Override the detected TTY output state (mostly for testing)
2086
+ def tty_output=( tty_output_state )
2087
+ @tty_output = tty_output_state
2088
+ end
2089
+
2090
+ # We will truncate output if we are outputting to a TTY or if we've been
2091
+ # given an explicit column width to honor
2092
+ def truncate_output?
2093
+ tty_output? || ENV['RAKE_COLUMNS']
1996
2094
  end
1997
2095
 
1998
2096
  # Display the tasks and dependencies.
@@ -2010,19 +2108,51 @@ module Rake
2010
2108
  end
2011
2109
  else
2012
2110
  width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
2013
- max_column = 80 - name.size - width - 7
2111
+ max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
2014
2112
  displayable_tasks.each do |t|
2015
2113
  printf "#{name} %-#{width}s # %s\n",
2016
- t.name_with_args, truncate(t.comment, max_column)
2114
+ t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
2017
2115
  end
2018
2116
  end
2019
2117
  end
2020
2118
 
2119
+ def terminal_width
2120
+ if ENV['RAKE_COLUMNS']
2121
+ result = ENV['RAKE_COLUMNS'].to_i
2122
+ else
2123
+ result = unix? ? dynamic_width : 80
2124
+ end
2125
+ (result < 10) ? 80 : result
2126
+ rescue
2127
+ 80
2128
+ end
2129
+
2130
+ # Calculate the dynamic width of the
2131
+ def dynamic_width
2132
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
2133
+ end
2134
+
2135
+ def dynamic_width_stty
2136
+ %x{stty size 2>/dev/null}.split[1].to_i
2137
+ end
2138
+
2139
+ def dynamic_width_tput
2140
+ %x{tput cols 2>/dev/null}.to_i
2141
+ end
2142
+
2143
+ def unix?
2144
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
2145
+ end
2146
+
2147
+ def windows?
2148
+ Config::CONFIG['host_os'] =~ /mswin/
2149
+ end
2150
+
2021
2151
  def truncate(string, width)
2022
2152
  if string.length <= width
2023
2153
  string
2024
2154
  else
2025
- string[0, width-3] + "..."
2155
+ ( string[0, width-3] || "" ) + "..."
2026
2156
  end
2027
2157
  end
2028
2158
 
@@ -2034,77 +2164,141 @@ module Rake
2034
2164
  end
2035
2165
  end
2036
2166
 
2037
- # Return a list of the command line options supported by the
2038
- # program.
2039
- def command_line_options
2040
- OPTIONS.collect { |lst| lst[0..-2] }
2041
- end
2042
-
2043
- # Do the option defined by +opt+ and +value+.
2044
- def do_option(opt, value)
2045
- case opt
2046
- when '--describe'
2047
- options.show_tasks = true
2048
- options.show_task_pattern = Regexp.new(value || '.')
2049
- options.full_description = true
2050
- when '--dry-run'
2051
- verbose(true)
2052
- nowrite(true)
2053
- options.dryrun = true
2054
- options.trace = true
2055
- when '--help'
2056
- help
2057
- exit
2058
- when '--libdir'
2059
- $:.push(value)
2060
- when '--nosearch'
2061
- options.nosearch = true
2062
- when '--prereqs'
2063
- options.show_prereqs = true
2064
- when '--quiet'
2065
- verbose(false)
2066
- when '--rakefile'
2067
- @rakefiles.clear
2068
- @rakefiles << value
2069
- when '--rakelibdir'
2070
- options.rakelib = value.split(':')
2071
- when '--require'
2072
- begin
2073
- require value
2074
- rescue LoadError => ex
2075
- begin
2076
- rake_require value
2077
- rescue LoadError => ex2
2078
- raise ex
2079
- end
2080
- end
2081
- when '--silent'
2082
- verbose(false)
2083
- options.silent = true
2084
- when '--tasks'
2085
- options.show_tasks = true
2086
- options.show_task_pattern = Regexp.new(value || '.')
2087
- options.full_description = false
2088
- when '--trace'
2089
- options.trace = true
2090
- verbose(true)
2091
- when '--verbose'
2092
- verbose(true)
2093
- when '--version'
2094
- puts "rake, version #{RAKEVERSION}"
2095
- exit
2096
- when '--classic-namespace'
2097
- require 'rake/classic_namespace'
2098
- options.classic_namespace = true
2099
- end
2167
+ # A list of all the standard options used in rake, suitable for
2168
+ # passing to OptionParser.
2169
+ def standard_rake_options
2170
+ [
2171
+ ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace",
2172
+ lambda { |value|
2173
+ require 'rake/classic_namespace'
2174
+ options.classic_namespace = true
2175
+ }
2176
+ ],
2177
+ ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
2178
+ lambda { |value|
2179
+ options.show_tasks = true
2180
+ options.full_description = true
2181
+ options.show_task_pattern = Regexp.new(value || '')
2182
+ }
2183
+ ],
2184
+ ['--dry-run', '-n', "Do a dry run without executing actions.",
2185
+ lambda { |value|
2186
+ verbose(true)
2187
+ nowrite(true)
2188
+ options.dryrun = true
2189
+ options.trace = true
2190
+ }
2191
+ ],
2192
+ ['--execute', '-e CODE', "Execute some Ruby code and exit.",
2193
+ lambda { |value|
2194
+ eval(value)
2195
+ exit
2196
+ }
2197
+ ],
2198
+ ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.",
2199
+ lambda { |value|
2200
+ puts eval(value)
2201
+ exit
2202
+ }
2203
+ ],
2204
+ ['--execute-continue', '-E CODE',
2205
+ "Execute some Ruby code, then continue with normal task processing.",
2206
+ lambda { |value| eval(value) }
2207
+ ],
2208
+ ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.",
2209
+ lambda { |value| $:.push(value) }
2210
+ ],
2211
+ ['--prereqs', '-P', "Display the tasks and dependencies, then exit.",
2212
+ lambda { |value| options.show_prereqs = true }
2213
+ ],
2214
+ ['--quiet', '-q', "Do not log messages to standard output.",
2215
+ lambda { |value| verbose(false) }
2216
+ ],
2217
+ ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.",
2218
+ lambda { |value|
2219
+ value ||= ''
2220
+ @rakefiles.clear
2221
+ @rakefiles << value
2222
+ }
2223
+ ],
2224
+ ['--rakelibdir', '--rakelib', '-R RAKELIBDIR',
2225
+ "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')",
2226
+ lambda { |value| options.rakelib = value.split(':') }
2227
+ ],
2228
+ ['--require', '-r MODULE', "Require MODULE before executing rakefile.",
2229
+ lambda { |value|
2230
+ begin
2231
+ require value
2232
+ rescue LoadError => ex
2233
+ begin
2234
+ rake_require value
2235
+ rescue LoadError => ex2
2236
+ raise ex
2237
+ end
2238
+ end
2239
+ }
2240
+ ],
2241
+ ['--rules', "Trace the rules resolution.",
2242
+ lambda { |value| options.trace_rules = true }
2243
+ ],
2244
+ ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.",
2245
+ lambda { |value| options.nosearch = true }
2246
+ ],
2247
+ ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.",
2248
+ lambda { |value|
2249
+ verbose(false)
2250
+ options.silent = true
2251
+ }
2252
+ ],
2253
+ ['--system', '-g',
2254
+ "Using system wide (global) rakefiles (usually '~/.rake/*.rake').",
2255
+ lambda { |value| options.load_system = true }
2256
+ ],
2257
+ ['--no-system', '--nosystem', '-G',
2258
+ "Use standard project Rakefile search paths, ignore system wide rakefiles.",
2259
+ lambda { |value| options.ignore_system = true }
2260
+ ],
2261
+ ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.",
2262
+ lambda { |value|
2263
+ options.show_tasks = true
2264
+ options.show_task_pattern = Regexp.new(value || '')
2265
+ options.full_description = false
2266
+ }
2267
+ ],
2268
+ ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.",
2269
+ lambda { |value|
2270
+ options.trace = true
2271
+ verbose(true)
2272
+ }
2273
+ ],
2274
+ ['--verbose', '-v', "Log message to standard output (default).",
2275
+ lambda { |value| verbose(true) }
2276
+ ],
2277
+ ['--version', '-V', "Display the program version.",
2278
+ lambda { |value|
2279
+ puts "rake, version #{RAKEVERSION}"
2280
+ exit
2281
+ }
2282
+ ]
2283
+ ]
2100
2284
  end
2101
2285
 
2102
2286
  # Read and handle the command line options.
2103
2287
  def handle_options
2104
2288
  options.rakelib = ['rakelib']
2105
2289
 
2106
- opts = GetoptLong.new(*command_line_options)
2107
- opts.each { |opt, value| do_option(opt, value) }
2290
+ opts = OptionParser.new
2291
+ opts.banner = "rake [-f rakefile] {options} targets..."
2292
+ opts.separator ""
2293
+ opts.separator "Options are ..."
2294
+
2295
+ opts.on_tail("-h", "--help", "-H", "Display this help message.") do
2296
+ puts opts
2297
+ exit
2298
+ end
2299
+
2300
+ standard_rake_options.each { |args| opts.on(*args) }
2301
+ parsed_argv = opts.parse(ARGV)
2108
2302
 
2109
2303
  # If class namespaces are requested, set the global options
2110
2304
  # according to the values in the options structure.
@@ -2115,12 +2309,11 @@ module Rake
2115
2309
  $dryrun = options.dryrun
2116
2310
  $silent = options.silent
2117
2311
  end
2118
- rescue NoMethodError => ex
2119
- raise GetoptLong::InvalidOption, "While parsing options, error = #{ex.class}:#{ex.message}"
2312
+ parsed_argv
2120
2313
  end
2121
2314
 
2122
2315
  # Similar to the regular Ruby +require+ command, but will check
2123
- # for .rake files in addition to .rb files.
2316
+ # for *.rake files in addition to *.rb files.
2124
2317
  def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
2125
2318
  return false if loaded.include?(file_name)
2126
2319
  paths.each do |path|
@@ -2135,34 +2328,95 @@ module Rake
2135
2328
  fail LoadError, "Can't find #{file_name}"
2136
2329
  end
2137
2330
 
2138
- def raw_load_rakefile # :nodoc:
2331
+ def find_rakefile_location
2139
2332
  here = Dir.pwd
2140
- while ! have_rakefile
2333
+ while ! (fn = have_rakefile)
2141
2334
  Dir.chdir("..")
2142
2335
  if Dir.pwd == here || options.nosearch
2143
- fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})"
2336
+ return nil
2144
2337
  end
2145
2338
  here = Dir.pwd
2146
2339
  end
2147
- puts "(in #{Dir.pwd})" unless options.silent
2148
- $rakefile = @rakefile
2149
- load File.expand_path(@rakefile) if @rakefile != ''
2150
- options.rakelib.each do |rlib|
2151
- Dir["#{rlib}/*.rake"].each do |name| add_import name end
2340
+ [fn, here]
2341
+ ensure
2342
+ Dir.chdir(Rake.original_dir)
2343
+ end
2344
+
2345
+ def raw_load_rakefile # :nodoc:
2346
+ rakefile, location = find_rakefile_location
2347
+ if (! options.ignore_system) &&
2348
+ (options.load_system || rakefile.nil?) &&
2349
+ directory?(system_dir)
2350
+ puts "(in #{Dir.pwd})" unless options.silent
2351
+ glob("#{system_dir}/*.rake") do |name|
2352
+ add_import name
2353
+ end
2354
+ else
2355
+ fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if
2356
+ rakefile.nil?
2357
+ @rakefile = rakefile
2358
+ Dir.chdir(location)
2359
+ puts "(in #{Dir.pwd})" unless options.silent
2360
+ $rakefile = @rakefile if options.classic_namespace
2361
+ load File.expand_path(@rakefile) if @rakefile && @rakefile != ''
2362
+ options.rakelib.each do |rlib|
2363
+ glob("#{rlib}/*.rake") do |name|
2364
+ add_import name
2365
+ end
2366
+ end
2152
2367
  end
2153
2368
  load_imports
2154
2369
  end
2155
2370
 
2371
+ def glob(path, &block)
2372
+ Dir[path.gsub("\\", '/')].each(&block)
2373
+ end
2374
+ private :glob
2375
+
2376
+ # The directory path containing the system wide rakefiles.
2377
+ def system_dir
2378
+ if ENV['RAKE_SYSTEM']
2379
+ ENV['RAKE_SYSTEM']
2380
+ elsif windows?
2381
+ win32_system_dir
2382
+ else
2383
+ standard_system_dir
2384
+ end
2385
+ end
2386
+
2387
+ # The standard directory containing system wide rake files.
2388
+ def standard_system_dir #:nodoc:
2389
+ File.join(File.expand_path('~'), '.rake')
2390
+ end
2391
+ private :standard_system_dir
2392
+
2393
+ # The standard directory containing system wide rake files on Win
2394
+ # 32 systems.
2395
+ def win32_system_dir #:nodoc:
2396
+ win32home = File.join(ENV['APPDATA'], 'Rake')
2397
+ unless directory?(win32home)
2398
+ raise Win32HomeError, "Unable to determine home path environment variable."
2399
+ else
2400
+ win32home
2401
+ end
2402
+ end
2403
+ private :win32_system_dir
2404
+
2405
+ def directory?(path)
2406
+ File.directory?(path)
2407
+ end
2408
+ private :directory?
2409
+
2156
2410
  # Collect the list of tasks on the command line. If no tasks are
2157
2411
  # given, return a list containing only the default task.
2158
2412
  # Environmental assignments are processed at this time as well.
2159
- def collect_tasks
2413
+ def collect_tasks(argv)
2160
2414
  @top_level_tasks = []
2161
- ARGV.each do |arg|
2415
+ argv.each do |arg|
2162
2416
  if arg =~ /^(\w+)=(.*)$/
2163
2417
  ENV[$1] = $2
2164
2418
  else
2165
- @top_level_tasks << arg
2419
+ @top_level_tasks << arg unless arg =~ /^-/
2166
2420
  end
2167
2421
  end
2168
2422
  @top_level_tasks.push("default") if @top_level_tasks.size == 0