bake 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,98 @@
1
+ require 'bake/toolset'
2
+ require 'fileutils'
3
+ require 'set'
4
+
5
+ module Bake
6
+ module Plugins
7
+ module Cpp
8
+ class ToolsetBase
9
+ include Toolset
10
+
11
+ def initialize
12
+ constructor :syslib, SystemLibrary
13
+ constructor :lib, Library
14
+ constructor :exe, Executable
15
+ constructor :run, Runner
16
+ end
17
+
18
+ def build(target)
19
+ if stale?(target)
20
+ FileUtils.mkdir_p(target[:outdir])
21
+ if target.is_a?(Cpp::Object)
22
+ compile(target)
23
+ # refresh the .includes file
24
+ includes = target.includes
25
+ filename = "#{target[:outdir]}/#{target.src.basename}.includes"
26
+ File.open(filename, "w") do |file|
27
+ includes.each { |inc| file.puts(inc) }
28
+ end
29
+ elsif target.is_a?(Cpp::Library)
30
+ ar(target)
31
+ elsif target.is_a?(Cpp::Executable)
32
+ link(target)
33
+ elsif target.is_a?(Cpp::Runner)
34
+ run(target)
35
+ else
36
+ raise "unknown Cpp target of class '#{target.class}"
37
+ end
38
+ end
39
+ end
40
+
41
+ def stale?(target)
42
+ t = Time.now
43
+ output_files(target).each do |out|
44
+ return true if !File.exists?(out)
45
+ out_time = File.mtime(out)
46
+ t = out_time if out_time < t
47
+ end
48
+ if target.is_a?(Cpp::Object)
49
+ return true if File.mtime(target.src) > t
50
+ target.includes.each { |file| return true if File.mtime(file) > t }
51
+ elsif target.is_a?(Cpp::Runner)
52
+ return File.mtime(output_files(target.deps[0])[0]) > t
53
+ else
54
+ is_linked = target.is_a?(Cpp::Executable) ||
55
+ (target.is_a?(Cpp::Library) && target[:libtype] == 'dynamic')
56
+ target.children.each do |child|
57
+ output_files(child).each do |file|
58
+ return true if File.mtime(file) > t
59
+ end
60
+ end
61
+ target.deps.each do |dep|
62
+ if is_linked && dep.is_a?(Cpp::Library) &&
63
+ dep[:libtype] != 'dynamic'
64
+ output_files(dep).each do |file|
65
+ return true if File.mtime(file) > t
66
+ end
67
+ end
68
+ end
69
+ end
70
+ return false
71
+ end
72
+
73
+ def clean(target)
74
+ output_files(target).each { |file| FileUtils.rm_f(file) }
75
+ if target.is_a?(Cpp::Object)
76
+ filename = "#{target[:outdir]}/#{target.src.basename}.includes"
77
+ FileUtils.rm_f(filename)
78
+ end
79
+ end
80
+
81
+ def output_files(target)
82
+ out = output(target)
83
+ return [] if !out
84
+ return out.to_a if out.respond_to?(:to_a)
85
+ return [ out ]
86
+ end
87
+
88
+ def sh(*args)
89
+ cmd = args.join(' ')
90
+ puts cmd
91
+ system(*args)
92
+ raise "error: #{cmd}" if !$? || !$?.success?
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
@@ -0,0 +1,18 @@
1
+ require 'bake/addon'
2
+
3
+ module Bake
4
+ module Plugins
5
+ class Macro
6
+ include Addon
7
+
8
+ def initialize
9
+ command(:macro)
10
+ end
11
+
12
+ def macro(context, name, &block)
13
+ context.register(name, &block)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,46 @@
1
+ require 'bake/addon'
2
+
3
+ module Bake
4
+ module Plugins
5
+ class System
6
+ include Addon
7
+
8
+ def initialize
9
+ command(:glob)
10
+ end
11
+
12
+ def glob(context, *args)
13
+ has_options = args.last.respond_to?(:to_hash)
14
+ options = has_options ? args.pop.to_hash : {}
15
+ exclude = array_opt(options, :exclude)
16
+ files = []
17
+ args.each do |pat|
18
+ matches = Dir[pat]
19
+ matches.each do |file|
20
+ if !exclude.find { |exc| File.fnmatch(exc, file) }
21
+ files.push(file)
22
+ end
23
+ end
24
+ end
25
+ if block_given?
26
+ for i in 0...files.size
27
+ yield(files[i])
28
+ end
29
+ end
30
+ return files
31
+ end
32
+
33
+ private
34
+ def array_opt(options, name)
35
+ opt = options[name]
36
+ return [] if !opt
37
+ if opt.respond_to?(:to_ary)
38
+ opt = opt.to_ary
39
+ else
40
+ opt = [ opt ]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,50 @@
1
+ require 'bake/target'
2
+
3
+ module Bake
4
+ class Project < Target
5
+ attr_accessor :loader
6
+
7
+ def initialize(parent, loader)
8
+ super(parent, nil)
9
+ @loader = loader
10
+ end
11
+
12
+ def current_project
13
+ return self
14
+ end
15
+
16
+ def parent_project
17
+ return parent ? parent.current_project : nil
18
+ end
19
+
20
+ def child_project(name)
21
+ child = children.find do |child|
22
+ child.name == name && child.is_a?(Project)
23
+ end
24
+ return child || loader.load_project(self, name)
25
+ end
26
+
27
+ def resolve(path)
28
+ index = path.index(':')
29
+ raise "invalid path '#{path}'" if !index
30
+ dir = path.slice(0, index)
31
+ name = path.slice(index + 1, path.length - index - 1)
32
+ proj = self
33
+ dir.split('/').each do |el|
34
+ if el == '.'
35
+ next
36
+ elsif el == '..'
37
+ proj = proj.parent_project
38
+ raise "invalid target path '#{path}'" if !proj
39
+ else
40
+ proj = proj.child_project(el)
41
+ raise "invalid target path '#{path}'" if !proj
42
+ end
43
+ end
44
+ target = proj.children.find { |child| child.name == name }
45
+ raise "invalid target path '#{path}'" if !target
46
+ return target
47
+ end
48
+ end
49
+ end
50
+
@@ -1,4 +1,5 @@
1
1
  require 'bake/target'
2
+ require 'bake/project'
2
3
 
3
4
  module Bake
4
5
  class ProjectLoader
@@ -10,18 +11,14 @@ module Bake
10
11
  load_invok_project
11
12
  end
12
13
 
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
14
+ def load_project(parent, name)
15
+ dir = File.join(parent[:cwdir], name)
16
+ raise "no such directory '#{dir}'" if !File.directory?(dir)
17
+ proj = Project.new(parent, self)
18
+ proj.name = name
19
+ proj.opt(:projname => name)
20
+ proj.opt(:cwdir => dir)
21
+ process(proj)
25
22
  return proj
26
23
  end
27
24
 
@@ -33,8 +30,7 @@ module Bake
33
30
  while true
34
31
  bakefile = File.join(dir, 'root.bake')
35
32
  if File.exists?(bakefile)
36
- project = Project.new(nil)
37
- project.loader = self
33
+ project = Project.new(nil, self)
38
34
  project.req(:rootdir => dir)
39
35
  project.opt(:projname => 'root')
40
36
  project.opt(:cwdir => dir)
@@ -56,7 +52,7 @@ module Bake
56
52
  def load_invok_project
57
53
  project = @root_project
58
54
  @invok_path.reverse_each do |name|
59
- project = load_project(name, project)
55
+ project = load_project(project, name)
60
56
  end
61
57
  @invok_project = project
62
58
  return project
@@ -15,5 +15,9 @@ class String
15
15
  def extname
16
16
  return File.extname(self)
17
17
  end
18
+
19
+ def underscore
20
+ return gsub(/([a-z0-9])([A-Z])/) { |val| $1 + '_' + $2 }.downcase
21
+ end
18
22
  end
19
23
 
@@ -1,115 +1,78 @@
1
1
  require 'bake/configuration'
2
+ require 'bake/string_utils'
2
3
 
3
4
  module Bake
4
5
  class Target
5
6
  include Configuration
6
7
 
7
- attr_reader :parent, :children, :deps
8
+ attr_reader :parent, :children, :deps, :toolset
8
9
  attr_accessor :name
9
10
 
10
- def initialize(parent)
11
+ def initialize(parent, toolset)
11
12
  @parent = parent
13
+ @toolset = toolset
12
14
  @children = []
13
15
  @deps = []
14
16
  parent.children << self if parent
15
- opt(:built? => false)
16
- end
17
17
 
18
- def child(name, klazz = nil)
19
- if klazz
20
- return children.find do |child|
21
- child.name == name && child.is_a?(klazz)
18
+ if toolset
19
+ mixin_name = self.class.name.split('::').last
20
+ toolset.class
21
+ if toolset.class.const_defined?(mixin_name)
22
+ extend(toolset.class.const_get(mixin_name))
22
23
  end
23
24
  end
24
- return children.find { |child| child.name == name }
25
+ post_initialize if respond_to?(:post_initialize)
25
26
  end
26
27
 
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
28
+ def build
29
+ return if @built
30
+ deps.each { |dep| dep.build }
31
+ children.each { |child| child.build }
32
+ if toolset
33
+ Dir.chdir(get(:cwdir)) do
34
+ toolset.build(self)
42
35
  end
43
- deps << target
44
- ret << target
45
36
  end
46
- return ret
37
+ @built = true
47
38
  end
48
39
 
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
40
+ def clean
41
+ children.each { |child| child.clean }
42
+ toolset.clean(self) if toolset
63
43
  end
64
- end
65
44
 
66
- class Project < Target
67
- SCHEME = nil
68
-
69
- attr_accessor :loader
45
+ def dep(*targets)
46
+ targets = normalize_targets(*targets)
47
+ targets.each { |target| add_dep(target) }
48
+ end
70
49
 
71
- def initialize(parent)
72
- super(parent)
73
- @mappings = {}
50
+ def add_dep(target)
51
+ deps << target
74
52
  end
75
53
 
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
54
+ def current_project
55
+ return parent.current_project
81
56
  end
82
57
 
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)
58
+ private
59
+ def normalize_targets(*targets)
60
+ normalized_targets = []
61
+ targets.flatten.each do |target|
62
+ if !target.is_a?(Target)
63
+ if File.exists?(target)
64
+ target = FileTarget.new(self, target)
65
+ else
66
+ target = current_project.resolve(target)
92
67
  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
68
  end
69
+ normalized_targets << target
101
70
  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
71
+ return normalized_targets
112
72
  end
113
73
  end
114
74
  end
115
75
 
76
+ require 'bake/file_target'
77
+ require 'bake/project'
78
+
@@ -1,10 +1,17 @@
1
+ require 'bake/plugin'
2
+
1
3
  module Bake
2
- class Toolset
3
- def sh(*args)
4
- cmd = args.join(' ')
5
- puts cmd
6
- system(*args)
7
- raise "error: #{cmd}" if !$? || !$?.success?
4
+ module Toolset
5
+ include Addon
6
+
7
+ def constructor(name, target_class)
8
+ toolset = self
9
+ command(name) do |*args|
10
+ block = args.last.is_a?(Proc) ? args.pop : nil
11
+ target = target_class.new(current, toolset, *args)
12
+ context_eval(target, &block) if block
13
+ return target
14
+ end
8
15
  end
9
16
  end
10
17
  end