teapot 0.3.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,69 @@
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'pathname'
22
+
23
+ module Teapot
24
+ module Build
25
+ module Targets
26
+ module Compiler
27
+ def build_prefix!(environment)
28
+ build_prefix = Pathname.new(environment[:build_prefix]) + "compiled"
29
+
30
+ build_prefix.mkpath
31
+
32
+ return build_prefix
33
+ end
34
+
35
+ def link_prefix!(environment)
36
+ prefix = Pathname.new(environment[:build_prefix]) + "linked"
37
+
38
+ prefix.mkpath
39
+
40
+ return prefix
41
+ end
42
+
43
+ def compile(environment, root, source_file, commands)
44
+ object_file = (build_prefix!(environment) + source_file).sub_ext('.o')
45
+
46
+ # Ensure there is a directory for the output file:
47
+ object_file.dirname.mkpath
48
+
49
+ case source_file.extname
50
+ when ".cpp", ".mm"
51
+ commands.run(
52
+ environment[:cxx],
53
+ environment[:cxxflags],
54
+ "-c", root + source_file, "-o", object_file
55
+ )
56
+ when ".c", ".m"
57
+ commands.run(
58
+ environment[:cc],
59
+ environment[:cflags],
60
+ "-c", root + source_file, "-o", object_file
61
+ )
62
+ end
63
+
64
+ return Array object_file
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,63 @@
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'teapot/build/target'
22
+
23
+ module Teapot
24
+ module Build
25
+ module Targets
26
+ class Directory < Build::Target
27
+ BUILD_FILE = "build.rb"
28
+
29
+ def initialize(parent, root = nil)
30
+ super parent
31
+
32
+ @root = root
33
+ @targets = []
34
+ end
35
+
36
+ def root
37
+ @root || @parent.root
38
+ end
39
+
40
+ attr :tasks
41
+
42
+ def << (target)
43
+ @targets << target
44
+ end
45
+
46
+ def add_directory(path)
47
+ directory = Directory.target(self, @root + path)
48
+
49
+ build_path = (directory.root + BUILD_FILE).to_s
50
+ directory.instance_eval(File.read(build_path), build_path)
51
+
52
+ self << directory
53
+ end
54
+
55
+ def execute(command, *arguments)
56
+ @targets.each do |target|
57
+ target.execute(command, *arguments)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,52 @@
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'teapot/build/targets/library'
22
+
23
+ module Teapot
24
+ module Build
25
+ module Targets
26
+ class Executable < Library
27
+ def subdirectory
28
+ options[:subdirectory] || "bin"
29
+ end
30
+
31
+ def link(environment, objects)
32
+ executable_file = link_prefix!(environment) + @name
33
+
34
+ Commands.run(
35
+ environment[:cxx],
36
+ environment[:cxxflags],
37
+ "-o", executable_file, objects,
38
+ environment[:ldflags]
39
+ )
40
+
41
+ return executable_file
42
+ end
43
+ end
44
+
45
+ class Directory
46
+ def compile_executable(*args, &block)
47
+ self << Executable.target(self, *args, &block)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,82 @@
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'teapot/build/target'
22
+ require 'teapot/build/targets/directory'
23
+ require 'teapot/build/targets/compiler'
24
+
25
+ require 'fileutils'
26
+
27
+ module Teapot
28
+ module Build
29
+ module Targets
30
+ module Installation
31
+ def install_prefix!(environment)
32
+ install_prefix = Pathname.new(environment[:install_prefix])
33
+
34
+ install_prefix.mkpath
35
+
36
+ return install_prefix
37
+ end
38
+ end
39
+
40
+ class Files < Build::Target
41
+ include Installation
42
+
43
+ def initialize(parent, options = {})
44
+ super parent
45
+ @options = options
46
+ end
47
+
48
+ attr :options
49
+
50
+ def subdirectory
51
+ options[:subdirectory] || "./"
52
+ end
53
+
54
+ def install(environment)
55
+ prefix = install_prefix!(environment)
56
+
57
+ if self.respond_to? :source_files
58
+ file_list = self.source_files(environment)
59
+
60
+ file_list.copy(prefix + subdirectory)
61
+ end
62
+ end
63
+ end
64
+
65
+ class Headers < Files
66
+ def subdirectory
67
+ super + "include/"
68
+ end
69
+ end
70
+
71
+ class Directory
72
+ def copy_files(*args, &block)
73
+ self << Files.target(self, *args, &block)
74
+ end
75
+
76
+ def copy_headers(*args, &block)
77
+ self << Headers.target(self, *args, &block)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,106 @@
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'teapot/build/target'
22
+ require 'teapot/build/targets/directory'
23
+ require 'teapot/build/targets/files'
24
+ require 'teapot/build/targets/compiler'
25
+
26
+ require 'fileutils'
27
+
28
+ module Teapot
29
+ module Build
30
+ module Targets
31
+ class Library < Build::Target
32
+ include Compiler
33
+ include Installation
34
+
35
+ def initialize(parent, name, options = {})
36
+ super parent
37
+
38
+ @name = name
39
+ @options = options
40
+ end
41
+
42
+ attr :options
43
+
44
+ def subdirectory
45
+ options[:subdirectory] || "lib"
46
+ end
47
+
48
+ def link(environment, objects)
49
+ library_file = link_prefix!(environment) + "lib#{@name}.a"
50
+
51
+ Linker.link_static(environment, library_file, objects)
52
+
53
+ return library_file
54
+ end
55
+
56
+ def build(environment)
57
+ file_list = self.source_files(environment)
58
+
59
+ pool = Commands::Pool.new
60
+
61
+ objects = file_list.collect do |source_file|
62
+ relative_path = source_file.relative_path_from(file_list.root)
63
+
64
+ compile(environment, file_list.root, relative_path, pool)
65
+ end
66
+
67
+ pool.wait
68
+
69
+ return Array link(environment, objects)
70
+ end
71
+
72
+ def install_file_list(file_list, prefix)
73
+ file_list.each do |path|
74
+ relative_path = path.relative_path_from(file_list.root)
75
+ destination_path = prefix + file_list.prefix + relative_path
76
+
77
+ destination_path.dirname.mkpath
78
+ FileUtils.cp path, destination_path
79
+ end
80
+ end
81
+
82
+ def install(environment)
83
+ prefix = install_prefix!(environment)
84
+
85
+ build(environment).each do |path|
86
+ destination_path = prefix + subdirectory + path.basename
87
+
88
+ destination_path.dirname.mkpath
89
+
90
+ FileUtils.cp path, destination_path
91
+ end
92
+
93
+ if self.respond_to? :headers
94
+ install_file_list(self.headers(environment), prefix + "include")
95
+ end
96
+ end
97
+ end
98
+
99
+ class Directory
100
+ def compile_library(*args, &block)
101
+ self << Library.target(self, *args, &block)
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
data/lib/teapot/build.rb CHANGED
@@ -18,227 +18,24 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'teapot/commands'
22
- require 'teapot/environment'
23
-
24
- require 'pathname'
25
- require 'rainbow'
26
- require 'shellwords'
27
-
28
- require 'teapot/build/linker'
21
+ require 'teapot/build/targets/directory'
22
+ require 'teapot/build/targets/files'
23
+ require 'teapot/build/targets/library'
24
+ require 'teapot/build/targets/executable'
25
+ require 'teapot/build/targets/application'
29
26
 
30
27
  module Teapot
31
28
  module Build
32
- class UnsupportedPlatform < StandardError
33
- end
34
-
35
- class Task
36
- def initialize(inputs, outputs)
37
- @inputs = inputs
38
- @outputs = outputs
39
- end
40
- end
41
-
42
- class Target
43
- def initialize(parent)
44
- @parent = parent
45
- @tasks = []
46
-
47
- @configure = nil
48
- end
49
-
50
- def root
51
- @parent.root
52
- end
53
-
54
- def configure(&block)
55
- @configure = Proc.new &block
56
- end
57
-
58
- def self.target(*args, &block)
59
- instance = self.new(*args)
60
-
61
- if block_given?
62
- instance.instance_eval(&block)
63
- end
64
-
65
- return instance
66
- end
67
-
68
- def execute(command, environment, *arguments)
69
- if @configure
70
- environment = environment.merge &@configure
71
- end
72
-
73
- # Flatten the environment to a hash:
74
- values = environment.flatten
75
-
76
- puts "Executing command #{command} for #{root}...".color(:cyan)
77
-
78
- # Show the environment to the user:
79
- Environment::System::dump(values)
80
-
81
- self.send(command, values, *arguments)
82
- end
83
- end
84
-
85
- class CompilerTarget < Target
86
- def initialize(parent, name, options = {})
87
- super parent
88
-
89
- @name = name
90
- @options = options
91
- end
92
-
93
- def build_prefix!(environment)
94
- build_prefix = Pathname.new(environment[:build_prefix]) + @name
95
-
96
- build_prefix.mkpath
97
-
98
- return build_prefix
99
- end
100
-
101
- def install_prefix!(environment)
102
- install_prefix = Pathname.new(environment[:install_prefix])
103
-
104
- install_prefix.mkpath
105
-
106
- return install_prefix
107
- end
108
-
109
- def compile(environment, source_file)
110
- object_file = (build_prefix!(environment) + source_file.basename).sub_ext('.o')
111
-
112
- case source_file.extname
113
- when ".cpp"
114
- Commands.run(
115
- environment[:cxx],
116
- environment[:cxxflags],
117
- "-c", source_file, "-o", object_file
118
- )
119
- when ".c"
120
- Commands.run(
121
- environment[:cc],
122
- environment[:cflags],
123
- "-c", source_file, "-o", object_file
124
- )
125
- end
126
-
127
- return Array object_file
128
- end
129
- end
130
-
131
- class Library < CompilerTarget
132
- def subdirectory
133
- "lib"
134
- end
135
-
136
- def link(environment, objects)
137
- library_file = build_prefix!(environment) + "lib#{@name}.a"
138
-
139
- Linker.link_static(environment, library_file, objects)
140
-
141
- return library_file
142
- end
143
-
144
- def build(environment)
145
- files = sources
146
-
147
- objects = sources.collect do |file|
148
- compile(environment, file)
149
- end
150
-
151
- return Array link(environment, objects)
152
- end
153
-
154
- def install(environment)
155
- prefix = install_prefix!(environment)
156
-
157
- build(environment).each do |path|
158
- destination_path = prefix + subdirectory + path.basename
159
-
160
- destination_path.dirname.mkpath
161
- FileUtils.cp path, destination_path
162
- end
163
-
164
- if defined? headers
165
- headers.each do |path|
166
- relative_path = path.relative_path_from(root)
167
- destination_path = prefix + "include" + relative_path
168
-
169
- destination_path.dirname.mkpath
170
- FileUtils.cp path, destination_path
171
- end
172
- end
173
-
174
- if defined? files
175
- files.each do |path|
176
- relative_path = path.relative_path_from(root)
177
- destination_path = prefix + relative_path
178
-
179
- destination_path.dirname.mkpath
180
- FileUtils.cp path, destination_path
181
- end
182
- end
183
- end
184
- end
185
-
186
- class Executable < Library
187
- def subdirectory
188
- "bin"
189
- end
190
-
191
- def link(environment, objects)
192
- executable_file = build_prefix!(environment) + "#{@name}"
193
-
194
- Commands.run(
195
- environment[:cxx],
196
- environment[:cxxflags],
197
- "-o", executable_file, objects,
198
- environment[:ldflags]
199
- )
200
-
201
- return executable_file
202
- end
29
+ def self.top(path)
30
+ Targets::Directory.target(nil, path)
203
31
  end
204
-
205
- class Directory < Target
206
- def initialize(parent, root)
207
- @root = root
208
- @targets = []
209
- end
210
32
 
211
- attr :root
212
- attr :tasks
33
+ def self.install_directory(root, directory, *args)
34
+ target = top(root)
213
35
 
214
- def add_library(*args, &block)
215
- @targets << Library.target(self, *args, &block)
216
- end
36
+ target.add_directory(directory)
217
37
 
218
- def add_executable(*args, &block)
219
- @targets << Executable.target(self, *args, &block)
220
- end
221
-
222
- def add_directory(path)
223
- directory = Directory.target(self, @root + path)
224
-
225
- $stderr.puts "Loading #{directory.root}..."
226
- build_path = (directory.root + "build.rb").to_s
227
- directory.instance_eval(File.read(build_path), build_path)
228
-
229
- @targets << directory
230
- end
231
-
232
- def execute(command, *arguments)
233
- $stderr.puts "Executing #{command} for #{@root}"
234
- @targets.each do |target|
235
- target.execute(command, *arguments)
236
- end
237
- end
238
- end
239
-
240
- def self.top(path)
241
- Directory.target(nil, path)
38
+ target.execute(:install, *args)
242
39
  end
243
40
  end
244
41
  end
@@ -18,6 +18,7 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require 'set'
21
22
  require 'rainbow'
22
23
  require 'shellwords'
23
24
 
@@ -41,5 +42,67 @@ module Teapot
41
42
  raise CommandError.new("Non-zero exit status")
42
43
  end
43
44
  end
45
+
46
+ def self.run!(*args)
47
+ run(*args)
48
+ rescue CommandError
49
+ false
50
+ end
51
+
52
+ def wait
53
+ # No parallel execution supported by default.
54
+ end
55
+
56
+ class Pool
57
+ def initialize(options = {})
58
+ @commands = []
59
+ @limit = options[:limit] || 16
60
+
61
+ @running = Set.new
62
+ end
63
+
64
+ def run(*args)
65
+ args = args.flatten.collect &:to_s
66
+
67
+ puts args.join(' ').color(:blue)
68
+
69
+ @commands << args
70
+
71
+ schedule!
72
+ end
73
+
74
+ def schedule!
75
+ while @running.size < @limit and @commands.size > 0
76
+ command = @commands.shift
77
+
78
+ pid = Process.fork do
79
+ exec(*command)
80
+
81
+ exit!(0)
82
+ end
83
+
84
+ @running << pid
85
+ end
86
+ end
87
+
88
+ def wait
89
+ while @running.size > 0
90
+ pid = Process.wait(0)
91
+ @running.delete(pid)
92
+
93
+ schedule!
94
+ end
95
+ end
96
+ end
97
+
98
+ def self.pipeline(parallel = false)
99
+ if parallel == false
100
+ # Non-parallel execution pipeline
101
+ Commands
102
+ else
103
+ # Pool based parallel execution pipeline
104
+ Pool.new(parallel == true ? {} : parallel)
105
+ end
106
+ end
44
107
  end
45
108
  end