teapot 2.2.0 → 3.2.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.
@@ -19,75 +19,77 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require 'samovar'
22
+ require 'console/terminal'
23
+
24
+ require_relative 'selection'
22
25
 
23
26
  module Teapot
24
27
  module Command
25
- class List < Samovar::Command
28
+ class List < Selection
26
29
  self.description = "List provisions and dependencies of the specified package."
27
30
 
28
- many :packages, "Limit the listing to only these packages, or all packages if none specified."
29
-
30
- def only
31
- if @packages.any?
32
- Set.new(@packages)
31
+ def terminal(output = $stdout)
32
+ Console::Terminal.for(output).tap do |terminal|
33
+ terminal[:definition] = terminal.style(nil, nil, :bright)
34
+ terminal[:dependency] = terminal.style(:blue)
35
+ terminal[:provision] = terminal.style(:green)
36
+ terminal[:package] = terminal.style(:yellow)
37
+ terminal[:import] = terminal.style(:cyan)
38
+ terminal[:error] = terminal.style(:red)
33
39
  end
34
40
  end
35
41
 
36
- def invoke(parent)
37
- context = parent.context
42
+ def process(selection)
43
+ context = selection.context
44
+ terminal = self.terminal
38
45
 
39
- logger = parent.logger
40
-
41
- context.configuration.packages.each do |package|
42
- # The root package is the local package for this context:
43
- next unless only == nil or only.include?(package.name)
46
+ selection.resolved.each do |package|
47
+ terminal.puts "Package #{package.name} (from #{package.path}):"
44
48
 
45
- logger.info "Package #{package.name} (from #{package.path}):".bright
46
-
47
49
  begin
48
50
  script = context.load(package)
49
51
  definitions = script.defined
50
52
 
51
53
  definitions.each do |definition|
52
- logger.info "\t#{definition}"
54
+ terminal.puts "\t#{definition}", style: :definition
53
55
 
54
56
  definition.description.each_line do |line|
55
- logger.info "\t\t#{line.chomp}".color(:cyan)
57
+ terminal.puts "\t\t#{line.chomp}", style: :description
56
58
  end if definition.description
57
59
 
58
60
  case definition
59
61
  when Project
60
- logger.info "\t\t- Summary: #{definition.summary}" if definition.summary
61
- logger.info "\t\t- License: #{definition.license}" if definition.license
62
- logger.info "\t\t- Website: #{definition.website}" if definition.website
63
- logger.info "\t\t- Version: #{definition.version}" if definition.version
62
+ terminal.puts "\t\t- Summary: #{definition.summary}" if definition.summary
63
+ terminal.puts "\t\t- License: #{definition.license}" if definition.license
64
+ terminal.puts "\t\t- Website: #{definition.website}" if definition.website
65
+ terminal.puts "\t\t- Version: #{definition.version}" if definition.version
64
66
 
65
67
  definition.authors.each do |author|
66
- contact_text = [author.email, author.website].compact.collect{|text|" <#{text}>"}.join
67
- logger.info "\t\t- Author: #{author.name}" + contact_text
68
+ contact_text = [author.email, author.website].compact.collect{|text| " <#{text}>"}.join
69
+ terminal.puts "\t\t- Author: #{author.name}" + contact_text
68
70
  end
69
71
  when Target
70
72
  definition.dependencies.each do |dependency|
71
- logger.info "\t\t- #{dependency}".color(:red)
73
+ terminal.puts "\t\t- #{dependency}", style: :dependency
72
74
  end
73
75
 
74
76
  definition.provisions.each do |name, provision|
75
- logger.info "\t\t- #{provision}".color(:green)
77
+ terminal.puts "\t\t- #{provision}", style: :provision
76
78
  end
77
79
  when Configuration
78
80
  definition.packages.each do |package|
79
- logger.info "\t\t- #{package}".color(:green)
81
+ terminal.puts "\t\t- #{package}", style: :package
80
82
  end
81
83
 
82
84
  definition.imports.select(&:explicit).each do |import|
83
- logger.info "\t\t- import #{import.name}".color(:red)
85
+ terminal.puts "\t\t- import #{import.name}", style: :import
84
86
  end
85
87
  end
86
88
  end
87
- rescue NonexistantTeapotError => error
88
- logger.info "\t#{error.message}".color(:red)
89
+ rescue MissingTeapotError => error
90
+ terminal.puts "\t#{error.message}", style: :error
89
91
  rescue IncompatibleTeapotError => error
90
- logger.info "\t#{error.message}".color(:red)
92
+ terminal.puts "\t#{error.message}", style: :error
91
93
  end
92
94
  end
93
95
  end
@@ -1,15 +1,15 @@
1
- # Copyright, 2015, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
5
5
  # in the Software without restriction, including without limitation the rights
6
6
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
7
  # copies of the Software, and to permit persons to whom the Software is
8
8
  # furnished to do so, subject to the following conditions:
9
- #
9
+ #
10
10
  # The above copyright notice and this permission notice shall be included in
11
11
  # all copies or substantial portions of the Software.
12
- #
12
+ #
13
13
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
14
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
15
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -18,61 +18,34 @@
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 'forwardable'
21
+ require 'samovar'
22
22
 
23
23
  module Teapot
24
- # Very similar to a set but uses a specific callback (defaults to &:name) for object identity.
25
- class IdentitySet
26
- include Enumerable
27
-
28
- def initialize(contents = [])
29
- @table = {}
24
+ module Command
25
+ class Selection < Samovar::Command
26
+ options
30
27
 
31
- contents.each do |object|
32
- add(object)
28
+ many :targets, "Only consider the specified targets, if any."
29
+
30
+ def targets
31
+ if @targets and @targets.any?
32
+ Set.new(@targets)
33
+ end
33
34
  end
34
- end
35
-
36
- attr :table
37
-
38
- def freeze
39
- @table.freeze
40
35
 
41
- super
42
- end
43
-
44
- def initialize_dup(other)
45
- @table = other.table.dup
46
- end
47
-
48
- def identity(object)
49
- object.name
50
- end
51
-
52
- def add(object)
53
- @table[identity(object)] = object
54
- end
55
-
56
- alias << add
57
-
58
- def delete(object)
59
- @table.delete(identity(object))
60
- end
61
-
62
- def include?(object)
63
- @table.include?(identity(object))
64
- end
65
-
66
- def each(&block)
67
- @table.each_value(&block)
68
- end
69
-
70
- def slice(names)
71
- names.collect{|name| @table[name]}
36
+ def selection(context)
37
+ if targets = self.targets
38
+ context.select(targets)
39
+ else
40
+ context.select(context.configuration[:build])
41
+ end
42
+ end
43
+
44
+ def invoke
45
+ context = parent.context
46
+
47
+ self.process(selection(parent.context))
48
+ end
72
49
  end
73
-
74
- extend Forwardable
75
-
76
- def_delegators :@table, :size, :empty?, :clear, :count, :[], :to_s, :inspect
77
50
  end
78
51
  end
@@ -18,30 +18,28 @@
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 'samovar'
21
+ require_relative 'selection'
22
22
  require 'rugged'
23
+ require 'console/terminal'
23
24
 
24
25
  module Teapot
25
26
  module Command
26
- class Status < Samovar::Command
27
+ class Status < Selection
27
28
  self.description = "List the git status of the specified package(s)."
28
29
 
29
- many :packages, "Limit the listing to only these packages, or all packages if none specified."
30
-
31
- def only
32
- if @packages.any?
33
- Set.new(@packages)
30
+ def terminal(output = $stdout)
31
+ Console::Terminal.for(output).tap do |terminal|
32
+ terminal[:worktree_new] = terminal.style(:green)
33
+ terminal[:worktree_modified] = terminal.style(:yellow)
34
+ terminal[:worktree_deleted] = terminal.style(:red)
34
35
  end
35
36
  end
36
37
 
37
- def invoke(parent)
38
- context = parent.context
39
- logger = parent.logger
38
+ def process(selection)
39
+ context = selection.context
40
+ terminal = self.terminal
40
41
 
41
- context.configuration.packages.each do |package|
42
- # The root package is the local package for this context:
43
- next unless only == nil or only.include?(package.name)
44
-
42
+ selection.resolved.each do |package|
45
43
  repository = Rugged::Repository.new(package.path.to_s)
46
44
 
47
45
  changes = {}
@@ -53,18 +51,10 @@ module Teapot
53
51
 
54
52
  next if changes.empty?
55
53
 
56
- logger.info "Package #{package.name} (from #{package.path}):".bright
54
+ terminal.puts "Package #{package.name} (from #{package.path}):"
57
55
 
58
- changes.each do |file, status|
59
- if status == [:worktree_new]
60
- logger.info "\t#{file}".color(:blue)
61
- elsif status == [:worktree_modified]
62
- logger.info "\t#{file}".color(:orange)
63
- elsif status == [:worktree_deleted]
64
- logger.info "\t#{file}".color(:red)
65
- else
66
- logger.info "\t#{file} #{status.inspect}"
67
- end
56
+ changes.each do |file, statuses|
57
+ terminal.puts "\t#{file} (#{statuses})", style: statuses.last
68
58
  end
69
59
  end
70
60
  end
@@ -18,12 +18,12 @@
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 'samovar'
21
+ require_relative 'selection'
22
22
  require 'graphviz'
23
23
 
24
24
  module Teapot
25
25
  module Command
26
- class Visualize < Samovar::Command
26
+ class Visualize < Selection
27
27
  self.description = "Generate a picture of the dependency graph."
28
28
 
29
29
  options do
@@ -31,8 +31,6 @@ module Teapot
31
31
  option '-d/--dependency-name <name>', "Show the partial chain for the given named dependency."
32
32
  end
33
33
 
34
- many :targets, "Visualize these targets, or use them to help the dependency resolution process."
35
-
36
34
  def dependency_names
37
35
  @targets || []
38
36
  end
@@ -41,15 +39,13 @@ module Teapot
41
39
  @options[:dependency_name]
42
40
  end
43
41
 
44
- def invoke(parent)
45
- context = parent.context
46
- selection = context.select(dependency_names)
42
+ def process(selection)
43
+ context = selection.context
47
44
  chain = selection.chain
48
45
 
49
46
  if dependency_name
50
47
  provider = selection.dependencies[dependency_name]
51
48
 
52
- # TODO The visualisation generated isn't quite right. It's introspecting too much from the packages and not reflecting #ordered and #provisions.
53
49
  chain = chain.partial(provider)
54
50
  end
55
51
 
@@ -58,7 +54,9 @@ module Teapot
58
54
  graph = visualization.generate(chain)
59
55
 
60
56
  if output_path = @options[:output_path]
61
- Graphviz::output(graph, :path => output_path)
57
+ Graphviz.output(graph, path: output_path, format: :svg)
58
+ else
59
+ $stdout.puts graph.to_dot
62
60
  end
63
61
 
64
62
  return graph
@@ -23,7 +23,7 @@ require 'set'
23
23
 
24
24
  require 'yaml/store'
25
25
 
26
- require_relative 'identity_set'
26
+ require 'build/dependency/set'
27
27
  require_relative 'definition'
28
28
 
29
29
  module Teapot
@@ -40,8 +40,8 @@ module Teapot
40
40
 
41
41
  @options = DEFAULT_OPTIONS.merge(options)
42
42
 
43
- @packages = IdentitySet.new(packages)
44
- @imports = IdentitySet.new
43
+ @packages = Build::Dependency::Set.new(packages)
44
+ @imports = Build::Dependency::Set.new
45
45
 
46
46
  @visibility = :private
47
47
 
@@ -50,7 +50,7 @@ module Teapot
50
50
  end
51
51
 
52
52
  def freeze
53
- return if frozen?
53
+ return self if frozen?
54
54
 
55
55
  @options.freeze
56
56
  @packages.freeze
@@ -63,6 +63,15 @@ module Teapot
63
63
  super
64
64
  end
65
65
 
66
+ def environment
67
+ configuration = self
68
+
69
+ Build::Environment.new(name: self.name) do
70
+ default build_path configuration.build_path
71
+ default platforms_path configuration.build_path
72
+ end
73
+ end
74
+
66
75
  # Controls how the configuration is exposed in the context.
67
76
  attr :visibility
68
77
 
@@ -128,11 +137,13 @@ module Teapot
128
137
  context.root + "teapot/packages/#{name}"
129
138
  end
130
139
 
131
- # The path where built products will be installed.
132
- def platforms_path
133
- context.root + "teapot/platforms/#{name}"
140
+ # The path where built products will be placed.
141
+ def build_path
142
+ context.root + "teapot/build/#{name}"
134
143
  end
135
144
 
145
+ alias platforms_path build_path
146
+
136
147
  def lock_path
137
148
  context.root + "#{@name}-lock.yml"
138
149
  end
@@ -146,7 +157,7 @@ module Teapot
146
157
  end
147
158
 
148
159
  # Process all import directives and return a new configuration based on the current configuration. Import directives bring packages and other import directives from the specififed configuration definition.
149
- def traverse(configurations, imported = IdentitySet.new, &block)
160
+ def traverse(configurations, imported = Build::Dependency::Set.new, &block)
150
161
  yield self # Whatever happens here, should ensure that...
151
162
 
152
163
  @imports.each do |import|
@@ -18,135 +18,9 @@
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_relative 'loader'
22
- require_relative 'package'
23
-
24
- require 'build/rulebook'
25
- require 'build/text/substitutions'
26
- require 'build/text/merge'
21
+ require_relative 'select'
27
22
 
28
23
  module Teapot
29
- class AlreadyDefinedError < StandardError
30
- def initialize(definition, previous)
31
- super "Definition #{definition.name} in #{definition.path} has already been defined in #{previous.path}!"
32
- end
33
-
34
- def self.check(definition, definitions)
35
- previous = definitions[definition.name]
36
-
37
- raise self.new(definition, previous) if previous
38
- end
39
- end
40
-
41
- # A selection is a specific view of the data exposed by the context at a specific point in time.
42
- class Select
43
- def initialize(context, configuration, names = [])
44
- @context = context
45
- @configuration = Configuration.new(context, configuration.package, configuration.name, [], configuration.options)
46
-
47
- @targets = {}
48
- @configurations = {}
49
- @projects = {}
50
- @rules = Build::Rulebook.new
51
-
52
- @dependencies = []
53
- @selection = Set.new
54
- @unresolved = Set.new
55
-
56
- load!(configuration, names)
57
-
58
- @chain = nil
59
- end
60
-
61
- attr :context
62
- attr :configuration
63
-
64
- attr :targets
65
- attr :projects
66
-
67
- # Alises as defined by Configuration#targets
68
- attr :aliases
69
-
70
- # All public configurations.
71
- attr :configurations
72
-
73
- attr :rules
74
-
75
- attr :dependencies
76
- attr :selection
77
- attr :unresolved
78
-
79
- def chain
80
- @chain ||= Build::Dependency::Chain.expand(@dependencies, @targets.values, @selection)
81
- end
82
-
83
- def direct_targets(ordered)
84
- @dependencies.collect do |dependency|
85
- ordered.find{|(package, _)| package.provides? dependency}
86
- end.compact
87
- end
88
-
89
- private
90
-
91
- # Add a definition to the current context.
92
- def append definition
93
- case definition
94
- when Target
95
- AlreadyDefinedError.check(definition, @targets)
96
- @targets[definition.name] = definition
97
- when Configuration
98
- # We define configurations in two cases, if they are public, or if they are part of the root package of this context.
99
- if definition.public? or definition.package == @context.root_package
100
- AlreadyDefinedError.check(definition, @configurations)
101
- @configurations[definition.name] = definition
102
- end
103
- when Project
104
- AlreadyDefinedError.check(definition, @projects)
105
- @projects[definition.name] = definition
106
- when Rule
107
- AlreadyDefinedError.check(definition, @rules)
108
- @rules << definition
109
- end
110
- end
111
-
112
- def load_package!(package)
113
- begin
114
- script = @context.load(package)
115
-
116
- # Load the definitions into the current selection:
117
- script.defined.each do |definition|
118
- append(definition)
119
- end
120
- rescue NonexistantTeapotError, IncompatibleTeapotError
121
- # If the package doesn't exist or the teapot version is too old, it failed:
122
- @unresolved << package
123
- end
124
- end
125
-
126
- def load!(configuration, names)
127
- # Load the root package which makes all the named configurations and targets available.
128
- load_package!(@context.root_package)
129
-
130
- # Load all packages defined by this configuration.
131
- configuration.traverse(@configurations) do |configuration|
132
- @configuration.merge(configuration) do |package|
133
- # puts "Load package: #{package} from #{configuration}"
134
- load_package!(package)
135
- end
136
- end
137
-
138
- @configuration.freeze
139
-
140
- names.each do |name|
141
- if @targets.key? name
142
- @selection << name
143
- else
144
- @dependencies << name
145
- end
146
- end
147
- end
148
- end
149
-
150
24
  # A context represents a specific root package instance with a given configuration and all related definitions. A context is stateful in the sense that package selection is specialized based on #select and #dependency_chain. These parameters are usually set up initially as part of the context setup.
151
25
  class Context
152
26
  def initialize(root, **options)
@@ -158,7 +32,7 @@ module Teapot
158
32
 
159
33
  @loaded = {}
160
34
 
161
- load_root_package(options) unless options[:load_root] == false
35
+ load_root_package(**options)
162
36
  end
163
37
 
164
38
  attr :root
@@ -174,8 +48,8 @@ module Teapot
174
48
  @repository ||= Rugged::Repository.new(@root.to_s)
175
49
  end
176
50
 
177
- def select(names = [], configuration = @configuration)
178
- Select.new(self, configuration, names)
51
+ def select(names = nil, configuration = @configuration)
52
+ Select.new(self, configuration, names || [])
179
53
  end
180
54
 
181
55
  def substitutions
@@ -195,6 +69,9 @@ module Teapot
195
69
  # e.g. foo-bar, typically used for targets, executables
196
70
  substitutions['PROJECT_TARGET_NAME'] = name.target
197
71
 
72
+ # e.g. foo_bar, typically used for variables.
73
+ substitutions['PROJECT_VARIABLE_NAME'] = name.key
74
+
198
75
  substitutions['LICENSE'] = @project.license
199
76
  end
200
77
 
@@ -228,13 +105,13 @@ module Teapot
228
105
 
229
106
  private
230
107
 
231
- def load_root_package(options)
108
+ def load_root_package(**options)
232
109
  # Load the root package:
233
110
  script = load(root_package)
234
-
111
+
235
112
  # Find the default configuration, if it exists:
236
113
  if configuration_name = options[:configuration]
237
- @configuration = @configurations[configuration_name]
114
+ @configuration = script.configurations[configuration_name]
238
115
  else
239
116
  @configuration = script.default_configuration
240
117
  end