shog-build 0.1.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.
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: []