/tools/Ruby/lib/ruby/gems/1.8/gems/rake-0.9.2/lib/rake/application.rb

http://github.com/agross/netopenspace · Ruby · 589 lines · 492 code · 45 blank · 52 comment · 24 complexity · aef09d693860a3c83b52bbeac10536e4 MD5 · raw file

  1. require 'shellwords'
  2. require 'optparse'
  3. require 'rake/task_manager'
  4. require 'rake/win32'
  5. module Rake
  6. ######################################################################
  7. # Rake main application object. When invoking +rake+ from the
  8. # command line, a Rake::Application object is created and run.
  9. #
  10. class Application
  11. include TaskManager
  12. # The name of the application (typically 'rake')
  13. attr_reader :name
  14. # The original directory where rake was invoked.
  15. attr_reader :original_dir
  16. # Name of the actual rakefile used.
  17. attr_reader :rakefile
  18. # List of the top level task names (task names from the command line).
  19. attr_reader :top_level_tasks
  20. DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
  21. # Initialize a Rake::Application object.
  22. def initialize
  23. super
  24. @name = 'rake'
  25. @rakefiles = DEFAULT_RAKEFILES.dup
  26. @rakefile = nil
  27. @pending_imports = []
  28. @imported = []
  29. @loaders = {}
  30. @default_loader = Rake::DefaultLoader.new
  31. @original_dir = Dir.pwd
  32. @top_level_tasks = []
  33. add_loader('rb', DefaultLoader.new)
  34. add_loader('rf', DefaultLoader.new)
  35. add_loader('rake', DefaultLoader.new)
  36. @tty_output = STDOUT.tty?
  37. end
  38. # Run the Rake application. The run method performs the following
  39. # three steps:
  40. #
  41. # * Initialize the command line options (+init+).
  42. # * Define the tasks (+load_rakefile+).
  43. # * Run the top level tasks (+run_tasks+).
  44. #
  45. # If you wish to build a custom rake command, you should call
  46. # +init+ on your application. Then define any tasks. Finally,
  47. # call +top_level+ to run your top level tasks.
  48. def run
  49. standard_exception_handling do
  50. init
  51. load_rakefile
  52. top_level
  53. end
  54. end
  55. # Initialize the command line parameters and app name.
  56. def init(app_name='rake')
  57. standard_exception_handling do
  58. @name = app_name
  59. handle_options
  60. collect_tasks
  61. end
  62. end
  63. # Find the rakefile and then load it and any pending imports.
  64. def load_rakefile
  65. standard_exception_handling do
  66. raw_load_rakefile
  67. end
  68. end
  69. # Run the top level tasks of a Rake application.
  70. def top_level
  71. standard_exception_handling do
  72. if options.show_tasks
  73. display_tasks_and_comments
  74. elsif options.show_prereqs
  75. display_prerequisites
  76. else
  77. top_level_tasks.each { |task_name| invoke_task(task_name) }
  78. end
  79. end
  80. end
  81. # Add a loader to handle imported files ending in the extension
  82. # +ext+.
  83. def add_loader(ext, loader)
  84. ext = ".#{ext}" unless ext =~ /^\./
  85. @loaders[ext] = loader
  86. end
  87. # Application options from the command line
  88. def options
  89. @options ||= OpenStruct.new
  90. end
  91. # private ----------------------------------------------------------------
  92. def invoke_task(task_string)
  93. name, args = parse_task_string(task_string)
  94. t = self[name]
  95. t.invoke(*args)
  96. end
  97. def parse_task_string(string)
  98. if string =~ /^([^\[]+)(\[(.*)\])$/
  99. name = $1
  100. args = $3.split(/\s*,\s*/)
  101. else
  102. name = string
  103. args = []
  104. end
  105. [name, args]
  106. end
  107. # Provide standard exception handling for the given block.
  108. def standard_exception_handling
  109. begin
  110. yield
  111. rescue SystemExit => ex
  112. # Exit silently with current status
  113. raise
  114. rescue OptionParser::InvalidOption => ex
  115. $stderr.puts ex.message
  116. exit(false)
  117. rescue Exception => ex
  118. # Exit with error message
  119. display_error_message(ex)
  120. exit(false)
  121. end
  122. end
  123. # Display the error message that caused the exception.
  124. def display_error_message(ex)
  125. $stderr.puts "#{name} aborted!"
  126. $stderr.puts ex.message
  127. if options.trace
  128. $stderr.puts ex.backtrace.join("\n")
  129. else
  130. $stderr.puts rakefile_location(ex.backtrace)
  131. end
  132. $stderr.puts "Tasks: #{ex.chain}" if has_chain?(ex)
  133. $stderr.puts "(See full trace by running task with --trace)" unless options.trace
  134. end
  135. # Warn about deprecated usage.
  136. #
  137. # Example:
  138. # Rake.application.deprecate("import", "Rake.import", caller.first)
  139. #
  140. def deprecate(old_usage, new_usage, call_site)
  141. return if options.ignore_deprecate
  142. $stderr.puts "WARNING: '#{old_usage}' is deprecated. " +
  143. "Please use '#{new_usage}' instead.\n" +
  144. " at #{call_site}"
  145. end
  146. # Does the exception have a task invocation chain?
  147. def has_chain?(exception)
  148. exception.respond_to?(:chain) && exception.chain
  149. end
  150. private :has_chain?
  151. # True if one of the files in RAKEFILES is in the current directory.
  152. # If a match is found, it is copied into @rakefile.
  153. def have_rakefile
  154. @rakefiles.each do |fn|
  155. if File.exist?(fn)
  156. others = Dir.glob(fn, File::FNM_CASEFOLD)
  157. return others.size == 1 ? others.first : fn
  158. elsif fn == ''
  159. return fn
  160. end
  161. end
  162. return nil
  163. end
  164. # True if we are outputting to TTY, false otherwise
  165. def tty_output?
  166. @tty_output
  167. end
  168. # Override the detected TTY output state (mostly for testing)
  169. def tty_output=( tty_output_state )
  170. @tty_output = tty_output_state
  171. end
  172. # We will truncate output if we are outputting to a TTY or if we've been
  173. # given an explicit column width to honor
  174. def truncate_output?
  175. tty_output? || ENV['RAKE_COLUMNS']
  176. end
  177. # Display the tasks and comments.
  178. def display_tasks_and_comments
  179. displayable_tasks = tasks.select { |t|
  180. t.comment && t.name =~ options.show_task_pattern
  181. }
  182. case options.show_tasks
  183. when :tasks
  184. width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
  185. max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
  186. displayable_tasks.each do |t|
  187. printf "#{name} %-#{width}s # %s\n",
  188. t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
  189. end
  190. when :describe
  191. displayable_tasks.each do |t|
  192. puts "#{name} #{t.name_with_args}"
  193. t.full_comment.split("\n").each do |line|
  194. puts " #{line}"
  195. end
  196. puts
  197. end
  198. when :lines
  199. displayable_tasks.each do |t|
  200. t.locations.each do |loc|
  201. printf "#{name} %-30s %s\n",t.name_with_args, loc
  202. end
  203. end
  204. else
  205. fail "Unknown show task mode: '#{options.show_tasks}'"
  206. end
  207. end
  208. def terminal_width
  209. if ENV['RAKE_COLUMNS']
  210. result = ENV['RAKE_COLUMNS'].to_i
  211. else
  212. result = unix? ? dynamic_width : 80
  213. end
  214. (result < 10) ? 80 : result
  215. rescue
  216. 80
  217. end
  218. # Calculate the dynamic width of the
  219. def dynamic_width
  220. @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
  221. end
  222. def dynamic_width_stty
  223. %x{stty size 2>/dev/null}.split[1].to_i
  224. end
  225. def dynamic_width_tput
  226. %x{tput cols 2>/dev/null}.to_i
  227. end
  228. def unix?
  229. RbConfig::CONFIG['host_os'] =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
  230. end
  231. def windows?
  232. Win32.windows?
  233. end
  234. def truncate(string, width)
  235. if string.length <= width
  236. string
  237. else
  238. ( string[0, width-3] || "" ) + "..."
  239. end
  240. end
  241. # Display the tasks and prerequisites
  242. def display_prerequisites
  243. tasks.each do |t|
  244. puts "#{name} #{t.name}"
  245. t.prerequisites.each { |pre| puts " #{pre}" }
  246. end
  247. end
  248. # A list of all the standard options used in rake, suitable for
  249. # passing to OptionParser.
  250. def standard_rake_options
  251. [
  252. ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace",
  253. lambda { |value|
  254. require 'rake/classic_namespace'
  255. options.classic_namespace = true
  256. }
  257. ],
  258. ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
  259. lambda { |value|
  260. options.show_tasks = :describe
  261. options.show_task_pattern = Regexp.new(value || '')
  262. TaskManager.record_task_metadata = true
  263. }
  264. ],
  265. ['--dry-run', '-n', "Do a dry run without executing actions.",
  266. lambda { |value|
  267. Rake.verbose(true)
  268. Rake.nowrite(true)
  269. options.dryrun = true
  270. options.trace = true
  271. }
  272. ],
  273. ['--execute', '-e CODE', "Execute some Ruby code and exit.",
  274. lambda { |value|
  275. eval(value)
  276. exit
  277. }
  278. ],
  279. ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.",
  280. lambda { |value|
  281. puts eval(value)
  282. exit
  283. }
  284. ],
  285. ['--execute-continue', '-E CODE',
  286. "Execute some Ruby code, then continue with normal task processing.",
  287. lambda { |value| eval(value) }
  288. ],
  289. ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.",
  290. lambda { |value| $:.push(value) }
  291. ],
  292. ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.",
  293. lambda { |value| options.nosearch = true }
  294. ],
  295. ['--prereqs', '-P', "Display the tasks and dependencies, then exit.",
  296. lambda { |value| options.show_prereqs = true }
  297. ],
  298. ['--quiet', '-q', "Do not log messages to standard output.",
  299. lambda { |value| Rake.verbose(false) }
  300. ],
  301. ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.",
  302. lambda { |value|
  303. value ||= ''
  304. @rakefiles.clear
  305. @rakefiles << value
  306. }
  307. ],
  308. ['--rakelibdir', '--rakelib', '-R RAKELIBDIR',
  309. "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')",
  310. lambda { |value| options.rakelib = value.split(':') }
  311. ],
  312. ['--require', '-r MODULE', "Require MODULE before executing rakefile.",
  313. lambda { |value|
  314. begin
  315. require value
  316. rescue LoadError => ex
  317. begin
  318. rake_require value
  319. rescue LoadError
  320. raise ex
  321. end
  322. end
  323. }
  324. ],
  325. ['--rules', "Trace the rules resolution.",
  326. lambda { |value| options.trace_rules = true }
  327. ],
  328. ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.",
  329. lambda { |value|
  330. Rake.verbose(false)
  331. options.silent = true
  332. }
  333. ],
  334. ['--system', '-g',
  335. "Using system wide (global) rakefiles (usually '~/.rake/*.rake').",
  336. lambda { |value| options.load_system = true }
  337. ],
  338. ['--no-system', '--nosystem', '-G',
  339. "Use standard project Rakefile search paths, ignore system wide rakefiles.",
  340. lambda { |value| options.ignore_system = true }
  341. ],
  342. ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.",
  343. lambda { |value|
  344. options.show_tasks = :tasks
  345. options.show_task_pattern = Regexp.new(value || '')
  346. Rake::TaskManager.record_task_metadata = true
  347. }
  348. ],
  349. ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.",
  350. lambda { |value|
  351. options.trace = true
  352. Rake.verbose(true)
  353. }
  354. ],
  355. ['--verbose', '-v', "Log message to standard output.",
  356. lambda { |value| Rake.verbose(true) }
  357. ],
  358. ['--version', '-V', "Display the program version.",
  359. lambda { |value|
  360. puts "rake, version #{RAKEVERSION}"
  361. exit
  362. }
  363. ],
  364. ['--where', '-W [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
  365. lambda { |value|
  366. options.show_tasks = :lines
  367. options.show_task_pattern = Regexp.new(value || '')
  368. Rake::TaskManager.record_task_metadata = true
  369. }
  370. ],
  371. ['--no-deprecation-warnings', '-X', "Disable the deprecation warnings.",
  372. lambda { |value|
  373. options.ignore_deprecate = true
  374. }
  375. ],
  376. ]
  377. end
  378. # Read and handle the command line options.
  379. def handle_options
  380. options.rakelib = ['rakelib']
  381. OptionParser.new do |opts|
  382. opts.banner = "rake [-f rakefile] {options} targets..."
  383. opts.separator ""
  384. opts.separator "Options are ..."
  385. opts.on_tail("-h", "--help", "-H", "Display this help message.") do
  386. puts opts
  387. exit
  388. end
  389. standard_rake_options.each { |args| opts.on(*args) }
  390. opts.environment('RAKEOPT')
  391. end.parse!
  392. # If class namespaces are requested, set the global options
  393. # according to the values in the options structure.
  394. if options.classic_namespace
  395. $show_tasks = options.show_tasks
  396. $show_prereqs = options.show_prereqs
  397. $trace = options.trace
  398. $dryrun = options.dryrun
  399. $silent = options.silent
  400. end
  401. end
  402. # Similar to the regular Ruby +require+ command, but will check
  403. # for *.rake files in addition to *.rb files.
  404. def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
  405. fn = file_name + ".rake"
  406. return false if loaded.include?(fn)
  407. paths.each do |path|
  408. full_path = File.join(path, fn)
  409. if File.exist?(full_path)
  410. Rake.load_rakefile(full_path)
  411. loaded << fn
  412. return true
  413. end
  414. end
  415. fail LoadError, "Can't find #{file_name}"
  416. end
  417. def find_rakefile_location
  418. here = Dir.pwd
  419. while ! (fn = have_rakefile)
  420. Dir.chdir("..")
  421. if Dir.pwd == here || options.nosearch
  422. return nil
  423. end
  424. here = Dir.pwd
  425. end
  426. [fn, here]
  427. ensure
  428. Dir.chdir(Rake.original_dir)
  429. end
  430. def print_rakefile_directory(location)
  431. $stderr.puts "(in #{Dir.pwd})" unless
  432. options.silent or original_dir == location
  433. end
  434. def raw_load_rakefile # :nodoc:
  435. rakefile, location = find_rakefile_location
  436. if (! options.ignore_system) &&
  437. (options.load_system || rakefile.nil?) &&
  438. system_dir && File.directory?(system_dir)
  439. print_rakefile_directory(location)
  440. glob("#{system_dir}/*.rake") do |name|
  441. add_import name
  442. end
  443. else
  444. fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if
  445. rakefile.nil?
  446. @rakefile = rakefile
  447. Dir.chdir(location)
  448. print_rakefile_directory(location)
  449. $rakefile = @rakefile if options.classic_namespace
  450. Rake.load_rakefile(File.expand_path(@rakefile)) if @rakefile && @rakefile != ''
  451. options.rakelib.each do |rlib|
  452. glob("#{rlib}/*.rake") do |name|
  453. add_import name
  454. end
  455. end
  456. end
  457. load_imports
  458. end
  459. def glob(path, &block)
  460. Dir[path.gsub("\\", '/')].each(&block)
  461. end
  462. private :glob
  463. # The directory path containing the system wide rakefiles.
  464. def system_dir
  465. @system_dir ||=
  466. begin
  467. if ENV['RAKE_SYSTEM']
  468. ENV['RAKE_SYSTEM']
  469. else
  470. standard_system_dir
  471. end
  472. end
  473. end
  474. # The standard directory containing system wide rake files.
  475. if Win32.windows?
  476. def standard_system_dir #:nodoc:
  477. Win32.win32_system_dir
  478. end
  479. else
  480. def standard_system_dir #:nodoc:
  481. File.join(File.expand_path('~'), '.rake')
  482. end
  483. end
  484. private :standard_system_dir
  485. # Collect the list of tasks on the command line. If no tasks are
  486. # given, return a list containing only the default task.
  487. # Environmental assignments are processed at this time as well.
  488. def collect_tasks
  489. @top_level_tasks = []
  490. ARGV.each do |arg|
  491. if arg =~ /^(\w+)=(.*)$/
  492. ENV[$1] = $2
  493. else
  494. @top_level_tasks << arg unless arg =~ /^-/
  495. end
  496. end
  497. @top_level_tasks.push("default") if @top_level_tasks.size == 0
  498. end
  499. # Add a file to the list of files to be imported.
  500. def add_import(fn)
  501. @pending_imports << fn
  502. end
  503. # Load the pending list of imported files.
  504. def load_imports
  505. while fn = @pending_imports.shift
  506. next if @imported.member?(fn)
  507. if fn_task = lookup(fn)
  508. fn_task.invoke
  509. end
  510. ext = File.extname(fn)
  511. loader = @loaders[ext] || @default_loader
  512. loader.load(fn)
  513. @imported << fn
  514. end
  515. end
  516. # Warn about deprecated use of top level constant names.
  517. def const_warning(const_name)
  518. @const_warning ||= false
  519. if ! @const_warning
  520. $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } +
  521. %{found at: #{rakefile_location}} # '
  522. $stderr.puts %{ Use --classic-namespace on rake command}
  523. $stderr.puts %{ or 'require "rake/classic_namespace"' in Rakefile}
  524. end
  525. @const_warning = true
  526. end
  527. def rakefile_location backtrace = caller
  528. backtrace.map { |t| t[/([^:]+):/,1] }
  529. re = /^#{@rakefile}$/
  530. re = /#{re.source}/i if windows?
  531. backtrace.find { |str| str =~ re } || ''
  532. end
  533. end
  534. end