From 760f69896387e54844a94cc04c919eafb1468e80 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Sun, 7 Jul 2013 17:17:46 -0400 Subject: [PATCH] fill in CC, Program, Environment.process() to get simple builds working --- lib/rscons.rb | 1 + lib/rscons/builder.rb | 3 ++ lib/rscons/builders/cc.rb | 33 +++++++++++++ lib/rscons/builders/program.rb | 37 +++++++++++---- lib/rscons/cache.rb | 1 + lib/rscons/environment.rb | 86 +++++++++++++++++++++++++++++++++- 6 files changed, 151 insertions(+), 10 deletions(-) diff --git a/lib/rscons.rb b/lib/rscons.rb index 90e4dba..bc97101 100644 --- a/lib/rscons.rb +++ b/lib/rscons.rb @@ -4,6 +4,7 @@ require "rscons/environment" require "rscons/version" require "rscons/monkey/module" +require "rscons/monkey/string" # default builders require "rscons/builders/cc" diff --git a/lib/rscons/builder.rb b/lib/rscons/builder.rb index fd2d26e..b24c036 100644 --- a/lib/rscons/builder.rb +++ b/lib/rscons/builder.rb @@ -3,5 +3,8 @@ module Rscons def initialize(env) @env = env end + def default_variables(env) + {} + end end end diff --git a/lib/rscons/builders/cc.rb b/lib/rscons/builders/cc.rb index a27c64b..fa0f7c0 100644 --- a/lib/rscons/builders/cc.rb +++ b/lib/rscons/builders/cc.rb @@ -1,4 +1,37 @@ module Rscons class CC < Builder + def default_variables(env) + { + 'CC' => 'gcc', + 'CFLAGS' => [], + 'CPPFLAGS' => [], + 'OBJSUFFIX' => '.o', + 'CSUFFIX' => '.c', + 'CCDEPGEN' => ['-MMD', '-MF', '$DEPFILE'], + 'CCCOM' => ['$CC', '-c', '-o', '$TARGET', '$CCDEPGEN', '$CPPFLAGS', '$CFLAGS', '$SOURCES'] + } + end + + def produces?(target, source) + target.has_suffix?(@env['OBJSUFFIX']) and source.has_suffix?(@env['CSUFFIX']) + end + + def run(target, sources, cache) + unless cache.up_to_date?(target, sources) + vars = { + 'TARGET' => target, + 'SOURCES' => sources, + 'DEPFILE' => target.set_suffix('.mf'), + } + @env.execute("CC #{target}", @env['CCCOM'], vars) + deps = sources + if File.exists?(vars['DEPFILE']) + deps += @env.parse_makefile_deps(vars['DEPFILE'], target) + FileUtils.rm_f(vars['DEPFILE']) + end + cache.register_build(target, deps.uniq) + end + target + end end end diff --git a/lib/rscons/builders/program.rb b/lib/rscons/builders/program.rb index 4bca0fb..00a7248 100644 --- a/lib/rscons/builders/program.rb +++ b/lib/rscons/builders/program.rb @@ -1,19 +1,38 @@ module Rscons class Program < Builder - def default_variables + def default_variables(env) { - 'CC' => 'gcc', - 'CFLAGS' => [], - 'CPPFLAGS' => [], + 'LD' => nil, 'OBJSUFFIX' => '.o', - 'CSUFFIX' => '.c', - 'CCDEPGEN' => ['-MMD', '-MF', '$DEPFILE'], - 'CCCOM' => ['$CC', '-c', '-o', '$TARGET', '$CCDEPGEN', '$CPPFLAGS', '$CFLAGS', '$SOURCES'] + 'LIBSUFFIX' => '.a', + 'LDFLAGS' => [], + 'LIBPATHS' => [], + 'LIBS' => [], + 'LDCOM' => ['$LD', '-o', '$TARGET', '$LDFLAGS', '$SOURCES', '-L$[LIBPATHS]', '-l$[LIBS]'] } end - def produces?(target, source) - target.has_suffix?(@env['OBJSUFFIX']) and source.has_suffix?(@env['CSUFFIX']) + def run(target, sources, cache) + # convert sources to object file names + sources = sources.map do |source| + if source.has_suffix?([@env['OBJSUFFIX'], @env['LIBSUFFIX']]) + source + else + o_file = source.set_suffix(@env['OBJSUFFIX', :string]) + builder = @env.builders.values.find { |b| b.produces?(o_file, source) } + builder or raise "No builder found to convert input source #{source.inspect} to an object file." + builder.run(o_file, [source], cache) + end + end + unless cache.up_to_date?(target, sources) + vars = { + 'TARGET' => target, + 'SOURCES' => sources, + 'LD' => @env['LD'] || @env['CC'], # TODO: figure out whether to use CC or CXX + } + @env.execute("LD #{target}", @env['LDCOM'], vars) + end + target end end end diff --git a/lib/rscons/cache.rb b/lib/rscons/cache.rb index 65ea184..9ef50db 100644 --- a/lib/rscons/cache.rb +++ b/lib/rscons/cache.rb @@ -25,6 +25,7 @@ module Rscons def up_to_date?(file, deps = nil) # TODO + false end def register_build(target, deps) diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index 5098d6e..26a8e5a 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -1,5 +1,9 @@ +require 'set' + module Rscons class Environment + attr_reader :builders + class << self alias_method :orig_new, :new end @@ -39,6 +43,12 @@ module Rscons def add_builder(builder) @builders[builder.class.short_name] = builder + var_defs = builder.default_variables(self) + if var_defs + var_defs.each_pair do |var, val| + @variables[var] ||= val + end + end end def [](key, type = nil) @@ -58,15 +68,70 @@ module Rscons def process cache = Cache.new + targets_processed = Set.new + process_target = proc do |target| + sources_built = @targets[target][:source].map do |src| + targets_processed.include?(src) or not @targets.include?(src) or process_target.call(src) + end.all? + if sources_built + @targets[target][:builder].run(target, + @targets[target][:source], + cache, + *@targets[target][:args]) + else + false + end + end + @targets.each do |target, info| + next if targets_processed.include?(target) + break unless process_target.call(target) + end cache.write end + def execute(short_desc, command, extra_vars) + merged_variables = @variables.merge(extra_vars) + expand_varref = proc do |varref| + if varref.is_a?(Array) + varref.map do |ent| + expand_varref.call(ent) + end + else + if varref =~ /^(.*)\$\[(\w+)\](.*)$/ + # expand array with given prefix, suffix + prefix, varname, suffix = $1, $2, $3 + varval = merged_variables[varname] + unless varval.is_a?(Array) + raise "Array expected for $#{varname}" + end + varval.map {|e| "#{prefix}#{e}#{suffix}"} + elsif varref =~ /^\$(.*)$/ + # expand a single variable reference + varname = $1 + varval = merged_variables[varname] + varval or raise "Could not find variable #{varname.inspect}" + expand_varref.call(varval) + else + varref + end + end + end + command = expand_varref.call(command.flatten).flatten + if @echo == :command + puts command.map { |c| c =~ /\s/ ? "'#{c}'" : c }.join(' ') + elsif @echo == :short + puts short_desc + end + system(*command) + end + alias_method :orig_method_missing, :method_missing def method_missing(method, *args) if @builders.has_key?(method.to_s) target, source, *rest = args + source = [source] unless source.is_a?(Array) @targets[target] = { - builder: method.to_s, + builder: @builders[method.to_s], source: source, args: rest, } @@ -74,5 +139,24 @@ module Rscons orig_method_missing(method, *args) end end + + def parse_makefile_deps(mf_fname, target) + deps = [] + buildup = '' + File.read(mf_fname).each_line do |line| + if line =~ /^(.*)\\\s*$/ + buildup += ' ' + $1 + else + if line =~ /^(.*): (.*)$/ + target, tdeps = $1.strip, $2 + if target == target + deps += tdeps.split(' ').map(&:strip) + end + end + buildup = '' + end + end + deps + end end end