jitsu 0.2.1 → 0.3.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/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"