Imported Upstream version 1.57.0
[platform/upstream/boost.git] / tools / build / src / build / property-set.jam
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)
6
7 import "class" : new ;
8 import feature ;
9 import path ;
10 import project ;
11 import property ;
12 import sequence ;
13 import set ;
14 import option ;
15
16 # Class for storing a set of properties.
17 #
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.
21 #
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.
26 #
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.
29 #
30 #   Several operations, like and refine and as-path are provided. They all use
31 # caching whenever possible.
32 #
33 class property-set
34 {
35     import errors ;
36     import feature ;
37     import path ;
38     import property ;
39     import property-set ;
40     import set ;
41
42     rule __init__ ( raw-properties * )
43     {
44         self.raw = $(raw-properties) ;
45
46         for local p in $(raw-properties)
47         {
48             if ! $(p:G)
49             {
50                 errors.error "Invalid property: '$(p)'" ;
51             }
52         }
53     }
54
55     # Returns Jam list of stored properties.
56     #
57     rule raw ( )
58     {
59         return $(self.raw) ;
60     }
61
62     rule str ( )
63     {
64         return "[" $(self.raw) "]" ;
65     }
66
67     # Returns properties that are neither incidental nor free.
68     #
69     rule base ( )
70     {
71         if ! $(self.base-initialized)
72         {
73             init-base ;
74         }
75         return $(self.base) ;
76     }
77
78     # Returns free properties which are not incidental.
79     #
80     rule free ( )
81     {
82         if ! $(self.base-initialized)
83         {
84             init-base ;
85         }
86         return $(self.free) ;
87     }
88
89     # Returns dependency properties.
90     #
91     rule dependency ( )
92     {
93         if ! $(self.dependency-initialized)
94         {
95             init-dependency ;
96         }
97         return $(self.dependency) ;
98     }
99
100     rule non-dependency ( )
101     {
102         if ! $(self.dependency-initialized)
103         {
104             init-dependency ;
105         }
106         return $(self.non-dependency) ;
107     }
108
109     rule conditional ( )
110     {
111         if ! $(self.conditional-initialized)
112         {
113             init-conditional ;
114         }
115         return $(self.conditional) ;
116     }
117
118     rule non-conditional ( )
119     {
120         if ! $(self.conditional-initialized)
121         {
122             init-conditional ;
123         }
124         return $(self.non-conditional) ;
125     }
126
127     # Returns incidental properties.
128     #
129     rule incidental ( )
130     {
131         if ! $(self.base-initialized)
132         {
133             init-base ;
134         }
135         return $(self.incidental) ;
136     }
137
138     rule refine ( ps )
139     {
140         if ! $(self.refined.$(ps))
141         {
142             local r = [ property.refine $(self.raw) : [ $(ps).raw ] ] ;
143             if $(r[1]) != "@error"
144             {
145                 self.refined.$(ps) = [ property-set.create $(r) ] ;
146             }
147             else
148             {
149                 self.refined.$(ps) = $(r) ;
150             }
151         }
152         return $(self.refined.$(ps)) ;
153     }
154
155     rule expand ( )
156     {
157         if ! $(self.expanded)
158         {
159             self.expanded = [ property-set.create [ feature.expand $(self.raw) ]
160                 ] ;
161         }
162         return $(self.expanded) ;
163     }
164
165     rule expand-composites ( )
166     {
167         if ! $(self.composites)
168         {
169             self.composites = [ property-set.create
170                 [ feature.expand-composites $(self.raw) ] ] ;
171         }
172         return $(self.composites) ;
173     }
174
175     rule evaluate-conditionals ( context ? )
176     {
177         context ?= $(__name__) ;
178         if ! $(self.evaluated.$(context))
179         {
180             self.evaluated.$(context) = [ property-set.create
181                 [ property.evaluate-conditionals-in-context $(self.raw) : [
182                 $(context).raw ] ] ] ;
183         }
184         return $(self.evaluated.$(context)) ;
185     }
186
187     rule propagated ( )
188     {
189         if ! $(self.propagated-ps)
190         {
191             local result ;
192             for local p in $(self.raw)
193             {
194                 if propagated in [ feature.attributes $(p:G) ]
195                 {
196                     result += $(p) ;
197                 }
198             }
199             self.propagated-ps = [ property-set.create $(result) ] ;
200         }
201         return $(self.propagated-ps) ;
202     }
203
204     rule add-defaults ( )
205     {
206         if ! $(self.defaults)
207         {
208             self.defaults = [ property-set.create
209                 [ feature.add-defaults $(self.raw) ] ] ;
210         }
211         return $(self.defaults) ;
212     }
213
214     rule as-path ( )
215     {
216         if ! $(self.as-path)
217         {
218             self.as-path = [ property.as-path [ base ] ] ;
219         }
220         return $(self.as-path) ;
221     }
222
223     # Computes the path to be used for a target with the given properties.
224     # Returns a list of
225     #   - the computed path
226     #   - if the path is relative to the build directory, a value of 'true'.
227     #
228     rule target-path ( )
229     {
230         if ! $(self.target-path)
231         {
232             # The <location> feature can be used to explicitly change the
233             # location of generated targets.
234             local l = [ get <location> ] ;
235             if $(l)
236             {
237                 self.target-path = $(l) ;
238             }
239             else
240             {
241                 local p = [ property-set.hash-maybe [ as-path ] ] ;
242
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> ] ;
249                 if $(prefix)
250                 {
251                     self.target-path = [ path.join $(prefix) $(p) ] ;
252                 }
253                 else
254                 {
255                     self.target-path = $(p) ;
256                 }
257                 if ! $(self.target-path)
258                 {
259                     self.target-path = . ;
260                 }
261                 # The path is relative to build dir.
262                 self.target-path += true ;
263             }
264         }
265         return $(self.target-path) ;
266     }
267
268     rule add ( ps )
269     {
270         if ! $(self.added.$(ps))
271         {
272             self.added.$(ps) = [ property-set.create $(self.raw) [ $(ps).raw ] ]
273                 ;
274         }
275         return $(self.added.$(ps)) ;
276     }
277
278     rule add-raw ( properties * )
279     {
280         return [ add [ property-set.create $(properties) ] ] ;
281     }
282
283     # Returns all values of 'feature'.
284     #
285     rule get ( feature )
286     {
287         if ! $(self.map-built)
288         {
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)
294             {
295                 $(v:G) += $(v:G=) ;
296             }
297         }
298         return $($(feature)) ;
299     }
300
301     # Returns true if the property-set contains all the
302     # specified properties.
303     #
304     rule contains-raw ( properties * )
305     {
306         if $(properties) in $(self.raw)
307         {
308             return true ;
309         }
310     }
311
312     # Returns true if the property-set has values for
313     # all the specified features
314     #
315     rule contains-features ( features * )
316     {
317         if $(features) in $(self.raw:G)
318         {
319             return true ;
320         }
321     }
322
323     # private
324
325     rule init-base ( )
326     {
327         for local p in $(self.raw)
328         {
329             local att = [ feature.attributes $(p:G) ] ;
330             # A feature can be both incidental and free, in which case we add it
331             # to incidental.
332             if incidental in $(att)
333             {
334                 self.incidental += $(p) ;
335             }
336             else if free in $(att)
337             {
338                 self.free += $(p) ;
339             }
340             else
341             {
342                 self.base += $(p) ;
343             }
344         }
345         self.base-initialized = true ;
346     }
347
348     rule init-dependency ( )
349     {
350         for local p in $(self.raw)
351         {
352             if dependency in [ feature.attributes $(p:G) ]
353             {
354                 self.dependency += $(p) ;
355             }
356             else
357             {
358                 self.non-dependency += $(p) ;
359             }
360         }
361         self.dependency-initialized = true ;
362     }
363
364     rule init-conditional ( )
365     {
366         for local p in $(self.raw)
367         {
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=) ]
373             {
374                 self.conditional += $(p) ;
375             }
376             else
377             {
378                 self.non-conditional += $(p) ;
379             }
380         }
381         self.conditional-initialized = true ;
382     }
383 }
384
385
386 # Creates a new 'property-set' instance for the given raw properties or returns
387 # an already existing ones.
388 #
389 rule create ( raw-properties * )
390 {
391     raw-properties = [ sequence.unique
392         [ sequence.insertion-sort $(raw-properties) ] ] ;
393
394     local key = $(raw-properties:J=-:E=) ;
395
396     if ! $(.ps.$(key))
397     {
398         .ps.$(key) = [ new property-set $(raw-properties) ] ;
399     }
400     return $(.ps.$(key)) ;
401 }
402 NATIVE_RULE property-set : create ;
403
404 if [ HAS_NATIVE_RULE class@property-set : get : 1 ]
405 {
406     NATIVE_RULE class@property-set : get ;
407 }
408
409 if [ HAS_NATIVE_RULE class@property-set : contains-features : 1 ]
410 {
411     NATIVE_RULE class@property-set : contains-features ;
412 }
413
414 # Creates a new 'property-set' instance after checking that all properties are
415 # valid and converting implicit properties into gristed form.
416 #
417 rule create-with-validation ( raw-properties * )
418 {
419     property.validate $(raw-properties) ;
420     return [ create [ property.make $(raw-properties) ] ] ;
421 }
422
423
424 # Creates a property-set from the input given by the user, in the context of
425 # 'jamfile-module' at 'location'.
426 #
427 rule create-from-user-input ( raw-properties * : jamfile-module location )
428 {
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) ] ] ;
433 }
434
435
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
441 #                          bound.
442 # - location            -- path to which path features are relative.
443 #
444 rule refine-from-user-input ( parent-requirements : specification * :
445     project-module : location )
446 {
447     if ! $(specification)
448     {
449         return $(parent-requirements) ;
450     }
451     else
452     {
453         local add-requirements ;
454         local remove-requirements ;
455
456         for local r in $(specification)
457         {
458             local m = [ MATCH "^-(.*)" : $(r) ] ;
459             if $(m)
460             {
461                 remove-requirements += $(m) ;
462             }
463             else
464             {
465                 add-requirements += $(r) ;
466             }
467         }
468
469         if $(remove-requirements)
470         {
471             # Need to create a property set, so that path features and indirect
472             # features are translated just like they are in project
473             # requirements.
474             local ps = [ property-set.create-from-user-input
475                 $(remove-requirements) : $(project-module) $(location) ] ;
476
477             parent-requirements = [ property-set.create
478                 [ set.difference [ $(parent-requirements).raw ]
479                 : [ $(ps).raw ] ] ] ;
480             specification = $(add-requirements) ;
481         }
482
483         local requirements = [ property-set.create-from-user-input
484             $(specification) : $(project-module) $(location) ] ;
485
486         return [ $(parent-requirements).refine $(requirements) ] ;
487     }
488 }
489
490
491 # Returns a property-set with an empty set of properties.
492 #
493 rule empty ( )
494 {
495     if ! $(.empty)
496     {
497         .empty = [ create ] ;
498     }
499     return $(.empty) ;
500 }
501
502
503 if [ option.get hash : : yes ] = yes
504 {
505     rule hash-maybe ( path ? )
506     {
507         path ?= "" ;
508         return [ MD5 $(path) ] ;
509     }
510 }
511 else
512 {
513     rule hash-maybe ( path ? )
514     {
515         return $(path) ;
516     }
517 }