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 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, only C++ and simple executables and static and dynamic libraries are
8
- supported. See the features dir for examples and http://ilkka.github.com/jitsu
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.2.1
1
+ 0.3.0
@@ -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
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{jitsu}
8
- s.version = "0.2.1"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Ilkka Laukkanen"]
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
- objects = sources_to_objects(sources).join(' ')
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
- out.write "build #{source_to_object src}: cxx #{src}\n"
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
- def self.handle_executable(out, target, sources, objects, conf)
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
- out.write "build #{target}: link #{objects}"
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
- def self.handle_static_library(out, target, sources, objects, conf)
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 #{objects}"
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
- def self.handle_dynamic_library(out, target, sources, objects, conf)
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 #{objects}"
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.2.1
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: 446406449
143
+ hash: 994791855
144
144
  segments:
145
145
  - 0
146
146
  version: "0"