reno 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2010, Zoxc
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of Zoxc nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/reno.rb ADDED
@@ -0,0 +1,20 @@
1
+ module Reno
2
+ require 'rexml/document'
3
+
4
+ ROOT = File.dirname(File.expand_path(__FILE__)) unless defined?(Reno::ROOT)
5
+
6
+ unless $LOAD_PATH.any? { |lp| File.expand_path(lp) == ROOT }
7
+ $LOAD_PATH.unshift(ROOT)
8
+ end
9
+
10
+ require 'reno/lock'
11
+ require 'reno/configuration'
12
+ require 'reno/builder'
13
+ require 'reno/cache'
14
+ require 'reno/platforms'
15
+ require 'reno/sourcefile'
16
+ require 'reno/options'
17
+ require 'reno/languages'
18
+ require 'reno/toolchains'
19
+ require 'reno/package'
20
+ end
@@ -0,0 +1,136 @@
1
+ require 'pathname'
2
+ require 'fileutils'
3
+
4
+ module Reno
5
+ class BuilderError < StandardError
6
+ end
7
+
8
+ class Builder
9
+ attr_reader :sources, :cache, :sqlcache, :base, :output, :objects, :package, :conf
10
+ attr :conf, true
11
+
12
+ def self.readydirs(path)
13
+ FileUtils.makedirs(File.dirname(path))
14
+ end
15
+
16
+ def self.cleanpath(base, path)
17
+ Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(base)).to_s
18
+ end
19
+
20
+ def self.execute(command, *args)
21
+ #puts [command, *args].join(' ')
22
+ IO.popen([command, *args]) do |f|
23
+ f.readlines
24
+ end
25
+ raise "#{command} failed with error code #{$?.exitstatus}" if $?.exitstatus != 0
26
+ end
27
+
28
+ def self.capture(command, *args)
29
+ #puts [command, *args].join(' ')
30
+ result = nil
31
+ IO.popen([command, *args]) do |f|
32
+ result = f.readlines.join('')
33
+ end
34
+ raise "#{command} failed with error code #{$?.exitstatus}" if $?.exitstatus != 0
35
+ result
36
+ end
37
+
38
+ def self.clean(filename)
39
+ if File.exists?(filename)
40
+ File.delete(filename)
41
+ begin
42
+ dir = File.dirname(filename)
43
+ while File.exists?(dir)
44
+ Dir.rmdir(dir)
45
+ dir = File.dirname(dir)
46
+ end
47
+ rescue SystemCallError
48
+ end
49
+ end
50
+ end
51
+
52
+ def puts(*args)
53
+ @puts_lock.synchronize do
54
+ Kernel.puts *args
55
+ end
56
+ end
57
+
58
+ def output(file)
59
+ result = File.expand_path(file, @bulid_base)
60
+ Builder.readydirs(result)
61
+ result
62
+ end
63
+
64
+ def initialize(package)
65
+ @package = package
66
+ @changed = Lock.new
67
+ @base = @package.base
68
+ @bulid_base = File.expand_path(@package.output, @base)
69
+ @objects = Lock.new([])
70
+ @files = Lock.new([])
71
+ @puts_lock = Mutex.new
72
+ @cache = Lock.new({})
73
+ end
74
+
75
+ def run(threads = 8)
76
+ # Creates an unique list of the files
77
+ @files.value = Dir[*@conf.get(:patterns)].map { |file| Builder.cleanpath(@base, file) }.uniq
78
+ @sqlcache = Cache.new(self)
79
+
80
+ # Start worker threads
81
+ workers = []
82
+ threads.times do
83
+ workers << Thread.new do
84
+ work
85
+ end
86
+ end
87
+
88
+ # Wait for all the workers
89
+ workers.each { |worker| worker.join }
90
+
91
+ # Normalize locks
92
+ @objects = @objects.value
93
+
94
+ # Link the package if it changed or the output doesn't exist
95
+
96
+ output_name = output(package.output_name)
97
+
98
+ if @changed.value || !File.exists?(output_name)
99
+ puts "Linking #{@package.name}..."
100
+
101
+ # Find a toochain to link this package
102
+ linker = @package.default[:linker]
103
+ linker = Toolchains::Hash.values.first unless linker
104
+ raise BuilderError, "Unable to find a linker to use." unless linker
105
+
106
+ linker.link(self, output(package.output_name))
107
+ else
108
+ puts "Nothing to do with #{@package.name}."
109
+ end
110
+ end
111
+
112
+ def get_file
113
+ @files.lock do |files|
114
+ files.pop
115
+ end
116
+ end
117
+
118
+ def output_file(result)
119
+ @objects.lock do |output|
120
+ output << result
121
+ end
122
+ end
123
+
124
+ def work
125
+ while filename = get_file
126
+ file = SourceFile.locate(self, filename)
127
+ rebuild = file.rebuild?
128
+ if rebuild
129
+ @changed.value = true
130
+ file.build
131
+ end
132
+ output_file file
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,57 @@
1
+ require 'pathname'
2
+ require 'sequel'
3
+
4
+ module Reno
5
+ class Cache
6
+ attr_reader :base, :db
7
+
8
+ class FileModel
9
+ def initialize(db, name, &block)
10
+ @db = db
11
+ @row = @db[:name => name]
12
+ unless @row
13
+ @row = block.call
14
+ @row[:id] = @db.insert(@row)
15
+ end
16
+ end
17
+
18
+ def [](name)
19
+ @row[name]
20
+ end
21
+
22
+ def []=(name, value)
23
+ @row[name] = value
24
+ @db.filter(:id => @row[:id]).update(name => value)
25
+ end
26
+ end
27
+
28
+ def setup_table(name, &block)
29
+ @db.create_table(name, &block) unless @db.table_exists?(name)
30
+ end
31
+
32
+ def initialize(builder)
33
+ @db = Sequel.sqlite(:database => builder.output('cache.db'))
34
+
35
+ setup_table :files do
36
+ primary_key :id, :type => Integer
37
+ String :name
38
+ String :md5
39
+ String :output
40
+ FalseClass :dependencies
41
+ end
42
+
43
+ setup_table :dependencies do
44
+ Integer :file
45
+ Integer :dependency
46
+ end
47
+
48
+ Languages.constants.each do |language|
49
+ language = Languages.const_get(language)
50
+
51
+ next if language.superclass != Languages::Language
52
+
53
+ language.setup_schema(self)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,50 @@
1
+ module Reno
2
+ class ConfigurationError < StandardError
3
+ end
4
+
5
+ class ConfigurationNode
6
+ attr_reader :langs, :builder, :parent, :hash, :patterns
7
+
8
+ def initialize(package, builder, parent, patterns)
9
+ @package = package
10
+ @builder = builder
11
+ @parent = parent
12
+ @patterns = patterns.to_a
13
+ @children = []
14
+ @hash = {}
15
+ end
16
+
17
+ def add(name, values)
18
+ @hash[name] = [] unless @hash.has_key?(name)
19
+ @hash[name].concat(values)
20
+ end
21
+
22
+ def get(name, file = nil, base = nil)
23
+ if file && !@patterns.empty?
24
+ Dir.chdir(base) do
25
+ return nil unless @patterns.any? { |pattern| File.fnmatch?(pattern, file) }
26
+ end
27
+ end
28
+
29
+ child_values = []
30
+
31
+ @children.each do |child|
32
+ child_values.concat(child.get(name, file).reject { |value| !value })
33
+ end
34
+
35
+ if Array === @hash[name]
36
+ @hash[name] + child_values
37
+ else
38
+ child_values
39
+ end
40
+ end
41
+
42
+ def derive(patterns)
43
+ patterns = patterns.to_a
44
+ child = ConfigurationNode.new(@package, @builder, self, patterns)
45
+ child.add(:patterns, patterns)
46
+ @children << child
47
+ child
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,61 @@
1
+ module Reno
2
+ module Languages
3
+ class LanguageError < StandardError
4
+ end
5
+
6
+ def self.locate(name)
7
+ Languages.constants.map { |lang| Languages.const_get(lang) }.each do |language|
8
+
9
+ next if language.superclass != Language
10
+
11
+ if language.name == name
12
+ return language
13
+ end
14
+ end
15
+
16
+ raise "Unable to find the language '#{name}'."
17
+ end
18
+
19
+ class Language
20
+ attr_reader :option
21
+
22
+ def inspect
23
+ "#<Language:#{self.class.name}>"
24
+ end
25
+
26
+ def initialize(option, block)
27
+ @option = option
28
+ instance_eval(&block) if block
29
+ end
30
+
31
+ def get_dependencies(file)
32
+ []
33
+ end
34
+
35
+ def read(name)
36
+ instance_variable_get "@#{name}"
37
+ end
38
+
39
+ class << self
40
+ attr :name, true
41
+
42
+ def table_name(name)
43
+ "lang_#{self.name}_#{name}".downcase.to_sym
44
+ end
45
+
46
+ def setup_table(cache, name, &block)
47
+ cache.setup_table(table_name(name), &block)
48
+ end
49
+
50
+ def setup_schema(cache)
51
+ end
52
+
53
+ def merge(nodes)
54
+ result = new(nil, nil)
55
+ nodes.each { |node| result.merge(node) }
56
+ result
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,156 @@
1
+ module Reno
2
+ module Languages
3
+ class C < Language
4
+ self.name = 'C'
5
+
6
+ def initialize(*args)
7
+ @defines = {}
8
+ @headers = []
9
+ @std = nil
10
+ @strict = nil
11
+ super
12
+ end
13
+
14
+ def compare(file)
15
+ defines = file.db[self.class.table_name(:defines)]
16
+ attribs = file.db[self.class.table_name(:attribs)]
17
+
18
+ hash = {}
19
+
20
+ defines.filter(:file => file.row[:id]).all do |row|
21
+ hash[row[:define]] = row[:value]
22
+ end
23
+
24
+ attribs = attribs.filter(:file => file.row[:id]).first
25
+ attribs = {:std => nil, :strict => false} unless attribs
26
+
27
+ hash == @defines and attribs[:std] == @std.to_s and attribs[:strict] == @strict
28
+ end
29
+
30
+ def print(*values)
31
+ puts values.inspect
32
+ end
33
+
34
+ def store(file)
35
+ defines = file.db[self.class.table_name(:defines)]
36
+ attribs = file.db[self.class.table_name(:attribs)]
37
+
38
+ # Delete existing rows
39
+ defines.filter(:file => file.row[:id]).delete
40
+ attribs.filter(:file => file.row[:id]).delete
41
+
42
+ # Add the current ones
43
+ @defines.each_pair do |key, value|
44
+ defines.insert(:file => file.row[:id], :define => key, :value => value)
45
+ end
46
+
47
+ attribs.insert(:file => file.row[:id], :strict => @strict, :std => @std.to_s)
48
+ end
49
+
50
+ def self.setup_schema(cache)
51
+ setup_table(cache, :defines) do
52
+ Integer :file
53
+ String :define
54
+ String :value
55
+ end
56
+
57
+ setup_table(cache, :attribs) do
58
+ Integer :file
59
+ FalseClass :strict
60
+ String :std
61
+ end
62
+ end
63
+
64
+ def merge(other)
65
+ @defines.merge!(other.read(:defines))
66
+
67
+ other_value = other.read(:std)
68
+ @std = other_value if other_value
69
+
70
+ other_value = other.read(:strict)
71
+ @strict = other_value if other_value != nil
72
+ end
73
+
74
+ def define(name, value = nil)
75
+ @defines[name] = value
76
+ end
77
+
78
+ def strict(value = true)
79
+ @strict = value
80
+ end
81
+
82
+ def std(value)
83
+ @std = value
84
+ end
85
+
86
+ def headers(*dirs)
87
+ @headers.concat(dirs)
88
+ end
89
+
90
+ def self.extensions
91
+ ['.c', '.h']
92
+ end
93
+
94
+ # The get_dependencies method is under the GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 license.
95
+ # It's from rant-0.5.8\lib\rant\c\include.rb
96
+
97
+ # include.rb - Support for C - parsing #include statements.
98
+ #
99
+ # Copyright (C) 2005 Stefan Lang <langstefan@gmx.at>
100
+
101
+
102
+ # Searches for all `#include' statements in the C/C++ source
103
+ # from the string +src+.
104
+ #
105
+ # Returns two arguments:
106
+ # 1. A list of all standard library includes (e.g. #include <stdio.h>).
107
+ # 2. A list of all local includes (e.g. #include "stdio.h").
108
+
109
+ def self.get_dependencies(file)
110
+ src = file.content
111
+
112
+ includes = []
113
+ in_block_comment = false
114
+ prev_line = nil
115
+
116
+ src.each_line do |line|
117
+ line.chomp!
118
+
119
+ if block_start_i = line.index("/*")
120
+ c_start_i = line.index("//")
121
+ if !c_start_i || block_start_i < c_start_i
122
+ if block_end_i = line.index("*/")
123
+ if block_end_i > block_start_i
124
+ line[block_start_i..block_end_i+1] = ""
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ if prev_line
131
+ line = prev_line << line
132
+ prev_line = nil
133
+ end
134
+
135
+ if line =~ /\\$/
136
+ prev_line = line.chomp[0...line.length-1]
137
+ end
138
+
139
+ if in_block_comment
140
+ in_block_comment = false if line =~ %r|\*/|
141
+ next
142
+ end
143
+
144
+ case line
145
+ when /\s*#\s*include\s+"([^"]+)"/
146
+ includes << $1
147
+ when %r|(?!//)[^/]*/\*|
148
+ in_block_comment = true
149
+ end
150
+ end
151
+
152
+ includes
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,33 @@
1
+ module Reno
2
+ class Lock
3
+ def initialize(value = nil)
4
+ @value = value
5
+ @mutex = Mutex.new
6
+ end
7
+
8
+ def lock
9
+ @mutex.synchronize do
10
+ yield @value
11
+ end
12
+ end
13
+
14
+ def value
15
+ if block_given?
16
+ @mutex.synchronize do
17
+ return @value if @value
18
+ @value = yield
19
+ end
20
+ else
21
+ @mutex.synchronize do
22
+ @value
23
+ end
24
+ end
25
+ end
26
+
27
+ def value=(value)
28
+ @mutex.synchronize do
29
+ @value = value
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,89 @@
1
+ module Reno
2
+ class Option
3
+ attr_reader :package, :parent, :name, :desc, :children
4
+
5
+ def initialize(package, parent, name = nil, desc = nil, default = nil, block)
6
+ @package = package
7
+ @parent = parent
8
+ @name = name
9
+ @desc = desc
10
+ @default = default
11
+ @children = []
12
+ @langs = {}
13
+ instance_eval(&block) if block
14
+ end
15
+
16
+ def apply_config(conf, data)
17
+ conf.add(:langs, [@langs])
18
+ @children.each do |child|
19
+ child_data = data.elements.find { |element| element.attribute('name').value == child.name.to_s && element.name == child.xml_node } if data
20
+ child.apply_config(conf, child_data)
21
+ end
22
+ end
23
+
24
+ def xml_node
25
+ "group"
26
+ end
27
+
28
+ def o(name, desc = nil, default = false, &block)
29
+ @children << BooleanOption.new(@package, self, name, desc, default, block)
30
+ end
31
+
32
+ def group(name, desc = nil, &block)
33
+ @children << Option.new(@package, self, name, desc, block)
34
+ end
35
+
36
+ def source(*args, &block)
37
+ @children << SourceOption.new(@package, self, block, args)
38
+ end
39
+
40
+ alias :sources :source
41
+
42
+ def default(hash)
43
+ @package.default.merge!(hash)
44
+ end
45
+
46
+ def lang(name = nil, &block)
47
+ lang = name || @package.default[:lang]
48
+
49
+ raise "Unable to find the default language." unless lang
50
+
51
+ lang = lang.to_s
52
+
53
+ return @langs[lang] if @langs[lang]
54
+
55
+ obj = Languages.locate(lang).new(self, block)
56
+ @langs[lang] = obj
57
+ obj
58
+ end
59
+ end
60
+
61
+ class BooleanOption < Option
62
+ def apply_config(conf, data)
63
+ if (data && data.attribute('value')) ? data.attribute('value').value == "true" : @default
64
+ super
65
+ end
66
+ end
67
+
68
+ def xml_node
69
+ "option"
70
+ end
71
+
72
+ end
73
+
74
+ class SourceOption < Option
75
+ def initialize(*args, sources)
76
+ @sources = sources
77
+ super *args
78
+ end
79
+
80
+ def apply_config(conf, data)
81
+ conf = conf.derive(@sources)
82
+ super
83
+ end
84
+
85
+ def xml_node
86
+ "file"
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,82 @@
1
+ module Reno
2
+ class PackageError < StandardError
3
+ end
4
+
5
+ Packages = {}
6
+
7
+ class PackageOption < Option
8
+ def name(name)
9
+ @package.name = name
10
+ end
11
+
12
+ def desc(desc)
13
+ @package.desc = desc
14
+ end
15
+
16
+ def version(version)
17
+ @package.version = version
18
+ end
19
+ end
20
+
21
+ class Package
22
+ attr_reader :default, :name, :base, :output
23
+ attr :desc, true
24
+ attr :version, true
25
+
26
+ def initialize(&block)
27
+ @default = {}
28
+ @output = 'build'
29
+ @base = Dir.getwd
30
+ # This should be the last thing to be set up. The object might depend on the other variables.
31
+ @option = PackageOption.new(self, nil, block)
32
+
33
+ self
34
+ end
35
+
36
+ def output_name
37
+ raise "You can't link packages!"
38
+ end
39
+
40
+ def name=(name)
41
+ raise "You have already assigned this package a name" if @name
42
+
43
+ @name = name.to_s
44
+
45
+ raise PackageError, "The package #{@name} already exist." if Packages[@name]
46
+ Packages[@name] = self
47
+ end
48
+
49
+ def builder(data = nil)
50
+ builder = Builder.new(self)
51
+ conf = ConfigurationNode.new(@option.package, builder, nil, [])
52
+ @option.apply_config(conf, data)
53
+ builder.conf = conf
54
+ builder
55
+ end
56
+
57
+ def load_config(data)
58
+ conf = PackageConf.new(self)
59
+ Options.new(conf, data, @options)
60
+ end
61
+ end
62
+
63
+ class Library < Package
64
+ def output_name
65
+ if Rake::Win32.windows?
66
+ @name + '.dll'
67
+ else
68
+ @name + '.so'
69
+ end
70
+ end
71
+ end
72
+
73
+ class Application < Package
74
+ def output_name
75
+ if Platforms.current == Platforms::Windows
76
+ @name + '.exe'
77
+ else
78
+ @name
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,17 @@
1
+ module Reno
2
+ module Platforms
3
+ class Windows
4
+ end
5
+
6
+ class Unix
7
+ end
8
+
9
+ def self.current
10
+ if RUBY_PLATFORM =~ /(win|w)32$/
11
+ Windows
12
+ else
13
+ Unix
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,139 @@
1
+ require 'digest/md5'
2
+
3
+ module Reno
4
+ class SourceFileError < StandardError
5
+ end
6
+
7
+ class SourceFile
8
+ attr_reader :path, :name, :row, :db, :builder
9
+
10
+ def self.locate(builder, filename)
11
+ builder.cache.lock do |cache|
12
+ return cache[filename] || new(builder, filename, cache)
13
+ end
14
+ end
15
+
16
+ def initialize(builder, filename, cache)
17
+ @builder = builder
18
+ @name = filename
19
+ @path = File.expand_path(@name, @builder.base)
20
+ @changed = Lock.new
21
+ @language = Lock.new
22
+ @lang_conf = Lock.new
23
+ @compiler = Lock.new
24
+ @output = Lock.new
25
+ @content = Lock.new
26
+ @digest = Lock.new
27
+ cache[filename] = self
28
+ @db = @builder.sqlcache.db
29
+ @row = Cache::FileModel.new(@db[:files], @name) do
30
+ {:name => @name, :md5 => digest, :dependencies => false, :output => nil}
31
+ end
32
+ end
33
+
34
+ def find_language
35
+ file_ext = File.extname(@name).downcase
36
+
37
+ Languages.constants.map { |name| Languages.const_get(name) }.each do |language|
38
+ next if language.superclass != Languages::Language
39
+
40
+ if language.extensions.any? { |ext| ext == file_ext }
41
+ return language
42
+ end
43
+ end
44
+
45
+ raise SourceFileError, "Unable to find a language for the file '#{@name}'."
46
+ end
47
+
48
+ def digest
49
+ @digest.value { Digest::MD5.hexdigest(content) }
50
+ end
51
+
52
+ def content
53
+ @content.value { File.open(@path, 'rb') { |f| f.read } }
54
+ end
55
+
56
+ def output
57
+ @output.value { @builder.output(@name + '.o') }
58
+ end
59
+
60
+ def language
61
+ @language.value { find_language }
62
+ end
63
+
64
+ def compiler
65
+ @compiler.value { Toolchains.locate(language) }
66
+ end
67
+
68
+ def lang_conf
69
+ @lang_conf.value do
70
+ langs = @builder.conf.get(:langs, nil).map { |langs| langs[language.name] }.reject { |lang| !lang }
71
+ language.merge(langs)
72
+ end
73
+ end
74
+
75
+ def get_dependencies
76
+ @builder.puts "Getting dependencies for #{@name}..."
77
+ @db[:dependencies].filter(:file => @row[:id]).delete
78
+
79
+ dependencies = compiler.get_dependencies(self)
80
+ dependencies.each do |path|
81
+ file = SourceFile.locate(@builder, Builder.cleanpath(@builder.base, path))
82
+ @db[:dependencies].insert(:file => @row[:id], :dependency => file.row[:id])
83
+ end
84
+
85
+ @row[:dependencies] = true
86
+ end
87
+
88
+ def dependencies_changed?
89
+ get_dependencies unless @row[:dependencies]
90
+
91
+ dependencies = @db[:dependencies].filter(:file => @row[:id]).all
92
+
93
+ dependencies.any? do |dependency|
94
+ SourceFile.locate(@builder, @db[:files][:id => dependency[:dependency]][:name]).changed?
95
+ end
96
+ end
97
+
98
+ def changed?
99
+ @changed.value do |changed|
100
+ # Check if the file has changed and reset dependencies and output if needed
101
+
102
+ if !File.exists?(@path)
103
+ @row[:dependencies] = false
104
+ changed = true
105
+ elsif @row[:md5] != digest then
106
+ @row[:dependencies] = false
107
+ @row[:md5] = digest
108
+ changed = true
109
+ else
110
+ changed = dependencies_changed? ? true : false
111
+ end
112
+
113
+ @row[:output] = nil if changed
114
+
115
+ changed
116
+ end
117
+ end
118
+
119
+ def rebuild?
120
+ return true unless @row[:output] and File.exists?(File.expand_path(@row[:output], @builder.base)) and lang_conf.compare(self)
121
+
122
+ changed?
123
+ end
124
+
125
+ def build
126
+ @builder.puts "Compiling #{@name}..."
127
+ compiler.compile(self)
128
+
129
+ output = @output.value
130
+
131
+ if File.exists? output
132
+ @row[:output] = output
133
+ lang_conf.store(self)
134
+ else
135
+ raise SourceFileError, "Can't find output '#{output}' from #{name}."
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,39 @@
1
+ module Reno
2
+ module Toolchains
3
+ class ToolchainError < StandardError
4
+ end
5
+
6
+ Hash = {}
7
+ Compilers = {}
8
+
9
+ def self.register(toolchain, language)
10
+ array = Compilers[language]
11
+ array = Compilers[language] = [] unless array
12
+ array << toolchain
13
+ end
14
+
15
+ def self.locate(language)
16
+ array = Compilers[language.name]
17
+
18
+ raise "Unable to find a compiler for the language '#{language.name}'." unless array
19
+
20
+ array.first
21
+ end
22
+
23
+ class Toolchain
24
+ attr_reader :option
25
+
26
+ def self.get_dependencies(file)
27
+ file.language.get_dependencies(file)
28
+ end
29
+
30
+ def self.register(name, *languages)
31
+ Hash[name] = self
32
+
33
+ languages.each do |language|
34
+ Toolchains.register(self, language)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,53 @@
1
+ module Reno
2
+ module Toolchains
3
+ class Gnu < Toolchain
4
+ register :gnu, 'C'
5
+
6
+ class << self
7
+ def command(file = nil)
8
+ ENV['CC'] || 'gcc'
9
+ end
10
+
11
+ def language(file)
12
+ case file.lang_conf
13
+ when Languages::C
14
+ 'c'
15
+ else
16
+ raise ToolchainError, "GCC doesn't support the language #{file.language.name}."
17
+ end
18
+ end
19
+
20
+ def compile(file)
21
+ Builder.execute(command(file), '-pipe', *defines(file), *std(file), '-x', language(file), '-c', file.path, '-o', file.output)
22
+ end
23
+
24
+ def std(file)
25
+ case file.lang_conf.read(:std)
26
+ when nil
27
+ return []
28
+ when :c89
29
+ result = '89'
30
+ when :c99
31
+ result = '99'
32
+ else
33
+ raise ToolchainError, "Unable to find language mode #{file.lang_conf.read(:std)}."
34
+ end
35
+
36
+ ["-std=#{file.lang_conf.read(:strict) ? "c" : "gnu"}#{result}"]
37
+ end
38
+
39
+ def defines(file)
40
+ defines = []
41
+ file.lang_conf.read(:defines).each_pair do |key, value|
42
+ defines << '-D' << (value ? "#{key}=#{value}" : key.to_s)
43
+ end
44
+ defines
45
+ end
46
+
47
+ def link(builder, output)
48
+ Builder.execute(command, '-pipe', *builder.objects.map { |object| object.output }, '-o', output)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reno
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Zoxc
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-04 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email:
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - LICENSE
26
+ - reno.rb
27
+ - reno/builder.rb
28
+ - reno/cache.rb
29
+ - reno/configuration.rb
30
+ - reno/languages/c.rb
31
+ - reno/languages.rb
32
+ - reno/lock.rb
33
+ - reno/options.rb
34
+ - reno/package.rb
35
+ - reno/platforms.rb
36
+ - reno/sourcefile.rb
37
+ - reno/toolchains/gnu.rb
38
+ - reno/toolchains.rb
39
+ has_rdoc: true
40
+ homepage:
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - .
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.9.0
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.5
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: Reno is a compiling framework in Ruby.
67
+ test_files: []
68
+