diff --git a/build_tests/variants/default.rb b/build_tests/variants/default.rb new file mode 100644 index 0000000..44ddae6 --- /dev/null +++ b/build_tests/variants/default.rb @@ -0,0 +1,8 @@ +variant "debug", default: false +variant "release" + +with_variants do + env "prog" do |env| + env.Program("^/prog.exe", "prog.c") + end +end diff --git a/build_tests/variants/error_with_variants_with_empty_variant_group.rb b/build_tests/variants/error_with_variants_with_empty_variant_group.rb deleted file mode 100644 index 8a65075..0000000 --- a/build_tests/variants/error_with_variants_with_empty_variant_group.rb +++ /dev/null @@ -1,5 +0,0 @@ -variant_group -variant_group -variant "foo" -with_variants do -end diff --git a/lib/rscons/application.rb b/lib/rscons/application.rb index 4afa5e0..9b3d817 100644 --- a/lib/rscons/application.rb +++ b/lib/rscons/application.rb @@ -5,8 +5,8 @@ module Rscons # Functionality for an instance of the rscons application invocation. class Application - # @return [Array] - # Active variant names. + # @return [Array] + # Active variants. attr_reader :active_variants # @return [String] @@ -54,11 +54,19 @@ module Rscons # List of task(s) to execute. # @param show_tasks [Boolean] # Flag to show tasks and exit. + # @param enabled_variants [String] + # User-specified variants list. # # @return [Integer] # Process exit code (0 on success). - def run(rsconscript, tasks_and_params, show_tasks) + def run(rsconscript, tasks_and_params, show_tasks, enabled_variants) Cache.instance["failed_commands"] = [] + @enabled_variants = enabled_variants + if enabled_variants == "" && !tasks_and_params.include?("configure") + if cache_enabled_variants = Cache.instance["configuration_data"]["enabled_variants"] + @enabled_variants = cache_enabled_variants + end + end @script = Script.new @script.load(rsconscript) if show_tasks @@ -66,6 +74,7 @@ module Rscons return 0 end apply_task_params(tasks_and_params) + enable_variants if tasks_and_params.empty? check_process_environments if Task.tasks["default"] @@ -79,6 +88,32 @@ module Rscons 0 end + # Apply user-specified variant enables and complain if they don't make + # sense given the build script variant configuration. + def enable_variants + unless @_variants_enabled + if @enabled_variants != "" + exact = !(@enabled_variants =~ /^(\+|-)/) + enabled_variants = @enabled_variants.split(",") + specified_variants = {} + enabled_variants.each do |enable_variant| + enable_variant =~ /^(\+|-)?(.*)$/ + enable_disable, variant_name = $1, $2 + specified_variants[variant_name] = enable_disable != "-" + end + each_variant do |variant| + if specified_variants.include?(variant[:name]) + variant[:enabled] = specified_variants[variant[:name]] + elsif exact + variant[:enabled] = false + end + end + end + @_variants_enabled = true + end + check_enabled_variants + end + # Show the last failures. # # @return [void] @@ -131,6 +166,7 @@ module Rscons # # @return [void] def check_configure + enable_variants unless Cache.instance["configuration_data"]["configured"] if @script.autoconf configure @@ -168,6 +204,7 @@ module Rscons co.close(false) raise e end + Cache.instance["configuration_data"]["enabled_variants"] = @enabled_variants co.close(true) end @@ -213,24 +250,48 @@ module Rscons end options = options.dup options[:name] = name - options[:active] = options.fetch(:default, true) + options[:enabled] = options.fetch(:default, true) options[:key] = options.fetch(:key, name) @variant_groups.last[:variants] << options end end + # Check if a variant is enabled. + # + # This can be used, for example, in a configuration block to omit or + # include configuration checks based on which variants have been + # configured. + # + # @param variant_name [String] + # Variant name. + # + # @return [Boolean] + # Whether the requested variant is enabled. + def variant_enabled?(variant_name) + each_variant do |variant| + if variant[:name] == variant_name + return variant[:enabled] + end + end + false + end + # Create a variant group. def variant_group(*args, &block) if args.first.is_a?(String) name = args.slice!(0) end - @variant_groups << {name: name, variants: []} + options = args.first || {} + @variant_groups << options.merge(name: name, variants: []) if block block[] end end - # Iterate through variants. + # Iterate through enabled variants. + # + # The given block is called for each combination of enabled variants + # across the defined variant groups. def with_variants(&block) if @active_variants raise "with_variants cannot be called within another with_variants block" @@ -238,18 +299,15 @@ module Rscons if @variant_groups.empty? raise "with_variants cannot be called with no variants defined" end - if @variant_groups.any? {|variant_group| variant_group[:variants].empty?} - raise "Error: empty variant group found" - end - iter_vgs = lambda do |variants| - if variants.size == @variant_groups.size - @active_variants = variants + iter_vgs = lambda do |iter_variants| + if iter_variants.size == @variant_groups.size + @active_variants = iter_variants.compact block[] @active_variants = nil else - @variant_groups[variants.size][:variants].each do |variant| - if variant[:active] - iter_vgs[variants + [variant]] + @variant_groups[iter_variants.size][:variants].each do |variant| + if variant[:enabled] + iter_vgs[iter_variants + [variant]] end end end @@ -259,6 +317,29 @@ module Rscons private + def check_enabled_variants + @variant_groups.each do |variant_group| + enabled_count = variant_group[:variants].count do |variant| + variant[:enabled] + end + if enabled_count == 0 + message = "No variants enabled for variant group" + if variant_group[:name] + message += " #{variant_group[:name].inspect}" + end + raise RsconsError.new(message) + end + end + end + + def each_variant + @variant_groups.each do |variant_group| + variant_group[:variants].each do |variant| + yield variant + end + end + end + def show_script_tasks puts "Tasks:" Task[].sort.each do |task_name, task| diff --git a/lib/rscons/cli.rb b/lib/rscons/cli.rb index 2af0580..9f60792 100644 --- a/lib/rscons/cli.rb +++ b/lib/rscons/cli.rb @@ -54,6 +54,7 @@ module Rscons def run_toplevel(argv) rsconscript = nil show_tasks = false + enabled_variants = "" OptionParser.new do |opts| @@ -61,6 +62,10 @@ module Rscons Rscons.application.build_dir = build_dir end + opts.on("-e", "--variants VS") do |variants| + enabled_variants = variants + end + opts.on("-f FILE") do |f| rsconscript = f end @@ -127,7 +132,7 @@ module Rscons end begin - Rscons.application.run(rsconscript, tasks_and_params, show_tasks) + Rscons.application.run(rsconscript, tasks_and_params, show_tasks, enabled_variants) rescue RsconsError => e Ansi.write($stderr, :red, e.message, :reset, "\n") 1 @@ -140,6 +145,7 @@ Usage: #{$0} [global options] [[task] [task options] ...] Global options: -b BUILD, --build=BUILD Set build directory (default: build) + -e VS, --variants=VS Enable or disable variants -f FILE Use FILE as Rsconscript -F, --show-failure Show failed command log from previous build and exit -h, --help Show rscons help and exit diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index 08f62fc..9eaf5f1 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -102,7 +102,6 @@ module Rscons end.compact @name = [base_name, *variant_keys].join("-") options = args.first || {} - Rscons.application.check_configure unless Cache.instance["configuration_data"]["configured"] raise "Project must be configured before creating an Environment" end diff --git a/lib/rscons/script.rb b/lib/rscons/script.rb index fa2c5a5..cf1ebf8 100644 --- a/lib/rscons/script.rb +++ b/lib/rscons/script.rb @@ -95,6 +95,7 @@ module Rscons # Create an environment. def env(*args, &block) + Rscons.application.check_configure Environment.new(*args, &block) end @@ -250,6 +251,11 @@ module Rscons Rscons.application.variant(*args) end + # Check if a variant is enabled. + def variant_enabled?(*args) + Rscons.application.variant_enabled?(*args) + end + # Create a variant group. def variant_group(*args, &block) Rscons.application.variant_group(*args, &block) @@ -257,6 +263,7 @@ module Rscons # Iterate through variants. def with_variants(&block) + Rscons.application.enable_variants Rscons.application.with_variants(&block) end diff --git a/spec/build_tests_spec.rb b/spec/build_tests_spec.rb index 2932c8a..18e24c3 100644 --- a/spec/build_tests_spec.rb +++ b/spec/build_tests_spec.rb @@ -3100,10 +3100,64 @@ EOF expect(result.status).to_not eq 0 end - it "raises an error when with_variants is called with an empty variant group" do + it "allows specifying the exact enabled variants on the command line 1" do test_dir "variants" - result = run_rscons(args: %w[-f error_with_variants_with_empty_variant_group.rb]) - expect(result.stderr).to match %r{Error: empty variant group found} + result = run_rscons(args: %w[-v -f multiple_groups.rb -e kde,debug]) + expect(result.stderr).to eq "" + expect(result.status).to eq 0 + expect(File.exist?("build/prog-kde-debug/prog.exe")).to be_truthy + expect(File.exist?("build/prog-kde-release/prog.exe")).to be_falsey + expect(File.exist?("build/prog-gnome-debug/prog.exe")).to be_falsey + expect(File.exist?("build/prog-gnome-release/prog.exe")).to be_falsey + end + + it "allows specifying the exact enabled variants on the command line 2" do + test_dir "variants" + result = run_rscons(args: %w[-v -f multiple_groups.rb -e kde,gnome,release]) + expect(result.stderr).to eq "" + expect(result.status).to eq 0 + expect(File.exist?("build/prog-kde-debug/prog.exe")).to be_falsey + expect(File.exist?("build/prog-kde-release/prog.exe")).to be_truthy + expect(File.exist?("build/prog-gnome-debug/prog.exe")).to be_falsey + expect(File.exist?("build/prog-gnome-release/prog.exe")).to be_truthy + end + + it "allows disabling a single variant on the command line" do + test_dir "variants" + result = run_rscons(args: %w[-v -f multiple_groups.rb --variants=-kde]) + expect(result.stderr).to eq "" + expect(result.status).to eq 0 + expect(File.exist?("build/prog-kde-debug/prog.exe")).to be_falsey + expect(File.exist?("build/prog-kde-release/prog.exe")).to be_falsey + expect(File.exist?("build/prog-gnome-debug/prog.exe")).to be_truthy + expect(File.exist?("build/prog-gnome-release/prog.exe")).to be_truthy + end + + it "allows turning off variants by default" do + test_dir "variants" + result = run_rscons(args: %w[-v -f default.rb]) + expect(File.exist?("build/prog-debug/prog.exe")).to be_falsey + expect(File.exist?("build/prog-release/prog.exe")).to be_truthy + end + + it "allows turning on an off-by-default-variant from the command line" do + test_dir "variants" + result = run_rscons(args: %w[-v -f default.rb -e +debug]) + expect(File.exist?("build/prog-debug/prog.exe")).to be_truthy + expect(File.exist?("build/prog-release/prog.exe")).to be_truthy + end + + it "allows only turning on an off-by-default-variant from the command line" do + test_dir "variants" + result = run_rscons(args: %w[-v -f default.rb -e debug]) + expect(File.exist?("build/prog-debug/prog.exe")).to be_truthy + expect(File.exist?("build/prog-release/prog.exe")).to be_falsey + end + + it "exits with an error if no variant in a variant group is activated" do + test_dir "variants" + result = run_rscons(args: %w[-v -f multiple_groups.rb --variants=kde]) + expect(result.stderr).to match %r{No variants enabled for variant group} expect(result.status).to_not eq 0 end end