Add variants - close #137

This commit is contained in:
Josh Holtrop 2022-02-20 08:54:33 -05:00
parent 19dee2b2a5
commit 8269a98d01
8 changed files with 214 additions and 25 deletions

View File

@ -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

View File

@ -1,5 +0,0 @@
variant_group
variant_group
variant "foo"
with_variants do
end

View File

@ -0,0 +1,14 @@
variant "one"
variant "two", default: false
configure do
if variant_enabled?("one")
puts "one enabled"
end
if variant_enabled?("two")
puts "two enabled"
end
if variant_enabled?("three")
puts "three enabled"
end
end

View File

@ -5,8 +5,8 @@ module Rscons
# Functionality for an instance of the rscons application invocation. # Functionality for an instance of the rscons application invocation.
class Application class Application
# @return [Array<String>] # @return [Array<Hash>]
# Active variant names. # Active variants.
attr_reader :active_variants attr_reader :active_variants
# @return [String] # @return [String]
@ -54,11 +54,19 @@ module Rscons
# List of task(s) to execute. # List of task(s) to execute.
# @param show_tasks [Boolean] # @param show_tasks [Boolean]
# Flag to show tasks and exit. # Flag to show tasks and exit.
# @param enabled_variants [String]
# User-specified variants list.
# #
# @return [Integer] # @return [Integer]
# Process exit code (0 on success). # 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"] = [] 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 = Script.new
@script.load(rsconscript) @script.load(rsconscript)
if show_tasks if show_tasks
@ -66,6 +74,7 @@ module Rscons
return 0 return 0
end end
apply_task_params(tasks_and_params) apply_task_params(tasks_and_params)
enable_variants
if tasks_and_params.empty? if tasks_and_params.empty?
check_process_environments check_process_environments
if Task.tasks["default"] if Task.tasks["default"]
@ -79,6 +88,32 @@ module Rscons
0 0
end 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. # Show the last failures.
# #
# @return [void] # @return [void]
@ -131,6 +166,7 @@ module Rscons
# #
# @return [void] # @return [void]
def check_configure def check_configure
enable_variants
unless Cache.instance["configuration_data"]["configured"] unless Cache.instance["configuration_data"]["configured"]
if @script.autoconf if @script.autoconf
configure configure
@ -168,6 +204,7 @@ module Rscons
co.close(false) co.close(false)
raise e raise e
end end
Cache.instance["configuration_data"]["enabled_variants"] = @enabled_variants
co.close(true) co.close(true)
end end
@ -213,24 +250,48 @@ module Rscons
end end
options = options.dup options = options.dup
options[:name] = name options[:name] = name
options[:active] = options.fetch(:default, true) options[:enabled] = options.fetch(:default, true)
options[:key] = options.fetch(:key, name) options[:key] = options.fetch(:key, name)
@variant_groups.last[:variants] << options @variant_groups.last[:variants] << options
end end
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. # Create a variant group.
def variant_group(*args, &block) def variant_group(*args, &block)
if args.first.is_a?(String) if args.first.is_a?(String)
name = args.slice!(0) name = args.slice!(0)
end end
@variant_groups << {name: name, variants: []} options = args.first || {}
@variant_groups << options.merge(name: name, variants: [])
if block if block
block[] block[]
end end
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) def with_variants(&block)
if @active_variants if @active_variants
raise "with_variants cannot be called within another with_variants block" raise "with_variants cannot be called within another with_variants block"
@ -238,18 +299,15 @@ module Rscons
if @variant_groups.empty? if @variant_groups.empty?
raise "with_variants cannot be called with no variants defined" raise "with_variants cannot be called with no variants defined"
end end
if @variant_groups.any? {|variant_group| variant_group[:variants].empty?} iter_vgs = lambda do |iter_variants|
raise "Error: empty variant group found" if iter_variants.size == @variant_groups.size
end @active_variants = iter_variants.compact
iter_vgs = lambda do |variants|
if variants.size == @variant_groups.size
@active_variants = variants
block[] block[]
@active_variants = nil @active_variants = nil
else else
@variant_groups[variants.size][:variants].each do |variant| @variant_groups[iter_variants.size][:variants].each do |variant|
if variant[:active] if variant[:enabled]
iter_vgs[variants + [variant]] iter_vgs[iter_variants + [variant]]
end end
end end
end end
@ -259,6 +317,29 @@ module Rscons
private 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 def show_script_tasks
puts "Tasks:" puts "Tasks:"
Task[].sort.each do |task_name, task| Task[].sort.each do |task_name, task|

View File

@ -54,6 +54,7 @@ module Rscons
def run_toplevel(argv) def run_toplevel(argv)
rsconscript = nil rsconscript = nil
show_tasks = false show_tasks = false
enabled_variants = ""
OptionParser.new do |opts| OptionParser.new do |opts|
@ -61,6 +62,10 @@ module Rscons
Rscons.application.build_dir = build_dir Rscons.application.build_dir = build_dir
end end
opts.on("-e", "--variants VS") do |variants|
enabled_variants = variants
end
opts.on("-f FILE") do |f| opts.on("-f FILE") do |f|
rsconscript = f rsconscript = f
end end
@ -127,7 +132,7 @@ module Rscons
end end
begin begin
Rscons.application.run(rsconscript, tasks_and_params, show_tasks) Rscons.application.run(rsconscript, tasks_and_params, show_tasks, enabled_variants)
rescue RsconsError => e rescue RsconsError => e
Ansi.write($stderr, :red, e.message, :reset, "\n") Ansi.write($stderr, :red, e.message, :reset, "\n")
1 1
@ -140,6 +145,7 @@ Usage: #{$0} [global options] [[task] [task options] ...]
Global options: Global options:
-b BUILD, --build=BUILD Set build directory (default: build) -b BUILD, --build=BUILD Set build directory (default: build)
-e VS, --variants=VS Enable or disable variants
-f FILE Use FILE as Rsconscript -f FILE Use FILE as Rsconscript
-F, --show-failure Show failed command log from previous build and exit -F, --show-failure Show failed command log from previous build and exit
-h, --help Show rscons help and exit -h, --help Show rscons help and exit

View File

@ -102,7 +102,6 @@ module Rscons
end.compact end.compact
@name = [base_name, *variant_keys].join("-") @name = [base_name, *variant_keys].join("-")
options = args.first || {} options = args.first || {}
Rscons.application.check_configure
unless Cache.instance["configuration_data"]["configured"] unless Cache.instance["configuration_data"]["configured"]
raise "Project must be configured before creating an Environment" raise "Project must be configured before creating an Environment"
end end

View File

@ -95,6 +95,7 @@ module Rscons
# Create an environment. # Create an environment.
def env(*args, &block) def env(*args, &block)
Rscons.application.check_configure
Environment.new(*args, &block) Environment.new(*args, &block)
end end
@ -250,6 +251,11 @@ module Rscons
Rscons.application.variant(*args) Rscons.application.variant(*args)
end end
# Check if a variant is enabled.
def variant_enabled?(*args)
Rscons.application.variant_enabled?(*args)
end
# Create a variant group. # Create a variant group.
def variant_group(*args, &block) def variant_group(*args, &block)
Rscons.application.variant_group(*args, &block) Rscons.application.variant_group(*args, &block)
@ -257,6 +263,7 @@ module Rscons
# Iterate through variants. # Iterate through variants.
def with_variants(&block) def with_variants(&block)
Rscons.application.enable_variants
Rscons.application.with_variants(&block) Rscons.application.with_variants(&block)
end end

View File

@ -3100,12 +3100,91 @@ EOF
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
end 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" test_dir "variants"
result = run_rscons(args: %w[-f error_with_variants_with_empty_variant_group.rb]) result = run_rscons(args: %w[-v -f multiple_groups.rb -e kde,debug])
expect(result.stderr).to match %r{Error: empty variant group found} 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 expect(result.status).to_not eq 0
end end
it "allows querying if a variant is enabled" do
test_dir "variants"
result = run_rscons(args: %w[-f variant_enabled.rb configure])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match %r{one enabled}
expect(result.stdout).to_not match %r{two enabled}
expect(result.stdout).to_not match %r{three enabled}
result = run_rscons(args: %w[-f variant_enabled.rb --variants=+two configure])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match %r{one enabled}
expect(result.stdout).to match %r{two enabled}
expect(result.stdout).to_not match %r{three enabled}
result = run_rscons(args: %w[-f variant_enabled.rb --variants=two configure])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to_not match %r{one enabled}
expect(result.stdout).to match %r{two enabled}
expect(result.stdout).to_not match %r{three enabled}
end
end end
end end