Module: Jitsu

Defined in:
lib/jitsu.rb,
lib/jitsu/errors.rb

Overview

Jitsu, a meta build system for Ninja Copyright © 2011 Ilkka Laukkanen <[email protected]>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <www.gnu.org/licenses/>.

Defined Under Namespace

Classes: JitsuError, SyntaxError

Constant Summary collapse

JITSU_FILE_NAME =
'build.jitsu'
NINJA_FILE_NAME =
'build.ninja'

Class Method Summary collapse

Class Method Details

.handle_dynamic_library(out, target, sources, targets) ⇒ Object

Output build rules for one dynamic library target.

for.

Parameters:

  • out (IO)

    the output stream where output is written.

  • target (Hash)

    the entire build spec hash for this target.

  • sources (Enumerable)

    a list of sourcefile names to output rules

  • targets  (Hash)

    all targets for the build



196
197
198
199
200
201
202
203
204
205
206
# File 'lib/jitsu.rb', line 196

def self.handle_dynamic_library(out, target, sources, targets)
  target['cxxflags'] ||= '${cxxflags}'
  target['cxxflags'] += ' -fPIC'
  output_sources(out, sources, target)
  out.write "build #{target['name']}: link #{sources_to_objects(sources).join ' '}"
  out.write " #{target['dependencies'].join(' ')}" if target['dependencies']
  out.write "\n"
  target['ldflags'] ||= '${ldflags}'
  target['ldflags'] += " -shared -Wl,-soname,#{target['name']}"
  out.write "  ldflags = #{target['ldflags']}\n"
end

.handle_executable(out, target, sources, targets) ⇒ Object

Output build rules for one executable target.

for.

Parameters:

  • out (IO)

    the output stream where output is written.

  • target (Hash)

    the entire build spec hash for this target.

  • sources (Enumerable)

    a list of sourcefile names to output rules

  • targets  (Hash)

    all targets for the build



163
164
165
166
167
168
169
170
171
172
173
# File 'lib/jitsu.rb', line 163

def self.handle_executable(out, target, sources, targets)
  output_sources(out, sources, target)
  libtool = libtool_needed_for targets.select { |tgt|
    target['dependencies'] and target['dependencies'].include? tgt['name']
  }
  rule = libtool ? "ltlink" : "link"
  out.write "build #{target['name']}: #{rule} #{sources_to_objects(sources).join ' '}"
  out.write " #{target['dependencies'].join(' ')}" if target['dependencies']
  out.write "\n"
  out.write "  ldflags = #{target['ldflags']}\n" if target['ldflags']
end

.handle_libtool_library(out, target, sources, targets) ⇒ Object

Output build rules for one libtool library target.

for.

Parameters:

  • out (IO)

    the output stream where output is written.

  • target (Hash)

    the entire build spec hash for this target.

  • sources (Enumerable)

    a list of sourcefile names to output rules

  • targets  (Hash)

    all targets for the build



215
216
217
218
219
220
221
222
223
# File 'lib/jitsu.rb', line 215

def self.handle_libtool_library(out, target, sources, targets)
  output_sources(out, sources, target)
  out.write "build #{target['name']}: ltlink #{sources_to_ltobjects(sources).join ' '}"
  out.write " #{target['dependencies'].join(' ')}" if target['dependencies']
  out.write "\n"
  target['ldflags'] ||= '${ldflags}'
  target['ldflags'] += " -rpath /usr/local/lib"
  out.write "  ldflags = #{target['ldflags']}\n"
end

.handle_static_library(out, target, sources, targets) ⇒ Object

Output build rules for one static library target.

for.

Parameters:

  • out (IO)

    the output stream where output is written.

  • target (Hash)

    the entire build spec hash for this target.

  • sources (Enumerable)

    a list of sourcefile names to output rules

  • targets  (Hash)

    all targets for the build



182
183
184
185
186
187
# File 'lib/jitsu.rb', line 182

def self.handle_static_library(out, target, sources, targets)
  output_sources(out, sources, target)
  out.write "build #{target['name']}: archive #{sources_to_objects(sources).join ' '}"
  out.write " #{target['dependencies'].join(' ')}" if target['dependencies']
  out.write "\n"
end

.jitsufileString

Get path to jitsu file. Search starting from current working directory upwards.

Returns:

  • (String)

     path to jitsu file or nil if not found.



49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/jitsu.rb', line 49

def self.jitsufile
  dir = '.'
  prev = nil
  until prev and File.expand_path(prev) == File.expand_path(dir) do
    candidate = Dir[File.join dir, JITSU_FILE_NAME].first
    if candidate and File.readable? candidate
      return candidate.gsub /^\.\//, ''
    end
    prev = dir
    dir = File.join(dir, '..')
  end
end

.libtool_needed_for(targets) ⇒ Boolean

Check if any of the targets needs libtool.

Parameters:

  • targets (Enum)

    the targets from a build specification hash.

Returns:

  • (Boolean)

    true if libtool required, nil otherwise.



81
82
83
# File 'lib/jitsu.rb', line 81

def self.libtool_needed_for(targets)
  not targets.select { |target| target['type'] == 'libtool_library' }.empty?
end

.ninjaString

Get path to ninja.

Returns:

  • (String)

    path to ‘ninja` or nil if ninja was not found.



40
41
42
43
# File 'lib/jitsu.rb', line 40

def self.ninja
  candidates = ENV['PATH'].split(/:/).map { |d| File.join d, 'ninja' }
  candidates.select { |n| File.executable? n }.first
end

.output(data) ⇒ Object

Output jitsu build specification as build.ninja file(s).

Parameters:

  • data (Hash)

     a build specification from e.g. Jitsu::read.

Returns:

  • nil



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/jitsu.rb', line 89

def self.output(data)
  File.open NINJA_FILE_NAME, 'w' do |f|
    libtool = libtool_needed_for data['targets']
    f.write <<-EOS
cxxflags =
ldflags =
cxx = g++
ld = g++
ar = ar
EOS
    if libtool
      f.write "libtool = libtool\n"
    end
    f.write <<-EOS

rule cxx
description = CC ${in}
depfile = ${out}.d
command = ${cxx} -MMD -MF ${out}.d ${cxxflags} -c ${in} -o ${out}

rule link
description = LD ${out}
command = ${ld} ${ldflags} -o ${out} ${in}

rule archive
description = AR ${out}
command = ${ar} rT ${out} ${in}
EOS
    if libtool_needed_for data['targets']
      f.write <<-EOS

rule ltcxx
description = CC ${in}
depfile = ${out}.d
command = ${libtool} --quiet --mode=compile ${cxx} -MMD -MF ${out}.d ${cxxflags} -c ${in}

rule ltlink
description = LD ${out}
command = ${libtool} --quiet --mode=link ${ld} ${ldflags} -o ${out} ${in}
EOS
    end
    data['targets'].each do |target|
      f.write "\n"
      sources = target['sources']
      Jitsu.send "handle_#{target['type']}".to_sym, f, target, sources, data['targets']
    end
    f.write("\nbuild all: phony || #{data['targets'].map { |t| t['name'] }.join(' ')}\n")
  end
end

.output_sources(out, sources, target) ⇒ Object

Output build rules for a list of sources.

for.

Parameters:

  • out (IO)

    the output stream where output is written.

  • sources (Enumerable)

    a list of sourcefile names to output rules

  • target (Hash)

    the entire build spec hash for this target.



145
146
147
148
149
150
151
152
153
154
# File 'lib/jitsu.rb', line 145

def self.output_sources(out, sources, target)
  cxxflags = target['cxxflags']
  libtool = target['type'] == 'libtool_library'
  rule = (libtool ? "ltcxx" : "cxx")
  sources.each do |src|
    object = (libtool ? source_to_ltobject(src) : source_to_object(src))
    out.write "build #{object}: #{rule} #{src}\n"
    out.write "  cxxflags = #{cxxflags}\n" if cxxflags
  end
end

.read(jitsufile) ⇒ Hash

Read a jitsu file and output the build specification.

Parameters:

  • jitsufile (String)

    path to jitsu file from e.g. Jitsu::jitsufile.

Returns:

  • (Hash)

    a hash of the build specification.



66
67
68
69
70
71
72
73
74
75
# File 'lib/jitsu.rb', line 66

def self.read(jitsufile)
  schema = YAML.load_file(File.join(File.dirname(__FILE__), 'schema.yaml'))
  validator = Kwalify::Validator.new(schema)
  parser = Kwalify::Yaml::Parser.new(validator)
  doc = parser.parse(File.read(jitsufile))
  if parser.errors and not parser.errors.empty?
    raise Jitsu::SyntaxError.new(jitsufile, "Syntax errors in Jitsufile", parser.errors)
  end
  doc
end

.source_to_ltobject(src) ⇒ String

Convert sourcefile name to corresponding libtool object file name.

Parameters:

  • src (String)

     source file path.

Returns:

  • (String)

     libtool object file path.



245
246
247
# File 'lib/jitsu.rb', line 245

def self.source_to_ltobject(src)
  src.gsub /\.[Cc]\w+$/, '.lo'
end

.source_to_object(src) ⇒ String

Convert sourcefile name to corresponding object file name.

Parameters:

  • src (String)

     source file path.

Returns:

  • (String)

     object file path.



229
230
231
# File 'lib/jitsu.rb', line 229

def self.source_to_object(src)
  src.gsub /\.[Cc]\w+$/, '.o'
end

.sources_to_ltobjects(srcs) ⇒ Enumerable

Convert a list of sourcefile names to corresponding libtool object file names.

Parameters:

  • srcs (Enumerable)

    source file paths.

Returns:

  • (Enumerable)

    libtool object file paths.



254
255
256
# File 'lib/jitsu.rb', line 254

def self.sources_to_ltobjects(srcs)
  srcs.map { |src| source_to_ltobject src }
end

.sources_to_objects(srcs) ⇒ Enumerable

Convert a list of sourcefile names to corresponding object file names.

Parameters:

  • srcs (Enumerable)

    source file paths.

Returns:

  • (Enumerable)

    object file paths.



237
238
239
# File 'lib/jitsu.rb', line 237

def self.sources_to_objects(srcs)
  srcs.map { |src| source_to_object src }
end

.workObject

Process jitsufiles in current directory.



33
34
35
# File 'lib/jitsu.rb', line 33

def self.work
  Jitsu.output(Jitsu.read(Jitsu.jitsufile))
end