bake 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.
- data/CONCEPTS +2 -0
- data/MIT-LICENSE +21 -0
- data/README +38 -0
- data/REFERENCE +2 -0
- data/TUTORIAL +133 -0
- data/bin/bake +10 -0
- data/lib/bake.rb +155 -0
- data/lib/bake/common_scheme.rb +42 -0
- data/lib/bake/configuration.rb +154 -0
- data/lib/bake/context.rb +101 -0
- data/lib/bake/cpp_scheme.rb +460 -0
- data/lib/bake/project_loader.rb +111 -0
- data/lib/bake/qt_scheme.rb +62 -0
- data/lib/bake/scheme.rb +104 -0
- data/lib/bake/scheme_loader.rb +28 -0
- data/lib/bake/string_utils.rb +19 -0
- data/lib/bake/target.rb +115 -0
- data/lib/bake/toolset.rb +11 -0
- data/lib/bake_version.rb +5 -0
- data/test/bake_test.rb +5 -0
- data/test/configuration_test.rb +102 -0
- data/test/context_test.rb +94 -0
- data/test/scheme_test.rb +121 -0
- data/test/target_test.rb +93 -0
- metadata +76 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'bake/target'
|
2
|
+
|
3
|
+
module Bake
|
4
|
+
class ProjectLoader
|
5
|
+
attr_reader :invok_project
|
6
|
+
|
7
|
+
def initialize(context, invok_dir, props)
|
8
|
+
@context = context
|
9
|
+
load_root_project(invok_dir, props)
|
10
|
+
load_invok_project
|
11
|
+
end
|
12
|
+
|
13
|
+
def load_project(name, from_proj)
|
14
|
+
proj = from_proj.child(name, Project)
|
15
|
+
if !proj
|
16
|
+
dir = File.join(from_proj[:cwdir], name)
|
17
|
+
raise "no such directory #{dir}" if !File.exists?(dir)
|
18
|
+
proj = Project.new(from_proj)
|
19
|
+
proj.loader = self
|
20
|
+
proj.name = name
|
21
|
+
proj.opt(:projname => name)
|
22
|
+
proj.opt(:cwdir => dir)
|
23
|
+
process(proj)
|
24
|
+
end
|
25
|
+
return proj
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def load_root_project(invok_dir, props)
|
30
|
+
project = nil
|
31
|
+
dir = invok_dir
|
32
|
+
@invok_path = []
|
33
|
+
while true
|
34
|
+
bakefile = File.join(dir, 'root.bake')
|
35
|
+
if File.exists?(bakefile)
|
36
|
+
project = Project.new(nil)
|
37
|
+
project.loader = self
|
38
|
+
project.req(:rootdir => dir)
|
39
|
+
project.opt(:projname => 'root')
|
40
|
+
project.opt(:cwdir => dir)
|
41
|
+
project.opt(:outdir => '${cwdir}')
|
42
|
+
props.each_pair { |key, val| project.opt(key => val) }
|
43
|
+
break
|
44
|
+
end
|
45
|
+
|
46
|
+
parent_dir = File.dirname(dir)
|
47
|
+
raise 'root.bake not found' if parent_dir == dir
|
48
|
+
@invok_path << File.basename(dir)
|
49
|
+
dir = parent_dir
|
50
|
+
end
|
51
|
+
|
52
|
+
@root_project = project
|
53
|
+
process(@root_project)
|
54
|
+
end
|
55
|
+
|
56
|
+
def load_invok_project
|
57
|
+
project = @root_project
|
58
|
+
@invok_path.reverse_each do |name|
|
59
|
+
project = load_project(name, project)
|
60
|
+
end
|
61
|
+
@invok_project = project
|
62
|
+
return project
|
63
|
+
end
|
64
|
+
|
65
|
+
def process(proj)
|
66
|
+
if proj.parent
|
67
|
+
dir = proj[:cwdir]
|
68
|
+
bakefile = File.join(dir, 'sub.bake')
|
69
|
+
if File.exists?(bakefile)
|
70
|
+
Dir.chdir(dir) { eval_bakefile(proj, bakefile) }
|
71
|
+
end
|
72
|
+
else
|
73
|
+
raise 'internal error' if !@root_project.equal?(proj)
|
74
|
+
Dir.chdir(@root_project[:cwdir]) do
|
75
|
+
if config_bakefile
|
76
|
+
eval_bakefile(@root_project, config_bakefile)
|
77
|
+
end
|
78
|
+
eval_bakefile(@root_project, root_bakefile)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def eval_bakefile(project, bakefile)
|
84
|
+
@context.context_eval(project, File.read(bakefile), bakefile)
|
85
|
+
end
|
86
|
+
|
87
|
+
def root_bakefile
|
88
|
+
return @root_bakefile if @root_bakefile
|
89
|
+
@root_bakefile = File.join(@root_project[:cwdir], 'root.bake')
|
90
|
+
return @root_bakefile
|
91
|
+
end
|
92
|
+
|
93
|
+
def config_bakefile
|
94
|
+
return @config_bakefile if @config_bakefile
|
95
|
+
bakefile = File.join(@root_project[:cwdir], 'config.bake')
|
96
|
+
if !File.exists?(bakefile)
|
97
|
+
home = ENV['HOME']
|
98
|
+
if home
|
99
|
+
bakefile = File.join(home, 'config.bake')
|
100
|
+
if !File.exists?(bakefile)
|
101
|
+
bakefile = nil
|
102
|
+
end
|
103
|
+
else
|
104
|
+
bakefile = nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
return @config_bakefile = bakefile
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'bake/toolset'
|
2
|
+
require 'bake/target'
|
3
|
+
require 'bake/cpp_scheme.rb'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module Qt
|
7
|
+
class Moc < Bake::Toolset
|
8
|
+
def build(target)
|
9
|
+
raise "unknown target '#{target}'" if !target.is_a?(MocFile)
|
10
|
+
return if !stale?(target)
|
11
|
+
src = target.src
|
12
|
+
sh("moc -o #{target.output} #{target.src}")
|
13
|
+
end
|
14
|
+
|
15
|
+
def clean(target)
|
16
|
+
FileUtils.rm_f(target.output)
|
17
|
+
end
|
18
|
+
|
19
|
+
def stale?(target)
|
20
|
+
return true if !File.exists?(target.output)
|
21
|
+
return File.mtime(target.src) > File.mtime(target.output)
|
22
|
+
end
|
23
|
+
|
24
|
+
def sh(*args)
|
25
|
+
cmd = args.join(' ')
|
26
|
+
puts cmd
|
27
|
+
system(*args)
|
28
|
+
raise "error: #{cmd}" if !$? || !$?.success?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class MocFile < Bake::Target
|
33
|
+
attr_reader :output
|
34
|
+
attr_accessor :src
|
35
|
+
|
36
|
+
def initialize(parent, src)
|
37
|
+
super(parent)
|
38
|
+
ext = File.extname(src)
|
39
|
+
@output = File.basename(src, ext) + '.moc' + ext
|
40
|
+
@src = src
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module Cpp
|
46
|
+
class Library
|
47
|
+
def moc(*args)
|
48
|
+
return args.flatten.collect do |file|
|
49
|
+
Qt::MocFile.new(Cpp::Object.new(self), file)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Executable
|
55
|
+
def moc(*args)
|
56
|
+
return args.flatten.collect do |file|
|
57
|
+
Qt::MocFile.new(Cpp::Object.new(self), file)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
data/lib/bake/scheme.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'bake/target'
|
2
|
+
require 'bake/toolset'
|
3
|
+
|
4
|
+
module Bake
|
5
|
+
class Scheme
|
6
|
+
attr_reader :name, :targets, :toolsets, :toolset
|
7
|
+
|
8
|
+
def initialize(mod)
|
9
|
+
@name = underscore(mod.name)
|
10
|
+
@module = mod
|
11
|
+
@targets = []
|
12
|
+
@toolsets = []
|
13
|
+
populate_targets_and_toolsets
|
14
|
+
raise "no toolsets in scheme '#{@name}'" if @toolsets.size == 0
|
15
|
+
raise "no targets in scheme '#{@name}'" if @targets.size == 0
|
16
|
+
@accessors = {}
|
17
|
+
populate_accessors
|
18
|
+
end
|
19
|
+
|
20
|
+
def toolset=(toolset)
|
21
|
+
if !toolset.is_a?(Toolset)
|
22
|
+
ts = toolset_class(toolset.to_s)
|
23
|
+
raise "could not find toolset '#{toolset}'" if !ts
|
24
|
+
@toolset = ts.new
|
25
|
+
return
|
26
|
+
end
|
27
|
+
if !@toolsets.include?(toolset.class)
|
28
|
+
raise "toolset #{toolset.class.name} is not in scheme #{name}"
|
29
|
+
end
|
30
|
+
@toolset = toolset
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_constructor?(accessor)
|
34
|
+
return accessors.has_key?(accessor)
|
35
|
+
end
|
36
|
+
|
37
|
+
def construct(type, *args)
|
38
|
+
if type.instance_of?(Class)
|
39
|
+
target_class = type
|
40
|
+
raise "#{type} not in #{name}" if !targets.include?(type)
|
41
|
+
else
|
42
|
+
target_class = accessors[type]
|
43
|
+
raise "no such constructor #{type}" if !target_class
|
44
|
+
end
|
45
|
+
target = target_class.new(*args)
|
46
|
+
return target
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
attr_reader :accessors
|
51
|
+
|
52
|
+
def underscore(str)
|
53
|
+
str = str.gsub(/[A-Z][a-z0-9]*/) { |val| val.downcase + '_' }.chop
|
54
|
+
return str.gsub('_::', '/')
|
55
|
+
end
|
56
|
+
|
57
|
+
def capitalize(str)
|
58
|
+
str = str.gsub(/[a-z0-9]+/) { |val| val.capitalize }
|
59
|
+
return str.gsub('_', '')
|
60
|
+
end
|
61
|
+
|
62
|
+
def toolset_class(name)
|
63
|
+
name = @module.name + '::' + capitalize(name)
|
64
|
+
return @toolsets.find { |t| t.name == name }
|
65
|
+
end
|
66
|
+
|
67
|
+
def populate_targets_and_toolsets
|
68
|
+
@module.constants.each do |const|
|
69
|
+
klazz = @module.const_get(const.to_sym)
|
70
|
+
if klazz.instance_of?(Class)
|
71
|
+
sup = klazz.superclass
|
72
|
+
while sup
|
73
|
+
if sup.equal?(Toolset)
|
74
|
+
@toolsets << klazz
|
75
|
+
break
|
76
|
+
elsif sup.equal?(Target)
|
77
|
+
@targets << klazz
|
78
|
+
break
|
79
|
+
end
|
80
|
+
sup = sup.superclass
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def populate_accessors
|
87
|
+
@targets.each do |target|
|
88
|
+
if target.const_defined?(:ACCESSORS)
|
89
|
+
accessors = target.const_get(:ACCESSORS)
|
90
|
+
if accessors.respond_to?(:to_ary)
|
91
|
+
accessors = accessors.to_ary
|
92
|
+
else
|
93
|
+
accessors = [ accessors ]
|
94
|
+
end
|
95
|
+
accessors.each { |accessor| @accessors[accessor] = target }
|
96
|
+
end
|
97
|
+
if !target.const_defined?(:SCHEME)
|
98
|
+
target.const_set(:SCHEME, self)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'bake/scheme'
|
2
|
+
|
3
|
+
module Bake
|
4
|
+
class SchemeLoader
|
5
|
+
attr_reader :schemes
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@schemes = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def scheme(name)
|
12
|
+
scheme = @schemes[name]
|
13
|
+
return scheme if scheme
|
14
|
+
return load_scheme(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_scheme(name)
|
18
|
+
file = File.join(File.dirname(__FILE__), "#{name}_scheme.rb")
|
19
|
+
raise "unknown scheme '#{name}'" if !File.exists?(file)
|
20
|
+
require "bake/#{name}_scheme"
|
21
|
+
mod = Bake.const_get(name.capitalize.to_sym)
|
22
|
+
scheme = Scheme.new(mod)
|
23
|
+
@schemes[scheme.name] = scheme
|
24
|
+
return scheme
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class String
|
2
|
+
def basename(options = {})
|
3
|
+
if options[:ext]
|
4
|
+
return File.basename(self, extname) + options[:ext]
|
5
|
+
elsif options[:noext]
|
6
|
+
return File.basename(self, extname)
|
7
|
+
end
|
8
|
+
return File.basename(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
def dirname
|
12
|
+
return File.dirname(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def extname
|
16
|
+
return File.extname(self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
data/lib/bake/target.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'bake/configuration'
|
2
|
+
|
3
|
+
module Bake
|
4
|
+
class Target
|
5
|
+
include Configuration
|
6
|
+
|
7
|
+
attr_reader :parent, :children, :deps
|
8
|
+
attr_accessor :name
|
9
|
+
|
10
|
+
def initialize(parent)
|
11
|
+
@parent = parent
|
12
|
+
@children = []
|
13
|
+
@deps = []
|
14
|
+
parent.children << self if parent
|
15
|
+
opt(:built? => false)
|
16
|
+
end
|
17
|
+
|
18
|
+
def child(name, klazz = nil)
|
19
|
+
if klazz
|
20
|
+
return children.find do |child|
|
21
|
+
child.name == name && child.is_a?(klazz)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
return children.find { |child| child.name == name }
|
25
|
+
end
|
26
|
+
|
27
|
+
def find(name)
|
28
|
+
target = children.find do |child|
|
29
|
+
!child.is_a?(Project) && child.name == name
|
30
|
+
end
|
31
|
+
target ||= parent.find(name) if parent
|
32
|
+
return target
|
33
|
+
end
|
34
|
+
|
35
|
+
def dep(*targets)
|
36
|
+
ret = []
|
37
|
+
targets.each do |target|
|
38
|
+
if !target.is_a?(Target)
|
39
|
+
real_target = parent.find(target) if parent
|
40
|
+
raise "uknown target '#{target}'" if !real_target
|
41
|
+
target = real_target
|
42
|
+
end
|
43
|
+
deps << target
|
44
|
+
ret << target
|
45
|
+
end
|
46
|
+
return ret
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s(level = 0)
|
50
|
+
str = (' ' * level) + (@name || '<empty>') +
|
51
|
+
' (' + self.class.name + ')'
|
52
|
+
if !deps.empty?
|
53
|
+
str += ' => '
|
54
|
+
deps.each do |target|
|
55
|
+
str += target.name + ' (' + target.class.name + ') '
|
56
|
+
end
|
57
|
+
end
|
58
|
+
str += "\n"
|
59
|
+
if !children.empty?
|
60
|
+
children.each { |target| str += target.to_s(level + 1) }
|
61
|
+
end
|
62
|
+
return str
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Project < Target
|
67
|
+
SCHEME = nil
|
68
|
+
|
69
|
+
attr_accessor :loader
|
70
|
+
|
71
|
+
def initialize(parent)
|
72
|
+
super(parent)
|
73
|
+
@mappings = {}
|
74
|
+
end
|
75
|
+
|
76
|
+
def map(mappings)
|
77
|
+
mappings.each do |name, loc|
|
78
|
+
raise "target '#{name}' already mapped" if @mappings[name]
|
79
|
+
@mappings[name] = loc
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def mapping(name)
|
84
|
+
target = nil
|
85
|
+
mapping = @mappings[name]
|
86
|
+
if mapping
|
87
|
+
if mapping.instance_of?(String)
|
88
|
+
path = @mappings[name].split('/')
|
89
|
+
proj = self
|
90
|
+
path.each do |dir|
|
91
|
+
proj = loader.load_project(dir, proj)
|
92
|
+
end
|
93
|
+
target = proj.child(name)
|
94
|
+
@mappings[name] = target
|
95
|
+
raise "invalid mapping #{@mappings[name]}" if !target
|
96
|
+
elsif mapping.is_a?(Target)
|
97
|
+
target = mapping
|
98
|
+
else
|
99
|
+
raise "mapping has invalid class '#{mapping.class.name}'"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
return target
|
103
|
+
end
|
104
|
+
|
105
|
+
def find(name)
|
106
|
+
target = mapping(name)
|
107
|
+
target ||= children.find do |child|
|
108
|
+
!child.is_a?(Project) && child.name == name
|
109
|
+
end
|
110
|
+
target ||= parent.find(name) if parent
|
111
|
+
return target
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|