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 +24 -0
- data/reno.rb +20 -0
- data/reno/builder.rb +136 -0
- data/reno/cache.rb +57 -0
- data/reno/configuration.rb +50 -0
- data/reno/languages.rb +61 -0
- data/reno/languages/c.rb +156 -0
- data/reno/lock.rb +33 -0
- data/reno/options.rb +89 -0
- data/reno/package.rb +82 -0
- data/reno/platforms.rb +17 -0
- data/reno/sourcefile.rb +139 -0
- data/reno/toolchains.rb +39 -0
- data/reno/toolchains/gnu.rb +53 -0
- metadata +68 -0
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
|
data/reno/builder.rb
ADDED
@@ -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
|
data/reno/cache.rb
ADDED
@@ -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
|
data/reno/languages.rb
ADDED
@@ -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
|
data/reno/languages/c.rb
ADDED
@@ -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
|
data/reno/lock.rb
ADDED
@@ -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
|
data/reno/options.rb
ADDED
@@ -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
|
data/reno/package.rb
ADDED
@@ -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
|
data/reno/platforms.rb
ADDED
data/reno/sourcefile.rb
ADDED
@@ -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
|
data/reno/toolchains.rb
ADDED
@@ -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
|
+
|