shog-build 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5054b9f6f86bbdef798bcba4906245c4112bd59bdeb7865d904f07c15b04c7c3
4
+ data.tar.gz: 244eb986186f443c54a123adb30a3b5e79aa73b38189600ad744a38867bc4c2c
5
+ SHA512:
6
+ metadata.gz: 537d73019d1952b24f1b05c2cae0b03d30dde67584e0fc670189a94aa8e4aa673b13d214cf5c3e291d38aeaa9ca83b71a66f9aa996513992adc314ca4918f964
7
+ data.tar.gz: efd5400d63f73c8a11d01f92a5ef12fe25db21452690a6b67d05bad4d0134f366b0704111aadba475229cd2c158bb297032d5b6c2c359816cf8e62e1dcbce658
data/LICENSE.md ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2017 [Anatol Pomozov](https://github.com/anatol).
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # Shog Build Generator
2
+
3
+ A Ruby generator for [Ninja](https://ninja-build.org/) build system.
4
+
5
+ ## Credits
6
+
7
+ Developed by Anatol Pomozov.
data/bin/shog ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require_relative '../lib/runner'
4
+
5
+ Shog::Runner.run(ARGV)
data/lib/context.rb ADDED
@@ -0,0 +1,87 @@
1
+ require_relative 'util'
2
+ require_relative 'pathset'
3
+
4
+ module Shog
5
+ # An instance of this class is visible to our build scripts with name @shog
6
+ class Context
7
+ attr_accessor :default_target, :rule, :config
8
+
9
+ def initialize(backend, emitter)
10
+ @backend = backend
11
+ @emitter = emitter
12
+
13
+ @rule = {}
14
+ @default_target = PathSet.new
15
+ @config = {}
16
+ end
17
+
18
+ def register_rule(type)
19
+ r = type.new
20
+ @emitter.rule(r)
21
+ @rule[r.id] = r
22
+ end
23
+
24
+ def bind
25
+ binding
26
+ end
27
+
28
+ def visit_dir(dir, new_ctx = true)
29
+ old_pwd = Path.pwd
30
+ ctx = new_ctx ? deep_clone() : self
31
+
32
+ Path.pwd = File.join(Path.pwd, dir)
33
+ script = cwd('shog.build')
34
+ @rule[:generate_build].deps << script
35
+ script_name = script.path
36
+ build_script = File.read(script_name)
37
+ out = ctx.bind.eval(build_script, script_name)
38
+
39
+ Path.pwd = old_pwd
40
+
41
+ return out
42
+ end
43
+
44
+ def visit(dirs)
45
+ case dirs
46
+ when String then visit_dir(dirs)
47
+ when Array then dirs.map { |d| visit_dir(d) }.flatten
48
+ else raise "Unknown object type for dirs: #{dirs.class.name}"
49
+ end
50
+ end
51
+
52
+ def cwd(src)
53
+ Path.make(src)
54
+ end
55
+
56
+ def emit(rule_id, src, params = {})
57
+ r = @rule[rule_id]
58
+ raise "Rule #{rule_id} is not registered" unless r
59
+
60
+ params[:input] = PathSet.make(src)
61
+ target = r.target(params)
62
+ # fix path to inputs, outputs, includes
63
+
64
+ @emitter.emit(target)
65
+ return target[:output]
66
+ end
67
+
68
+ def emit_each(rule_id, srcs, params = {})
69
+ out = []
70
+ for s in srcs
71
+ p = params.dup
72
+ p[:input] = s
73
+ out += emit(rule_id, s, p)
74
+ end
75
+ return out
76
+ end
77
+
78
+ def deep_clone
79
+ ctx = Context.new(@backend, @emitter)
80
+ ctx.rule = @rule.deep_clone
81
+ ctx.rule[:generate_build].deps = @rule[:generate_build].deps # deps are global across the build
82
+ ctx.default_target = @default_target.deep_clone
83
+ ctx.config = @config
84
+ ctx
85
+ end
86
+ end
87
+ end
data/lib/generator.rb ADDED
@@ -0,0 +1,39 @@
1
+ require_relative 'context'
2
+ require_relative 'rule/cc'
3
+ require_relative 'rule/link'
4
+ require_relative 'rule/objcopy'
5
+ require_relative 'rule/kconfig'
6
+ require_relative 'rule/generate_build'
7
+ require_relative 'rule/yacc'
8
+
9
+ module Shog
10
+ class Generator
11
+ def initialize(backend)
12
+ @backend = backend
13
+ end
14
+
15
+ def generate
16
+ emitter = @backend.emitter
17
+ ctx = Context.new(@backend, emitter)
18
+
19
+ if File.exists?('Kconfig') and not File.exists?('.config')
20
+ system 'conf --alldefconfig -s Kconfig'
21
+ end
22
+
23
+ # Register all rules
24
+ ctx.register_rule(CC)
25
+ ctx.register_rule(Link)
26
+ ctx.register_rule(ObjCopy)
27
+ ctx.register_rule(Kconfig)
28
+ ctx.register_rule(GenerateBuild)
29
+ ctx.register_rule(Yacc)
30
+
31
+ Path.pwd = '.'
32
+ ctx.visit_dir('.', false)
33
+
34
+ emitter.default(ctx.default_target)
35
+
36
+ emitter.finish
37
+ end
38
+ end
39
+ end
data/lib/ninja.rb ADDED
@@ -0,0 +1,65 @@
1
+ module Shog
2
+ class Ninja
3
+ attr_reader :backend_file
4
+
5
+ def initialize
6
+ @out_dir = 'out'
7
+ @backend_file = File.join(@out_dir, 'build.ninja')
8
+ end
9
+
10
+ def configured?
11
+ File.exists?(@backend_file)
12
+ end
13
+
14
+ def emitter
15
+ Emitter.new(@backend_file)
16
+ end
17
+
18
+ def run
19
+ system "ninja -C #{@out_dir}"
20
+ end
21
+
22
+ class Emitter
23
+ def initialize(file)
24
+ @out = File.open(file, 'w')
25
+ end
26
+
27
+ def finish
28
+ @out.close
29
+ end
30
+
31
+ def default(target)
32
+ unless target.empty?
33
+ @out.puts "default #{target.join(' ')}"
34
+ @out.puts
35
+ end
36
+ end
37
+
38
+ def rule(r)
39
+ vars = r.rule
40
+ @out.puts "rule #{r.id.to_s}"
41
+ for k, v in vars
42
+ @out.puts " #{k}=#{v}"
43
+ end
44
+ @out.puts
45
+ end
46
+
47
+ def emit(target)
48
+ rule = target[:rule]
49
+ input = target[:input].join(' ')
50
+ implicit_input = if target[:implicit_input] and not target[:implicit_input].empty?
51
+ ' | ' + target[:implicit_input].join(' ')
52
+ else
53
+ ''
54
+ end
55
+ output = target[:output].join(' ')
56
+ variables = target[:variables]
57
+ @out.puts "build #{output}: #{rule} #{input}#{implicit_input}"
58
+ for k, v in variables
59
+ @out.puts " #{k}=#{v}"
60
+ end if variables
61
+ @out.puts
62
+ end
63
+ end
64
+ end
65
+ end
data/lib/path.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'pathname'
2
+
3
+ module Shog
4
+ class Path
5
+ @pwd = '.'
6
+
7
+ class << self
8
+ attr_accessor :pwd
9
+ end
10
+
11
+ attr_reader :path, :outoftree, :absolute
12
+
13
+ private :initialize
14
+
15
+ def initialize(path, outoftree, absolute = false)
16
+ @path = path
17
+ @outoftree = outoftree
18
+ @absolute = absolute
19
+ end
20
+
21
+ def self.normalize(path)
22
+ Pathname.new(path).cleanpath.to_s
23
+ end
24
+
25
+ def self.make(path, params = {})
26
+ if path.is_a?(String)
27
+ abolute = false
28
+ if params.key?(:absolute) and params[:absolute]
29
+ path = File.expand_path(path)
30
+ absolute = true
31
+ elsif not params.key?(:root) and not params[:root]
32
+ path = File.join(Path.pwd, path)
33
+ end
34
+ path = Path.normalize(path)
35
+ outoftree = if params.key?(:outoftree)
36
+ params[:outoftree]
37
+ else
38
+ false # by default we assume input file
39
+ end
40
+ return Path.new(path, outoftree, absolute)
41
+ elsif path.is_a?(Path)
42
+ if params.key?(:outoftree) and params[:outoftree] != path.outoftree
43
+ return Path.new(path.path, params[:outoftree])
44
+ else
45
+ return path
46
+ end
47
+ else
48
+ raise "Cannot make path from #{path}"
49
+ end
50
+ end
51
+
52
+ def dir
53
+ path = File.dirname(@path)
54
+ Path.new(path, @outoftree)
55
+ end
56
+
57
+ def with_suffix(suffix)
58
+ Path.new(Path.normalize(@path + suffix), @outoftree)
59
+ end
60
+
61
+ def to_str
62
+ # as we build files by ninja from inside outoftree directory then outs should not have any prefixes, but inputs should be accessed by '../'
63
+ if @outoftree or @absolute
64
+ @path
65
+ else
66
+ File.join('..', @path)
67
+ end
68
+ end
69
+ end
70
+ end
data/lib/pathset.rb ADDED
@@ -0,0 +1,46 @@
1
+ require_relative 'path'
2
+
3
+ module Shog
4
+ class PathSet < Array
5
+ def self.make(set)
6
+ if set.is_a?(PathSet)
7
+ set
8
+ elsif set.is_a?(Path)
9
+ ary = PathSet.new
10
+ ary << set
11
+ ary
12
+ elsif set.is_a?(Array)
13
+ set.map { |p| Path.make(p) }
14
+ elsif set.is_a?(String)
15
+ ary = PathSet.new
16
+ ary << set
17
+ ary
18
+ else
19
+ raise "Unknown path type #{set.class}"
20
+ end
21
+ end
22
+
23
+ def <<(p)
24
+ if p.is_a?(PathSet)
25
+ self.concat(p)
26
+ elsif p.is_a?(Path)
27
+ self.push(p)
28
+ elsif p.is_a?(Array)
29
+ p.each { |p| self.push(Path.make(p)) }
30
+ elsif p.is_a?(String)
31
+ self.push(Path.make(p))
32
+ else
33
+ raise "Unknown path type #{p}"
34
+ end
35
+ end
36
+
37
+ def single_path
38
+ raise "Expected number of paths is 1" if self.size != 1
39
+ self[0]
40
+ end
41
+
42
+ def +(ary)
43
+ self.concat(PathSet.make(ary))
44
+ end
45
+ end
46
+ end
data/lib/rule/cc.rb ADDED
@@ -0,0 +1,55 @@
1
+ require_relative '../path'
2
+
3
+ module Shog
4
+ class CC
5
+ attr_accessor :cflags, :includes, :implicit_input, :bin
6
+
7
+ def id
8
+ :cc
9
+ end
10
+
11
+ def initialize
12
+ @cflags = []
13
+ @includes = PathSet.new
14
+ @implicit_input = PathSet.new
15
+ end
16
+
17
+ def rule
18
+ {
19
+ 'command' => '$bin $cflags -MMD -MQ $out -MF $out.d -c $in -o $out',
20
+ 'description' => 'Compile $in',
21
+ 'deps' => 'gcc',
22
+ 'depfile' => '$out.d',
23
+ }
24
+ end
25
+
26
+ def target(params)
27
+ input = PathSet.new
28
+ input << params[:input]
29
+
30
+ output = PathSet.new
31
+ if params[:output]
32
+ output << Path.make(params[:output], :outoftree => true)
33
+ else
34
+ output << Path.make(params[:input].single_path, :outoftree => true).with_suffix('.o')
35
+ end
36
+
37
+ cflags = @cflags.dup
38
+ cflags << params[:cflags] if params[:cflags]
39
+ cflags += @includes.map { |i| '-I' + i }
40
+ includes = params[:includes]
41
+ if includes
42
+ includes = PathSet.make(includes)
43
+ cflags += includes.map { |i| '-I' + i }
44
+ end
45
+
46
+ variables = {
47
+ 'cflags' => cflags.join(' '),
48
+ 'bin' => params[:bin] || @bin || 'gcc',
49
+ }
50
+ implicit_input = @implicit_input.dup
51
+ implicit_input += params[:implicit_input] if params[:implicit_input]
52
+ {:rule => 'cc', :input => input, :implicit_input => implicit_input, :output => output, :variables => variables}
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../path'
2
+
3
+ module Shog
4
+ class GenerateBuild
5
+ attr_accessor :deps
6
+
7
+ def id
8
+ :generate_build
9
+ end
10
+
11
+ def initialize
12
+ @deps = PathSet.new
13
+ end
14
+
15
+ def rule
16
+ {
17
+ 'command' => 'cd .. && shog generate',
18
+ 'description' => 'Generate Build Script',
19
+ }
20
+ end
21
+
22
+ def target(params)
23
+ output = PathSet.new
24
+ output << Path.make('build.ninja', :outoftree => true, :root => true)
25
+ variables = {
26
+ 'generator' => '1',
27
+ }
28
+ input = PathSet.new(params[:input])
29
+ input += @deps
30
+ {:rule => 'generate_build', :input => input, :output => output, :variables => variables}
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../path'
2
+
3
+ module Shog
4
+ class Kconfig
5
+ def id
6
+ :kconfig
7
+ end
8
+
9
+ def initialize
10
+ @ldflags = []
11
+ end
12
+
13
+ def rule
14
+ {
15
+ 'command' => 'KCONFIG_CONFIG=$out conf --oldconfig -s $in',
16
+ 'description' => 'Generate .config file',
17
+ }
18
+ end
19
+
20
+ def target(params)
21
+ output = PathSet.new(Path.make(params[:output]))
22
+ input = PathSet.new(Path.make(params[:input]))
23
+ {:rule => 'kconfig', :input => input, :output => output}
24
+ end
25
+
26
+ def self.parse(file)
27
+ config = {}
28
+ for line in IO.readlines(file)
29
+ next if line.start_with?('#')
30
+ if line =~ /^CONFIG_(.*?)=(.*)$/
31
+ key = $1.to_sym
32
+ val = $2
33
+ case val
34
+ when "y" then val = true
35
+ when /^\d+$/ then val = val.to_i
36
+ when /^\"(.*)\"$/ then val = $1
37
+ end
38
+ config[key] = val
39
+ end
40
+ end
41
+ config
42
+ end
43
+ end
44
+ end
data/lib/rule/link.rb ADDED
@@ -0,0 +1,40 @@
1
+ require_relative '../path'
2
+
3
+ module Shog
4
+ class Link
5
+ attr_accessor :bin, :implicit_input
6
+
7
+ def initialize
8
+ @implicit_input = PathSet.new
9
+ end
10
+
11
+ def id
12
+ :ld
13
+ end
14
+
15
+ def rule
16
+ {
17
+ 'command' => '$bin $ldflags $in -o $out',
18
+ 'description' => 'Linking $out',
19
+ }
20
+ end
21
+
22
+ def target(params)
23
+ output = PathSet.make(Path.make(params[:output], :outoftree => true))
24
+ input = PathSet.make(params[:input])
25
+ ldflags = params[:ldflags]
26
+ if ldflags.nil?
27
+ ldflags = ''
28
+ elsif ldflags.is_a?(Array)
29
+ ldflags = ldflags.join(' ')
30
+ end
31
+ variables = {
32
+ 'ldflags' => ldflags,
33
+ 'bin' => params[:bin] || @bin || 'gcc',
34
+ }
35
+ implicit_input = @implicit_input.dup
36
+ implicit_input += params[:implicit_input] if params[:implicit_input]
37
+ {:rule => 'ld', :input => input, :implicit_input => implicit_input, :output => output, :variables => variables}
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,29 @@
1
+ require_relative '../path'
2
+
3
+ module Shog
4
+ class ObjCopy
5
+ attr_accessor :bin
6
+
7
+ def id
8
+ :objcopy
9
+ end
10
+
11
+ def rule
12
+ {
13
+ 'command' => '$bin -O $arch $in $out',
14
+ 'description' => 'Objcopy $out',
15
+ }
16
+ end
17
+
18
+ def target(params)
19
+ input = PathSet.make(params[:input])
20
+ output = PathSet.make(Path.make(params[:output], :outoftree => true))
21
+
22
+ variables = {
23
+ 'bin' => params[:bin] || @bin || 'objcopy',
24
+ }
25
+ variables['arch'] = params[:arch] if params[:arch]
26
+ {:rule => 'objcopy', :input => input, :output => output, :variables => variables}
27
+ end
28
+ end
29
+ end
data/lib/rule/yacc.rb ADDED
@@ -0,0 +1,22 @@
1
+ require_relative '../path'
2
+
3
+ module Shog
4
+ class Yacc
5
+ def id
6
+ :yacc
7
+ end
8
+
9
+ def rule
10
+ {
11
+ 'command' => 'yacc -o $out $in',
12
+ 'description' => 'Yacc $in',
13
+ }
14
+ end
15
+
16
+ def target(params)
17
+ input = PathSet.make(params[:input])
18
+ output = PathSet.make(Path.make(params[:output], :outoftree => true))
19
+ {:rule => 'yacc', :input => input, :output => output, :variables => {}}
20
+ end
21
+ end
22
+ end
data/lib/runner.rb ADDED
@@ -0,0 +1,34 @@
1
+ require_relative 'ninja'
2
+ require_relative 'generator'
3
+
4
+ module Shog
5
+ module Runner
6
+ WORKDIR = 'out'
7
+
8
+ def generate(backend)
9
+ Dir.mkdir(WORKDIR) unless Dir.exists?(WORKDIR)
10
+ gen = Generator.new(backend)
11
+ gen.generate
12
+ end
13
+
14
+ def run(argv)
15
+ unless File.exists?('shog.build')
16
+ puts "shog.build file is not found in #{Dir.pwd}"
17
+ exit 1
18
+ end
19
+
20
+ backend = Ninja.new
21
+
22
+ cmd = argv[0]
23
+ if cmd == 'generate'
24
+ generate(backend)
25
+ return
26
+ end
27
+
28
+ generate(backend) unless backend.configured?
29
+ backend.run
30
+ end
31
+
32
+ module_function :run, :generate
33
+ end
34
+ end
data/lib/util.rb ADDED
@@ -0,0 +1,6 @@
1
+ class Object
2
+ def deep_clone
3
+ # https://stackoverflow.com/questions/8206523/how-to-create-a-deep-copy-of-an-object-in-ruby
4
+ Marshal.load(Marshal.dump(self))
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shog-build
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Anatol Pomozov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '12.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '12.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.7'
41
+ description:
42
+ email:
43
+ - anatol.pomozov@gmail.com
44
+ executables:
45
+ - shog
46
+ extensions: []
47
+ extra_rdoc_files:
48
+ - README.md
49
+ - LICENSE.md
50
+ files:
51
+ - LICENSE.md
52
+ - README.md
53
+ - bin/shog
54
+ - lib/context.rb
55
+ - lib/generator.rb
56
+ - lib/ninja.rb
57
+ - lib/path.rb
58
+ - lib/pathset.rb
59
+ - lib/rule/cc.rb
60
+ - lib/rule/generate_build.rb
61
+ - lib/rule/kconfig.rb
62
+ - lib/rule/link.rb
63
+ - lib/rule/objcopy.rb
64
+ - lib/rule/yacc.rb
65
+ - lib/runner.rb
66
+ - lib/util.rb
67
+ homepage: https://github.com/anatol/shog
68
+ licenses:
69
+ - MIT
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '2.4'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 2.7.3
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: Ruby frontend for Ninja build system
91
+ test_files: []