teapot 1.0.0.pre.rc9 → 1.0.0.pre.rc10

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,51 +0,0 @@
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
- module Teapot
22
- class Environment
23
- class Evaluator
24
- def initialize(environment)
25
- @environment = environment
26
- end
27
-
28
- def method_missing(name)
29
- object_value(@environment[name])
30
- end
31
-
32
- # Compute the literal object value for a given key:
33
- def object_value(value)
34
- case value
35
- when Array
36
- value.collect{|item| object_value(item)}.flatten
37
- when Symbol
38
- object_value(@environment[value])
39
- when Proc
40
- object_value(instance_exec(&value))
41
- when Default
42
- object_value(value.value)
43
- when Replace
44
- object_value(value.value)
45
- else
46
- value
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,102 +0,0 @@
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 'digest/md5'
22
-
23
- module Teapot
24
- class Environment
25
- def to_h
26
- @values
27
- end
28
-
29
- def to_hash
30
- hash = {}
31
-
32
- # Flatten this chain of environments:
33
- flatten_to_hash(hash)
34
-
35
- # Evaluate all items to their respective object value:
36
- evaluator = Evaluator.new(hash)
37
-
38
- # Evaluate all the individual environment values so that they are flat:
39
- Hash[hash.map{|key, value| [key, evaluator.object_value(value)]}]
40
- end
41
-
42
- def flatten
43
- self.class.new(nil, self.to_hash)
44
- end
45
-
46
- def defined
47
- @values.select{|name,value| Define === value}
48
- end
49
-
50
- def inspect(output = $stdout, indent = "")
51
- @values.each do |(key, value)|
52
- output.puts "#{indent}#{key}: #{value}"
53
- end
54
-
55
- @parent.inspect(output, indent + "\t") if @parent
56
- end
57
-
58
- # This should be stable within environments that produce the same results.
59
- def checksum
60
- digester = Digest::MD5.new
61
-
62
- checksum_recursively(digester)
63
-
64
- return digester.hexdigest
65
- end
66
-
67
- protected
68
-
69
- def checksum_recursively(digester)
70
- @values.each do |(key, value)|
71
- digester.update(key.to_s)
72
- digester.update(value.to_s)
73
- end
74
-
75
- @parent.checksum_recursively(digester) if @parent
76
- end
77
-
78
- # We fold in the ancestors one at a time from oldest to youngest.
79
- def flatten_to_hash(hash)
80
- if @parent
81
- @parent.flatten_to_hash(hash)
82
- end
83
-
84
- @values.each do |key, value|
85
- previous = hash[key]
86
-
87
- if Replace === value
88
- # Replace the parent value
89
- hash[key] = value
90
- elsif Array === previous
91
- # Merge with the parent value
92
- hash[key] = previous + Array(value)
93
- elsif Default === value
94
- # Update the parent value if not defined.
95
- hash[key] = previous || value
96
- else
97
- hash[key] = value
98
- end
99
- end
100
- end
101
- end
102
- end
@@ -1,56 +0,0 @@
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 'rainbow'
22
- require 'rainbow/ext/string'
23
-
24
- module Teapot
25
- class Environment
26
- module System
27
- def self.shell_escape(value)
28
- case value
29
- when Array
30
- value.flatten.collect{|argument| shell_escape(argument)}.join(' ')
31
- else
32
- # Ensure that any whitespace has been escaped:
33
- value.to_s.gsub(/ /, '\ ')
34
- end
35
- end
36
-
37
- def self.convert_to_shell(environment)
38
- Hash[environment.values.map{|key, value| [
39
- key.to_s.upcase,
40
- shell_escape(value)
41
- ]}]
42
- end
43
-
44
- def self.dump(environment, io = STDOUT)
45
- environment.values.each do |key, value|
46
- io.puts "#{key}:".rjust(20).color(:magenta) + " #{value.inspect}"
47
- end
48
- end
49
- end
50
-
51
- # Construct an environment from a given system environment:
52
- def self.system_environment(env = ENV)
53
- self.new(Hash[env.map{|key, value| [key.downcase.to_sym, value]}])
54
- end
55
- end
56
- end
@@ -1,188 +0,0 @@
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
- module Teapot
22
- # A rule is a function with a specific set of input and output parameters, which can match against a given set of specific inputs and outputs. For example, there might be several rules for compiling, but the specific rules depend on the language being compiled.
23
- class Rule
24
- class Parameter
25
- def initialize(direction, name, options = {}, &block)
26
- @direction = direction
27
- @name = name
28
-
29
- @options = options
30
-
31
- @dynamic = block_given? ? Proc.new(&block) : nil
32
- end
33
-
34
- attr :direction
35
- attr :name
36
-
37
- attr :options
38
-
39
- def dynamic?
40
- @dynamic != nil
41
- end
42
-
43
- def implicit?
44
- dynamic? and @options[:implicit]
45
- end
46
-
47
- # Optional parameters are those that are either defined as optional or implicit.
48
- def optional?
49
- @options[:optional] || implicit?
50
- end
51
-
52
- def applicable? arguments
53
- value = arguments.fetch(@name) do
54
- # Value couldn't be found, if it wasn't optional, this parameter didn't apply:
55
- return optional?
56
- end
57
-
58
- # If a pattern is provided, we must match it.
59
- if pattern = @options[:pattern]
60
- return Array(value).all? {|item| pattern.match(item)}
61
- end
62
-
63
- return true
64
- end
65
-
66
- def compute(arguments, scope)
67
- if implicit?
68
- # Can be replaced if supplied:
69
- arguments[@name] || scope.instance_exec(arguments, &@dynamic)
70
- elsif dynamic?
71
- # Argument is optional:
72
- scope.instance_exec(arguments[@name], arguments, &@dynamic)
73
- else
74
- arguments[@name]
75
- end
76
- end
77
-
78
- def inspect
79
- "#{direction}:#{@name} (#{options.inspect})"
80
- end
81
- end
82
-
83
- def initialize(process_name, type)
84
- @name = process_name + "." + type
85
- @full_name = @name.gsub(/[^\w]/, '_')
86
-
87
- @process_name = process_name.gsub('-', '_').to_sym
88
- @type = type
89
-
90
- @apply = nil
91
-
92
- @parameters = []
93
- end
94
-
95
- # compile.cpp
96
- attr :name
97
-
98
- # compile
99
- attr :process_name
100
-
101
- # compile_cpp
102
- attr :full_name
103
-
104
- attr :primary_output
105
-
106
- def input(name, options = {}, &block)
107
- @parameters << Parameter.new(:input, name, options, &block)
108
- end
109
-
110
- def parameter(name, options = {}, &block)
111
- @parameters << Parameter.new(:argument, name, options, &block)
112
- end
113
-
114
- def output(name, options = {}, &block)
115
- @parameters << Parameter.new(:output, name, options, &block)
116
-
117
- @primary_output ||= @parameters.last
118
- end
119
-
120
- # Check if this rule can process these parameters
121
- def applicable?(arguments)
122
- @parameters.each do |parameter|
123
- next if parameter.implicit?
124
-
125
- return false unless parameter.applicable?(arguments)
126
- end
127
-
128
- return true
129
- end
130
-
131
- # The scope is the context in which the dynamic rule computation is done, usually an instance of Task.
132
- def normalize(arguments, scope)
133
- Hash[
134
- @parameters.collect do |parameter|
135
- [parameter.name, parameter.compute(arguments, scope)]
136
- end
137
- ]
138
- end
139
-
140
- def files(arguments)
141
- input_files = []
142
- output_files = []
143
-
144
- @parameters.each do |parameter|
145
- # This could probably be improved a bit, we are assuming all parameters are file based:
146
- value = arguments[parameter.name]
147
-
148
- next unless value
149
-
150
- case parameter.direction
151
- when :input
152
- input_files << value
153
- when :output
154
- output_files << value
155
- end
156
- end
157
-
158
- return Build::Files::Composite.new(input_files), Build::Files::Composite.new(output_files)
159
- end
160
-
161
- def apply(&block)
162
- @apply = Proc.new(&block)
163
- end
164
-
165
- def apply!(scope, arguments)
166
- scope.instance_exec(arguments, &@apply) if @apply
167
- end
168
-
169
- def result(arguments)
170
- if @primary_output
171
- arguments[@primary_output.name]
172
- end
173
- end
174
-
175
- def to_s
176
- "<#{self.class.name} #{@name.dump}>"
177
- end
178
- end
179
-
180
- class NoApplicableRule < StandardError
181
- def initialize(name, arguments)
182
- super "No applicable rule with name #{name}.* for parameters: #{arguments.inspect}"
183
-
184
- @name = name
185
- @arguments = arguments
186
- end
187
- end
188
- end
@@ -1,91 +0,0 @@
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/rule'
22
-
23
- module Teapot
24
- class Rulebook
25
- def initialize
26
- @rules = {}
27
- @processes = {}
28
- end
29
-
30
- attr :rules
31
-
32
- def << rule
33
- @rules[rule.name] = rule
34
-
35
- # A cache for fast process/file-type lookup:
36
- processes = @processes[rule.process_name] ||= []
37
- processes << rule
38
- end
39
-
40
- def [] name
41
- @rules[name]
42
- end
43
-
44
- def with(superclass, state = {})
45
- task_class = Class.new(superclass)
46
-
47
- # Define methods for all processes, e.g. task_class#compile
48
- @processes.each do |key, rules|
49
- # Define general rules, which use rule applicability for disambiguation:
50
- task_class.send(:define_method, key) do |arguments, &block|
51
- rule = rules.find{|rule| rule.applicable? arguments }
52
-
53
- if rule
54
- update(rule, arguments, &block)
55
- else
56
- raise NoApplicableRule.new(key, arguments)
57
- end
58
- end
59
- end
60
-
61
- # Define methods for all rules, e.g. task_class#compile_cpp
62
- @rules.each do |key, rule|
63
- task_class.send(:define_method, rule.full_name) do |arguments, &block|
64
- update(rule, arguments, &block)
65
- end
66
- end
67
-
68
- state.each do |key, value|
69
- task_class.send(:define_method, key) do
70
- value
71
- end
72
- end
73
-
74
- return task_class
75
- end
76
-
77
- def self.for(environment)
78
- rulebook = self.new
79
-
80
- environment.defined.each do |name, define|
81
- object = define.klass.new(*name.split('.', 2))
82
-
83
- object.instance_eval(&define.block)
84
-
85
- rulebook << object
86
- end
87
-
88
- return rulebook
89
- end
90
- end
91
- end