custom config checks - close #108

This commit is contained in:
Josh Holtrop 2019-08-17 16:06:37 -04:00
parent d202e15a57
commit 0ccf43cc96
4 changed files with 207 additions and 104 deletions

View File

@ -0,0 +1,26 @@
configure do
custom_check("Checking 'grep' version") do |op|
stdout, stderr, status = op.log_and_test_command(%w[grep --version])
should_fail = true
if status != 0
fail_message = "error executing grep"
elsif stdout =~ /^grep \(GNU grep\) 1\./
fail_message = "too old!"
status = 1
elsif stdout =~ /^grep \(GNU grep\) 2\./
fail_message = "we'll work with it but you should upgrade"
status = 1
should_fail = false
op.store_merge("CPPDEFINES" => "GREP_WORKAROUND")
else
op.store_append("CPPDEFINES" => "GREP_FULL")
end
op.complete(status, success_message: "good!", fail_message: fail_message, fail: should_fail)
end
end
build do
Environment.new do |env|
puts env["CPPDEFINES"]
end
end

View File

@ -69,7 +69,7 @@ module Rscons
cc = ccc.find do |cc|
test_c_compiler(cc)
end
common_config_checks(cc ? 0 : 1, success_message: cc)
complete(cc ? 0 : 1, success_message: cc)
end
# Check for a working C++ compiler.
@ -87,7 +87,7 @@ module Rscons
cc = ccc.find do |cc|
test_cxx_compiler(cc)
end
common_config_checks(cc ? 0 : 1, success_message: cc)
complete(cc ? 0 : 1, success_message: cc)
end
# Check for a working D compiler.
@ -105,7 +105,7 @@ module Rscons
dc = cdc.find do |dc|
test_d_compiler(dc)
end
common_config_checks(dc ? 0 : 1, success_message: dc)
complete(dc ? 0 : 1, success_message: dc)
end
# Check for a package or configure program output.
@ -122,7 +122,7 @@ module Rscons
if status == 0
store_parse(stdout, options)
end
common_config_checks(status, options)
complete(status, options)
end
# Check for a C header.
@ -158,7 +158,7 @@ module Rscons
break
end
end
common_config_checks(status, options)
complete(status, options)
end
# Check for a C++ header.
@ -194,7 +194,7 @@ module Rscons
break
end
end
common_config_checks(status, options)
complete(status, options)
end
# Check for a D import.
@ -230,7 +230,7 @@ module Rscons
break
end
end
common_config_checks(status, options)
complete(status, options)
end
# Check for a library.
@ -268,14 +268,113 @@ module Rscons
if status == 0
store_append({"LIBS" => [lib]}, options)
end
common_config_checks(status, options)
complete(status, options)
end
# Check for a executable program.
def check_program(program, options = {})
Ansi.write($stdout, "Checking for program '", :cyan, program, :reset, "'... ")
path = Util.find_executable(program)
common_config_checks(path ? 0 : 1, options.merge(success_message: path))
complete(path ? 0 : 1, options.merge(success_message: path))
end
# Execute a test command and log the result.
#
# @return [String, String, Process::Status]
# stdout, stderr, status
def log_and_test_command(command)
begin
@log_fh.puts("Command: #{command.join(" ")}")
stdout, stderr, status = Open3.capture3(*command)
@log_fh.puts("Exit status: #{status.to_i}")
@log_fh.write(stdout)
@log_fh.write(stderr)
[stdout, stderr, status]
rescue Errno::ENOENT
["", "", 127]
end
end
# Store construction variables for merging into the Cache.
#
# @param vars [Hash]
# Hash containing the variables to merge.
# @param options [Hash]
# Options.
def store_merge(vars, options = {})
store_vars = store_common(options)
store_vars["merge"] ||= {}
vars.each_pair do |key, value|
store_vars["merge"][key] = value
end
end
# Store construction variables for appending into the Cache.
#
# @param vars [Hash]
# Hash containing the variables to append.
# @param options [Hash]
# Options.
def store_append(vars, options = {})
store_vars = store_common(options)
store_vars["append"] ||= {}
vars.each_pair do |key, value|
if store_vars["append"][key].is_a?(Array) and value.is_a?(Array)
store_vars["append"][key] += value
else
store_vars["append"][key] = value
end
end
end
# Store flags to be parsed into the Cache.
#
# @param flags [String]
# String containing the flags to parse.
# @param options [Hash]
# Options.
def store_parse(flags, options = {})
store_vars = store_common(options)
store_vars["parse"] ||= []
store_vars["parse"] << flags
end
# Perform processing common to several configure checks.
#
# @param status [Process::Status, Integer]
# Process exit code. 0 for success, non-zero for error.
# @param options [Hash]
# Common check options.
# @option options [Boolean] :fail
# Whether to fail configuration if the requested item is not found.
# This defaults to true if the :set_define option is not specified,
# otherwise defaults to false if :set_define option is specified.
# @option options [String] :set_define
# A define to set (in CPPDEFINES) if the requested item is found.
# @option options [String] :success_message
# Message to print on success (default "found").
def complete(status, options)
success_message = options[:success_message] || "found"
fail_message = options[:fail_message] || "not found"
if status == 0
Ansi.write($stdout, :green, "#{success_message}\n")
if options[:set_define]
store_append("CPPDEFINES" => [options[:set_define]])
end
else
should_fail =
if options.has_key?(:fail)
options[:fail]
else
!options[:set_define]
end
if should_fail
Ansi.write($stdout, :red, "#{fail_message}\n")
raise ConfigureFailure.new
else
Ansi.write($stdout, :yellow, "#{fail_message}\n")
end
end
end
private
@ -381,64 +480,6 @@ module Rscons
end
end
# Execute a test command and log the result.
def log_and_test_command(command)
begin
@log_fh.puts("Command: #{command.join(" ")}")
stdout, stderr, status = Open3.capture3(*command)
@log_fh.puts("Exit status: #{status.to_i}")
@log_fh.write(stdout)
@log_fh.write(stderr)
[stdout, stderr, status]
rescue Errno::ENOENT
["", "", 127]
end
end
# Store construction variables for merging into the Cache.
#
# @param vars [Hash]
# Hash containing the variables to merge.
# @param options [Hash]
# Options.
def store_merge(vars, options = {})
store_vars = store_common(options)
store_vars["merge"] ||= {}
vars.each_pair do |key, value|
store_vars["merge"][key] = value
end
end
# Store construction variables for appending into the Cache.
#
# @param vars [Hash]
# Hash containing the variables to append.
# @param options [Hash]
# Options.
def store_append(vars, options = {})
store_vars = store_common(options)
store_vars["append"] ||= {}
vars.each_pair do |key, value|
if store_vars["append"][key].is_a?(Array) and value.is_a?(Array)
store_vars["append"][key] += value
else
store_vars["append"][key] = value
end
end
end
# Store flags to be parsed into the Cache.
#
# @param flags [String]
# String containing the flags to parse.
# @param options [Hash]
# Options.
def store_parse(flags, options = {})
store_vars = store_common(options)
store_vars["parse"] ||= []
store_vars["parse"] << flags
end
# Common functionality for all store methods.
#
# @param options [Hash]
@ -462,42 +503,5 @@ module Rscons
end
end
# Perform processing common to several configure checks.
#
# @param status [Process::Status, Integer]
# Process exit code.
# @param options [Hash]
# Common check options.
# @option options [Boolean] :fail
# Whether to fail configuration if the requested item is not found.
# This defaults to true if the :set_define option is not specified,
# otherwise defaults to false if :set_define option is specified.
# @option options [String] :set_define
# A define to set (in CPPDEFINES) if the requested item is found.
# @option options [String] :success_message
# Message to print on success (default "found").
def common_config_checks(status, options)
success_message = options[:success_message] || "found"
if status == 0
Ansi.write($stdout, :green, "#{success_message}\n")
if options[:set_define]
store_append("CPPDEFINES" => [options[:set_define]])
end
else
should_fail =
if options.has_key?(:fail)
options[:fail]
else
!options[:set_define]
end
if should_fail
Ansi.write($stdout, :red, "not found\n")
raise ConfigureFailure.new
else
Ansi.write($stdout, :yellow, "not found\n")
end
end
end
end
end

View File

@ -81,6 +81,19 @@ module Rscons
@configure_op.__send__(method_name, *args)
end
end
# Perform a custom configuration check.
#
# @param message [String]
# Custom configuration check message (e.g. "Checking for foo").
# rscons will add "... " to the end of the message.
# @yieldparam configure_op [ConfigureOp]
# {ConfigureOp} object.
# @return [void]
def custom_check(message, &block)
$stdout.write(message + "... ")
block[@configure_op]
end
end
# @return [String, nil]

View File

@ -2086,6 +2086,66 @@ EOF
end
end
context "custom_check" do
context "when running a test command" do
context "when executing the command fails" do
context "when failures are fatal" do
it "fails configuration with the correct error message" do
test_dir "configure"
create_exe "grep", "exit 4"
result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.stdout).to match /Checking 'grep' version\.\.\. error executing grep/
expect(result.status).to_not eq 0
end
end
context "when the custom logic indicates a failure" do
it "fails configuration with the correct error message" do
test_dir "configure"
create_exe "grep", "echo 'grep (GNU grep) 1.1'"
result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.stdout).to match /Checking 'grep' version\.\.\. too old!/
expect(result.status).to_not eq 0
end
end
end
context "when failures are not fatal" do
context "when the custom logic indicates a failure" do
it "displays the correct message and does not fail configuration" do
test_dir "configure"
create_exe "grep", "echo 'grep (GNU grep) 2.1'"
result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.stdout).to match /Checking 'grep' version\.\.\. we'll work with it but you should upgrade/
expect(result.status).to eq 0
result = run_rscons(rsconscript: "custom_config_check.rb", op: "build")
expect(result.stderr).to eq ""
expect(result.stdout).to match /GREP_WORKAROUND/
expect(result.status).to eq 0
end
end
end
context "when the custom logic indicates success" do
it "passes configuration with the correct message" do
test_dir "configure"
create_exe "grep", "echo 'grep (GNU grep) 3.0'"
result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.stdout).to match /Checking 'grep' version\.\.\. good!/
expect(result.status).to eq 0
result = run_rscons(rsconscript: "custom_config_check.rb", op: "build")
expect(result.stderr).to eq ""
expect(result.stdout).to match /GREP_FULL/
expect(result.status).to eq 0
end
end
end
end
it "does everything" do
test_dir "configure"
create_exe "pkg-config", "echo '-DMYPACKAGE'"