diff --git a/lib/rscons.rb b/lib/rscons.rb index a12a372..826bab1 100644 --- a/lib/rscons.rb +++ b/lib/rscons.rb @@ -54,4 +54,33 @@ module Rscons def self.set_suffix(path, suffix) path.sub(/\.[^.]*$/, suffix) end + + # Return the system shell and arguments for executing a shell command. + # @return [Array] The shell and flag. + def self.get_system_shell + @@shell ||= + begin + test_shell = lambda do |*args| + begin + "success" == IO.popen([*args, "echo success"]) do |io| + io.read.strip + end + rescue + false + end + end + if ENV["SHELL"] and ENV["SHELL"] != "" and test_shell[ENV["SHELL"], "-c"] + [ENV["SHELL"], "-c"] + elsif Object.const_get("RUBY_PLATFORM") =~ /mingw/ + if test_shell["sh", "-c"] + # Using Rscons from MSYS should use MSYS's shell. + ["sh", "-c"] + else + ["cmd", "/c"] + end + else + ["sh", "-c"] + end + end + end end diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index 2a5881e..b0480dc 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -349,6 +349,35 @@ module Rscons path.sub(%r{^\^(?=[\\/])}, @build_root) end + # Execute a command using the system shell. + # + # The shell is automatically determined but can be overridden by the SHELL + # construction variable. If the SHELL construction variable is specified, + # the flag to pass to the shell is automatically dtermined but can be + # overridden by the SHELLFLAG construction variable. + # + # @param command [String] Command to execute. + # + # @return [String] The command's standard output. + def shell(command) + shell_cmd = + if self["SHELL"] + flag = self["SHELLFLAG"] || begin + if self["SHELL"] == "cmd" + "/c" + else + "-c" + end + end + [self["SHELL"], flag] + else + Rscons.get_system_shell + end + IO.popen([*shell_cmd, command]) do |io| + io.read + end + end + private # Expand target and source paths before invoking builders. diff --git a/spec/rscons/environment_spec.rb b/spec/rscons/environment_spec.rb index b7adc3d..5f05e50 100644 --- a/spec/rscons/environment_spec.rb +++ b/spec/rscons/environment_spec.rb @@ -360,6 +360,25 @@ module Rscons end end + describe "#shell" do + it "executes the given shell command and returns the results" do + env = Environment.new + expect(env.shell("echo hello").strip).to eq("hello") + end + it "determines shell flag to be /c when SHELL is specified as 'cmd'" do + env = Environment.new + env["SHELL"] = "cmd" + IO.should_receive(:popen).with(["cmd", "/c", "my_cmd"]) + env.shell("my_cmd") + end + it "determines shell flag to be -c when SHELL is specified as something else" do + env = Environment.new + env["SHELL"] = "my_shell" + IO.should_receive(:popen).with(["my_shell", "-c", "my_cmd"]) + env.shell("my_cmd") + end + end + describe ".parse_makefile_deps" do it 'handles dependencies on one line' do File.should_receive(:read).with('makefile').and_return(< "my_shell"} + ENV.stub(:[]) {|*args| my_ENV[*args]} + io = StringIO.new("success\n") + IO.should_receive(:popen).with(["my_shell", "-c", "echo success"]).and_yield(io) + expect(Rscons.get_system_shell).to eq(["my_shell", "-c"]) + end + + it "uses sh -c on a mingw platform if it tests successfully" do + my_ENV = {"SHELL" => nil} + ENV.stub(:[]) {|*args| my_ENV[*args]} + io = StringIO.new("success\n") + IO.should_receive(:popen).with(["sh", "-c", "echo success"]).and_yield(io) + Object.should_receive(:const_get).with("RUBY_PLATFORM").and_return("x86-mingw") + expect(Rscons.get_system_shell).to eq(["sh", "-c"]) + end + + it "uses cmd /c on a mingw platform if sh -c does not test successfully" do + my_ENV = {"SHELL" => nil} + ENV.stub(:[]) {|*args| my_ENV[*args]} + io = StringIO.new("success\n") + IO.should_receive(:popen).with(["sh", "-c", "echo success"]).and_raise "ENOENT" + Object.should_receive(:const_get).with("RUBY_PLATFORM").and_return("x86-mingw") + expect(Rscons.get_system_shell).to eq(["cmd", "/c"]) + end + + it "uses sh -c on a non-mingw platform if SHELL is not specified" do + my_ENV = {"SHELL" => nil} + ENV.stub(:[]) {|*args| my_ENV[*args]} + Object.should_receive(:const_get).with("RUBY_PLATFORM").and_return("x86-linux") + expect(Rscons.get_system_shell).to eq(["sh", "-c"]) + end + end end