From 19dee2b2a57818504670772587ebbfbb5c6c8693 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Wed, 16 Feb 2022 12:23:30 -0500 Subject: [PATCH] Add variants - #137 Add build script methods: variant, variant_group, with_variants --- build_tests/variants/Rsconscript | 13 ++++ .../variants/error_nested_with_variants.rb | 5 ++ ..._with_variants_with_empty_variant_group.rb | 5 ++ .../error_with_variants_without_variants.rb | 2 + build_tests/variants/multiple_groups.rb | 27 ++++++++ build_tests/variants/nil_key.rb | 13 ++++ build_tests/variants/prog.c | 4 ++ lib/rscons/application.rb | 63 +++++++++++++++++ lib/rscons/environment.rb | 8 ++- lib/rscons/script.rb | 16 +++++ spec/build_tests_spec.rb | 69 +++++++++++++++++++ 11 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 build_tests/variants/Rsconscript create mode 100644 build_tests/variants/error_nested_with_variants.rb create mode 100644 build_tests/variants/error_with_variants_with_empty_variant_group.rb create mode 100644 build_tests/variants/error_with_variants_without_variants.rb create mode 100644 build_tests/variants/multiple_groups.rb create mode 100644 build_tests/variants/nil_key.rb create mode 100644 build_tests/variants/prog.c diff --git a/build_tests/variants/Rsconscript b/build_tests/variants/Rsconscript new file mode 100644 index 0000000..dd578cf --- /dev/null +++ b/build_tests/variants/Rsconscript @@ -0,0 +1,13 @@ +variant "debug" +variant "release" + +with_variants do + env "prog" do |env| + if variant("debug") + env["CPPDEFINES"] << "DEBUG" + else + env["CPPDEFINES"] << "NDEBUG" + end + env.Program("^/prog.exe", "prog.c") + end +end diff --git a/build_tests/variants/error_nested_with_variants.rb b/build_tests/variants/error_nested_with_variants.rb new file mode 100644 index 0000000..98c195d --- /dev/null +++ b/build_tests/variants/error_nested_with_variants.rb @@ -0,0 +1,5 @@ +variant "foo" +with_variants do + with_variants do + 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 new file mode 100644 index 0000000..8a65075 --- /dev/null +++ b/build_tests/variants/error_with_variants_with_empty_variant_group.rb @@ -0,0 +1,5 @@ +variant_group +variant_group +variant "foo" +with_variants do +end diff --git a/build_tests/variants/error_with_variants_without_variants.rb b/build_tests/variants/error_with_variants_without_variants.rb new file mode 100644 index 0000000..fc05511 --- /dev/null +++ b/build_tests/variants/error_with_variants_without_variants.rb @@ -0,0 +1,2 @@ +with_variants do +end diff --git a/build_tests/variants/multiple_groups.rb b/build_tests/variants/multiple_groups.rb new file mode 100644 index 0000000..38ca81d --- /dev/null +++ b/build_tests/variants/multiple_groups.rb @@ -0,0 +1,27 @@ +variant_group "desktop-environment" do + variant "kde" + variant "gnome" +end + +variant_group "debug" do + variant "debug" + variant "release" +end + +with_variants do + env "prog" do |env| + if variant("kde") + env["CPPDEFINES"] << "KDE" + end + if variant("gnome") + env["CPPDEFINES"] << "GNOME" + end + if variant("debug") + env["CPPDEFINES"] << "DEBUG" + end + if variant("release") + env["CPPDEFINES"] << "NDEBUG" + end + env.Program("^/prog.exe", "prog.c") + end +end diff --git a/build_tests/variants/nil_key.rb b/build_tests/variants/nil_key.rb new file mode 100644 index 0000000..02beb76 --- /dev/null +++ b/build_tests/variants/nil_key.rb @@ -0,0 +1,13 @@ +variant "debug" +variant "release", key: nil + +with_variants do + env "prog" do |env| + if variant("debug") + env["CPPDEFINES"] << "DEBUG" + else + env["CPPDEFINES"] << "NDEBUG" + end + env.Program("^/prog.exe", "prog.c") + end +end diff --git a/build_tests/variants/prog.c b/build_tests/variants/prog.c new file mode 100644 index 0000000..b9de9de --- /dev/null +++ b/build_tests/variants/prog.c @@ -0,0 +1,4 @@ +int main(int argc, char * argv[]) +{ + return 42; +} diff --git a/lib/rscons/application.rb b/lib/rscons/application.rb index 0498449..4afa5e0 100644 --- a/lib/rscons/application.rb +++ b/lib/rscons/application.rb @@ -5,6 +5,10 @@ module Rscons # Functionality for an instance of the rscons application invocation. class Application + # @return [Array] + # Active variant names. + attr_reader :active_variants + # @return [String] # Top-level build directory. attr_accessor :build_dir @@ -35,6 +39,7 @@ module Rscons @build_dir = ENV["RSCONS_BUILD_DIR"] || "build" ENV.delete("RSCONS_BUILD_DIR") @n_threads = Util.determine_n_threads + @variant_groups = [] end # Run the application. @@ -194,6 +199,64 @@ module Rscons cache.write end + # Define a variant, or within a with_variants block, query if it is + # active. + # + # @param name [String] + # Variant name. + def variant(name, options = {}) + if @active_variants + !!@active_variants.find {|variant| variant[:name] == name} + else + if @variant_groups.empty? + variant_group + end + options = options.dup + options[:name] = name + options[:active] = options.fetch(:default, true) + options[:key] = options.fetch(:key, name) + @variant_groups.last[:variants] << options + end + 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: []} + if block + block[] + end + end + + # Iterate through variants. + def with_variants(&block) + if @active_variants + raise "with_variants cannot be called within another with_variants block" + end + 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 + block[] + @active_variants = nil + else + @variant_groups[variants.size][:variants].each do |variant| + if variant[:active] + iter_vgs[variants + [variant]] + end + end + end + end + iter_vgs[[]] + end + private def show_script_tasks diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index e200868..08f62fc 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -93,10 +93,14 @@ module Rscons def initialize(*args, &block) @id = self.class.get_id if args.first.is_a?(String) - @name = args.slice!(0) + base_name = args.slice!(0) else - @name = "e.#{@id}" + base_name = "e.#{@id}" end + variant_keys = (Rscons.application.active_variants || []).map do |variant| + variant[:key] + end.compact + @name = [base_name, *variant_keys].join("-") options = args.first || {} Rscons.application.check_configure unless Cache.instance["configuration_data"]["configured"] diff --git a/lib/rscons/script.rb b/lib/rscons/script.rb index 433e86c..fa2c5a5 100644 --- a/lib/rscons/script.rb +++ b/lib/rscons/script.rb @@ -244,6 +244,22 @@ module Rscons Util.task(*args, &block) end + # Define a variant, or within a with_variants block, query if it is + # active. + def variant(*args) + Rscons.application.variant(*args) + end + + # Create a variant group. + def variant_group(*args, &block) + Rscons.application.variant_group(*args, &block) + end + + # Iterate through variants. + def with_variants(&block) + Rscons.application.with_variants(&block) + end + [ :cd, :chmod, diff --git a/spec/build_tests_spec.rb b/spec/build_tests_spec.rb index dba0a78..2932c8a 100644 --- a/spec/build_tests_spec.rb +++ b/spec/build_tests_spec.rb @@ -3039,4 +3039,73 @@ EOF end end + context "variants" do + it "appends variant names to environment names to form build directories" do + test_dir "variants" + result = run_rscons + expect(result.stderr).to eq "" + expect(result.status).to eq 0 + 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 querying active variants and changing behavior" do + test_dir "variants" + result = run_rscons(args: %w[-v]) + expect(result.stderr).to eq "" + expect(result.status).to eq 0 + expect(File.exist?("build/prog-debug/prog.exe")).to be_truthy + expect(File.exist?("build/prog-release/prog.exe")).to be_truthy + expect(result.stdout).to match %r{gcc .*-o.*build/prog-debug/.*-DDEBUG} + expect(result.stdout).to match %r{gcc .*-o.*build/prog-release/.*-DNDEBUG} + end + + it "allows specifying a nil key for a variant" do + test_dir "variants" + result = run_rscons(args: %w[-v -f nil_key.rb]) + expect(result.stderr).to eq "" + expect(result.status).to eq 0 + expect(File.exist?("build/prog-debug/prog.exe")).to be_truthy + expect(File.exist?("build/prog/prog.exe")).to be_truthy + expect(result.stdout).to match %r{gcc .*-o.*build/prog-debug/.*-DDEBUG} + expect(result.stdout).to match %r{gcc .*-o.*build/prog/.*-DNDEBUG} + end + + it "allows multiple variant groups" do + test_dir "variants" + result = run_rscons(args: %w[-v -f multiple_groups.rb]) + 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_truthy + expect(File.exist?("build/prog-gnome-debug/prog.exe")).to be_truthy + expect(File.exist?("build/prog-gnome-release/prog.exe")).to be_truthy + expect(result.stdout).to match %r{gcc .*-o.*build/prog-kde-debug/.*-DKDE.*-DDEBUG} + expect(result.stdout).to match %r{gcc .*-o.*build/prog-kde-release/.*-DKDE.*-DNDEBUG} + expect(result.stdout).to match %r{gcc .*-o.*build/prog-gnome-debug/.*-DGNOME.*-DDEBUG} + expect(result.stdout).to match %r{gcc .*-o.*build/prog-gnome-release/.*-DGNOME.*-DNDEBUG} + end + + it "raises an error when with_variants is called within another with_variants block" do + test_dir "variants" + result = run_rscons(args: %w[-f error_nested_with_variants.rb]) + expect(result.stderr).to match %r{with_variants cannot be called within another with_variants block} + expect(result.status).to_not eq 0 + end + + it "raises an error when with_variants is called with no variants defined" do + test_dir "variants" + result = run_rscons(args: %w[-f error_with_variants_without_variants.rb]) + expect(result.stderr).to match %r{with_variants cannot be called with no variants defined} + expect(result.status).to_not eq 0 + end + + it "raises an error when with_variants is called with an empty variant group" 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} + expect(result.status).to_not eq 0 + end + end + end