teapot 0.3.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/teapot CHANGED
@@ -23,195 +23,161 @@
23
23
  require 'teapot/config'
24
24
 
25
25
  require 'uri'
26
- require 'rake'
27
26
  require 'rainbow'
27
+ require 'fileutils'
28
+ require 'benchmark'
29
+ require 'yaml'
28
30
 
29
- $app = Rake.application = Rake::Application.new
30
- $app.init('teapot')
31
+ require 'trollop'
31
32
 
32
- # Fetch remote packages and link local packages based on the Teapot configuration:
33
- task :fetch do
34
- config = Teapot::Config.load_default
35
- context = Teapot::Context.new(config)
36
-
37
- base_uri = URI(config.options[:source].to_s)
38
-
39
- if base_uri.scheme == nil || base_uri.scheme == 'file'
40
- base_uri = URI "file://" + File.expand_path(base_uri.path, config.root) + "/"
41
- end
33
+ OPTIONS = Trollop::options do
34
+ opt :only, "Only compiled direct dependencies."
35
+ end
36
+
37
+ module Application
38
+ def self.fetch
39
+ config = Teapot::Config.load_default
40
+ context = Teapot::Context.new(config)
42
41
 
43
- puts "Base URI: #{base_uri}".color(:cyan)
42
+ base_uri = URI(config.options[:source].to_s)
44
43
 
45
- config.records.each do |record|
46
- next if record.transient?
44
+ if base_uri.scheme == nil || base_uri.scheme == 'file'
45
+ base_uri = URI "file://" + File.expand_path(base_uri.path, config.root) + "/"
46
+ end
47
47
 
48
- destination_path = record.destination_path
48
+ puts "Base URI: #{base_uri}".color(:cyan)
49
49
 
50
- if record.local?
51
- puts "Linking local #{record}...".color(:cyan)
52
-
53
- local_path = config.root + record.options[:local]
50
+ config.packages.each do |package|
51
+ destination_path = package.path
52
+
53
+ if package.local?
54
+ puts "Linking local #{package}...".color(:cyan)
54
55
 
55
- # Make the top level directory if required:
56
- FileUtils.mkdir_p(destination_path.dirname.to_s)
56
+ local_path = config.root + package.options[:local]
57
57
 
58
- unless File.exist? destination_path
59
- FileUtils.ln_s local_path, destination_path
60
- end
61
- else
62
- puts "Fetching #{record}...".color(:cyan)
63
-
64
- branch = record.options.fetch(:version, 'master')
65
-
66
- unless File.exist? destination_path
67
- puts "Cloning package at path #{destination_path} ...".color(:cyan)
68
- FileUtils.mkdir_p(destination_path.to_s)
58
+ # Make the top level directory if required:
59
+ destination_path.dirname.mkpath
69
60
 
70
- source_uri = URI record.uri
71
-
72
- unless source_uri.absolute?
73
- source_uri = base_uri + source_uri
61
+ unless destination_path.exist?
62
+ destination_path.make_symlink(local_path)
74
63
  end
64
+ else
65
+ puts "Fetching #{package}...".color(:cyan)
75
66
 
76
- # Git can't handle the default formatting that Ruby uses for file URIs.
77
- if source_uri.scheme == "file"
78
- source_uri = "file://" + source_uri.path
79
- end
67
+ branch = package.options.fetch(:version, 'master')
68
+
69
+ unless destination_path.exist?
70
+ puts "Cloning package at path #{destination_path} ...".color(:cyan)
71
+ destination_path.mkpath
80
72
 
81
- Teapot::Commands.run("git", "clone", source_uri, destination_path, "--branch", branch)
73
+ Teapot::Commands.run("git", "clone", package.relative_url(base_uri), destination_path, "--branch", branch)
82
74
 
83
- Dir.chdir(destination_path) do
84
- Teapot::Commands.run("git", "submodule", "update", "--init", "--recursive")
85
- end
86
- else
87
- puts "Updating package at path #{destination_path} ...".color(:cyan)
75
+ Dir.chdir(destination_path) do
76
+ Teapot::Commands.run("git", "submodule", "update", "--init", "--recursive")
77
+ end
78
+ else
79
+ puts "Updating package at path #{destination_path} ...".color(:cyan)
88
80
 
89
- Dir.chdir(destination_path) do
90
- Teapot::Commands.run("git", "fetch", "origin")
81
+ Dir.chdir(destination_path) do
82
+ Teapot::Commands.run("git", "fetch", "origin")
91
83
 
92
- Teapot::Commands.run("git", "checkout", branch)
84
+ Teapot::Commands.run("git", "checkout", branch)
93
85
 
94
- # Pull any changes, if you might get the error from above:
95
- # Your branch is behind 'origin/0.1' by 1 commit, and can be fast-forwarded.
96
- Teapot::Commands.run("git", "pull")
86
+ # Pull any changes, if you might get the error from above:
87
+ # Your branch is behind 'origin/0.1' by 1 commit, and can be fast-forwarded.
88
+ Teapot::Commands.run("git", "pull")
97
89
 
98
- Teapot::Commands.run("git", "submodule", "update", "--init", "--recursive")
90
+ Teapot::Commands.run("git", "submodule", "update", "--init", "--recursive")
91
+ end
99
92
  end
100
93
  end
101
94
  end
102
- end
103
-
104
- puts "Completed fetch successfully.".color(:green)
105
- end
106
-
107
- # Build packages based on the Teapot configuration:
108
- task :build do |task, arguments|
109
- config = Teapot::Config.load_default
110
- context = Teapot::Context.new(config)
111
95
 
112
- config.records.each do |record|
113
- record.load(context)
96
+ puts "Completed fetch successfully.".color(:green)
114
97
  end
115
98
 
116
- # Configure the packages and platforms based on the ENV provided by the user:
117
- build_package = ENV['PACKAGE']
118
- build_platform = ENV['PLATFORM']
99
+ def self.install(package_names = ARGV)
100
+ config = Teapot::Config.load_default
101
+ context = Teapot::Context.new(config)
119
102
 
120
- if build_package
121
- package = context.packages[build_package]
122
-
123
- unless package
124
- fail "Could not find package #{build_package}".color(:red)
103
+ config.packages.each do |package|
104
+ context.load(package)
125
105
  end
126
106
 
127
- packages = [package]
128
- else
129
- packages = context.packages.values
130
- end
131
-
132
- if build_platform
133
- platform = context.platforms[build_platform]
107
+ context.select(package_names)
134
108
 
135
- unless platform
136
- fail "Could not find platform #{build_platform}".color(:red)
137
- end
109
+ chain = Teapot::Dependency::chain(context.selection, context.dependencies, context.targets.values)
138
110
 
139
- platforms = [platform]
140
- else
141
- platforms = context.platforms.values
142
- end
143
-
144
- unless ENV['ONLY']
145
- build_order = Teapot::Package.build_order(context.packages, packages)
146
- else
147
- build_order = {:ordered => packages, :unresolved => []}
148
- end
149
-
150
- if build_order[:unresolved].size > 0
151
- puts "Unresolved packages:"
152
- build_order[:unresolved].each do |(name, from)|
153
- puts "\tPackage: #{name} (from #{from})".color(:red)
154
- end
111
+ if chain.unresolved.size > 0
112
+ puts "Unresolved dependencies:"
155
113
 
156
- fail "Cannot continue build due to unresolved dependencies!".color(:red)
157
- else
158
- ordered = build_order[:ordered]
159
- end
114
+ chain.unresolved.each do |(name, parent)|
115
+ puts "#{parent} depends on #{name.inspect}".color(:red)
116
+
117
+ conflicts = chain.conflicts[name]
118
+
119
+ if conflicts
120
+ conflicts.each do |conflict|
121
+ puts " - provided by #{conflict.inspect}".color(:red)
122
+ end
123
+ end
124
+ end
125
+
126
+ abort "Cannot continue build due to unresolved dependencies!"
127
+ end
160
128
 
161
- puts "Building #{ordered.join(', ')} for variant #{config.variant}".color(:cyan)
129
+ puts "Resolved: #{chain.resolved.inspect}".color(:magenta)
162
130
 
163
- platforms.each do |platform|
164
- next unless platform.available?
131
+ ordered = chain.ordered
165
132
 
166
- puts "Building for #{platform}...".color(:cyan)
133
+ if OPTIONS[:only]
134
+ ordered = context.direct_targets(ordered)
135
+ end
167
136
 
168
- platform.prepare!
137
+ ordered.each do |(target, dependency)|
138
+ puts "Building #{target.name} for dependency #{dependency}...".color(:cyan)
169
139
 
170
- ordered.each do |package|
171
- next if package.record.transient?
172
-
173
- puts "Building #{package}...".color(:cyan)
174
-
175
- package.build!(platform, :variant => config.variant)
140
+ if target.respond_to? :install! and !ENV['TEAPOT_DRY']
141
+ target.install!(context)
142
+ end
176
143
  end
177
- end
178
-
179
- puts "Completed build successfully.".color(:green)
180
- end
181
-
182
- # List available/installed packages based on the Teapot configuration:
183
- task :list do
184
- config = Teapot::Config.load_default
185
- context = Teapot::Context.new(config)
186
144
 
187
- config.records.each do |record|
188
- record.load(context)
145
+ puts "Completed build successfully.".color(:green)
189
146
  end
190
147
 
191
- build_order = Teapot::Package.build_order(context.packages, context.packages.values)
192
-
193
- if build_order[:unresolved].size > 0
194
- puts "Unresolved packages:"
195
- build_order[:unresolved].each do |(name, from)|
196
- puts "\tPackage: #{name} (from #{from})".color(:red)
197
- end
198
- end
148
+ def self.list
149
+ config = Teapot::Config.load_default
150
+ context = Teapot::Context.new(config)
199
151
 
200
- puts "Ordered packages:"
201
- build_order[:ordered].each do |package|
202
- puts "\t#{package.class}: #{package.name}".color(:green)
152
+ config.packages.each do |package|
153
+ targets = context.load(package)
203
154
 
204
- if package.depends.size > 0
205
- puts "\t\t(depends on #{package.depends.join(', ')})".color(:green)
155
+ targets.each do |target|
156
+ puts "Target #{target.name} from #{package.name}"
157
+
158
+ target.dependencies.each do |name|
159
+ puts " - depends on #{name.inspect}".color(:red)
160
+ end
161
+
162
+ target.provisions.each do |(name, provision)|
163
+ if Teapot::Dependency::Alias === provision
164
+ puts " - provides #{name.inspect} => #{provision.dependencies.inspect}".color(:green)
165
+ else
166
+ puts " - provides #{name.inspect}".color(:green)
167
+ end
168
+ end
169
+ end
206
170
  end
207
171
  end
208
172
 
209
- puts "Platforms:"
210
- context.platforms.each do |name, platform|
211
- puts "\t#{platform.class}: #{platform.name}".color(:green)
173
+ def self.debug
174
+ puts ARGV.inspect
212
175
  end
213
176
  end
214
177
 
215
- task :default => [:fetch, :build, :list]
178
+ time = Benchmark.measure do
179
+ action = ARGV.shift.to_sym
180
+ Application.send(action) if Application.methods.include?(action)
181
+ end
216
182
 
217
- $app.top_level
183
+ puts time.format("Elapsed Time: %r").color(:magenta)
@@ -19,62 +19,51 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require 'fileutils'
22
- require 'teapot/environment'
23
22
 
24
23
  module Teapot
25
- class UnavailableError < StandardError
26
- end
27
-
28
- class Platform
29
- def initialize(context, record, name)
30
- @context = context
31
- @record = record
32
-
33
- @name = name
34
- @configure = nil
35
-
36
- @available = false
37
- end
38
-
39
- attr :name
24
+ module Build
25
+ class Component
26
+ def initialize(root, name, environment)
27
+ @root = root
28
+ @name = name
29
+ @environment = environment
40
30
 
41
- def prefix
42
- @context.config.build_path + @name.to_s
43
- end
31
+ @parts = [@name]
32
+ end
44
33
 
45
- def cmake_modules_path
46
- prefix + "share/cmake/modules"
47
- end
48
-
49
- def configure(&block)
50
- @configure = Proc.new &block
51
- end
52
-
53
- def environment
54
- if @available
55
- return Environment.combine(
56
- Environment.new(&@configure),
57
- @record.options[:environment],
58
- )
59
- else
60
- raise UnavailableError.new("Platform is not available for configuration!")
34
+ attr :root
35
+ attr :name
36
+ attr :parts
37
+
38
+ def add(path)
39
+ @parts << path
61
40
  end
62
- end
41
+
42
+ def variant
43
+ @environment[:variant]
44
+ end
45
+
46
+ def destination_path
47
+ @environment[:build_prefix] + "source"
48
+ end
49
+
50
+ def prepare!
51
+ source_path = destination_path + @name
63
52
 
64
- def make_available!
65
- @available = true
66
- end
53
+ if source_path.exist?
54
+ source_path.rmtree
55
+ end
67
56
 
68
- def available?
69
- @available
70
- end
57
+ source_path.mkpath
71
58
 
72
- def to_s
73
- "<Platform: #{@name} (#{@available ? 'available' : 'inactive'})>"
74
- end
59
+ @parts.each do |path|
60
+ full_path = @root + path
61
+
62
+ FileUtils.cp_r(full_path.children, source_path.to_s)
63
+ end
75
64
 
76
- def prepare!
77
- FileUtils.mkdir_p cmake_modules_path
65
+ return source_path
66
+ end
78
67
  end
79
68
  end
80
69
  end
@@ -0,0 +1,67 @@
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
+ require 'fileutils'
23
+
24
+ module Teapot
25
+ module Build
26
+ class FileList
27
+ include Enumerable
28
+
29
+ def self.[] (root, pattern, prefix = nil)
30
+ self.new(root, pattern, prefix)
31
+ end
32
+
33
+ def initialize(root, pattern, prefix = nil)
34
+ @root = root
35
+ @pattern = pattern
36
+ @prefix = Pathname.new(prefix || ".")
37
+ end
38
+
39
+ attr :root
40
+ attr :pattern
41
+ attr :prefix
42
+
43
+ def each(&block)
44
+ Pathname.glob(@root + @pattern).each &block
45
+ end
46
+
47
+ def copy(destination)
48
+ self.each do |path|
49
+ # Compute the destination path, which is formed using the relative path:
50
+ relative_path = path.relative_path_from(@root)
51
+ destination_path = destination + @prefix + relative_path
52
+
53
+ if path.directory?
54
+ # Make a directory at the destination:
55
+ destination_path.mkpath
56
+ else
57
+ # Make the path if it doesn't already exist:
58
+ destination_path.dirname.mkpath
59
+
60
+ # Copy the file to the destination:
61
+ FileUtils.cp path, destination_path
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -18,12 +18,17 @@
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
+
21
23
  module Teapot
22
24
  module Build
23
25
  module Linker
26
+ class UnsupportedPlatform < StandardError
27
+ end
28
+
24
29
  def self.link_static(environment, library_file, objects)
25
30
  if RUBY_PLATFORM =~ /darwin/
26
- Teapot::Commands.run(
31
+ Commands.run(
27
32
  environment[:libtool] || "libtool",
28
33
  "-static", "-o", library_file, objects,
29
34
  )
@@ -0,0 +1,77 @@
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/commands'
22
+ require 'teapot/environment'
23
+
24
+ require 'pathname'
25
+ require 'rainbow'
26
+ require 'fileutils'
27
+
28
+ require 'teapot/build/linker'
29
+ require 'teapot/build/component'
30
+ require 'teapot/build/file_list'
31
+
32
+ module Teapot
33
+ module Build
34
+ class Target
35
+ def initialize(parent)
36
+ @parent = parent
37
+ @configure = nil
38
+ end
39
+
40
+ attr :parent
41
+
42
+ def root
43
+ @parent.root
44
+ end
45
+
46
+ def configure(&block)
47
+ @configure = Proc.new &block
48
+ end
49
+
50
+ def self.target(*args, &block)
51
+ instance = self.new(*args)
52
+
53
+ if block_given?
54
+ instance.instance_eval(&block)
55
+ end
56
+
57
+ return instance
58
+ end
59
+
60
+ def execute(command, environment, *arguments)
61
+ if @configure
62
+ environment = environment.merge &@configure
63
+ end
64
+
65
+ # Flatten the environment to a hash:
66
+ values = environment.flatten
67
+
68
+ puts "Performing #{self.class}/#{command} for #{root}...".color(:cyan)
69
+
70
+ # Show the environment to the user:
71
+ Environment::System::dump(values)
72
+
73
+ self.send(command, values, *arguments)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,55 @@
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
+ require 'teapot/build/targets/directory'
23
+
24
+ module Teapot
25
+ module Build
26
+ module Targets
27
+ class Application < Directory
28
+ def initialize(parent, name, options = {})
29
+ super parent
30
+
31
+ @name = name
32
+ @options = options
33
+ end
34
+
35
+ def subdirectory
36
+ "apps/#{@name}"
37
+ end
38
+
39
+ def << target
40
+ if target.respond_to? :subdirectory
41
+ target.options[:subdirectory] = subdirectory
42
+ end
43
+
44
+ super
45
+ end
46
+ end
47
+
48
+ class Directory
49
+ def add_application(*args, &block)
50
+ self << Application.target(self, *args, &block)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end