reno 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+