Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
---|---|---|---|---|
lib/bee_build.rb | 285 | 212 | 98.60%
|
98.58%
|
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
1 # Copyright 2006-2012 Michel Casabianca <michel.casabianca@gmail.com> |
2 # |
3 # Licensed under the Apache License, Version 2.0 (the "License"); |
4 # you may not use this file except in compliance with the License. |
5 # You may obtain a copy of the License at |
6 # |
7 # http://www.apache.org/licenses/LICENSE-2.0 |
8 # |
9 # Unless required by applicable law or agreed to in writing, software |
10 # distributed under the License is distributed on an "AS IS" BASIS, |
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 # See the License for the specific language governing permissions and |
13 # limitations under the License. |
14 |
15 require 'rubygems' |
16 require 'syck' |
17 require 'yaml' |
18 require 'bee_util' |
19 require 'bee_context' |
20 require 'bee_properties' |
21 require 'bee_target' |
22 require 'bee_targets' |
23 require 'bee_task_packagemanager' |
24 |
25 module Bee |
26 |
27 # Class for a build, built from an object resulting from YAML build file parsing. |
28 class Build |
29 |
30 include Bee::Util::BuildErrorMixin |
31 include Bee::Util::HashCheckerMixin |
32 |
33 # Build key. |
34 KEY = 'build' |
35 # Build entry description. |
36 DESCRIPTION = { |
37 'build' => :mandatory, |
38 'default' => :optional, |
39 'description' => :optional, |
40 'context' => :optional, |
41 'extends' => :optional, |
42 'abstract' => :optional, |
43 'alias' => :optional |
44 } |
45 |
46 # Build file. |
47 attr_reader :file |
48 # Base directory, that is directory where lives the build file. |
49 attr_reader :base |
50 # Current directory, where was started the script. |
51 attr_reader :here |
52 # Build name. |
53 attr_reader :name |
54 # Parent build. |
55 attr_reader :extends |
56 # Abstractness. |
57 attr_reader :abstract |
58 # Build description. |
59 attr_reader :description |
60 # Build properties. |
61 attr_reader :properties |
62 # Hash for targets, indexed by target name. |
63 attr_reader :targets |
64 # Context for Ruby scripts and properties. |
65 attr_reader :context |
66 # Package manager, for task invocation. |
67 attr_reader :package_manager |
68 # Build listener, responsible for displaying build status. |
69 attr_reader :listener |
70 |
71 # Load a build from a YAML build file. |
72 # - file: YAML build file or URL. |
73 # - recursive: tells if we look for build file recursively (defaults to |
74 # nil). |
75 # - properties: a hash of additional properties passed on command line. |
76 def self.load(file, recursive=nil, properties={}) |
77 raise "Can't use recursive URL" if recursive and Bee::Util::url?(file) |
78 if recursive |
79 begin |
80 file = Bee::Util::find(file) |
81 rescue |
82 raise Bee::Util::BuildError.new("Build file '#{file}' " + |
83 "not found recursively") |
84 end |
85 end |
86 begin |
87 yaml = Bee::Util::get_file(file) |
88 object = YAML::load(yaml) |
89 rescue |
90 raise Bee::Util::BuildError. |
91 new("Error loading build file '#{file}': #{$!}") |
92 end |
93 return Build.new(object, file, properties) |
94 end |
95 |
96 # Constructor: |
97 # - object: object tree resulting from YAML build file parsing. |
98 # - file: build file (nil if none). |
99 # - properties: a hash of additional properties passed on command line. |
100 # - here: current directory. |
101 def initialize(object, file, properties={}, here=Dir.pwd) |
102 @file = file |
103 @base = get_base(@file) |
104 @here = here |
105 @properties = Bee::Properties.new |
106 @scripts = [] |
107 @targets = Targets.new(self) |
108 parse(object) |
109 @properties.overwrite(properties) |
110 @properties.defaults({:base => @base, :here => @here}) |
111 @context = Context.new(@properties.expressions, @scripts) |
112 @package_manager = Bee::Task::PackageManager.new(self) |
113 end |
114 |
115 # Run build. Raise a BuildError exception on build error if no listener |
116 # was given to be notified of the build failure: |
117 # - targets: list of targets to run. |
118 # - listener: listener for the build. |
119 def run(targets, listener=nil, dry=false) |
120 @listener = listener |
121 working_directory = Dir.getwd |
122 @listener.start(self, dry) if @listener |
123 begin |
124 error "Abstract build file, must be extended to run" if @abstract |
125 if not Bee::Util::url?(@base) |
126 Dir.chdir(@base) |
127 end |
128 @context.evaluate |
129 @targets.run(targets, dry) |
130 @listener.success() if @listener |
131 rescue Bee::Util::BuildError |
132 @listener.error($!) if @listener |
133 raise $! |
134 ensure |
135 @listener.stop() if @listener |
136 Dir.chdir(working_directory) |
137 remove_instance_variable(:@listener) |
138 end |
139 end |
140 |
141 private |
142 |
143 # Parse entries in build object. |
144 # - object: object tree resulting from YAML build file parsing. |
145 def parse(object) |
146 error "Build must be a list" unless object.kind_of?(Array) |
147 first = true |
148 for entry in object |
149 if entry.key?(Build::KEY) |
150 parse_build(entry) |
151 error "Build info entry must be first one in build file" if not first |
152 first = false |
153 elsif entry.key?(Properties::KEY) |
154 properties = entry[Properties::KEY] |
155 # if properties is a string, this is a YAML file to load as a Hash |
156 if properties.kind_of?(String) |
157 filename = properties |
158 begin |
159 properties = YAML::load(Bee::Util::get_file(filename, @base)) |
160 @properties.write(properties) |
161 rescue Exception |
162 error "Error loading properties file '#{filename}': #{$!}" |
163 end |
164 else |
165 @properties.write(properties) |
166 end |
167 first = false |
168 elsif entry.key?(Target::KEY) |
169 @targets.add(entry) |
170 first = false |
171 else |
172 error "Unknown entry:\n#{YAML::dump(entry)}" |
173 end |
174 end |
175 # manage extended builds |
176 if @extends |
177 for parent in @extends |
178 @properties.extend(parent.properties.expressions) |
179 @targets.extend(parent.targets) |
180 end |
181 end |
182 end |
183 |
184 # Parse a build entry. |
185 # - entry: the build entry to parse. |
186 def parse_build(entry) |
187 begin |
188 check_hash(entry, Build::DESCRIPTION) |
189 rescue |
190 error "Error parsing build info entry: #{$!}" |
191 end |
192 error "Duplicate build info" if @name |
193 @name = entry['build'] |
194 # check that 'default' entry is a string or an array |
195 error "'default' entry of the 'build' block must be a string or an array" if |
196 entry['default'] and (!entry['default'].kind_of?(String) and |
197 !entry['default'].kind_of?(Array)) |
198 if entry['default'] |
199 @targets.default = Array(entry['default']) |
200 @targets.default_set = true |
201 end |
202 # check that 'alias' entry is a hash |
203 error "'alias' entry of the 'build' block must be a hash" if |
204 entry['alias'] and !entry['alias'].kind_of?(Hash) |
205 if entry['alias'] |
206 @targets.alias = entry['alias'] |
207 @targets.alias_set = @targets.alias.keys() |
208 else |
209 @targets.alias_set = [] |
210 end |
211 @description = entry['description'] |
212 @abstract = entry['abstract'] |
213 # load parents build if any |
214 parents = Array(entry['extends']) |
215 if parents.length > 0 |
216 @extends = [] |
217 for extended in parents |
218 absolute_path = Bee::Util::absolute_path(extended, @base) |
219 begin |
220 @extends << Bee::Build::load(absolute_path) |
221 rescue Exception |
222 error "Error loading parent build file '#{extended}': #{$!}" |
223 end |
224 end |
225 end |
226 # check that there are no property and target collisions in parents |
227 if @extends |
228 properties = [] |
229 collisions = [] |
230 for parent in @extends |
231 parent_properties = parent.properties.expressions.keys |
232 collisions += properties & parent_properties |
233 properties += parent_properties |
234 end |
235 collisions = collisions - Bee::Properties::SYSTEM_PROPERTIES |
236 collisions = collisions.uniq.map { |e| e.to_s }.sort |
237 if collisions.length > 0 |
238 error "Properties in parents are colliding: #{collisions.join(', ')}" |
239 end |
240 targets = [] |
241 collisions = [] |
242 for parent in @extends |
243 parent_targets = parent.targets.hash.keys |
244 collisions += targets & parent_targets |
245 targets += parent_targets |
246 end |
247 collisions = collisions.uniq.map { |e| e.to_s }.sort |
248 if collisions.length > 0 |
249 error "Targets in parents are colliding: #{collisions.join(', ')}" |
250 end |
251 end |
252 # load context files if any |
253 context = entry['context'] |
254 @scripts = Array(context) if context |
255 end |
256 |
257 # Get base for a given file. |
258 # - file: build file. |
259 def get_base(file) |
260 if file |
261 if Bee::Util::url?(file) |
262 return File.dirname(file) |
263 else |
264 return File.expand_path(File.dirname(file)) |
265 end |
266 else |
267 return File.expand_path(Dir.pwd) |
268 end |
269 end |
270 |
271 end |
272 |
273 # Return Bee version. Try to load bee_version file or return UNKNOWN. |
274 def version |
275 begin |
276 require 'bee_version' |
277 return Bee::VERSION |
278 rescue Exception |
279 return 'UNKNOWN' |
280 end |
281 end |
282 |
283 module_function :version |
284 |
285 end |
Generated on Fri Oct 09 02:07:48 +0200 2015 with rcov 1.0.0