From a0e822b2b1b1c85afd50035d56162028743b8ced Mon Sep 17 00:00:00 2001 From: Eric Hodel Date: Wed, 28 Jun 2017 17:31:37 -0700 Subject: [PATCH 1/5] Set default rake options explicitly Currently it is impossible to start rake from within a library. By setting the default options explicitly we won't have to call handle_options after replacing ARGV. --- lib/rake/application.rb | 26 ++++++++++++++++++++++++-- test/test_rake_application_options.rb | 26 +++++++++++++------------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/lib/rake/application.rb b/lib/rake/application.rb index ae594e098..4fd59bba0 100644 --- a/lib/rake/application.rb +++ b/lib/rake/application.rb @@ -619,8 +619,7 @@ def select_trace_output(options, trace_option, value) # :nodoc: # arguments that we didn't understand, which should (in theory) be just # task names and env vars. def handle_options # :nodoc: - options.rakelib = ["rakelib"] - options.trace_output = $stderr + set_default_options OptionParser.new do |opts| opts.banner = "#{Rake.application.name} [-f rakefile] {options} targets..." @@ -782,5 +781,28 @@ def rakefile_location(backtrace=caller) # :nodoc: backtrace.find { |str| str =~ re } || "" end + def set_default_options + options.always_multitask = false + options.backtrace = false + options.build_all = false + options.dryrun = false + options.ignore_deprecate = false + options.ignore_system = false + options.job_stats = false + options.load_system = false + options.nosearch = false + options.rakelib = %w[rakelib] + options.show_all_tasks = false + options.show_prereqs = false + options.show_task_pattern = nil + options.show_tasks = nil + options.silent = false + options.suppress_backtrace_pattern = nil + options.thread_pool_size = Rake.suggested_thread_count + options.trace = false + options.trace_output = $stderr + options.trace_rules = false + end + end end diff --git a/test/test_rake_application_options.rb b/test/test_rake_application_options.rb index b34c2f634..eb4060973 100644 --- a/test/test_rake_application_options.rb +++ b/test/test_rake_application_options.rb @@ -30,19 +30,19 @@ def clear_argv def test_default_options opts = command_line - assert_nil opts.backtrace - assert_nil opts.dryrun - assert_nil opts.ignore_system - assert_nil opts.load_system - assert_nil opts.always_multitask - assert_nil opts.nosearch + assert_equal false, opts.backtrace + assert_equal false, opts.dryrun + assert_equal false, opts.ignore_system + assert_equal false, opts.load_system + assert_equal false, opts.always_multitask + assert_equal false, opts.nosearch assert_equal ["rakelib"], opts.rakelib - assert_nil opts.show_prereqs + assert_equal false, opts.show_prereqs assert_nil opts.show_task_pattern assert_nil opts.show_tasks - assert_nil opts.silent - assert_nil opts.trace - assert_nil opts.thread_pool_size + assert_equal false, opts.silent + assert_equal false, opts.trace + assert_equal Rake.suggested_thread_count, opts.thread_pool_size assert_equal ["rakelib"], opts.rakelib assert ! Rake::FileUtilsExt.verbose_flag assert ! Rake::FileUtilsExt.nowrite_flag @@ -115,7 +115,7 @@ def test_help def test_jobs flags([]) do |opts| - assert_nil opts.thread_pool_size + assert_equal Rake.suggested_thread_count, opts.thread_pool_size end flags(["--jobs", "0"], ["-j", "0"]) do |opts| assert_equal 0, opts.thread_pool_size @@ -329,12 +329,12 @@ def test_tasks flags("--tasks", "-T") do |opts| assert_equal :tasks, opts.show_tasks assert_equal(//.to_s, opts.show_task_pattern.to_s) - assert_nil opts.show_all_tasks + assert_equal false, opts.show_all_tasks end flags(["--tasks", "xyz"], ["-Txyz"]) do |opts| assert_equal :tasks, opts.show_tasks assert_equal(/xyz/.to_s, opts.show_task_pattern.to_s) - assert_nil opts.show_all_tasks + assert_equal false, opts.show_all_tasks end flags(["--tasks", "xyz", "--comments"]) do |opts| assert_equal :tasks, opts.show_tasks From 84e1fe27d5803a32ada80e84d3fc05509200cc18 Mon Sep 17 00:00:00 2001 From: Eric Hodel Date: Wed, 28 Jun 2017 17:52:24 -0700 Subject: [PATCH 2/5] Allow implicit ARGV for Rake::Application#run This allows users to pass in an explicit ARGV for rake instead of changing their application's ARGV. While the previous commit allows users to create a Rake application explicitly by calling parts of Rake::Application#init directly, some users will not want to dig through the source to set up a Rake application. Swapping ARGV is confusing and error-prone, so this allows them to specify a set of Rake options directly. --- lib/rake/application.rb | 12 ++-- test/test_rake_application.rb | 83 ++++++++------------------- test/test_rake_application_options.rb | 5 +- 3 files changed, 31 insertions(+), 69 deletions(-) diff --git a/lib/rake/application.rb b/lib/rake/application.rb index 4fd59bba0..650b6522c 100644 --- a/lib/rake/application.rb +++ b/lib/rake/application.rb @@ -74,19 +74,19 @@ def initialize # If you wish to build a custom rake command, you should call # +init+ on your application. Then define any tasks. Finally, # call +top_level+ to run your top level tasks. - def run + def run(argv = ARGV) standard_exception_handling do - init + init 'rake', argv load_rakefile top_level end end # Initialize the command line parameters and app name. - def init(app_name="rake") + def init(app_name="rake", argv = ARGV) standard_exception_handling do @name = app_name - args = handle_options + args = handle_options argv collect_command_line_tasks(args) end end @@ -618,7 +618,7 @@ def select_trace_output(options, trace_option, value) # :nodoc: # Read and handle the command line options. Returns the command line # arguments that we didn't understand, which should (in theory) be just # task names and env vars. - def handle_options # :nodoc: + def handle_options(argv) # :nodoc: set_default_options OptionParser.new do |opts| @@ -633,7 +633,7 @@ def handle_options # :nodoc: standard_rake_options.each { |args| opts.on(*args) } opts.environment("RAKEOPT") - end.parse(ARGV) + end.parse(argv) end # Similar to the regular Ruby +require+ command, but will check diff --git a/test/test_rake_application.rb b/test/test_rake_application.rb index 141dd576f..0212c9b22 100644 --- a/test/test_rake_application.rb +++ b/test/test_rake_application.rb @@ -10,13 +10,6 @@ def setup @app.options.rakelib = [] end - def setup_command_line(*options) - ARGV.clear - options.each do |option| - ARGV << option - end - end - def test_display_exception_details obj = Object.new obj.instance_eval("def #{__method__}; raise 'test'; end", "ruby") @@ -187,7 +180,7 @@ def test_load_rakefile rakefile_unittest @app.instance_eval do - handle_options + handle_options [] options.silent = true load_rakefile end @@ -215,7 +208,7 @@ def test_load_rakefile_from_subdir Dir.chdir "subdir" @app.instance_eval do - handle_options + handle_options [] options.silent = true load_rakefile end @@ -246,7 +239,7 @@ def test_load_rakefile_doesnt_print_rakefile_directory_from_subdir_if_silent _, err = capture_io do @app.instance_eval do - handle_options + handle_options [] options.silent = true raw_load_rakefile end @@ -258,12 +251,11 @@ def test_load_rakefile_doesnt_print_rakefile_directory_from_subdir_if_silent def test_load_rakefile_not_found skip if jruby9? - ARGV.clear Dir.chdir @tempdir ENV["RAKE_SYSTEM"] = "not_exist" @app.instance_eval do - handle_options + handle_options [] options.silent = true end @@ -280,7 +272,7 @@ def test_load_from_system_rakefile rake_system_dir @app.instance_eval do - handle_options + handle_options [] options.silent = true options.load_system = true options.rakelib = [] @@ -302,7 +294,7 @@ def @app.standard_system_dir ENV["RAKE_SYSTEM"] = nil @app.instance_eval do - handle_options + handle_options [] options.silent = true options.load_system = true options.rakelib = [] @@ -362,28 +354,22 @@ def test_building_imported_files_on_demand def test_handle_options_should_not_strip_options_from_argv assert !@app.options.trace - valid_option = "--trace" - setup_command_line(valid_option) + argv = %w[--trace] + @app.handle_options argv - @app.handle_options - - assert ARGV.include?(valid_option) + assert_includes argv, '--trace' assert @app.options.trace end def test_handle_options_trace_default_is_stderr - setup_command_line("--trace") - - @app.handle_options + @app.handle_options %w[--trace] assert_equal STDERR, @app.options.trace_output assert @app.options.trace end def test_handle_options_trace_overrides_to_stdout - setup_command_line("--trace=stdout") - - @app.handle_options + @app.handle_options %w[--trace=stdout] assert_equal STDOUT, @app.options.trace_output assert @app.options.trace @@ -392,18 +378,16 @@ def test_handle_options_trace_overrides_to_stdout def test_handle_options_trace_does_not_eat_following_task_names assert !@app.options.trace - setup_command_line("--trace", "sometask") + argv = %w[--trace sometask] + @app.handle_options argv - @app.handle_options - assert ARGV.include?("sometask") + assert argv.include?("sometask") assert @app.options.trace end def test_good_run ran = false - ARGV << '--rakelib=""' - @app.options.silent = true @app.instance_eval do @@ -413,7 +397,7 @@ def test_good_run rakefile_default out, err = capture_io do - @app.run + @app.run %w[--rakelib=""] end assert ran @@ -423,10 +407,9 @@ def test_good_run def test_display_task_run ran = false - setup_command_line("-f", "-s", "--tasks", '--rakelib=""') @app.last_description = "COMMENT" @app.define_task(Rake::Task, "default") - out, = capture_io { @app.run } + out, = capture_io { @app.run %w[-f -s --tasks --rakelib=""] } assert @app.options.show_tasks assert ! ran assert_match(/rake default/, out) @@ -435,13 +418,12 @@ def test_display_task_run def test_display_prereqs ran = false - setup_command_line("-f", "-s", "--prereqs", '--rakelib=""') @app.last_description = "COMMENT" t = @app.define_task(Rake::Task, "default") t.enhance([:a, :b]) @app.define_task(Rake::Task, "a") @app.define_task(Rake::Task, "b") - out, = capture_io { @app.run } + out, = capture_io { @app.run %w[-f -s --prereqs --rakelib=""] } assert @app.options.show_prereqs assert ! ran assert_match(/rake a$/, out) @@ -451,37 +433,28 @@ def test_display_prereqs def test_bad_run @app.intern(Rake::Task, "default").enhance { fail } - setup_command_line("-f", "-s", '--rakelib=""') _, err = capture_io { - assert_raises(SystemExit) { @app.run } + assert_raises(SystemExit) { @app.run %w[-f -s --rakelib=""]} } assert_match(/see full trace/i, err) - ensure - ARGV.clear end def test_bad_run_with_trace @app.intern(Rake::Task, "default").enhance { fail } - setup_command_line("-f", "-s", "-t") _, err = capture_io { - assert_raises(SystemExit) { @app.run } + assert_raises(SystemExit) { @app.run %w[-f -s -t] } } refute_match(/see full trace/i, err) - ensure - ARGV.clear end def test_bad_run_with_backtrace @app.intern(Rake::Task, "default").enhance { fail } - setup_command_line("-f", "-s", "--backtrace") _, err = capture_io { assert_raises(SystemExit) { - @app.run + @app.run %w[-f -s --backtrace] } } refute_match(/see full trace/, err) - ensure - ARGV.clear end CustomError = Class.new(RuntimeError) @@ -490,10 +463,9 @@ def test_bad_run_includes_exception_name @app.intern(Rake::Task, "default").enhance { raise CustomError, "intentional" } - setup_command_line("-f", "-s") _, err = capture_io { assert_raises(SystemExit) { - @app.run + @app.run %w[-f -s] } } assert_match(/CustomError: intentional/, err) @@ -503,10 +475,9 @@ def test_rake_error_excludes_exception_name @app.intern(Rake::Task, "default").enhance { fail "intentional" } - setup_command_line("-f", "-s") _, err = capture_io { assert_raises(SystemExit) { - @app.run + @app.run %w[-f -s] } } refute_match(/RuntimeError/, err) @@ -527,28 +498,22 @@ def test_printing_original_exception_cause raise custom_error, "Secondary Error" end } - setup_command_line("-f", "-s") _ ,err = capture_io { assert_raises(SystemExit) { - @app.run + @app.run %w[-f -s] } } if cause_supported? assert_match(/Original Error/, err) end assert_match(/Secondary Error/, err) - ensure - ARGV.clear end def test_run_with_bad_options @app.intern(Rake::Task, "default").enhance { fail } - setup_command_line("-f", "-s", "--xyzzy") assert_raises(SystemExit) { - capture_io { @app.run } + capture_io { @app.run %w[-f -s --xyzzy] } } - ensure - ARGV.clear end def test_standard_exception_handling_invalid_option diff --git a/test/test_rake_application_options.rb b/test/test_rake_application_options.rb index eb4060973..a8a71a981 100644 --- a/test/test_rake_application_options.rb +++ b/test/test_rake_application_options.rb @@ -445,8 +445,6 @@ def test_rake_explicit_task_library def flags(*sets) sets.each do |set| - ARGV.clear - @exit = catch(:system_exit) { command_line(*set) } yield(@app.options) if block_given? @@ -454,13 +452,12 @@ def flags(*sets) end def command_line(*options) - options.each do |opt| ARGV << opt end @app = Rake::Application.new def @app.exit(*args) throw :system_exit, :exit end @app.instance_eval do - args = handle_options + args = handle_options options collect_command_line_tasks(args) end @tasks = @app.top_level_tasks From ee7e30be5abbdd4e0190bcdf06abf6521e72cb91 Mon Sep 17 00:00:00 2001 From: Eric Hodel Date: Wed, 28 Jun 2017 17:58:12 -0700 Subject: [PATCH 3/5] Remove test ARGV manipulation These are no longer necessary --- test/test_rake_application_options.rb | 6 ------ test/test_rake_task_argument_parsing.rb | 6 ++---- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/test/test_rake_application_options.rb b/test/test_rake_application_options.rb index a8a71a981..d774678b6 100644 --- a/test/test_rake_application_options.rb +++ b/test/test_rake_application_options.rb @@ -9,7 +9,6 @@ def setup super @testkey = ENV["TESTKEY"] - clear_argv Rake::FileUtilsExt.verbose_flag = false Rake::FileUtilsExt.nowrite_flag = false TESTING_REQUIRE.clear @@ -17,17 +16,12 @@ def setup def teardown ENV["TESTKEY"] = @testkey - clear_argv Rake::FileUtilsExt.verbose_flag = false Rake::FileUtilsExt.nowrite_flag = false super end - def clear_argv - ARGV.pop until ARGV.empty? - end - def test_default_options opts = command_line assert_equal false, opts.backtrace diff --git a/test/test_rake_task_argument_parsing.rb b/test/test_rake_task_argument_parsing.rb index e34126591..fc494a463 100644 --- a/test/test_rake_task_argument_parsing.rb +++ b/test/test_rake_task_argument_parsing.rb @@ -96,16 +96,14 @@ def @app.unix?() raise end end def test_no_rakeopt - ARGV << "--trace" app = Rake::Application.new - app.init + app.init %w[--trace] assert !app.options.silent end def test_rakeopt_with_blank_options - ARGV << "--trace" app = Rake::Application.new - app.init + app.init %w[--trace] assert !app.options.silent end From ae4f78662accf3145654a005e9f0c0a38b1bd287 Mon Sep 17 00:00:00 2001 From: Eric Hodel Date: Wed, 28 Jun 2017 18:14:48 -0700 Subject: [PATCH 4/5] Add Rake::with_application This allows users to use Rake as a library more easily. This allows loading multiple rakefiles from separate sources into separate Rake applications without task name collisions. --- lib/rake/rake_module.rb | 30 ++++++++++++++++++++++++++++++ test/test_rake_application.rb | 30 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/lib/rake/rake_module.rb b/lib/rake/rake_module.rb index f5cfde58a..1ee9d7d63 100644 --- a/lib/rake/rake_module.rb +++ b/lib/rake/rake_module.rb @@ -34,6 +34,36 @@ def add_rakelib(*files) application.options.rakelib ||= [] application.options.rakelib.concat(files) end + + # Make +block_application+ the default rake application inside a block so + # you can load rakefiles into a different application. + # + # This is useful when you want to run rake tasks inside a library without + # running rake in a sub-shell. + # + # Example: + # + # Dir.chdir 'other/directory' + # + # other_rake = Rake.with_application do |rake| + # rake.set_default_options + # + # rake.load_rakefile + # end + # + # puts other_rake.tasks + + def with_application(block_application = Rake::Application.new) + orig_application = Rake.application + + Rake.application = block_application + + yield block_application + + block_application + ensure + Rake.application = orig_application + end end end diff --git a/test/test_rake_application.rb b/test/test_rake_application.rb index 0212c9b22..c7d45652c 100644 --- a/test/test_rake_application.rb +++ b/test/test_rake_application.rb @@ -10,6 +10,36 @@ def setup @app.options.rakelib = [] end + def test_class_with_application + orig_app = Rake.application + + return_app = Rake.with_application do |yield_app| + refute_equal orig_app, yield_app, 'new application must be yielded' + + assert_equal yield_app, Rake.application, + 'new application must be default in block' + end + + refute_equal orig_app, return_app, 'new application not returned' + assert_equal orig_app, Rake.application, 'original application not default' + end + + def test_class_with_application_user_defined + orig_app = Rake.application + + user_app = Rake::Application.new + + return_app = Rake.with_application user_app do |yield_app| + assert_equal user_app, yield_app, 'user application must be yielded' + + assert_equal user_app, Rake.application, + 'user application must be default in block' + end + + assert_equal user_app, return_app, 'user application not returned' + assert_equal orig_app, Rake.application, 'original application not default' + end + def test_display_exception_details obj = Object.new obj.instance_eval("def #{__method__}; raise 'test'; end", "ruby") From 4f9c15644910c4c82c318f18753480adf4caa284 Mon Sep 17 00:00:00 2001 From: Eric Hodel Date: Wed, 28 Jun 2017 18:36:29 -0700 Subject: [PATCH 5/5] Always set_default_options This allows Rake.with_application to work without users having to know to set the default options manually. --- lib/rake/application.rb | 2 ++ lib/rake/rake_module.rb | 2 -- test/test_rake_application.rb | 9 +++++++++ test/test_rake_task.rb | 12 +++++++++--- test/test_rake_win32.rb | 6 +++++- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/rake/application.rb b/lib/rake/application.rb index 650b6522c..8c896888f 100644 --- a/lib/rake/application.rb +++ b/lib/rake/application.rb @@ -62,6 +62,8 @@ def initialize add_loader("rake", DefaultLoader.new) @tty_output = STDOUT.tty? @terminal_columns = ENV["RAKE_COLUMNS"].to_i + + set_default_options end # Run the Rake application. The run method performs the following diff --git a/lib/rake/rake_module.rb b/lib/rake/rake_module.rb index 1ee9d7d63..03c295624 100644 --- a/lib/rake/rake_module.rb +++ b/lib/rake/rake_module.rb @@ -46,8 +46,6 @@ def add_rakelib(*files) # Dir.chdir 'other/directory' # # other_rake = Rake.with_application do |rake| - # rake.set_default_options - # # rake.load_rakefile # end # diff --git a/test/test_rake_application.rb b/test/test_rake_application.rb index c7d45652c..456f7d878 100644 --- a/test/test_rake_application.rb +++ b/test/test_rake_application.rb @@ -49,6 +49,8 @@ def test_display_exception_details end out, err = capture_io do + @app.set_default_options # reset trace output IO + @app.display_error_message ex end @@ -65,6 +67,8 @@ def test_display_exception_details_bad_encoding end out, err = capture_io do + @app.set_default_options # reset trace output IO + @app.display_error_message ex end @@ -86,6 +90,8 @@ def test_display_exception_details_cause end out, err = capture_io do + @app.set_default_options # reset trace output IO + @app.display_error_message ex end @@ -472,6 +478,7 @@ def test_bad_run def test_bad_run_with_trace @app.intern(Rake::Task, "default").enhance { fail } _, err = capture_io { + @app.set_default_options assert_raises(SystemExit) { @app.run %w[-f -s -t] } } refute_match(/see full trace/i, err) @@ -563,6 +570,8 @@ def test_standard_exception_handling_invalid_option def test_standard_exception_handling_other out, err = capture_io do + @app.set_default_options # reset trace output IO + e = assert_raises SystemExit do @app.standard_exception_handling do raise "blah" diff --git a/test/test_rake_task.rb b/test/test_rake_task.rb index b2bbb6d95..8bfdedb4e 100644 --- a/test/test_rake_task.rb +++ b/test/test_rake_task.rb @@ -62,10 +62,14 @@ def test_invoke_with_circular_dependencies end def test_dry_run_prevents_actions - Rake.application.options.dryrun = true runlist = [] t1 = task(:t1) { |t| runlist << t.name; 3321 } - _, err = capture_io { t1.invoke } + _, err = capture_io { + Rake.application.set_default_options # reset trace output IO + Rake.application.options.dryrun = true + + t1.invoke + } assert_match(/execute .*t1/i, err) assert_match(/dry run/i, err) refute_match(/invoke/i, err) @@ -75,9 +79,11 @@ def test_dry_run_prevents_actions end def test_tasks_can_be_traced - Rake.application.options.trace = true t1 = task(:t1) _, err = capture_io { + Rake.application.set_default_options # reset trace output IO + Rake.application.options.trace = true + t1.invoke } assert_match(/invoke t1/i, err) diff --git a/test/test_rake_win32.rb b/test/test_rake_win32.rb index 292af4715..6c341f486 100644 --- a/test/test_rake_win32.rb +++ b/test/test_rake_win32.rb @@ -65,7 +65,11 @@ def test_win32_backtrace_with_different_case rake.options.trace = true rake.instance_variable_set(:@rakefile, "Rakefile") - _, err = capture_io { rake.display_error_message(ex) } + _, err = capture_io { + rake.set_default_options # reset trace output IO + + rake.display_error_message(ex) + } assert_match(/rakefile/, err) end