]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | # Copyright 2001, 2002, 2003 Dave Abrahams |
2 | # Copyright 2006 Rene Rivera | |
3 | # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus | |
20effc67 | 4 | # Copyright 2020 Nikita Kniazev |
7c673cae FG |
5 | # Distributed under the Boost Software License, Version 1.0. |
6 | # (See accompanying file LICENSE_1_0.txt or copy at | |
7 | # http://www.boost.org/LICENSE_1_0.txt) | |
8 | ||
9 | import feature ; | |
10 | import indirect ; | |
11 | import path ; | |
12 | import regex ; | |
13 | import string ; | |
14 | import sequence ; | |
15 | import set ; | |
16 | import utility ; | |
17 | ||
18 | ||
19 | # Refines 'properties' by overriding any non-free and non-conditional properties | |
20 | # for which a different value is specified in 'requirements'. Returns the | |
21 | # resulting list of properties. | |
22 | # | |
23 | rule refine ( properties * : requirements * ) | |
24 | { | |
25 | local result ; | |
26 | local unset ; | |
27 | ||
28 | # Collect all non-free features in requirements | |
29 | for local r in $(requirements) | |
30 | { | |
31 | # Do not consider conditional requirements. | |
11fdf7f2 | 32 | if ! [ MATCH "(:<)" : $(r:G=) ] && ! free in [ feature.attributes $(r:G) ] |
7c673cae | 33 | { |
11fdf7f2 TL |
34 | if ! $(r) in $(properties) |
35 | { | |
36 | # Kill subfeatures of properties that we're changing | |
37 | local sub = [ modules.peek feature : $(r:G).subfeatures ] ; | |
38 | if $(sub) | |
39 | { | |
40 | # non-specific subfeatures are still valid | |
41 | sub = [ MATCH "(.*:.*)" : $(sub) ] ; | |
42 | local name = [ utility.ungrist $(r:G) ] ; | |
43 | unset += <$(name)-$(sub)> ; | |
44 | } | |
45 | } | |
7c673cae FG |
46 | unset += $(r:G) ; |
47 | } | |
48 | } | |
49 | ||
50 | # Remove properties that are overridden by requirements | |
51 | for local p in $(properties) | |
52 | { | |
11fdf7f2 | 53 | if [ MATCH "(:<)" : $(p:G=) ] || ! $(p:G) in $(unset) |
7c673cae FG |
54 | { |
55 | result += $(p) ; | |
56 | } | |
57 | } | |
58 | ||
59 | return [ sequence.unique $(result) $(requirements) ] ; | |
60 | } | |
61 | ||
62 | ||
63 | # Removes all conditional properties whose conditions are not met. For those | |
64 | # with met conditions, removes the condition. Properties in conditions are | |
65 | # looked up in 'context'. | |
66 | # | |
67 | rule evaluate-conditionals-in-context ( properties * : context * ) | |
68 | { | |
69 | local base ; | |
70 | local conditionals ; | |
11fdf7f2 | 71 | local indirect ; |
7c673cae FG |
72 | for local p in $(properties) |
73 | { | |
11fdf7f2 | 74 | if [ MATCH "(:<)" : $(p) ] && ! free in [ feature.attributes $(p:G) ] |
7c673cae FG |
75 | { |
76 | conditionals += $(p) ; | |
77 | } | |
11fdf7f2 TL |
78 | else if $(p:G) = <conditional> |
79 | { | |
80 | indirect += $(p) ; | |
81 | } | |
7c673cae FG |
82 | else |
83 | { | |
84 | base += $(p) ; | |
85 | } | |
86 | } | |
87 | ||
88 | local result = $(base) ; | |
89 | for local p in $(conditionals) | |
90 | { | |
91 | # Separate condition and property. | |
11fdf7f2 | 92 | local s = [ MATCH "^(.*):(<.*)" : $(p) ] ; |
7c673cae FG |
93 | # Split condition into individual properties. |
94 | local condition = [ regex.split $(s[1]) "," ] ; | |
95 | # Evaluate condition. | |
96 | if ! [ MATCH ^(!).* : $(condition:G=) ] | |
97 | { | |
98 | # Only positive checks | |
99 | if $(condition) in $(context) | |
100 | { | |
101 | result += $(s[2]) ; | |
102 | } | |
103 | } | |
104 | else | |
105 | { | |
106 | # Have negative checks | |
107 | local fail ; | |
20effc67 | 108 | for local c in $(condition) |
7c673cae | 109 | { |
20effc67 TL |
110 | local c = [ MATCH ^(!)?(.*) : $(c) ] ; |
111 | # It is XOR: $(c[1]) = "!" ^ $(c[2]) in $(context) | |
112 | if $(c[1]) = "!" && $(c[2]) in $(context) || $(c[1]) != "!" && ! ( $(c[2]) in $(context) ) | |
7c673cae | 113 | { |
20effc67 TL |
114 | fail = true ; |
115 | break ; | |
7c673cae | 116 | } |
7c673cae FG |
117 | } |
118 | if ! $(fail) | |
119 | { | |
120 | result += $(s[2]) ; | |
121 | } | |
122 | } | |
123 | } | |
11fdf7f2 TL |
124 | # Import here to avoid cyclic dependency |
125 | import project ; | |
126 | for local i in [ MATCH "^@(.*)" : $(indirect:G=) ] | |
127 | { | |
128 | # If the rule was set in a project module, translate paths | |
129 | # relative to that project's location. | |
130 | local m = [ indirect.get-module $(i) ] ; | |
131 | local p = [ project.target $(m) : allow-missing ] ; | |
132 | local new = [ indirect.call $(i) $(context) ] ; | |
133 | if $(p) && [ $(p).location ] | |
134 | { | |
92f5a8d4 TL |
135 | local location = [ $(p).location ] ; |
136 | local project-id = [ project.attribute $(m) id ] ; | |
137 | project-id ?= [ path.root $(location) [ path.pwd ] ] ; | |
138 | result += | |
139 | [ translate $(new) : $(project-id) : $(location) : $(m) ] ; | |
11fdf7f2 TL |
140 | } |
141 | else | |
142 | { | |
143 | result += $(new) ; | |
144 | } | |
145 | } | |
7c673cae FG |
146 | return $(result) ; |
147 | } | |
148 | ||
149 | ||
11fdf7f2 TL |
150 | # Returns <relevant> properties indicating how the conditionals in |
151 | # properties affect feature relevance. If the optional argument cond | |
152 | # is passed, it is treated as extra conditions for all properties. | |
153 | # | |
154 | rule evaluate-conditional-relevance ( properties * : cond * ) | |
155 | { | |
156 | cond = [ sequence.transform utility.ungrist : $(cond:G) ] ; | |
157 | local result ; | |
158 | for local p in $(properties) | |
159 | { | |
160 | # Separate condition and property. | |
161 | local s = [ MATCH "^(.*):(<.*)" : $(p) ] ; | |
162 | if ! $(s) || free in [ feature.attributes $(p:G) ] | |
163 | { | |
164 | local value = [ utility.ungrist $(p:G) ] ; | |
165 | result += <relevant>$(value):<relevant>$(cond) ; | |
166 | } | |
167 | else | |
168 | { | |
169 | local condition = [ regex.split $(s[1]) "," ] ; | |
170 | condition = [ MATCH "^!?(.*)" : $(condition) ] ; | |
171 | condition = [ sequence.transform utility.ungrist : $(condition:G) ] $(cond) ; | |
172 | local value = [ utility.ungrist $(s[2]:G) ] ; | |
173 | result += <relevant>$(value):<relevant>$(condition) ; | |
174 | } | |
175 | } | |
176 | return [ sequence.unique $(result) ] ; | |
177 | } | |
178 | ||
179 | ||
7c673cae FG |
180 | rule expand-subfeatures-in-conditions ( properties * ) |
181 | { | |
182 | local result ; | |
183 | for local p in $(properties) | |
184 | { | |
11fdf7f2 | 185 | local s = [ MATCH "^(.*):(<.*)" : $(p) ] ; |
7c673cae FG |
186 | if ! $(s) |
187 | { | |
188 | result += $(p) ; | |
189 | } | |
190 | else | |
191 | { | |
192 | local condition = $(s[1]) ; | |
193 | local value = $(s[2]) ; | |
194 | # Condition might include several elements. | |
195 | condition = [ regex.split $(condition) "," ] ; | |
196 | local e ; | |
197 | for local c in $(condition) | |
198 | { | |
199 | # It is common for a condition to include a toolset or | |
200 | # subfeatures that have not been defined. In that case we want | |
201 | # the condition to simply 'never be satisfied' and validation | |
202 | # would only produce a spurious error so we prevent it by | |
203 | # passing 'true' as the second parameter. | |
204 | e += [ feature.expand-subfeatures $(c) : true ] ; | |
205 | } | |
206 | if $(e) = $(condition) | |
207 | { | |
208 | # (todo) | |
209 | # This is just an optimization and possibly a premature one at | |
210 | # that. | |
211 | # (todo) (12.07.2008.) (Jurko) | |
212 | result += $(p) ; | |
213 | } | |
214 | else | |
215 | { | |
11fdf7f2 | 216 | result += "$(e:J=,):$(value)" ; |
7c673cae FG |
217 | } |
218 | } | |
219 | } | |
220 | return $(result) ; | |
221 | } | |
222 | ||
223 | ||
224 | # Helper for as-path, below. Orders properties with the implicit ones first, and | |
225 | # within the two sections in alphabetical order of feature name. | |
226 | # | |
227 | local rule path-order ( x y ) | |
228 | { | |
229 | if $(y:G) && ! $(x:G) | |
230 | { | |
231 | return true ; | |
232 | } | |
233 | else if $(x:G) && ! $(y:G) | |
234 | { | |
235 | return ; | |
236 | } | |
237 | else | |
238 | { | |
239 | if ! $(x:G) | |
240 | { | |
241 | x = [ feature.expand-subfeatures $(x) ] ; | |
242 | y = [ feature.expand-subfeatures $(y) ] ; | |
243 | } | |
244 | ||
245 | if $(x[1]) < $(y[1]) | |
246 | { | |
247 | return true ; | |
248 | } | |
249 | } | |
250 | } | |
251 | ||
252 | ||
253 | local rule abbreviate-dashed ( string ) | |
254 | { | |
255 | local r ; | |
256 | for local part in [ regex.split $(string) - ] | |
257 | { | |
258 | r += [ string.abbreviate $(part) ] ; | |
259 | } | |
260 | return $(r:J=-) ; | |
261 | } | |
262 | ||
263 | ||
264 | local rule identity ( string ) | |
265 | { | |
266 | return $(string) ; | |
267 | } | |
268 | ||
269 | ||
270 | if --abbreviate-paths in [ modules.peek : ARGV ] | |
271 | { | |
272 | .abbrev = abbreviate-dashed ; | |
273 | } | |
274 | else | |
275 | { | |
276 | .abbrev = identity ; | |
277 | } | |
278 | ||
279 | ||
280 | # Returns a path representing the given expanded property set. | |
281 | # | |
282 | rule as-path ( properties * ) | |
283 | { | |
284 | local entry = .result.$(properties:J=-) ; | |
285 | ||
286 | if ! $($(entry)) | |
287 | { | |
288 | # Trim redundancy. | |
289 | properties = [ feature.minimize $(properties) ] ; | |
290 | ||
291 | # Sort according to path-order. | |
292 | properties = [ sequence.insertion-sort $(properties) : path-order ] ; | |
293 | ||
294 | local components ; | |
295 | for local p in $(properties) | |
296 | { | |
297 | if ! hidden in [ feature.attributes $(p:G) ] | |
298 | { | |
299 | if $(p:G) | |
300 | { | |
301 | local f = [ utility.ungrist $(p:G) ] ; | |
302 | p = $(f)-$(p:G=) ; | |
303 | } | |
304 | components += [ $(.abbrev) $(p) ] ; | |
305 | } | |
306 | } | |
307 | ||
308 | $(entry) = $(components:J=/) ; | |
309 | } | |
310 | ||
311 | return $($(entry)) ; | |
312 | } | |
313 | ||
314 | ||
315 | # Exit with error if property is not valid. | |
316 | # | |
317 | local rule validate1 ( property ) | |
318 | { | |
319 | local msg ; | |
320 | if $(property:G) | |
321 | { | |
322 | local feature = $(property:G) ; | |
323 | local value = $(property:G=) ; | |
324 | ||
325 | if ! [ feature.valid $(feature) ] | |
326 | { | |
327 | # Ungrist for better error messages. | |
328 | feature = [ utility.ungrist $(property:G) ] ; | |
329 | msg = "unknown feature '$(feature)'" ; | |
330 | } | |
331 | else if $(value) && ! free in [ feature.attributes $(feature) ] | |
332 | { | |
333 | feature.validate-value-string $(feature) $(value) ; | |
334 | } | |
335 | else if ! ( $(value) || ( optional in [ feature.attributes $(feature) ] ) ) | |
336 | { | |
337 | # Ungrist for better error messages. | |
338 | feature = [ utility.ungrist $(property:G) ] ; | |
339 | msg = "No value specified for feature '$(feature)'" ; | |
340 | } | |
341 | } | |
342 | else | |
343 | { | |
344 | local feature = [ feature.implied-feature $(property) ] ; | |
345 | feature.validate-value-string $(feature) $(property) ; | |
346 | } | |
347 | if $(msg) | |
348 | { | |
349 | import errors ; | |
350 | errors.error "Invalid property "'$(property:J=" ")'": "$(msg:J=" "). ; | |
351 | } | |
352 | } | |
353 | ||
354 | ||
355 | rule validate ( properties * ) | |
356 | { | |
357 | for local p in $(properties) | |
358 | { | |
359 | validate1 $(p) ; | |
360 | } | |
361 | } | |
362 | ||
363 | ||
364 | rule validate-property-sets ( property-sets * ) | |
365 | { | |
366 | for local s in $(property-sets) | |
367 | { | |
368 | validate [ feature.split $(s) ] ; | |
369 | } | |
370 | } | |
371 | ||
372 | ||
373 | # Expands any implicit property values in the given property 'specification' so | |
374 | # they explicitly state their feature. | |
375 | # | |
376 | rule make ( specification * ) | |
377 | { | |
378 | local result ; | |
379 | for local e in $(specification) | |
380 | { | |
381 | if $(e:G) | |
382 | { | |
383 | result += $(e) ; | |
384 | } | |
385 | else if [ feature.is-implicit-value $(e) ] | |
386 | { | |
387 | local feature = [ feature.implied-feature $(e) ] ; | |
388 | result += $(feature)$(e) ; | |
389 | } | |
390 | else | |
391 | { | |
392 | import errors ; | |
393 | errors.error "'$(e)' is not a valid property specification" ; | |
394 | } | |
395 | } | |
396 | return $(result) ; | |
397 | } | |
398 | ||
399 | ||
400 | # Returns a property set containing all the elements in 'properties' that do not | |
401 | # have their attributes listed in 'attributes'. | |
402 | # | |
403 | rule remove ( attributes + : properties * ) | |
404 | { | |
405 | local result ; | |
406 | for local e in $(properties) | |
407 | { | |
408 | if ! [ set.intersection $(attributes) : [ feature.attributes $(e:G) ] ] | |
409 | { | |
410 | result += $(e) ; | |
411 | } | |
412 | } | |
413 | return $(result) ; | |
414 | } | |
415 | ||
416 | ||
417 | # Returns a property set containing all the elements in 'properties' that have | |
418 | # their attributes listed in 'attributes'. | |
419 | # | |
420 | rule take ( attributes + : properties * ) | |
421 | { | |
422 | local result ; | |
423 | for local e in $(properties) | |
424 | { | |
425 | if [ set.intersection $(attributes) : [ feature.attributes $(e:G) ] ] | |
426 | { | |
427 | result += $(e) ; | |
428 | } | |
429 | } | |
430 | return $(result) ; | |
431 | } | |
432 | ||
433 | ||
434 | # Selects properties corresponding to any of the given features. | |
435 | # | |
436 | rule select ( features * : properties * ) | |
437 | { | |
438 | local result ; | |
439 | ||
440 | # Add any missing angle brackets. | |
441 | local empty = "" ; | |
442 | features = $(empty:G=$(features)) ; | |
443 | ||
444 | for local p in $(properties) | |
445 | { | |
446 | if $(p:G) in $(features) | |
447 | { | |
448 | result += $(p) ; | |
449 | } | |
450 | } | |
451 | return $(result) ; | |
452 | } | |
453 | ||
454 | ||
455 | # Returns a modified version of properties with all values of the given feature | |
456 | # replaced by the given value. If 'value' is empty the feature will be removed. | |
457 | # | |
458 | rule change ( properties * : feature value ? ) | |
459 | { | |
460 | local result ; | |
461 | for local p in $(properties) | |
462 | { | |
463 | if $(p:G) = $(feature) | |
464 | { | |
465 | result += $(value:G=$(feature)) ; | |
466 | } | |
467 | else | |
468 | { | |
469 | result += $(p) ; | |
470 | } | |
471 | } | |
472 | return $(result) ; | |
473 | } | |
474 | ||
475 | ||
476 | # If 'property' is a conditional property, returns the condition and the | |
477 | # property. E.g. <variant>debug,<toolset>gcc:<inlining>full will become | |
478 | # <variant>debug,<toolset>gcc <inlining>full. Otherwise, returns an empty | |
479 | # string. | |
480 | # | |
481 | rule split-conditional ( property ) | |
482 | { | |
483 | return [ MATCH "^(.+):(<.+)" : $(property) ] ; | |
484 | } | |
485 | ||
486 | ||
487 | rule translate-path-value ( value : path ) | |
488 | { | |
489 | local t ; | |
490 | for local v in [ regex.split $(value) "&&" ] | |
491 | { | |
492 | t += [ path.root [ path.make $(v) ] $(path) ] ; | |
493 | } | |
494 | return $(t:TJ="&&") ; | |
495 | } | |
496 | ||
497 | rule translate-dependency-value ( value : project-id : project-location ) | |
498 | { | |
499 | local split-target = [ regex.match ^(.*)//(.*) : $(value) ] ; | |
500 | if $(split-target) | |
501 | { | |
502 | local rooted = [ path.root [ path.make $(split-target[1]) ] | |
503 | [ path.root $(project-location) [ path.pwd ] ] ] ; | |
504 | return $(rooted)//$(split-target[2]) ; | |
505 | } | |
506 | else if [ path.is-rooted $(value) ] | |
507 | { | |
508 | return $(value) ; | |
509 | } | |
510 | else | |
511 | { | |
512 | return $(project-id)//$(value) ; | |
513 | } | |
514 | } | |
515 | ||
516 | rule translate-indirect-value ( rulename : context-module ) | |
517 | { | |
518 | if [ MATCH "^([^%]*)%([^%]+)$" : $(rulename) ] | |
519 | { | |
520 | # Rule is already in the 'indirect-rule' format. | |
521 | return @$(rulename) ; | |
522 | } | |
523 | else | |
524 | { | |
525 | local v ; | |
526 | if ! [ MATCH "([.])" : $(rulename) ] | |
527 | { | |
528 | # This is an unqualified rule name. The user might want to | |
529 | # set flags on this rule name and toolset.flag | |
530 | # auto-qualifies it. Need to do the same here so flag | |
531 | # setting works. We can arrange for toolset.flag to *not* | |
532 | # auto-qualify the argument but then two rules defined in | |
533 | # two Jamfiles would conflict. | |
534 | rulename = $(context-module).$(rulename) ; | |
535 | } | |
536 | v = [ indirect.make $(rulename) : $(context-module) ] ; | |
537 | return @$(v) ; | |
538 | } | |
539 | ||
540 | } | |
541 | ||
542 | # Equivalent to a calling all of: | |
543 | # translate-path | |
544 | # translate-indirect | |
545 | # translate-dependency | |
546 | # expand-subfeatures-in-conditions | |
547 | # make | |
548 | # | |
549 | rule translate ( properties * : project-id : project-location : context-module ) | |
550 | { | |
20effc67 | 551 | local translate-path-rule = [ MATCH "^<translate-path>[@](.*)$" : "$(properties)" ] ; |
7c673cae FG |
552 | local result ; |
553 | for local p in $(properties) | |
554 | { | |
555 | local split = [ split-conditional $(p) ] ; | |
556 | local condition property ; | |
557 | ||
558 | if $(split) | |
559 | { | |
560 | condition = $(split[1]) ; | |
561 | property = $(split[2]) ; | |
562 | ||
563 | local e ; | |
564 | for local c in [ regex.split $(condition) "," ] | |
565 | { | |
20effc67 TL |
566 | # strip negation for expansion and readd after |
567 | c = [ MATCH "^(!)?(.*)" : $(c) ] ; | |
568 | local expanded = [ feature.expand-subfeatures $(c[2]) : true ] ; | |
569 | e += $(c[1])$(expanded) ; | |
7c673cae FG |
570 | } |
571 | ||
11fdf7f2 | 572 | condition = "$(e:J=,):" ; |
7c673cae FG |
573 | } |
574 | else | |
575 | { | |
576 | property = $(p) ; | |
577 | } | |
578 | ||
579 | local feature = $(property:G) ; | |
580 | if ! $(feature) | |
581 | { | |
582 | if [ feature.is-implicit-value $(property) ] | |
583 | { | |
584 | feature = [ feature.implied-feature $(property) ] ; | |
585 | result += $(condition:E=)$(feature)$(property) ; | |
586 | } | |
587 | else | |
588 | { | |
589 | import errors ; | |
590 | errors.error "'$(property)' is not a valid property specification" ; | |
591 | } | |
592 | } else { | |
593 | local attributes = [ feature.attributes $(feature) ] ; | |
594 | local value ; | |
595 | # Only free features should be translated | |
596 | if free in $(attributes) | |
597 | { | |
598 | if path in $(attributes) | |
599 | { | |
20effc67 TL |
600 | if $(translate-path-rule) |
601 | { | |
602 | value = [ $(translate-path-rule) $(feature) $(property:G=) : $(properties) : $(project-id) : $(project-location) ] ; | |
603 | } | |
604 | if ! $(value) | |
605 | { | |
606 | value = [ translate-path-value $(property:G=) : $(project-location) ] ; | |
607 | } | |
7c673cae FG |
608 | result += $(condition:E=)$(feature)$(value) ; |
609 | } | |
610 | else if dependency in $(attributes) | |
611 | { | |
612 | value = [ translate-dependency-value $(property:G=) : $(project-id) : $(project-location) ] ; | |
613 | result += $(condition:E=)$(feature)$(value) ; | |
614 | } | |
615 | else | |
616 | { | |
617 | local m = [ MATCH ^@(.+) : $(property:G=) ] ; | |
618 | if $(m) | |
619 | { | |
620 | value = [ translate-indirect-value $(m) : $(context-module) ] ; | |
621 | result += $(condition:E=)$(feature)$(value) ; | |
622 | } | |
623 | else | |
624 | { | |
625 | result += $(condition:E=)$(property) ; | |
626 | } | |
627 | } | |
628 | } | |
629 | else | |
630 | { | |
631 | result += $(condition:E=)$(property) ; | |
632 | } | |
633 | } | |
634 | } | |
635 | return $(result) ; | |
636 | } | |
637 | ||
638 | # Interpret all path properties in 'properties' as relative to 'path'. The | |
639 | # property values are assumed to be in system-specific form, and will be | |
640 | # translated into normalized form. | |
641 | # | |
642 | rule translate-paths ( properties * : path ) | |
643 | { | |
644 | local result ; | |
645 | for local p in $(properties) | |
646 | { | |
647 | local split = [ split-conditional $(p) ] ; | |
648 | local condition = "" ; | |
649 | if $(split) | |
650 | { | |
11fdf7f2 | 651 | condition = "$(split[1]):" ; |
7c673cae FG |
652 | p = $(split[2]) ; |
653 | } | |
654 | ||
655 | if path in [ feature.attributes $(p:G) ] | |
656 | { | |
657 | local values = [ regex.split $(p:TG=) "&&" ] ; | |
658 | local t ; | |
659 | for local v in $(values) | |
660 | { | |
661 | t += [ path.root [ path.make $(v) ] $(path) ] ; | |
662 | } | |
663 | t = $(t:J="&&") ; | |
664 | result += $(condition)$(t:TG=$(p:G)) ; | |
665 | } | |
666 | else | |
667 | { | |
668 | result += $(condition)$(p) ; | |
669 | } | |
670 | } | |
671 | return $(result) ; | |
672 | } | |
673 | ||
674 | ||
675 | # Assumes that all feature values that start with '@' are names of rules, used | |
676 | # in 'context-module'. Such rules can be either local to the module or global. | |
677 | # Converts such values into 'indirect-rule' format (see indirect.jam), so they | |
678 | # can be called from other modules. Does nothing for such values that are | |
679 | # already in the 'indirect-rule' format. | |
680 | # | |
681 | rule translate-indirect ( specification * : context-module ) | |
682 | { | |
683 | local result ; | |
684 | for local p in $(specification) | |
685 | { | |
686 | local m = [ MATCH ^@(.+) : $(p:G=) ] ; | |
687 | if $(m) | |
688 | { | |
689 | local v ; | |
690 | if [ MATCH "^([^%]*)%([^%]+)$" : $(m) ] | |
691 | { | |
692 | # Rule is already in the 'indirect-rule' format. | |
693 | v = $(m) ; | |
694 | } | |
695 | else | |
696 | { | |
697 | if ! [ MATCH "([.])" : $(m) ] | |
698 | { | |
699 | # This is an unqualified rule name. The user might want to | |
700 | # set flags on this rule name and toolset.flag | |
701 | # auto-qualifies it. Need to do the same here so flag | |
702 | # setting works. We can arrange for toolset.flag to *not* | |
703 | # auto-qualify the argument but then two rules defined in | |
704 | # two Jamfiles would conflict. | |
705 | m = $(context-module).$(m) ; | |
706 | } | |
707 | v = [ indirect.make $(m) : $(context-module) ] ; | |
708 | } | |
709 | ||
710 | v = @$(v) ; | |
711 | result += $(v:G=$(p:G)) ; | |
712 | } | |
713 | else | |
714 | { | |
715 | result += $(p) ; | |
716 | } | |
717 | } | |
718 | return $(result) ; | |
719 | } | |
720 | ||
721 | ||
722 | # Binds all dependency properties in a list relative to the given project. | |
723 | # Targets with absolute paths will be left unchanged and targets which have a | |
724 | # project specified will have the path to the project interpreted relative to | |
725 | # the specified location. | |
726 | # | |
727 | rule translate-dependencies ( specification * : project-id : location ) | |
728 | { | |
729 | local result ; | |
730 | for local p in $(specification) | |
731 | { | |
732 | local split = [ split-conditional $(p) ] ; | |
733 | local condition = "" ; | |
734 | if $(split) | |
735 | { | |
11fdf7f2 | 736 | condition = "$(split[1]):" ; |
7c673cae FG |
737 | p = $(split[2]) ; |
738 | } | |
739 | if dependency in [ feature.attributes $(p:G) ] | |
740 | { | |
741 | local split-target = [ regex.match ^(.*)//(.*) : $(p:G=) ] ; | |
742 | if $(split-target) | |
743 | { | |
744 | local rooted = [ path.root [ path.make $(split-target[1]) ] | |
745 | [ path.root $(location) [ path.pwd ] ] ] ; | |
746 | result += $(condition)$(p:G)$(rooted)//$(split-target[2]) ; | |
747 | } | |
748 | else if [ path.is-rooted $(p:G=) ] | |
749 | { | |
750 | result += $(condition)$(p) ; | |
751 | } | |
752 | else | |
753 | { | |
754 | result += $(condition)$(p:G)$(project-id)//$(p:G=) ; | |
755 | } | |
756 | } | |
757 | else | |
758 | { | |
759 | result += $(condition)$(p) ; | |
760 | } | |
761 | } | |
762 | return $(result) ; | |
763 | } | |
764 | ||
765 | ||
766 | # Class maintaining a property set -> string mapping. | |
767 | # | |
768 | class property-map | |
769 | { | |
770 | import numbers ; | |
771 | import sequence ; | |
772 | ||
773 | rule __init__ ( ) | |
774 | { | |
775 | self.next-flag = 1 ; | |
776 | } | |
777 | ||
778 | # Associate 'value' with 'properties'. | |
779 | # | |
780 | rule insert ( properties * : value ) | |
781 | { | |
782 | self.all-flags += self.$(self.next-flag) ; | |
783 | self.$(self.next-flag) = $(value) $(properties) ; | |
784 | ||
785 | self.next-flag = [ numbers.increment $(self.next-flag) ] ; | |
786 | } | |
787 | ||
788 | # Returns the value associated with 'properties' or any subset of it. If | |
789 | # more than one subset has a value assigned to it, returns the value for the | |
790 | # longest subset, if it is unique. | |
791 | # | |
792 | rule find ( property-set ) | |
793 | { | |
794 | # First find all matches. | |
795 | local matches ; | |
796 | local match-ranks ; | |
797 | for local i in $(self.all-flags) | |
798 | { | |
799 | local list = $($(i)) ; | |
800 | if [ $(property-set).contains-raw $(list[2-]) ] | |
801 | { | |
802 | matches += $(list[1]) ; | |
803 | match-ranks += [ sequence.length $(list) ] ; | |
804 | } | |
805 | } | |
806 | local best = [ sequence.select-highest-ranked $(matches) | |
807 | : $(match-ranks) ] ; | |
808 | if $(best[2]) | |
809 | { | |
810 | import errors : error : errors.error ; | |
811 | errors.error "Ambiguous key $(properties:J= :E=)" ; | |
812 | } | |
813 | return $(best) ; | |
814 | } | |
815 | ||
816 | # Returns the value associated with 'properties'. If 'value' parameter is | |
817 | # given, replaces the found value. | |
818 | # | |
819 | rule find-replace ( properties * : value ? ) | |
820 | { | |
821 | # First find all matches. | |
822 | local matches ; | |
823 | local match-ranks ; | |
824 | for local i in $(self.all-flags) | |
825 | { | |
826 | if $($(i)[2-]) in $(properties) | |
827 | { | |
828 | matches += $(i) ; | |
829 | match-ranks += [ sequence.length $($(i)) ] ; | |
830 | } | |
831 | } | |
832 | local best = [ sequence.select-highest-ranked $(matches) | |
833 | : $(match-ranks) ] ; | |
834 | if $(best[2]) | |
835 | { | |
836 | import errors : error : errors.error ; | |
837 | errors.error "Ambiguous key $(properties:J= :E=)" ; | |
838 | } | |
839 | local original = $($(best)[1]) ; | |
840 | if $(value)-is-set | |
841 | { | |
842 | $(best) = $(value) $($(best)[2-]) ; | |
843 | } | |
844 | return $(original) ; | |
845 | } | |
846 | } | |
847 | ||
848 | ||
849 | rule __test__ ( ) | |
850 | { | |
851 | import assert ; | |
852 | import "class" : new ; | |
853 | import errors : try catch ; | |
854 | import feature ; | |
855 | ||
856 | # Local rules must be explicitly re-imported. | |
857 | import property : path-order abbreviate-dashed ; | |
858 | ||
859 | feature.prepare-test property-test-temp ; | |
860 | ||
861 | feature.feature toolset : gcc : implicit symmetric ; | |
862 | feature.subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 3.0 3.0.1 | |
863 | 3.0.2 : optional ; | |
864 | feature.feature define : : free ; | |
865 | feature.feature runtime-link : dynamic static : symmetric link-incompatible ; | |
866 | feature.feature optimization : on off ; | |
867 | feature.feature variant : debug release : implicit composite symmetric ; | |
868 | feature.feature rtti : on off : link-incompatible ; | |
869 | ||
870 | feature.compose <variant>debug : <define>_DEBUG <optimization>off ; | |
871 | feature.compose <variant>release : <define>NDEBUG <optimization>on ; | |
872 | ||
873 | validate <toolset>gcc <toolset>gcc-3.0.1 : $(test-space) ; | |
874 | ||
875 | assert.true path-order $(test-space) debug <define>foo ; | |
876 | assert.false path-order $(test-space) <define>foo debug ; | |
877 | assert.true path-order $(test-space) gcc debug ; | |
878 | assert.false path-order $(test-space) debug gcc ; | |
879 | assert.true path-order $(test-space) <optimization>on <rtti>on ; | |
880 | assert.false path-order $(test-space) <rtti>on <optimization>on ; | |
881 | ||
882 | assert.result-set-equal <toolset>gcc <rtti>off <define>FOO | |
883 | : refine <toolset>gcc <rtti>off | |
884 | : <define>FOO | |
885 | : $(test-space) ; | |
886 | ||
887 | assert.result-set-equal <toolset>gcc <optimization>on | |
888 | : refine <toolset>gcc <optimization>off | |
889 | : <optimization>on | |
890 | : $(test-space) ; | |
891 | ||
892 | assert.result-set-equal <toolset>gcc <rtti>off | |
893 | : refine <toolset>gcc : <rtti>off : $(test-space) ; | |
894 | ||
895 | assert.result-set-equal <toolset>gcc <rtti>off <rtti>off:<define>FOO | |
896 | : refine <toolset>gcc : <rtti>off <rtti>off:<define>FOO | |
897 | : $(test-space) ; | |
898 | ||
899 | assert.result-set-equal <toolset>gcc:<define>foo <toolset>gcc:<define>bar | |
900 | : refine <toolset>gcc:<define>foo : <toolset>gcc:<define>bar | |
901 | : $(test-space) ; | |
902 | ||
20effc67 TL |
903 | assert.result |
904 | : evaluate-conditionals-in-context | |
905 | <variant>release,<rtti>off:<define>MY_RELEASE | |
906 | : <toolset>gcc <variant>release <rtti>on ; | |
907 | ||
7c673cae FG |
908 | assert.result <define>MY_RELEASE |
909 | : evaluate-conditionals-in-context | |
910 | <variant>release,<rtti>off:<define>MY_RELEASE | |
911 | : <toolset>gcc <variant>release <rtti>off ; | |
912 | ||
20effc67 TL |
913 | assert.result <define>MY_RELEASE |
914 | : evaluate-conditionals-in-context | |
915 | <variant>release,!<rtti>off:<define>MY_RELEASE | |
916 | : <toolset>gcc <variant>release <rtti>on ; | |
917 | ||
918 | assert.result | |
919 | : evaluate-conditionals-in-context | |
920 | <variant>release,!<rtti>off:<define>MY_RELEASE | |
921 | : <toolset>gcc <variant>release <rtti>off ; | |
922 | ||
7c673cae FG |
923 | assert.result debug |
924 | : as-path <optimization>off <variant>debug | |
925 | : $(test-space) ; | |
926 | ||
927 | assert.result gcc/debug/rtti-off | |
928 | : as-path <toolset>gcc <optimization>off <rtti>off <variant>debug | |
929 | : $(test-space) ; | |
930 | ||
931 | assert.result optmz-off : abbreviate-dashed optimization-off ; | |
932 | assert.result rntm-lnk-sttc : abbreviate-dashed runtime-link-static ; | |
933 | ||
934 | try ; | |
935 | validate <feature>value : $(test-space) ; | |
936 | catch "Invalid property '<feature>value': unknown feature 'feature'." ; | |
937 | ||
938 | try ; | |
939 | validate <rtti>default : $(test-space) ; | |
940 | catch \"default\" is not a known value of feature <rtti> ; | |
941 | ||
942 | validate <define>WHATEVER : $(test-space) ; | |
943 | ||
944 | try ; | |
945 | validate <rtti> : $(test-space) ; | |
946 | catch "Invalid property '<rtti>': No value specified for feature 'rtti'." ; | |
947 | ||
948 | try ; | |
949 | validate value : $(test-space) ; | |
950 | catch \"value\" is not an implicit feature value ; | |
951 | ||
952 | assert.result-set-equal <rtti>on | |
953 | : remove free implicit : <toolset>gcc <define>foo <rtti>on : $(test-space) ; | |
954 | ||
955 | assert.result-set-equal <include>a | |
956 | : select include : <include>a <toolset>gcc ; | |
957 | ||
958 | assert.result-set-equal <include>a | |
959 | : select include bar : <include>a <toolset>gcc ; | |
960 | ||
961 | assert.result-set-equal <include>a <toolset>gcc | |
962 | : select include <bar> <toolset> : <include>a <toolset>gcc ; | |
963 | ||
964 | assert.result-set-equal <toolset>kylix <include>a | |
965 | : change <toolset>gcc <include>a : <toolset> kylix ; | |
966 | ||
967 | pm = [ new property-map ] ; | |
968 | $(pm).insert <toolset>gcc : o ; | |
969 | $(pm).insert <toolset>gcc <os>NT : obj ; | |
970 | $(pm).insert <toolset>gcc <os>CYGWIN : obj ; | |
971 | ||
972 | assert.equal o : [ $(pm).find-replace <toolset>gcc ] ; | |
973 | ||
974 | assert.equal obj : [ $(pm).find-replace <toolset>gcc <os>NT ] ; | |
975 | ||
976 | try ; | |
977 | $(pm).find-replace <toolset>gcc <os>NT <os>CYGWIN ; | |
978 | catch "Ambiguous key <toolset>gcc <os>NT <os>CYGWIN" ; | |
979 | ||
980 | # Test ordinary properties. | |
981 | assert.result : split-conditional <toolset>gcc ; | |
982 | ||
983 | # Test properties with ":". | |
11fdf7f2 | 984 | assert.result : split-conditional <define>"FOO=A::B" ; |
7c673cae FG |
985 | |
986 | # Test conditional feature. | |
987 | assert.result-set-equal <toolset>gcc,<toolset-gcc:version>3.0 <define>FOO | |
988 | : split-conditional <toolset>gcc,<toolset-gcc:version>3.0:<define>FOO ; | |
989 | ||
20effc67 TL |
990 | # Test translate does not choke on negations in conditional |
991 | assert.result <toolset>gcc,!<rtti>off:<define>HELLO | |
992 | : translate <toolset>gcc,!<rtti>off:<define>HELLO | |
993 | : project-id : project-location : context-module ; | |
994 | ||
7c673cae FG |
995 | feature.finish-test property-test-temp ; |
996 | } |