From 80a8dc04f3a640d741d8c7de402d9ac731af435a Mon Sep 17 00:00:00 2001 From: ignisf Date: Sun, 15 Jun 2014 11:58:20 +0300 Subject: [PATCH] Refactor the compiler detection module --- ext/libv8/compiler.rb | 39 ++++++++++++++++++-------- spec/compiler_spec.rb | 64 +++++++++++++++++++++++++++++++++++++++++++ spec/spec_helper.rb | 38 +++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 spec/compiler_spec.rb diff --git a/ext/libv8/compiler.rb b/ext/libv8/compiler.rb index c6b0f76..fd9bfb8 100644 --- a/ext/libv8/compiler.rb +++ b/ext/libv8/compiler.rb @@ -19,24 +19,39 @@ module Libv8 end def available_compilers(*compiler_names) - compiler_names.map { |name| find name }.reject(&:nil?) + available = compiler_names.select { |compiler_name| available? compiler_name } + available.map { |compiler_name| type_of(compiler_name).new compiler_name } end - def find(name) - return nil if name.empty? - path, _, status = Open3.capture3 "which #{name}" - path.chomp! - determine_type(path).new(path) if status.success? - end - - def determine_type(compiler_path) - compiler_version = Open3.capture3("#{compiler_path} -v")[0..1].join - - case compiler_version + def type_of(compiler_name) + case version_string_of(compiler_name) when /\bclang\b/i then Clang when /^gcc/i then GCC else GenericCompiler end end + + def version_string_of(compiler_name) + command_result = execute_command "#{compiler_name} -v 2>&1" + + unless command_result.status.success? + raise "Could not get version string of compiler #{compiler_name}" + end + + command_result.output + end + + def available?(command) + execute_command("which #{command} 2>&1").status.success? + end + + def execute_command(command) + output = `#{command}` + status = $? + ExecutionResult.new output, status + end + + class ExecutionResult < Struct.new(:output, :status) + end end end diff --git a/spec/compiler_spec.rb b/spec/compiler_spec.rb new file mode 100644 index 0000000..36ecffa --- /dev/null +++ b/spec/compiler_spec.rb @@ -0,0 +1,64 @@ +$:.unshift File.expand_path '../../ext/libv8', __FILE__ + +require 'spec_helper' +require 'compiler' + +RSpec.configure do |c| + c.include CompilerHelpers +end + +module Libv8 + describe Compiler do + describe '::type_of' do + it 'returns a GCC class for GCC 4.9.0' do + stub_as_available 'c++', :gcc, '4.9.0' + Compiler.available_compilers('c++').first.should be_a Compiler::GCC + end + + it 'returns a Clang class for Clang 3.4.1' do + stub_as_available 'c++', :clang, '3.4.1' + Compiler.available_compilers('c++').first.should be_a Compiler::Clang + end + end + + describe '::available_compilers' do + it 'returns instances of the available compilers' do + stub_as_available 'c++', :clang, '3.4.1' + stub_as_unavailable 'g++' + stub_as_available 'clang++', :clang, '3.4.1' + + available_compilers = Compiler.available_compilers 'c++', 'g++', 'clang++' + available_compilers.map(&:class).should have(2).items + available_compilers.all? { |compiler| compiler.should be_a Compiler::Clang } + end + end + + describe '::version_string_of' do + context 'when calling the compiler command succedes' do + it 'returns the version string of the compiler' do + stub_as_available 'c++', :clang, '3.4.1' + Compiler.version_string_of('c++').should eq version_output_of(:clang, '3.4.1') + end + end + + context 'when calling the compiler command fails' do + it 'raises an exception' do + stub_as_unavailable 'c++' + expect { Compiler.version_string_of('c++') }.to raise_exception + end + end + end + + describe '::available?' do + it 'returns true when the command is available' do + stub_as_available 'c++', :clang, '3.4.1' + Compiler::available?('c++').should be_true + end + + it 'returns false when the command cannot be found ' do + stub_as_unavailable 'c++' + Compiler::available?('c++').should be_false + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9e6d775..986cc5a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,3 +2,41 @@ $:.unshift File.expand_path '../../lib', __FILE__ require 'rspec' require 'rspec-spies' require 'libv8' + +module CompilerHelpers + VERSION_OUTPUTS = { + :gcc => { + "4.9.0" => %Q{Using built-in specs.\nCOLLECT_GCC=c++\nCOLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.0/lto-wrapper\nTarget: x86_64-unknown-linux-gnu\nConfigured with: /build/gcc-multilib/src/gcc-4.9-20140604/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-cloog-backend=isl --disable-cloog-version-check --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-multilib --disable-werror --enable-checking=release\nThread model: posix\ngcc version 4.9.0 20140604 (prerelease) (GCC)\n} + }, + :clang => { + "3.4.1" => %Q{clang version 3.4.1 (tags/RELEASE_34/dot1-final)\nTarget: x86_64-unknown-linux-gnu\nThread model: posix\nFound candidate GCC installation: /usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.0\nFound candidate GCC installation: /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0\nFound candidate GCC installation: /usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.0\nFound candidate GCC installation: /usr/lib64/gcc/x86_64-unknown-linux-gnu/4.9.0\nSelected GCC installation: /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0\n} + } + } + + def version_output_of(name, version) + VERSION_OUTPUTS[name][version] + end + + def success_status + double success?: true + end + + def failure_status + double success?: false + end + + def stub_shell_command(command, output, status) + allow(Libv8::Compiler).to receive(:execute_command).with(command) do + double output: output, status: status + end + end + + def stub_as_available(command, name, version) + stub_shell_command "which #{command} 2>&1", '', success_status + stub_shell_command "#{command} -v 2>&1", version_output_of(name, version), success_status + end + + def stub_as_unavailable(command) + stub_shell_command "which #{command} 2>&1", '', failure_status + end +end