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 Boost.Build'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.
42 rule __init__ ( raw-properties * )
44 self.raw = $(raw-properties) ;
46 for local p in $(raw-properties)
50 errors.error "Invalid property: '$(p)'" ;
55 # Returns Jam list of stored properties.
64 return "[" $(self.raw) "]" ;
67 # Returns properties that are neither incidental nor free.
71 if ! $(self.base-initialized)
78 # Returns free properties which are not incidental.
82 if ! $(self.base-initialized)
89 # Returns dependency properties.
93 if ! $(self.dependency-initialized)
97 return $(self.dependency) ;
100 rule non-dependency ( )
102 if ! $(self.dependency-initialized)
106 return $(self.non-dependency) ;
111 if ! $(self.conditional-initialized)
115 return $(self.conditional) ;
118 rule non-conditional ( )
120 if ! $(self.conditional-initialized)
124 return $(self.non-conditional) ;
127 # Returns incidental properties.
131 if ! $(self.base-initialized)
135 return $(self.incidental) ;
140 if ! $(self.refined.$(ps))
142 local r = [ property.refine $(self.raw) : [ $(ps).raw ] ] ;
143 if $(r[1]) != "@error"
145 self.refined.$(ps) = [ property-set.create $(r) ] ;
149 self.refined.$(ps) = $(r) ;
152 return $(self.refined.$(ps)) ;
157 if ! $(self.expanded)
159 self.expanded = [ property-set.create [ feature.expand $(self.raw) ]
162 return $(self.expanded) ;
165 rule expand-composites ( )
167 if ! $(self.composites)
169 self.composites = [ property-set.create
170 [ feature.expand-composites $(self.raw) ] ] ;
172 return $(self.composites) ;
175 rule evaluate-conditionals ( context ? )
177 context ?= $(__name__) ;
178 if ! $(self.evaluated.$(context))
180 self.evaluated.$(context) = [ property-set.create
181 [ property.evaluate-conditionals-in-context $(self.raw) : [
182 $(context).raw ] ] ] ;
184 return $(self.evaluated.$(context)) ;
189 if ! $(self.propagated-ps)
192 for local p in $(self.raw)
194 if propagated in [ feature.attributes $(p:G) ]
199 self.propagated-ps = [ property-set.create $(result) ] ;
201 return $(self.propagated-ps) ;
204 rule add-defaults ( )
206 if ! $(self.defaults)
208 self.defaults = [ property-set.create
209 [ feature.add-defaults $(self.raw) ] ] ;
211 return $(self.defaults) ;
218 self.as-path = [ property.as-path [ base ] ] ;
220 return $(self.as-path) ;
223 # Computes the path to be used for a target with the given properties.
225 # - the computed path
226 # - if the path is relative to the build directory, a value of 'true'.
230 if ! $(self.target-path)
232 # The <location> feature can be used to explicitly change the
233 # location of generated targets.
234 local l = [ get <location> ] ;
237 self.target-path = $(l) ;
241 local p = [ property-set.hash-maybe [ as-path ] ] ;
243 # A real ugly hack. Boost regression test system requires
244 # specific target paths, and it seems that changing it to handle
245 # other directory layout is really hard. For that reason, we
246 # teach V2 to do the things regression system requires. The
247 # value of '<location-prefix>' is prepended to the path.
248 local prefix = [ get <location-prefix> ] ;
251 self.target-path = [ path.join $(prefix) $(p) ] ;
255 self.target-path = $(p) ;
257 if ! $(self.target-path)
259 self.target-path = . ;
261 # The path is relative to build dir.
262 self.target-path += true ;
265 return $(self.target-path) ;
270 if ! $(self.added.$(ps))
272 self.added.$(ps) = [ property-set.create $(self.raw) [ $(ps).raw ] ]
275 return $(self.added.$(ps)) ;
278 rule add-raw ( properties * )
280 return [ add [ property-set.create $(properties) ] ] ;
283 # Returns all values of 'feature'.
287 if ! $(self.map-built)
289 # For each feature, create a member var and assign all values to it.
290 # Since all regular member vars start with 'self', there will be no
291 # conflicts between names.
292 self.map-built = true ;
293 for local v in $(self.raw)
298 return $($(feature)) ;
301 # Returns true if the property-set contains all the
302 # specified properties.
304 rule contains-raw ( properties * )
306 if $(properties) in $(self.raw)
312 # Returns true if the property-set has values for
313 # all the specified features
315 rule contains-features ( features * )
317 if $(features) in $(self.raw:G)
327 for local p in $(self.raw)
329 local att = [ feature.attributes $(p:G) ] ;
330 # A feature can be both incidental and free, in which case we add it
332 if incidental in $(att)
334 self.incidental += $(p) ;
336 else if free in $(att)
345 self.base-initialized = true ;
348 rule init-dependency ( )
350 for local p in $(self.raw)
352 if dependency in [ feature.attributes $(p:G) ]
354 self.dependency += $(p) ;
358 self.non-dependency += $(p) ;
361 self.dependency-initialized = true ;
364 rule init-conditional ( )
366 for local p in $(self.raw)
368 # TODO: Note that non-conditional properties may contain colon (':')
369 # characters as well, e.g. free or indirect properties. Indirect
370 # properties for example contain a full Jamfile path in their value
371 # which on Windows file systems contains ':' as the drive separator.
372 if [ MATCH (:) : $(p:G=) ]
374 self.conditional += $(p) ;
378 self.non-conditional += $(p) ;
381 self.conditional-initialized = true ;
386 # Creates a new 'property-set' instance for the given raw properties or returns
387 # an already existing ones.
389 rule create ( raw-properties * )
391 raw-properties = [ sequence.unique
392 [ sequence.insertion-sort $(raw-properties) ] ] ;
394 local key = $(raw-properties:J=-:E=) ;
398 .ps.$(key) = [ new property-set $(raw-properties) ] ;
400 return $(.ps.$(key)) ;
402 NATIVE_RULE property-set : create ;
404 if [ HAS_NATIVE_RULE class@property-set : get : 1 ]
406 NATIVE_RULE class@property-set : get ;
409 if [ HAS_NATIVE_RULE class@property-set : contains-features : 1 ]
411 NATIVE_RULE class@property-set : contains-features ;
414 # Creates a new 'property-set' instance after checking that all properties are
415 # valid and converting implicit properties into gristed form.
417 rule create-with-validation ( raw-properties * )
419 property.validate $(raw-properties) ;
420 return [ create [ property.make $(raw-properties) ] ] ;
424 # Creates a property-set from the input given by the user, in the context of
425 # 'jamfile-module' at 'location'.
427 rule create-from-user-input ( raw-properties * : jamfile-module location )
429 local project-id = [ project.attribute $(jamfile-module) id ] ;
430 project-id ?= [ path.root $(location) [ path.pwd ] ] ;
431 return [ property-set.create [ property.translate $(raw-properties)
432 : $(project-id) : $(location) : $(jamfile-module) ] ] ;
436 # Refines requirements with requirements provided by the user. Specially handles
437 # "-<property>value" syntax in specification to remove given requirements.
438 # - parent-requirements -- property-set object with requirements to refine.
439 # - specification -- string list of requirements provided by the user.
440 # - project-module -- module to which context indirect features will be
442 # - location -- path to which path features are relative.
444 rule refine-from-user-input ( parent-requirements : specification * :
445 project-module : location )
447 if ! $(specification)
449 return $(parent-requirements) ;
453 local add-requirements ;
454 local remove-requirements ;
456 for local r in $(specification)
458 local m = [ MATCH "^-(.*)" : $(r) ] ;
461 remove-requirements += $(m) ;
465 add-requirements += $(r) ;
469 if $(remove-requirements)
471 # Need to create a property set, so that path features and indirect
472 # features are translated just like they are in project
474 local ps = [ property-set.create-from-user-input
475 $(remove-requirements) : $(project-module) $(location) ] ;
477 parent-requirements = [ property-set.create
478 [ set.difference [ $(parent-requirements).raw ]
479 : [ $(ps).raw ] ] ] ;
480 specification = $(add-requirements) ;
483 local requirements = [ property-set.create-from-user-input
484 $(specification) : $(project-module) $(location) ] ;
486 return [ $(parent-requirements).refine $(requirements) ] ;
491 # Returns a property-set with an empty set of properties.
497 .empty = [ create ] ;
503 if [ option.get hash : : yes ] = yes
505 rule hash-maybe ( path ? )
508 return [ MD5 $(path) ] ;
513 rule hash-maybe ( path ? )