bake 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|