teapot 0.3.2 → 0.5.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/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