1 # Copyright 2003 Dave Abrahams
2 # Copyright 2003, 2004, 2005, 2006 Vladimir Prus
3 # Distributed under the Boost Software License, Version 1.0.
4 # (See accompanying file LICENSE_1_0.txt or copy at
5 # http://www.boost.org/LICENSE_1_0.txt)
16 # Class for storing a set of properties.
18 # There is 1<->1 correspondence between identity and value. No two instances
19 # of the class are equal. To maintain this property, the 'property-set.create'
20 # rule should be used to create new instances. Instances are immutable.
22 # Each property is classified with regard to its effect on build results.
23 # Incidental properties have no effect on build results, from B2's
24 # point of view. Others are either free, or non-free and we refer to non-free
25 # ones as 'base'. Each property belongs to exactly one of those categories.
27 # It is possible to get a list of properties belonging to each category as
28 # well as a list of properties with a specific attribute.
30 # Several operations, like and refine and as-path are provided. They all use
31 # caching whenever possible.
43 rule __init__ ( raw-properties * )
45 self.raw = $(raw-properties) ;
47 for local p in $(raw-properties)
51 errors.error "Invalid property: '$(p)'" ;
56 # Returns Jam list of stored properties.
65 return "[" $(self.raw) "]" ;
68 # Returns properties that are neither incidental nor free.
72 if ! $(self.base-initialized)
79 # Returns free properties which are not incidental.
83 if ! $(self.base-initialized)
90 # Returns relevant base properties. This is used for computing
91 # target paths, so it must return the expanded set of relevant
94 rule base-relevant ( )
96 if ! $(self.relevant-initialized)
100 return $(self.base-relevant) ;
103 # Returns all properties marked as relevant by features-ps
104 # Does not attempt to expand features-ps in any way, as
105 # this matches what virtual-target.register needs.
107 rule relevant ( features-ps )
109 if ! $(self.relevant.$(features-ps))
112 local features = [ $(features-ps).get <relevant> ] ;
113 features = <$(features)> ;
114 local ignore-relevance = [ modules.peek
115 property-set : .ignore-relevance ] ;
116 for local p in $(self.raw)
118 if $(ignore-relevance) || $(p:G) in $(features)
120 local att = [ feature.attributes $(p:G) ] ;
121 if ! ( incidental in $(att) )
127 self.relevant.$(features-ps) = [ property-set.create $(result) ] ;
129 return $(self.relevant.$(features-ps)) ;
132 # Returns dependency properties.
136 if ! $(self.dependency-initialized)
140 return $(self.dependency) ;
143 rule non-dependency ( )
145 if ! $(self.dependency-initialized)
149 return $(self.non-dependency) ;
154 if ! $(self.conditional-initialized)
158 return $(self.conditional) ;
161 rule non-conditional ( )
163 if ! $(self.conditional-initialized)
167 return $(self.non-conditional) ;
170 # Returns incidental properties.
174 if ! $(self.base-initialized)
178 return $(self.incidental) ;
183 if ! $(self.refined.$(ps))
185 local r = [ property.refine $(self.raw) : [ $(ps).raw ] ] ;
186 if $(r[1]) != "@error"
188 self.refined.$(ps) = [ property-set.create $(r) ] ;
192 self.refined.$(ps) = $(r) ;
195 return $(self.refined.$(ps)) ;
200 if ! $(self.expanded)
202 self.expanded = [ property-set.create [ feature.expand $(self.raw) ]
205 return $(self.expanded) ;
208 rule expand-composites ( )
210 if ! $(self.composites)
212 self.composites = [ property-set.create
213 [ feature.expand-composites $(self.raw) ] ] ;
215 return $(self.composites) ;
218 rule evaluate-conditionals ( context ? )
220 context ?= $(__name__) ;
221 if ! $(self.evaluated.$(context))
223 self.evaluated.$(context) = [ property-set.create
224 [ property.evaluate-conditionals-in-context $(self.raw) : [
225 $(context).raw ] ] ] ;
227 return $(self.evaluated.$(context)) ;
232 if ! $(self.propagated-ps)
235 for local p in $(self.raw)
237 if propagated in [ feature.attributes $(p:G) ]
242 self.propagated-ps = [ property-set.create $(result) ] ;
244 return $(self.propagated-ps) ;
247 rule add-defaults ( )
249 if ! $(self.defaults)
251 self.defaults = [ property-set.create
252 [ feature.add-defaults $(self.raw) ] ] ;
254 return $(self.defaults) ;
261 self.as-path = [ property.as-path [ base-relevant ] ] ;
263 return $(self.as-path) ;
266 # Computes the path to be used for a target with the given properties.
268 # - the computed path
269 # - if the path is relative to the build directory, a value of 'true'.
273 if ! $(self.target-path)
275 # The <location> feature can be used to explicitly change the
276 # location of generated targets.
277 local l = [ get <location> ] ;
280 self.target-path = $(l) ;
284 local p = [ property-set.hash-maybe [ as-path ] ] ;
286 # A real ugly hack. Boost regression test system requires
287 # specific target paths, and it seems that changing it to handle
288 # other directory layout is really hard. For that reason, we
289 # teach V2 to do the things regression system requires. The
290 # value of '<location-prefix>' is prepended to the path.
291 local prefix = [ get <location-prefix> ] ;
294 self.target-path = [ path.join $(prefix) $(p) ] ;
298 self.target-path = $(p) ;
300 if ! $(self.target-path)
302 self.target-path = . ;
304 # The path is relative to build dir.
305 self.target-path += true ;
308 return $(self.target-path) ;
313 if ! $(self.added.$(ps))
315 self.added.$(ps) = [ property-set.create $(self.raw) [ $(ps).raw ] ]
318 return $(self.added.$(ps)) ;
321 rule add-raw ( properties * )
323 return [ add [ property-set.create $(properties) ] ] ;
326 # Returns all values of 'feature'.
330 if ! $(self.map-built)
332 # For each feature, create a member var and assign all values to it.
333 # Since all regular member vars start with 'self', there will be no
334 # conflicts between names.
335 self.map-built = true ;
336 for local v in $(self.raw)
341 return $($(feature)) ;
344 # Returns true if the property-set contains all the
345 # specified properties.
347 rule contains-raw ( properties * )
349 if $(properties) in $(self.raw)
355 # Returns true if the property-set has values for
356 # all the specified features
358 rule contains-features ( features * )
360 if $(features) in $(self.raw:G)
370 for local p in $(self.raw)
372 local att = [ feature.attributes $(p:G) ] ;
373 # A feature can be both incidental and free, in which case we add it
375 if incidental in $(att)
377 self.incidental += $(p) ;
379 else if free in $(att)
388 self.base-initialized = true ;
391 rule init-relevant ( )
393 local relevant-features = [ get <relevant> ] ;
394 relevant-features = [ feature.expand-relevant $(relevant-features) ] ;
395 relevant-features = <$(relevant-features)> ;
396 ignore-relevance = [ modules.peek property-set : .ignore-relevance ] ;
397 for local p in $(self.raw)
399 if $(ignore-relevance) || $(p:G) in $(relevant-features)
401 local att = [ feature.attributes $(p:G) ] ;
402 if ! ( incidental in $(att) )
404 self.relevant += $(p) ;
405 if ! ( free in $(att) )
407 self.base-relevant += $(p) ;
412 self.relevant-initialized = true ;
415 rule init-dependency ( )
417 for local p in $(self.raw)
419 if dependency in [ feature.attributes $(p:G) ]
421 self.dependency += $(p) ;
425 self.non-dependency += $(p) ;
428 self.dependency-initialized = true ;
431 rule init-conditional ( )
433 for local p in $(self.raw)
435 # TODO: Note that non-conditional properties may contain colon (':')
436 # characters as well, e.g. free or indirect properties. Indirect
437 # properties for example contain a full Jamfile path in their value
438 # which on Windows file systems contains ':' as the drive separator.
439 if ( [ MATCH "(:)" : $(p:G=) ] && ! ( free in [ feature.attributes $(p:G) ] ) ) || $(p:G) = <conditional>
441 self.conditional += $(p) ;
445 self.non-conditional += $(p) ;
448 self.conditional-initialized = true ;
452 # This is a temporary measure to help users work around
453 # any problems. Remove it once we've verified that
455 if --ignore-relevance in [ modules.peek : ARGV ]
457 .ignore-relevance = true ;
460 # Creates a new 'property-set' instance for the given raw properties or returns
461 # an already existing ones.
463 rule create ( raw-properties * )
465 raw-properties = [ sequence.unique
466 [ sequence.insertion-sort $(raw-properties) ] ] ;
468 local key = $(raw-properties:J=-:E=) ;
472 .ps.$(key) = [ new property-set $(raw-properties) ] ;
474 return $(.ps.$(key)) ;
476 NATIVE_RULE property-set : create ;
478 if [ HAS_NATIVE_RULE class@property-set : get : 1 ]
480 NATIVE_RULE class@property-set : get ;
483 if [ HAS_NATIVE_RULE class@property-set : contains-features : 1 ]
485 NATIVE_RULE class@property-set : contains-features ;
488 # Creates a new 'property-set' instance after checking that all properties are
489 # valid and converting implicit properties into gristed form.
491 rule create-with-validation ( raw-properties * )
493 property.validate $(raw-properties) ;
494 return [ create [ property.make $(raw-properties) ] ] ;
498 # Creates a property-set from the input given by the user, in the context of
499 # 'jamfile-module' at 'location'.
501 rule create-from-user-input ( raw-properties * : jamfile-module location )
503 local project-id = [ project.attribute $(jamfile-module) id ] ;
504 project-id ?= [ path.root $(location) [ path.pwd ] ] ;
505 return [ property-set.create [ property.translate $(raw-properties)
506 : $(project-id) : $(location) : $(jamfile-module) ] ] ;
510 # Refines requirements with requirements provided by the user. Specially handles
511 # "-<property>value" syntax in specification to remove given requirements.
512 # - parent-requirements -- property-set object with requirements to refine.
513 # - specification -- string list of requirements provided by the user.
514 # - project-module -- module to which context indirect features will be
516 # - location -- path to which path features are relative.
518 rule refine-from-user-input ( parent-requirements : specification * :
519 project-module : location )
521 if ! $(specification)
523 return $(parent-requirements) ;
527 local add-requirements ;
528 local remove-requirements ;
530 for local r in $(specification)
532 local m = [ MATCH "^-(.*)" : $(r) ] ;
535 remove-requirements += $(m) ;
539 add-requirements += $(r) ;
543 if $(remove-requirements)
545 # Need to create a property set, so that path features and indirect
546 # features are translated just like they are in project
548 local ps = [ property-set.create-from-user-input
549 $(remove-requirements) : $(project-module) $(location) ] ;
551 parent-requirements = [ property-set.create
552 [ set.difference [ $(parent-requirements).raw ]
553 : [ $(ps).raw ] ] ] ;
554 specification = $(add-requirements) ;
557 local requirements = [ property-set.create-from-user-input
558 $(specification) : $(project-module) $(location) ] ;
560 return [ $(parent-requirements).refine $(requirements) ] ;
565 # Returns a property-set with an empty set of properties.
571 .empty = [ create ] ;
577 if [ option.get hash : : yes ] = yes
579 rule hash-maybe ( path ? )
582 return [ MD5 $(path) ] ;
587 rule hash-maybe ( path ? )