jitsu 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,24 @@
1
+ = jitsu
2
+
3
+ Ninja (https://github.com/martine/ninja) is an excellent build system, but
4
+ Ninja files are hard to write, by design. Jitsu is a simple meta build system
5
+ that writes Ninja files for you from YAML project descriptions.
6
+
7
+ For now, simple executables and static libraries are supported. See the
8
+ features dir for examples and http://ilkka.github.com/jitsu for docs.
9
+
10
+ == Contributing to jitsu
11
+
12
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
13
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
14
+ * Fork the project
15
+ * Start a feature/bugfix branch
16
+ * Commit and push until you are happy with your contribution
17
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
18
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
19
+
20
+ == Copyright
21
+
22
+ Copyright (c) 2011 Ilkka Laukkanen. See LICENSE.txt for
23
+ further details.
24
+
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "jitsu"
16
+ gem.homepage = "http://github.com/ilkka/jitsu"
17
+ gem.license = "GPLv3"
18
+ gem.summary = %Q{Meta build system for Ninja}
19
+ gem.description = <<-EOS
20
+ Jitsu is a frontend or meta build system for Ninja
21
+ (http://github.com/martine/ninja), a lightning-fast but
22
+ in itself (and by design) feature-poor build system.
23
+ Jitsu reads project descriptions and generates Ninja
24
+ buildfiles.
25
+ EOS
26
+ gem.email = "ilkka.s.laukkanen@gmail.com"
27
+ gem.authors = ["Ilkka Laukkanen"]
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rspec/core'
32
+ require 'rspec/core/rake_task'
33
+ RSpec::Core::RakeTask.new(:spec) do |spec|
34
+ spec.pattern = FileList['spec/**/*_spec.rb']
35
+ end
36
+
37
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
38
+ spec.pattern = 'spec/**/*_spec.rb'
39
+ spec.rcov = true
40
+ end
41
+
42
+ require 'cucumber/rake/task'
43
+ Cucumber::Rake::Task.new(:features)
44
+
45
+ task :default => :spec
46
+
47
+ require 'yard'
48
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
data/bin/jitsu ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ require 'trollop'
3
+ require 'logger'
4
+
5
+ require 'jitsu'
6
+
7
+ $Log = Logger.new STDOUT
8
+ $Log.level = Logger::WARN
9
+ $Log.progname = "jitsu"
10
+
11
+ if not Jitsu.ninja
12
+ warn "Could not find ninja. No surprise there."
13
+ exit 1
14
+ end
15
+
16
+ opts = Trollop::options do
17
+ version <<-EOS
18
+ jitsu #{File.read File.join(File.dirname(__FILE__), '..', 'VERSION')}
19
+ Copyright (C) 2011 Ilkka Laukkanen <ilkka.s.laukkanen@gmail.com>
20
+ Licensed under the MIT License
21
+ EOS
22
+ banner <<-EOS
23
+ jitsu, the meta build system for ninja.
24
+
25
+ Usage: jitsu [ OPTIONS ]
26
+
27
+ where OPTIONS are one or more of the following:
28
+ EOS
29
+ opt :verbose, "Be more verbose", :short => '-V'
30
+ opt :debug, "Output messages useful for debugging", :short => '-D'
31
+ end
32
+
33
+ $Log.level = Logger::INFO if opts[:verbose]
34
+ $Log.level = Logger::DEBUG if opts[:debug]
35
+
36
+ if not Jitsu.jitsu_file
37
+ warn "Could not find jitsu file"
38
+ exit 2
39
+ end
40
+
data/cucumber.yml ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ default: --color
@@ -0,0 +1,69 @@
1
+ Feature: Build C++ programs
2
+ In order to build C++ programs
3
+ As a user
4
+ I want to use Jitsu to generate build.ninja files
5
+
6
+ Scenario: Build a simple executable
7
+ Given a directory
8
+ And a file "main.cpp" with contents
9
+ """
10
+ #include <iostream>
11
+
12
+ int main(int argc, char* argv[]) {
13
+ std::cout << "Hello World" << std::endl;
14
+ return 0;
15
+ }
16
+ """
17
+ And a file "build.jitsu" with contents
18
+ """
19
+ ---
20
+ targets:
21
+ target:
22
+ type: executable
23
+ sources:
24
+ - main.cpp
25
+ """
26
+ When I run jitsu
27
+ And I run "ninja target"
28
+ And I run "./target"
29
+ Then the output should be "Hello World" with a newline
30
+
31
+ Scenario: Build an executable with a library
32
+ Given a directory
33
+ And a file "lib.cpp" with contents
34
+ """
35
+ #include <iostream>
36
+
37
+ void print_hello() {
38
+ std::cout << "Hello World" << std::endl;
39
+ }
40
+ """
41
+ And a file "main.cpp" with contents
42
+ """
43
+ void print_hello();
44
+
45
+ int main(int argc, char* argv[]) {
46
+ print_hello();
47
+ return 0;
48
+ }
49
+ """
50
+ And a file "build.jitsu" with contents
51
+ """
52
+ ---
53
+ targets:
54
+ hello:
55
+ type: executable
56
+ sources:
57
+ - main.cpp
58
+ dependencies:
59
+ - lib.a
60
+ lib.a:
61
+ type: library
62
+ sources:
63
+ - lib.cpp
64
+ """
65
+ When I run jitsu
66
+ And I run "ninja hello"
67
+ And I run "./hello"
68
+ Then the output should be "Hello World" with a newline
69
+
@@ -0,0 +1,32 @@
1
+ require 'tmpdir'
2
+
3
+ Given /^a directory$/ do
4
+ @tmpdir = Dir.mktmpdir
5
+ end
6
+
7
+ Given /^a file "([^"]*)" with contents$/ do |filename, contents|
8
+ Dir.chdir @tmpdir do |dir|
9
+ File.open filename, 'w' do |f|
10
+ f.write contents
11
+ end
12
+ end
13
+ end
14
+
15
+ When /^I run jitsu$/ do
16
+ Dir.chdir @tmpdir do |dir|
17
+ Jitsu.output(Jitsu.read(Jitsu.jitsufile))
18
+ end
19
+ end
20
+
21
+ When /^I run "([^"]*)"$/ do |command|
22
+ parts = command.split(/ /)
23
+ Dir.chdir @tmpdir do |dir|
24
+ parts[0] = File.expand_path(parts[0]) if parts[0].index(/\//)
25
+ @output = `#{parts.join(' ')}`
26
+ end
27
+ end
28
+
29
+ Then /^the output should be "([^"]*)" with a newline$/ do |desired|
30
+ @output.should == desired + "\n"
31
+ end
32
+
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :development)
4
+ rescue Bundler::BundlerError => e
5
+ $stderr.puts e.message
6
+ $stderr.puts "Run `bundle install` to install missing gems"
7
+ exit e.status_code
8
+ end
9
+
10
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
11
+ require 'jitsu'
12
+
13
+ require 'rspec/expectations'
data/jitsu.gemspec ADDED
@@ -0,0 +1,87 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{jitsu}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ilkka Laukkanen"]
12
+ s.date = %q{2011-02-13}
13
+ s.default_executable = %q{jitsu}
14
+ s.description = %q{Jitsu is a frontend or meta build system for Ninja
15
+ (http://github.com/martine/ninja), a lightning-fast but
16
+ in itself (and by design) feature-poor build system.
17
+ Jitsu reads project descriptions and generates Ninja
18
+ buildfiles.
19
+ }
20
+ s.email = %q{ilkka.s.laukkanen@gmail.com}
21
+ s.executables = ["jitsu"]
22
+ s.extra_rdoc_files = [
23
+ "LICENSE.txt",
24
+ "README.rdoc"
25
+ ]
26
+ s.files = [
27
+ ".autotest",
28
+ ".document",
29
+ ".rspec",
30
+ ".rvmrc",
31
+ "Gemfile",
32
+ "Gemfile.lock",
33
+ "LICENSE.txt",
34
+ "README.rdoc",
35
+ "Rakefile",
36
+ "VERSION",
37
+ "bin/jitsu",
38
+ "cucumber.yml",
39
+ "features/jitsu.feature",
40
+ "features/step_definitions/jitsu_steps.rb",
41
+ "features/support/env.rb",
42
+ "jitsu.gemspec",
43
+ "lib/jitsu.rb",
44
+ "spec/jitsu_spec.rb",
45
+ "spec/spec_helper.rb"
46
+ ]
47
+ s.homepage = %q{http://github.com/ilkka/jitsu}
48
+ s.licenses = ["GPLv3"]
49
+ s.require_paths = ["lib"]
50
+ s.rubygems_version = %q{1.5.0}
51
+ s.summary = %q{Meta build system for Ninja}
52
+ s.test_files = [
53
+ "spec/jitsu_spec.rb",
54
+ "spec/spec_helper.rb"
55
+ ]
56
+
57
+ if s.respond_to? :specification_version then
58
+ s.specification_version = 3
59
+
60
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
61
+ s.add_runtime_dependency(%q<trollop>, ["~> 1.16.2"])
62
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
63
+ s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
64
+ s.add_development_dependency(%q<cucumber>, [">= 0"])
65
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
66
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
67
+ s.add_development_dependency(%q<rcov>, [">= 0"])
68
+ else
69
+ s.add_dependency(%q<trollop>, ["~> 1.16.2"])
70
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
71
+ s.add_dependency(%q<yard>, ["~> 0.6.0"])
72
+ s.add_dependency(%q<cucumber>, [">= 0"])
73
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
74
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
75
+ s.add_dependency(%q<rcov>, [">= 0"])
76
+ end
77
+ else
78
+ s.add_dependency(%q<trollop>, ["~> 1.16.2"])
79
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
80
+ s.add_dependency(%q<yard>, ["~> 0.6.0"])
81
+ s.add_dependency(%q<cucumber>, [">= 0"])
82
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
83
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
84
+ s.add_dependency(%q<rcov>, [">= 0"])
85
+ end
86
+ end
87
+
data/lib/jitsu.rb ADDED
@@ -0,0 +1,93 @@
1
+ require 'yaml'
2
+
3
+ module Jitsu
4
+ JITSU_FILE_NAME = 'build.jitsu'
5
+ NINJA_FILE_NAME = 'build.ninja'
6
+
7
+ # Get path to ninja.
8
+ #
9
+ # @return [String] path to `ninja` or nil if ninja was not found.
10
+ def self.ninja
11
+ candidates = ENV['PATH'].split(/:/).map { |d| File.join d, 'ninja' }
12
+ candidates.select { |n| File.executable? n }.first
13
+ end
14
+
15
+ # Get path to jitsu file. Search starting from current working
16
+ # directory upwards.
17
+ #
18
+ # @return [String] path to jitsu file or nil if not found.
19
+ def self.jitsufile
20
+ dir = '.'
21
+ while File.expand_path(dir) != '/' do
22
+ candidate = Dir[File.join dir, JITSU_FILE_NAME].first
23
+ if candidate and File.readable? candidate
24
+ return candidate.gsub /^\.\//, ''
25
+ end
26
+ dir = File.join dir, '..'
27
+ end
28
+ end
29
+
30
+ # Read a jitsu file and output the build specification.
31
+ #
32
+ # @param jitsufile [String] path to jitsu file from e.g. Jitsu::jitsufile.
33
+ # @return [Hash] a hash of the build specification.
34
+ def self.read(jitsufile)
35
+ YAML.load(File.open(jitsufile, 'r').read)
36
+ end
37
+
38
+ # Output jitsu build specification as build.ninja file(s).
39
+ #
40
+ # @param data [Hash] a build specification from e.g. Jitsu::read.
41
+ # @return nil
42
+ def self.output(data)
43
+ File.open NINJA_FILE_NAME, 'w' do |f|
44
+ f.write <<-EOS
45
+ cxxflags =
46
+ ldflags =
47
+ cxx = g++
48
+ ld = g++
49
+ ar = ar
50
+
51
+ rule cxx
52
+ description = CC ${in}
53
+ depfile = ${out}.d
54
+ command = ${cxx} -MMD -MF ${out}.d ${cxxflags} -c ${in} -o ${out}
55
+
56
+ rule link
57
+ description = LD ${out}
58
+ command = ${ld} ${ldflags} -o ${out} ${in}
59
+
60
+ rule archive
61
+ description = AR ${out}
62
+ command = ${ar} rT ${out} ${in}
63
+ EOS
64
+ data['targets'].each do |target,conf|
65
+ f.write "\n"
66
+ sources = conf['sources']
67
+ sources.each do |src|
68
+ f.write "build #{source_to_object src}: cxx #{src}\n"
69
+ if conf['cxxflags']
70
+ f.write " cxxflags = #{conf['cxxflags']}\n"
71
+ end
72
+ end
73
+ f.write "build #{target}: "
74
+ case conf['type']
75
+ when 'executable'
76
+ f.write "link #{sources_to_objects(sources).join(' ')}"
77
+ f.write(' ' + conf['dependencies'].join(' ')) if conf['dependencies']
78
+ when 'library'
79
+ f.write "archive #{sources_to_objects(sources).join(' ')}"
80
+ end
81
+ f.write "\n"
82
+ end
83
+ end
84
+ end
85
+
86
+ def self.source_to_object(src)
87
+ src.gsub /\.[Cc]\w+$/, '.o'
88
+ end
89
+
90
+ def self.sources_to_objects(srcs)
91
+ srcs.map { |src| source_to_object src }
92
+ end
93
+ end
@@ -0,0 +1,158 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Jitsu" do
4
+ it "finds ninja if ninja is in PATH" do
5
+ File.executable?(Jitsu.ninja).should be_true
6
+ end
7
+
8
+ it "fails to find ninja if ninja is not in PATH" do
9
+ oldpath = ENV['PATH']
10
+ ENV['PATH'] = ""
11
+ Jitsu.ninja.should == nil
12
+ ENV['PATH'] = oldpath
13
+ end
14
+
15
+ it "finds no jitsu file if a jitsu file is not present" do
16
+ Dir.mktmpdir do |dir|
17
+ Dir.chdir dir do |dir|
18
+ Jitsu.jitsufile.should == nil
19
+ end
20
+ end
21
+ end
22
+
23
+ it "finds a build.jitsu file in the current directory" do
24
+ Dir.mktmpdir do |dir|
25
+ Dir.chdir dir do |dir|
26
+ File.open('build.jitsu', 'w') do |f|
27
+ f.write 'fuubar'
28
+ Jitsu.jitsufile.should == 'build.jitsu'
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ it "finds a build.jitsu file in the parent directory" do
35
+ Dir.mktmpdir do |dir|
36
+ Dir.chdir dir do |dir|
37
+ File.open('build.jitsu', 'w') do |f|
38
+ f.write 'fuubar'
39
+ end
40
+ Dir.mkdir 'subdir'
41
+ Dir.chdir 'subdir' do |dir|
42
+ Jitsu.jitsufile.should == '../build.jitsu'
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ it "reads YAML from jitsu files" do
49
+ Dir.mktmpdir do |dir|
50
+ Dir.chdir dir do |dir|
51
+ File.open 'build.jitsu', 'w' do |f|
52
+ f.write <<-EOS
53
+ ---
54
+ targets:
55
+ test1:
56
+ type: executable
57
+ sources: test1.cpp
58
+ cxxflags: -g -Wall
59
+ dependencies:
60
+ - test2
61
+ test2:
62
+ type: library
63
+ sources:
64
+ - test2.cpp
65
+ cxxflags: -ansi -pedantic
66
+ EOS
67
+ end
68
+ data = Jitsu.read Jitsu.jitsufile
69
+ data['targets'].keys.should == ['test1', 'test2']
70
+ end
71
+ end
72
+ Dir.mktmpdir do |dir|
73
+ Dir.chdir dir do |dir|
74
+ File.open 'build.jitsu', 'w' do |f|
75
+ f.write <<-EOS
76
+ ---
77
+ targets:
78
+ aaa1:
79
+ type: executable
80
+ sources:
81
+ - aaa1a.cpp
82
+ - aaa1b.cpp
83
+ cxxflags: -g -Wall
84
+ dependencies:
85
+ - aaa2
86
+ aaa2:
87
+ type: library
88
+ sources:
89
+ - aaa2.cpp
90
+ cxxflags: -ansi -pedantic
91
+ EOS
92
+ end
93
+ data = Jitsu.read Jitsu.jitsufile
94
+ data['targets'].keys.should == ['aaa1', 'aaa2']
95
+ end
96
+ end
97
+ end
98
+
99
+ it "outputs a build.jitsu file" do
100
+ Dir.mktmpdir do |dir|
101
+ Dir.chdir dir do |dir|
102
+ File.open 'build.jitsu', 'w' do |f|
103
+ f.write <<-EOS
104
+ ---
105
+ targets:
106
+ aaa1:
107
+ type: executable
108
+ sources:
109
+ - aaa1a.cpp
110
+ - aaa1b.cpp
111
+ cxxflags: -g -Wall
112
+ dependencies:
113
+ - aaa2.a
114
+ aaa2.a:
115
+ type: library
116
+ sources:
117
+ - aaa2.cpp
118
+ cxxflags: -ansi -pedantic
119
+ EOS
120
+ end
121
+ data = Jitsu.read Jitsu.jitsufile
122
+ Jitsu.output data
123
+ Dir['build.ninja'].length.should == 1
124
+ ninjafile = <<-EOS
125
+ cxxflags =
126
+ ldflags =
127
+ cxx = g++
128
+ ld = g++
129
+ ar = ar
130
+
131
+ rule cxx
132
+ description = CC ${in}
133
+ depfile = ${out}.d
134
+ command = ${cxx} -MMD -MF ${out}.d ${cxxflags} -c ${in} -o ${out}
135
+
136
+ rule link
137
+ description = LD ${out}
138
+ command = ${ld} ${ldflags} -o ${out} ${in}
139
+
140
+ rule archive
141
+ description = AR ${out}
142
+ command = ${ar} rT ${out} ${in}
143
+
144
+ build aaa1a.o: cxx aaa1a.cpp
145
+ cxxflags = -g -Wall
146
+ build aaa1b.o: cxx aaa1b.cpp
147
+ cxxflags = -g -Wall
148
+ build aaa1: link aaa1a.o aaa1b.o aaa2.a
149
+
150
+ build aaa2.o: cxx aaa2.cpp
151
+ cxxflags = -ansi -pedantic
152
+ build aaa2.a: archive aaa2.o
153
+ EOS
154
+ File.open('build.ninja', 'r').read.should == ninjafile
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'jitsu'
5
+ require 'tmpdir'
6
+
7
+ # Requires supporting files with custom matchers and macros, etc,
8
+ # in ./support/ and its subdirectories.
9
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
10
+
11
+ RSpec.configure do |config|
12
+
13
+ end