jitsu 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +3 -3
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/features/jitsu.feature +48 -0
- data/jitsu.gemspec +1 -1
- data/lib/jitsu.rb +80 -12
- data/spec/jitsu_spec.rb +99 -0
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -4,9 +4,9 @@ Ninja (https://github.com/martine/ninja) is an excellent build system, but
|
|
4
4
|
Ninja files are hard to write, by design. Jitsu is a simple meta build system
|
5
5
|
that writes Ninja files for you from YAML project descriptions.
|
6
6
|
|
7
|
-
For now,
|
8
|
-
|
9
|
-
for docs.
|
7
|
+
For now, C++ is the only supported langugage. Jitsu can build executables and
|
8
|
+
static, dynamic and libtool libraries. See the features dir for examples and
|
9
|
+
http://ilkka.github.com/jitsu for docs.
|
10
10
|
|
11
11
|
== Using Jitsu
|
12
12
|
|
data/Rakefile
CHANGED
@@ -37,12 +37,13 @@ end
|
|
37
37
|
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
38
38
|
spec.pattern = 'spec/**/*_spec.rb'
|
39
39
|
spec.rcov = true
|
40
|
+
spec.rcov_opts = "-x '^[\/]'"
|
40
41
|
end
|
41
42
|
|
42
43
|
require 'cucumber/rake/task'
|
43
44
|
Cucumber::Rake::Task.new(:features)
|
44
45
|
|
45
|
-
task :default => :spec
|
46
|
+
task :default => [:spec, :features]
|
46
47
|
|
47
48
|
require 'yard'
|
48
49
|
yardtask = YARD::Rake::YardocTask.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/features/jitsu.feature
CHANGED
@@ -113,3 +113,51 @@ Feature: Build C++ programs
|
|
113
113
|
And I run "ninja all"
|
114
114
|
And I run "env LD_PRELOAD=./lib.so ./blah"
|
115
115
|
Then the output should be "Hello World" with a newline
|
116
|
+
|
117
|
+
Scenario: Build an executable using libtool
|
118
|
+
Given a directory
|
119
|
+
And a file "lib.h" with contents
|
120
|
+
"""
|
121
|
+
#include <string>
|
122
|
+
|
123
|
+
std::string greeting();
|
124
|
+
"""
|
125
|
+
And a file "lib.cpp" with contents
|
126
|
+
"""
|
127
|
+
#include "lib.h"
|
128
|
+
|
129
|
+
std::string greeting() {
|
130
|
+
return std::string("Hello World");
|
131
|
+
}
|
132
|
+
"""
|
133
|
+
And a file "main.cpp" with contents
|
134
|
+
"""
|
135
|
+
#include <iostream>
|
136
|
+
|
137
|
+
extern std::string greeting();
|
138
|
+
|
139
|
+
int main(int argc, char* argv[]) {
|
140
|
+
std::cout << greeting() << std::endl;
|
141
|
+
return 0;
|
142
|
+
}
|
143
|
+
"""
|
144
|
+
And a file "build.jitsu" with contents
|
145
|
+
"""
|
146
|
+
---
|
147
|
+
targets:
|
148
|
+
lib.la:
|
149
|
+
type: libtool_library
|
150
|
+
sources:
|
151
|
+
- lib.cpp
|
152
|
+
blah:
|
153
|
+
type: executable
|
154
|
+
sources:
|
155
|
+
- main.cpp
|
156
|
+
dependencies:
|
157
|
+
- lib.la
|
158
|
+
"""
|
159
|
+
When I run jitsu
|
160
|
+
And I run "ninja all"
|
161
|
+
And I run "libtool --mode=execute ./blah"
|
162
|
+
Then the output should be "Hello World" with a newline
|
163
|
+
|
data/jitsu.gemspec
CHANGED
data/lib/jitsu.rb
CHANGED
@@ -35,18 +35,32 @@ module Jitsu
|
|
35
35
|
YAML.load(File.open(jitsufile, 'r').read)
|
36
36
|
end
|
37
37
|
|
38
|
+
# Check if any of the targets needs libtool.
|
39
|
+
#
|
40
|
+
# @param targets [Enum] the targets from a build specification hash.
|
41
|
+
# @return [Boolean] true if libtool required, nil otherwise.
|
42
|
+
def self.libtool_needed_for(targets)
|
43
|
+
not targets.select { |key,val| val['type'] == 'libtool_library' }.empty?
|
44
|
+
end
|
45
|
+
|
38
46
|
# Output jitsu build specification as build.ninja file(s).
|
39
47
|
#
|
40
48
|
# @param data [Hash] a build specification from e.g. Jitsu::read.
|
41
49
|
# @return nil
|
42
50
|
def self.output(data)
|
43
51
|
File.open NINJA_FILE_NAME, 'w' do |f|
|
52
|
+
libtool = libtool_needed_for data['targets']
|
44
53
|
f.write <<-EOS
|
45
54
|
cxxflags =
|
46
55
|
ldflags =
|
47
56
|
cxx = g++
|
48
57
|
ld = g++
|
49
58
|
ar = ar
|
59
|
+
EOS
|
60
|
+
if libtool
|
61
|
+
f.write "libtool = libtool\n"
|
62
|
+
end
|
63
|
+
f.write <<-EOS
|
50
64
|
|
51
65
|
rule cxx
|
52
66
|
description = CC ${in}
|
@@ -61,11 +75,23 @@ rule archive
|
|
61
75
|
description = AR ${out}
|
62
76
|
command = ${ar} rT ${out} ${in}
|
63
77
|
EOS
|
78
|
+
if libtool_needed_for data['targets']
|
79
|
+
f.write <<-EOS
|
80
|
+
|
81
|
+
rule ltcxx
|
82
|
+
description = CC ${in}
|
83
|
+
depfile = ${out}.d
|
84
|
+
command = ${libtool} --quiet --mode=compile ${cxx} -MMD -MF ${out}.d ${cxxflags} -c ${in}
|
85
|
+
|
86
|
+
rule ltlink
|
87
|
+
description = LD ${out}
|
88
|
+
command = ${libtool} --quiet --mode=link ${ld} ${ldflags} -o ${out} ${in}
|
89
|
+
EOS
|
90
|
+
end
|
64
91
|
data['targets'].each do |target,conf|
|
65
92
|
f.write "\n"
|
66
93
|
sources = conf['sources']
|
67
|
-
|
68
|
-
Jitsu.send "handle_#{conf['type']}".to_sym, f, target, sources, objects, conf
|
94
|
+
Jitsu.send "handle_#{conf['type']}".to_sym, f, target, sources, conf, data['targets']
|
69
95
|
end
|
70
96
|
f.write("\nbuild all: phony || #{data['targets'].keys.join(' ')}\n")
|
71
97
|
end
|
@@ -79,8 +105,11 @@ EOS
|
|
79
105
|
# @param conf [Hash] the entire build spec hash for this target.
|
80
106
|
def self.output_sources(out, sources, conf)
|
81
107
|
cxxflags = conf['cxxflags']
|
108
|
+
libtool = conf['type'] == 'libtool_library'
|
109
|
+
rule = (libtool ? "ltcxx" : "cxx")
|
82
110
|
sources.each do |src|
|
83
|
-
|
111
|
+
object = (libtool ? source_to_ltobject(src) : source_to_object(src))
|
112
|
+
out.write "build #{object}: #{rule} #{src}\n"
|
84
113
|
out.write " cxxflags = #{cxxflags}\n" if cxxflags
|
85
114
|
end
|
86
115
|
end
|
@@ -91,11 +120,15 @@ EOS
|
|
91
120
|
# @param target [String] the filename of the target.
|
92
121
|
# @param sources [Enumerable] a list of sourcefile names to output rules
|
93
122
|
# for.
|
94
|
-
# @param objects [Enumerable] a list of all object files for the target.
|
95
123
|
# @param conf [Hash] the entire build spec hash for this target.
|
96
|
-
|
124
|
+
# @param targets [Hash] all targets for the build
|
125
|
+
def self.handle_executable(out, target, sources, conf, targets)
|
97
126
|
output_sources(out, sources, conf)
|
98
|
-
|
127
|
+
libtool = libtool_needed_for targets.select { |key,val|
|
128
|
+
conf['dependencies'] and conf['dependencies'].include? key
|
129
|
+
}
|
130
|
+
rule = libtool ? "ltlink" : "link"
|
131
|
+
out.write "build #{target}: #{rule} #{sources_to_objects(sources).join ' '}"
|
99
132
|
out.write " #{conf['dependencies'].join(' ')}" if conf['dependencies']
|
100
133
|
out.write "\n"
|
101
134
|
out.write " ldflags = #{conf['ldflags']}\n" if conf['ldflags']
|
@@ -107,11 +140,11 @@ EOS
|
|
107
140
|
# @param target [String] the filename of the target.
|
108
141
|
# @param sources [Enumerable] a list of sourcefile names to output rules
|
109
142
|
# for.
|
110
|
-
# @param objects [Enumerable] a list of all object files for the target.
|
111
143
|
# @param conf [Hash] the entire build spec hash for this target.
|
112
|
-
|
144
|
+
# @param targets [Hash] all targets for the build
|
145
|
+
def self.handle_static_library(out, target, sources, conf, targets)
|
113
146
|
output_sources(out, sources, conf)
|
114
|
-
out.write "build #{target}: archive #{
|
147
|
+
out.write "build #{target}: archive #{sources_to_objects(sources).join ' '}"
|
115
148
|
out.write " #{conf['dependencies'].join(' ')}" if conf['dependencies']
|
116
149
|
out.write "\n"
|
117
150
|
end
|
@@ -122,13 +155,13 @@ EOS
|
|
122
155
|
# @param target [String] the filename of the target.
|
123
156
|
# @param sources [Enumerable] a list of sourcefile names to output rules
|
124
157
|
# for.
|
125
|
-
# @param objects [Enumerable] a list of all object files for the target.
|
126
158
|
# @param conf [Hash] the entire build spec hash for this target.
|
127
|
-
|
159
|
+
# @param targets [Hash] all targets for the build
|
160
|
+
def self.handle_dynamic_library(out, target, sources, conf, targets)
|
128
161
|
conf['cxxflags'] ||= '${cxxflags}'
|
129
162
|
conf['cxxflags'] += ' -fPIC'
|
130
163
|
output_sources(out, sources, conf)
|
131
|
-
out.write "build #{target}: link #{
|
164
|
+
out.write "build #{target}: link #{sources_to_objects(sources).join ' '}"
|
132
165
|
out.write " #{conf['dependencies'].join(' ')}" if conf['dependencies']
|
133
166
|
out.write "\n"
|
134
167
|
conf['ldflags'] ||= '${ldflags}'
|
@@ -136,6 +169,24 @@ EOS
|
|
136
169
|
out.write " ldflags = #{conf['ldflags']}\n"
|
137
170
|
end
|
138
171
|
|
172
|
+
# Output build rules for one libtool library target.
|
173
|
+
#
|
174
|
+
# @param out [IO] the output stream where output is written.
|
175
|
+
# @param target [String] the filename of the target.
|
176
|
+
# @param sources [Enumerable] a list of sourcefile names to output rules
|
177
|
+
# for.
|
178
|
+
# @param conf [Hash] the entire build spec hash for this target.
|
179
|
+
# @param targets [Hash] all targets for the build
|
180
|
+
def self.handle_libtool_library(out, target, sources, conf, targets)
|
181
|
+
output_sources(out, sources, conf)
|
182
|
+
out.write "build #{target}: ltlink #{sources_to_ltobjects(sources).join ' '}"
|
183
|
+
out.write " #{conf['dependencies'].join(' ')}" if conf['dependencies']
|
184
|
+
out.write "\n"
|
185
|
+
conf['ldflags'] ||= '${ldflags}'
|
186
|
+
conf['ldflags'] += " -rpath /usr/local/lib"
|
187
|
+
out.write " ldflags = #{conf['ldflags']}\n"
|
188
|
+
end
|
189
|
+
|
139
190
|
# Convert sourcefile name to corresponding object file name.
|
140
191
|
#
|
141
192
|
# @param src [String] source file path.
|
@@ -151,4 +202,21 @@ EOS
|
|
151
202
|
def self.sources_to_objects(srcs)
|
152
203
|
srcs.map { |src| source_to_object src }
|
153
204
|
end
|
205
|
+
|
206
|
+
# Convert sourcefile name to corresponding libtool object file name.
|
207
|
+
#
|
208
|
+
# @param src [String] source file path.
|
209
|
+
# @return [String] libtool object file path.
|
210
|
+
def self.source_to_ltobject(src)
|
211
|
+
src.gsub /\.[Cc]\w+$/, '.lo'
|
212
|
+
end
|
213
|
+
|
214
|
+
# Convert a list of sourcefile names to corresponding libtool object file
|
215
|
+
# names.
|
216
|
+
#
|
217
|
+
# @param srcs [Enumerable] source file paths.
|
218
|
+
# @return [Enumerable] libtool object file paths.
|
219
|
+
def self.sources_to_ltobjects(srcs)
|
220
|
+
srcs.map { |src| source_to_ltobject src }
|
221
|
+
end
|
154
222
|
end
|
data/spec/jitsu_spec.rb
CHANGED
@@ -180,6 +180,105 @@ build aaa3.so: link aaa3.o
|
|
180
180
|
ldflags = ${ldflags} -shared -Wl,-soname,aaa3.so
|
181
181
|
|
182
182
|
build all: phony || aaa1 aaa2.a aaa3.so
|
183
|
+
EOS
|
184
|
+
end
|
185
|
+
File.open('build.ninja', 'r').read.should == ninjafile
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it "outputs a build.jitsu file with libtool" do
|
191
|
+
Dir.mktmpdir do |dir|
|
192
|
+
Dir.chdir dir do |dir|
|
193
|
+
File.open 'build.jitsu', 'w' do |f|
|
194
|
+
f.write <<-EOS
|
195
|
+
---
|
196
|
+
targets:
|
197
|
+
aaa1:
|
198
|
+
type: executable
|
199
|
+
sources:
|
200
|
+
- aaa1a.cpp
|
201
|
+
- aaa1b.cpp
|
202
|
+
dependencies:
|
203
|
+
- aaa2.a
|
204
|
+
- aaa3.la
|
205
|
+
aaa2.a:
|
206
|
+
type: static_library
|
207
|
+
sources:
|
208
|
+
- aaa2.cpp
|
209
|
+
cxxflags: -ansi -pedantic
|
210
|
+
aaa3.la:
|
211
|
+
type: libtool_library
|
212
|
+
sources:
|
213
|
+
- aaa3.cpp
|
214
|
+
EOS
|
215
|
+
end
|
216
|
+
data = Jitsu.read Jitsu.jitsufile
|
217
|
+
Jitsu.output data
|
218
|
+
Dir['build.ninja'].length.should == 1
|
219
|
+
ninjafile = <<-EOS
|
220
|
+
cxxflags =
|
221
|
+
ldflags =
|
222
|
+
cxx = g++
|
223
|
+
ld = g++
|
224
|
+
ar = ar
|
225
|
+
libtool = libtool
|
226
|
+
|
227
|
+
rule cxx
|
228
|
+
description = CC ${in}
|
229
|
+
depfile = ${out}.d
|
230
|
+
command = ${cxx} -MMD -MF ${out}.d ${cxxflags} -c ${in} -o ${out}
|
231
|
+
|
232
|
+
rule link
|
233
|
+
description = LD ${out}
|
234
|
+
command = ${ld} ${ldflags} -o ${out} ${in}
|
235
|
+
|
236
|
+
rule archive
|
237
|
+
description = AR ${out}
|
238
|
+
command = ${ar} rT ${out} ${in}
|
239
|
+
|
240
|
+
rule ltcxx
|
241
|
+
description = CC ${in}
|
242
|
+
depfile = ${out}.d
|
243
|
+
command = ${libtool} --quiet --mode=compile ${cxx} -MMD -MF ${out}.d ${cxxflags} -c ${in}
|
244
|
+
|
245
|
+
rule ltlink
|
246
|
+
description = LD ${out}
|
247
|
+
command = ${libtool} --quiet --mode=link ${ld} ${ldflags} -o ${out} ${in}
|
248
|
+
|
249
|
+
EOS
|
250
|
+
# the targets are reversed on 1.8.7 :p
|
251
|
+
if RUBY_VERSION.start_with? '1.8'
|
252
|
+
ninjafile += <<-EOS
|
253
|
+
build aaa3.lo: ltcxx aaa3.cpp
|
254
|
+
build aaa3.la: ltlink aaa3.lo
|
255
|
+
ldflags = ${ldflags} -rpath /usr/local/lib
|
256
|
+
|
257
|
+
build aaa2.o: cxx aaa2.cpp
|
258
|
+
cxxflags = -ansi -pedantic
|
259
|
+
build aaa2.a: archive aaa2.o
|
260
|
+
|
261
|
+
build aaa1a.o: cxx aaa1a.cpp
|
262
|
+
build aaa1b.o: cxx aaa1b.cpp
|
263
|
+
build aaa1: ltlink aaa1a.o aaa1b.o aaa2.a aaa3.la
|
264
|
+
|
265
|
+
build all: phony || aaa3.la aaa2.a aaa1
|
266
|
+
EOS
|
267
|
+
else
|
268
|
+
ninjafile += <<-EOS
|
269
|
+
build aaa1a.o: cxx aaa1a.cpp
|
270
|
+
build aaa1b.o: cxx aaa1b.cpp
|
271
|
+
build aaa1: ltlink aaa1a.o aaa1b.o aaa2.a aaa3.la
|
272
|
+
|
273
|
+
build aaa2.o: cxx aaa2.cpp
|
274
|
+
cxxflags = -ansi -pedantic
|
275
|
+
build aaa2.a: archive aaa2.o
|
276
|
+
|
277
|
+
build aaa3.lo: ltcxx aaa3.cpp
|
278
|
+
build aaa3.la: ltlink aaa3.lo
|
279
|
+
ldflags = ${ldflags} -rpath /usr/local/lib
|
280
|
+
|
281
|
+
build all: phony || aaa1 aaa2.a aaa3.la
|
183
282
|
EOS
|
184
283
|
end
|
185
284
|
File.open('build.ninja', 'r').read.should == ninjafile
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: jitsu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.3.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Ilkka Laukkanen
|
@@ -140,7 +140,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
140
140
|
requirements:
|
141
141
|
- - ">="
|
142
142
|
- !ruby/object:Gem::Version
|
143
|
-
hash:
|
143
|
+
hash: 994791855
|
144
144
|
segments:
|
145
145
|
- 0
|
146
146
|
version: "0"
|