diff --git a/build_tests/configure/autoconf_rebuild.rb b/build_tests/configure/autoconf_rebuild.rb new file mode 100644 index 0000000..381b6f1 --- /dev/null +++ b/build_tests/configure/autoconf_rebuild.rb @@ -0,0 +1,10 @@ +configure do + check_c_compiler + check_c_header "stdio.h" +end + +build do + Environment.new do |env| + env.Program("simple.exe", "simple.c") + end +end diff --git a/lib/rscons.rb b/lib/rscons.rb index bcf1fe9..7d1ab44 100644 --- a/lib/rscons.rb +++ b/lib/rscons.rb @@ -1,5 +1,6 @@ require_relative "rscons/ansi" require_relative "rscons/application" +require_relative "rscons/basic_environment" require_relative "rscons/build_target" require_relative "rscons/builder" require_relative "rscons/cache" diff --git a/lib/rscons/basic_environment.rb b/lib/rscons/basic_environment.rb new file mode 100644 index 0000000..d284210 --- /dev/null +++ b/lib/rscons/basic_environment.rb @@ -0,0 +1,212 @@ +module Rscons + # The BasicEnvironment class contains a collection of construction variables. + class BasicEnvironment + + # Create a BasicEnvironment object. + def initialize + @varset = VarSet.new(Rscons.application.default_varset) + end + + # Get a construction variable's value. + # + # @see VarSet#[] + def [](*args) + @varset.__send__(:[], *args) + end + + # Set a construction variable's value. + # + # @see VarSet#[]= + def []=(*args) + @varset.__send__(:[]=, *args) + end + + # Add a set of construction variables to the BasicEnvironment. + # + # @param values [VarSet, Hash] New set of variables. + # + # @return [void] + def append(values) + @varset.append(values) + end + + # Expand a construction variable reference. + # + # @param varref [nil, String, Array, Proc, Symbol, TrueClass, FalseClass] Variable reference to expand. + # @param extra_vars [Hash, VarSet] + # Extra variables to use in addition to (or replace) the Environment's + # construction variables when expanding the variable reference. + # + # @return [nil, String, Array, Symbol, TrueClass, FalseClass] Expansion of the variable reference. + def expand_varref(varref, extra_vars = nil) + vars = + if extra_vars.nil? + @varset + else + @varset.merge(extra_vars) + end + lambda_args = [env: self, vars: vars] + vars.expand_varref(varref, lambda_args) + end + alias_method :build_command, :expand_varref + + # @!method parse_flags(flags) + # @!method parse_flags!(flags) + # + # Parse command-line flags for compilation/linking options into separate + # construction variables. + # + # For {#parse_flags}, the parsed construction variables are returned in a + # Hash instead of merging them directly to the Environment. They can be + # merged with {#merge_flags}. The {#parse_flags!} version immediately + # merges the parsed flags as well. + # + # Example: + # # Import FreeType build options + # env.parse_flags!("!freetype-config --cflags --libs") + # + # @param flags [String] + # String containing the flags to parse, or if the flags string begins + # with "!", a shell command to execute using {#shell} to obtain the + # flags to parse. + # + # @return [Hash] Set of construction variables to append. + def parse_flags(flags) + if flags =~ /^!(.*)$/ + flags = shell($1) + end + rv = {} + words = Shellwords.split(flags) + skip = false + words.each_with_index do |word, i| + if skip + skip = false + next + end + append = lambda do |var, val| + rv[var] ||= [] + rv[var] += val + end + handle = lambda do |var, val| + if val.nil? or val.empty? + val = words[i + 1] + skip = true + end + if val and not val.empty? + append[var, [val]] + end + end + if word == "-arch" + if val = words[i + 1] + append["CCFLAGS", ["-arch", val]] + append["LDFLAGS", ["-arch", val]] + end + skip = true + elsif word =~ /^#{self["CPPDEFPREFIX"]}(.*)$/ + handle["CPPDEFINES", $1] + elsif word == "-include" + if val = words[i + 1] + append["CCFLAGS", ["-include", val]] + end + skip = true + elsif word == "-isysroot" + if val = words[i + 1] + append["CCFLAGS", ["-isysroot", val]] + append["LDFLAGS", ["-isysroot", val]] + end + skip = true + elsif word =~ /^#{self["INCPREFIX"]}(.*)$/ + handle["CPPPATH", $1] + elsif word =~ /^#{self["LIBLINKPREFIX"]}(.*)$/ + handle["LIBS", $1] + elsif word =~ /^#{self["LIBDIRPREFIX"]}(.*)$/ + handle["LIBPATH", $1] + elsif word == "-mno-cygwin" + append["CCFLAGS", [word]] + append["LDFLAGS", [word]] + elsif word == "-mwindows" + append["LDFLAGS", [word]] + elsif word == "-pthread" + append["CCFLAGS", [word]] + append["LDFLAGS", [word]] + elsif word =~ /^-Wa,(.*)$/ + append["ASFLAGS", $1.split(",")] + elsif word =~ /^-Wl,(.*)$/ + append["LDFLAGS", $1.split(",")] + elsif word =~ /^-Wp,(.*)$/ + append["CPPFLAGS", $1.split(",")] + elsif word.start_with?("-") + append["CCFLAGS", [word]] + elsif word.start_with?("+") + append["CCFLAGS", [word]] + append["LDFLAGS", [word]] + else + append["LIBS", [word]] + end + end + rv + end + + def parse_flags!(flags) + flags = parse_flags(flags) + merge_flags(flags) + flags + end + + # Merge construction variable flags into this Environment's construction + # variables. + # + # This method does the same thing as {#append}, except that Array values in + # +flags+ are appended to the end of Array construction variables instead + # of replacing their contents. + # + # @param flags [Hash] + # Set of construction variables to merge into the current Environment. + # This can be the value (or a modified version) returned by + # {#parse_flags}. + # + # @return [void] + def merge_flags(flags) + flags.each_pair do |key, val| + if self[key].is_a?(Array) and val.is_a?(Array) + self[key] += val + else + self[key] = val + end + end + end + + # Print the Environment's construction variables for debugging. + def dump + varset_hash = @varset.to_h + varset_hash.keys.sort_by(&:to_s).each do |var| + var_str = var.is_a?(Symbol) ? var.inspect : var + Ansi.write($stdout, :cyan, var_str, :reset, " => #{varset_hash[var].inspect}\n") + end + end + + # Load construction variables saved from the configure operation. + def load_configuration_data! + if vars = Cache.instance.configuration_data["vars"] + if default_vars = vars["_default_"] + apply_configuration_data!(default_vars) + end + end + end + + def apply_configuration_data!(vars) + if merge_vars = vars["merge"] + append(merge_vars) + end + if append_vars = vars["append"] + merge_flags(append_vars) + end + if parse_vars = vars["parse"] + parse_vars.each do |parse_string| + parse_flags!(parse_string) + end + end + end + + end +end diff --git a/lib/rscons/configure_op.rb b/lib/rscons/configure_op.rb index d3b23b9..06b0b3d 100644 --- a/lib/rscons/configure_op.rb +++ b/lib/rscons/configure_op.rb @@ -113,7 +113,7 @@ module Rscons "_SOURCES" => "#{@work_dir}/cfgtest.c", "_TARGET" => "#{@work_dir}/cfgtest.exe", } - command = Environment.new.build_command("${LDCMD}", vars) + command = BasicEnvironment.new.build_command("${LDCMD}", vars) _, _, status = log_and_test_command(command) common_config_checks(status, options) end @@ -134,7 +134,7 @@ module Rscons "_SOURCES" => "#{@work_dir}/cfgtest.cxx", "_TARGET" => "#{@work_dir}/cfgtest.exe", } - command = Environment.new.build_command("${LDCMD}", vars) + command = BasicEnvironment.new.build_command("${LDCMD}", vars) _, _, status = log_and_test_command(command) common_config_checks(status, options) end @@ -155,7 +155,7 @@ module Rscons "_SOURCES" => "#{@work_dir}/cfgtest.d", "_TARGET" => "#{@work_dir}/cfgtest.exe", } - command = Environment.new.build_command("${LDCMD}", vars) + command = BasicEnvironment.new.build_command("${LDCMD}", vars) _, _, status = log_and_test_command(command) common_config_checks(status, options) end @@ -176,7 +176,7 @@ module Rscons "_SOURCES" => "#{@work_dir}/cfgtest.c", "_TARGET" => "#{@work_dir}/cfgtest.exe", } - command = Environment.new.build_command("${LDCMD}", vars) + command = BasicEnvironment.new.build_command("${LDCMD}", vars) _, _, status = log_and_test_command(command) if status == 0 store_append({"LIBS" => [lib]}, options) @@ -276,7 +276,7 @@ module Rscons merge = {"DC" => dc} when :ldc2 command = %W[#{dc} -of #{@work_dir}/cfgtest.exe #{@work_dir}/cfgtest.d] - env = Environment.new + env = BasicEnvironment.new merge = { "DC" => dc, "DCCMD" => env["DCCMD"].map {|e| if e == "-o"; "-of"; else; e; end}, diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index 14e681d..3111e45 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -7,7 +7,7 @@ module Rscons # The Environment class is the main programmatic interface to Rscons. It # contains a collection of construction variables, options, builders, and # rules for building targets. - class Environment + class Environment < BasicEnvironment class << self @@ -64,12 +64,12 @@ module Rscons # If a block is given, the Environment object is yielded to the block and # when the block returns, the {#process} method is automatically called. def initialize(options = {}) + super() @id = self.class.get_id self.class.register(self) @threaded_commands = Set.new @registered_build_dependencies = {} @side_effects = {} - @varset = VarSet.new(Rscons.application.default_varset) @job_set = JobSet.new(@registered_build_dependencies, @side_effects) @user_deps = {} @builders = {} @@ -240,29 +240,6 @@ module Rscons "#{@build_root}#{extra_path}/#{Util.make_relative_path(Rscons.set_suffix(source_fname, suffix))}".gsub("\\", "/") end - # Get a construction variable's value. - # - # @see VarSet#[] - def [](*args) - @varset.__send__(:[], *args) - end - - # Set a construction variable's value. - # - # @see VarSet#[]= - def []=(*args) - @varset.__send__(:[]=, *args) - end - - # Add a set of construction variables to the Environment. - # - # @param values [VarSet, Hash] New set of variables. - # - # @return [void] - def append(values) - @varset.append(values) - end - # Build all build targets specified in the Environment. # # When a block is passed to Environment.new, this method is automatically @@ -353,25 +330,6 @@ module Rscons @job_set.clear! end - # Expand a construction variable reference. - # - # @param varref [nil, String, Array, Proc, Symbol, TrueClass, FalseClass] Variable reference to expand. - # @param extra_vars [Hash, VarSet] - # Extra variables to use in addition to (or replace) the Environment's - # construction variables when expanding the variable reference. - # - # @return [nil, String, Array, Symbol, TrueClass, FalseClass] Expansion of the variable reference. - def expand_varref(varref, extra_vars = nil) - vars = if extra_vars.nil? - @varset - else - @varset.merge(extra_vars) - end - lambda_args = [env: self, vars: vars] - vars.expand_varref(varref, lambda_args) - end - alias_method :build_command, :expand_varref - # Execute a builder command. # # @param short_desc [String] Message to print if the Environment's echo @@ -700,141 +658,6 @@ module Rscons end end - # @!method parse_flags(flags) - # @!method parse_flags!(flags) - # - # Parse command-line flags for compilation/linking options into separate - # construction variables. - # - # For {#parse_flags}, the parsed construction variables are returned in a - # Hash instead of merging them directly to the Environment. They can be - # merged with {#merge_flags}. The {#parse_flags!} version immediately - # merges the parsed flags as well. - # - # Example: - # # Import FreeType build options - # env.parse_flags!("!freetype-config --cflags --libs") - # - # @param flags [String] - # String containing the flags to parse, or if the flags string begins - # with "!", a shell command to execute using {#shell} to obtain the - # flags to parse. - # - # @return [Hash] Set of construction variables to append. - def parse_flags(flags) - if flags =~ /^!(.*)$/ - flags = shell($1) - end - rv = {} - words = Shellwords.split(flags) - skip = false - words.each_with_index do |word, i| - if skip - skip = false - next - end - append = lambda do |var, val| - rv[var] ||= [] - rv[var] += val - end - handle = lambda do |var, val| - if val.nil? or val.empty? - val = words[i + 1] - skip = true - end - if val and not val.empty? - append[var, [val]] - end - end - if word == "-arch" - if val = words[i + 1] - append["CCFLAGS", ["-arch", val]] - append["LDFLAGS", ["-arch", val]] - end - skip = true - elsif word =~ /^#{self["CPPDEFPREFIX"]}(.*)$/ - handle["CPPDEFINES", $1] - elsif word == "-include" - if val = words[i + 1] - append["CCFLAGS", ["-include", val]] - end - skip = true - elsif word == "-isysroot" - if val = words[i + 1] - append["CCFLAGS", ["-isysroot", val]] - append["LDFLAGS", ["-isysroot", val]] - end - skip = true - elsif word =~ /^#{self["INCPREFIX"]}(.*)$/ - handle["CPPPATH", $1] - elsif word =~ /^#{self["LIBLINKPREFIX"]}(.*)$/ - handle["LIBS", $1] - elsif word =~ /^#{self["LIBDIRPREFIX"]}(.*)$/ - handle["LIBPATH", $1] - elsif word == "-mno-cygwin" - append["CCFLAGS", [word]] - append["LDFLAGS", [word]] - elsif word == "-mwindows" - append["LDFLAGS", [word]] - elsif word == "-pthread" - append["CCFLAGS", [word]] - append["LDFLAGS", [word]] - elsif word =~ /^-Wa,(.*)$/ - append["ASFLAGS", $1.split(",")] - elsif word =~ /^-Wl,(.*)$/ - append["LDFLAGS", $1.split(",")] - elsif word =~ /^-Wp,(.*)$/ - append["CPPFLAGS", $1.split(",")] - elsif word.start_with?("-") - append["CCFLAGS", [word]] - elsif word.start_with?("+") - append["CCFLAGS", [word]] - append["LDFLAGS", [word]] - else - append["LIBS", [word]] - end - end - rv - end - - def parse_flags!(flags) - flags = parse_flags(flags) - merge_flags(flags) - flags - end - - # Merge construction variable flags into this Environment's construction - # variables. - # - # This method does the same thing as {#append}, except that Array values in - # +flags+ are appended to the end of Array construction variables instead - # of replacing their contents. - # - # @param flags [Hash] - # Set of construction variables to merge into the current Environment. - # This can be the value (or a modified version) returned by - # {#parse_flags}. - # - # @return [void] - def merge_flags(flags) - flags.each_pair do |key, val| - if self[key].is_a?(Array) and val.is_a?(Array) - self[key] += val - else - self[key] = val - end - end - end - - # Print the Environment's construction variables for debugging. - def dump - varset_hash = @varset.to_h - varset_hash.keys.sort_by(&:to_s).each do |var| - var_str = var.is_a?(Symbol) ? var.inspect : var - Ansi.write($stdout, :cyan, var_str, :reset, " => #{varset_hash[var].inspect}\n") - end - end - # Get the number of threads to use for parallelized builds in this # Environment. # @@ -1060,29 +883,6 @@ module Rscons deps end - # Load construction variables saved from the configure operation. - def load_configuration_data! - if vars = Cache.instance.configuration_data["vars"] - if default_vars = vars["_default_"] - apply_configuration_data!(default_vars) - end - end - end - - def apply_configuration_data!(vars) - if merge_vars = vars["merge"] - append(merge_vars) - end - if append_vars = vars["append"] - merge_flags(append_vars) - end - if parse_vars = vars["parse"] - parse_vars.each do |parse_string| - parse_flags!(parse_string) - end - end - end - end Environment.class_init diff --git a/spec/build_tests_spec.rb b/spec/build_tests_spec.rb index 8c6c6e6..b8560a4 100644 --- a/spec/build_tests_spec.rb +++ b/spec/build_tests_spec.rb @@ -1902,6 +1902,18 @@ EOF expect(result.stderr).to match /Project must be configured before processing an Environment/ expect(result.status).to_not eq 0 end + + it "does not rebuild after building with auto-configuration" do + test_dir "configure" + result = run_rscons(rsconscript: "autoconf_rebuild.rb") + expect(result.stderr).to eq "" + expect(result.status).to eq 0 + expect(File.exists?("simple.exe")).to be_truthy + result = run_rscons(rsconscript: "autoconf_rebuild.rb") + expect(result.stderr).to eq "" + expect(result.status).to eq 0 + expect(result.stdout).to eq "" + end end context "distclean" do