diff --git a/doc/user_guide.md b/doc/user_guide.md index 3a1e31c..8e98b1f 100644 --- a/doc/user_guide.md +++ b/doc/user_guide.md @@ -78,9 +78,12 @@ cache file in order to avoid rebuilding a target when it is already up to date. ### Build Directory Rscons was designed to store temporary build artifacts (for example, object -files, dependency files, etc...) in a `build` directory. +files, dependency files, etc...) and build system metadata in a +"build directory". This keeps files generated by the build cleanly separated from user-controlled source files. +By default a build directory named "build" is used, but this can be overridden +by the user by using the `-b`/`--build` command-line option. ## Getting Started @@ -94,7 +97,7 @@ To use Rscons on your project, you must: Rscons is designed to be distributed as a stand-alone single file script that can be copied into and versioned in a project's source tree. -The only dependency required to run Rscons is to have a Ruby interpreter +The only requirement to run Rscons is that the system has a Ruby interpreter installed. The latest release can be downloaded from [https://github.com/holtrop/rscons/releases](https://github.com/holtrop/rscons/releases). Simply copy the `rscons` executable script into the desired location within diff --git a/lib/rscons/application.rb b/lib/rscons/application.rb index 1d18c98..9ddf3e0 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 [String] + # Top-level build directory. + attr_accessor :build_dir + # @return [Boolean] # Whether to output ANSI color escape sequences. attr_accessor :do_ansi_color @@ -23,6 +27,7 @@ module Rscons # Create Application instance. def initialize + @build_dir = "build" @n_threads = Util.determine_n_threads @vars = VarSet.new @operations = Set.new @@ -171,11 +176,8 @@ module Rscons # Exit code. def distclean cache = Cache.instance - build_dir = cache["configuration_data"]["build_dir"] clean - if build_dir - FileUtils.rm_rf(build_dir) - end + FileUtils.rm_rf(@build_dir) cache.clear 0 end diff --git a/lib/rscons/cache.rb b/lib/rscons/cache.rb index 343e292..3cc8e04 100644 --- a/lib/rscons/cache.rb +++ b/lib/rscons/cache.rb @@ -51,9 +51,6 @@ module Rscons # } class Cache - # Name of the file to store cache information in - CACHE_FILE = ".rsconscache" - # Prefix for phony cache entries. PHONY_PREFIX = ":PHONY:" @@ -70,6 +67,11 @@ module Rscons initialize! end + # Get the path to the cache file. + def cache_file + File.join(Rscons.application.build_dir, ".rsconscache") + end + # Access cache value. def [](key) @cache[key] @@ -84,7 +86,7 @@ module Rscons # # @return [void] def clear - FileUtils.rm_f(CACHE_FILE) + FileUtils.rm_f(cache_file) initialize! end @@ -100,7 +102,7 @@ module Rscons # @return [void] def write @cache["version"] = VERSION - File.open(CACHE_FILE, "w") do |fh| + File.open(cache_file, "w") do |fh| fh.puts(JSON.dump(@cache)) end end @@ -360,9 +362,9 @@ module Rscons # Create a Cache object and load in the previous contents from the cache # file. def initialize! - @cache = JSON.load(File.read(CACHE_FILE)) rescue {} + @cache = JSON.load(File.read(cache_file)) rescue {} unless @cache.is_a?(Hash) - $stderr.puts "Warning: #{CACHE_FILE} was corrupt. Contents:\n#{@cache.inspect}" + $stderr.puts "Warning: #{cache_file} was corrupt. Contents:\n#{@cache.inspect}" @cache = {} end @cache["targets"] ||= {} diff --git a/lib/rscons/cli.rb b/lib/rscons/cli.rb index 8c60c09..e651bcc 100644 --- a/lib/rscons/cli.rb +++ b/lib/rscons/cli.rb @@ -6,6 +6,7 @@ USAGE = < options[:prefix]) end diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index 4c53240..ab728c8 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -97,7 +97,7 @@ module Rscons else :short end - @build_root = "#{Cache.instance["configuration_data"]["build_dir"]}/e.#{@id}" + @build_root = "#{Rscons.application.build_dir}/e.#{@id}" @n_threads = Rscons.application.n_threads if block_given? diff --git a/spec/build_tests_spec.rb b/spec/build_tests_spec.rb index 092e2e7..49c40e2 100644 --- a/spec/build_tests_spec.rb +++ b/spec/build_tests_spec.rb @@ -208,6 +208,15 @@ EOF expect(nr(`./simple.exe`)).to eq "This is a simple C program\n" end + it "builds a C program with one source file in an alternate build directory" do + test_dir("simple") + result = run_rscons(rscons_args: %w[-b b]) + expect(result.stderr).to eq "" + expect(Dir.exist?("build")).to be_falsey + expect(File.exists?("b/e.1/simple.c.o")).to be_truthy + expect(nr(`./simple.exe`)).to eq "This is a simple C program\n" + end + it "allows specifying a Builder object as the source to another build target" do test_dir("simple") result = run_rscons(rsconscript: "builder_as_source.rb") @@ -1296,7 +1305,8 @@ EOF context "Cache management" do it "prints a warning when the cache is corrupt" do test_dir("simple") - File.open(Rscons::Cache::CACHE_FILE, "w") do |fh| + FileUtils.mkdir("build") + File.open("build/.rsconscache", "w") do |fh| fh.puts("[1]") end result = run_rscons @@ -1748,6 +1758,22 @@ EOF expect(result.status).to_not eq 0 end + it "automatically runs the configure operation if the project is not yet configured in the given build directory" do + test_dir "configure" + + result = run_rscons(rsconscript: "check_c_compiler.rb") + expect(result.stderr).to eq "" + expect(result.status).to eq 0 + expect(result.stdout).to match /Checking for C compiler\.\.\./ + expect(Dir.exist?("build/configure")).to be_truthy + + result = run_rscons(rsconscript: "check_c_compiler.rb", rscons_args: %w[--build=bb]) + expect(result.stderr).to eq "" + expect(result.status).to eq 0 + expect(result.stdout).to match /Checking for C compiler\.\.\./ + expect(Dir.exist?("bb/configure")).to be_truthy + end + context "check_c_compiler" do {"check_c_compiler.rb" => "when no arguments are given", "check_c_compiler_find_first.rb" => "when arguments are given"}.each_pair do |rsconscript, desc| @@ -2284,7 +2310,6 @@ EOF expect(result.stderr).to eq "" expect(result.status).to eq 0 expect(result.stdout).to match /Configuring configure test\.\.\./ - expect(result.stdout).to match /Setting build directory\.\.\. bb/ expect(result.stdout).to match %r{Setting prefix\.\.\. /my/prefix} expect(result.stdout).to match /Checking for C compiler\.\.\. gcc/ expect(result.stdout).to match /Checking for C\+\+ compiler\.\.\. g\+\+/ @@ -2295,6 +2320,8 @@ EOF expect(result.stdout).to match /Checking for D import 'std.stdio'\.\.\. found/ expect(result.stdout).to match /Checking for library 'm'\.\.\. found/ expect(result.stdout).to match /Checking for program 'ls'\.\.\. .*ls/ + expect(Dir.exist?("build")).to be_falsey + expect(Dir.exist?("bb/configure")).to be_truthy end it "aggregates multiple set_define's" do @@ -2357,7 +2384,6 @@ EOF expect(result.status).to eq 0 expect(File.exists?("simple.o")).to be_falsey expect(File.exists?("build")).to be_falsey - expect(File.exists?(Rscons::Cache::CACHE_FILE)).to be_falsey end end