]>
Commit | Line | Data |
---|---|---|
1 | # Copyright Vladimir Prus 2002. | |
2 | # Copyright Rene Rivera 2006. | |
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 | # Supports 'abstract' targets, which are targets explicitly defined in a | |
8 | # Jamfile. | |
9 | # | |
10 | # Abstract targets are represented by classes derived from 'abstract-target' | |
11 | # class. The first abstract target is 'project-target', which is created for | |
12 | # each Jamfile, and can be obtained by the 'target' rule in the Jamfile's module | |
13 | # (see project.jam). | |
14 | # | |
15 | # Project targets keep a list of 'main-target' instances. A main target is what | |
16 | # the user explicitly defines in a Jamfile. It is possible to have several | |
17 | # definitions for a main target, for example to have different lists of sources | |
18 | # for different platforms. So, main targets keep a list of alternatives. | |
19 | # | |
20 | # Each alternative is an instance of 'abstract-target'. When a main target | |
21 | # subvariant is defined by some rule, that rule will decide what class to use, | |
22 | # create an instance of that class and add it to the list of alternatives for | |
23 | # the main target. | |
24 | # | |
25 | # Rules supplied by the build system will use only targets derived from the | |
26 | # 'basic-target' class, which will provide some default behaviour. There will be | |
27 | # different classes derived from it such as 'make-target', created by the 'make' | |
28 | # rule, and 'typed-target', created by rules such as 'exe' and 'lib'. | |
29 | # | |
30 | # +--------------------------+ | |
31 | # | abstract-target | | |
32 | # +==========================+ | |
33 | # | name | | |
34 | # | project | | |
35 | # | | | |
36 | # | generate(properties) = 0 | | |
37 | # +-------------+------------+ | |
38 | # | | |
39 | # ^ | |
40 | # / \ | |
41 | # +-+-+ | |
42 | # | | |
43 | # | | |
44 | # +------------------+-----+-------------------------------+ | |
45 | # | | | | |
46 | # | | | | |
47 | # +-----------+----------+ +------+------+ +-------+------+ | |
48 | # | project-target | | main-target | | basic-target | | |
49 | # +======================+ 1 * +=============+ alternatives +==============+ | |
50 | # | generate(properties) |o-----+ generate |<>------------->| generate | | |
51 | # | main-target | +-------------+ | construct = 0| | |
52 | # +----------------------+ +-------+------+ | |
53 | # | | |
54 | # ^ | |
55 | # / \ | |
56 | # +-+-+ | |
57 | # | | |
58 | # | | |
59 | # ...--+-----------------+-----------------+------------------+ | |
60 | # | | | | | |
61 | # | | | | | |
62 | # ... ---+-----+ +-------+------+ +------+------+ +-------+------+ | |
63 | # | | typed-target | | make-target | | stage-target | | |
64 | # . +==============+ +=============+ +==============+ | |
65 | # . | construct | | construct | | construct | | |
66 | # +--------------+ +-------------+ +--------------+ | |
67 | ||
68 | import assert ; | |
69 | import build-request ; | |
70 | import "class" : new ; | |
71 | import feature ; | |
72 | import indirect ; | |
73 | import path ; | |
74 | import property ; | |
75 | import property-set ; | |
76 | import sequence ; | |
77 | import set ; | |
78 | import toolset ; | |
79 | ||
80 | ||
81 | # Base class for all abstract targets. | |
82 | # | |
83 | class abstract-target | |
84 | { | |
85 | import assert ; | |
86 | import "class" ; | |
87 | import errors ; | |
88 | import project ; | |
89 | ||
90 | rule __init__ ( name # Name of the target in Jamfile. | |
91 | : project-target # The project target to which this one belongs. | |
92 | ) | |
93 | { | |
94 | # Note: it might seem that we don't need either name or project at all. | |
95 | # However, there are places where we really need it. One example is | |
96 | # error messages which should name problematic targets. Another is | |
97 | # setting correct paths for sources and generated files. | |
98 | ||
99 | self.name = $(name) ; | |
100 | self.project = $(project-target) ; | |
101 | self.location = [ errors.nearest-user-location ] ; | |
102 | } | |
103 | ||
104 | # Returns the name of this target. | |
105 | rule name ( ) | |
106 | { | |
107 | return $(self.name) ; | |
108 | } | |
109 | ||
110 | # Returns the project for this target. | |
111 | rule project ( ) | |
112 | { | |
113 | return $(self.project) ; | |
114 | } | |
115 | ||
116 | # Return the location where the target was declared. | |
117 | rule location ( ) | |
118 | { | |
119 | return $(self.location) ; | |
120 | } | |
121 | ||
122 | # Returns a user-readable name for this target. | |
123 | rule full-name ( ) | |
124 | { | |
125 | local location = [ $(self.project).get location ] ; | |
126 | return $(location)/$(self.name) ; | |
127 | } | |
128 | ||
129 | # Generates virtual targets for this abstract target using the specified | |
130 | # properties, unless a different value of some feature is required by the | |
131 | # target. | |
132 | # On success, returns: | |
133 | # - a property-set with the usage requirements to be applied to dependants | |
134 | # - a list of produced virtual targets, which may be empty. | |
135 | # If 'property-set' is empty, performs the default build of this target, in | |
136 | # a way specific to the derived class. | |
137 | # | |
138 | rule generate ( property-set ) | |
139 | { | |
140 | errors.error "method should be defined in derived classes" ; | |
141 | } | |
142 | ||
143 | rule rename ( new-name ) | |
144 | { | |
145 | self.name = $(new-name) ; | |
146 | } | |
147 | } | |
148 | ||
149 | ||
150 | if --debug-building in [ modules.peek : ARGV ] | |
151 | { | |
152 | modules.poke : .debug-building : true ; | |
153 | } | |
154 | ||
155 | ||
156 | rule indent ( ) | |
157 | { | |
158 | return $(.indent:J="") ; | |
159 | } | |
160 | ||
161 | ||
162 | rule increase-indent ( ) | |
163 | { | |
164 | .indent += " " ; | |
165 | } | |
166 | ||
167 | ||
168 | rule decrease-indent ( ) | |
169 | { | |
170 | .indent = $(.indent[2-]) ; | |
171 | } | |
172 | ||
173 | ||
174 | # Project target class (derived from 'abstract-target'). | |
175 | # | |
176 | # This class has the following responsibilities: | |
177 | # - Maintaining a list of main targets in this project and building them. | |
178 | # | |
179 | # Main targets are constructed in two stages: | |
180 | # - When Jamfile is read, a number of calls to 'add-alternative' is made. At | |
181 | # that time, alternatives can also be renamed to account for inline targets. | |
182 | # - The first time 'main-target' or 'has-main-target' rule is called, all | |
183 | # alternatives are enumerated and main targets are created. | |
184 | # | |
185 | class project-target : abstract-target | |
186 | { | |
187 | import project ; | |
188 | import targets ; | |
189 | import path ; | |
190 | import print ; | |
191 | import property-set ; | |
192 | import set ; | |
193 | import sequence ; | |
194 | import "class" : new ; | |
195 | ||
196 | rule __init__ ( name : project-module parent-project ? | |
197 | : requirements * : default-build * ) | |
198 | { | |
199 | abstract-target.__init__ $(name) : $(__name__) ; | |
200 | ||
201 | self.project-module = $(project-module) ; | |
202 | self.location = [ project.attribute $(project-module) location ] ; | |
203 | self.requirements = $(requirements) ; | |
204 | self.default-build = $(default-build) ; | |
205 | ||
206 | if $(parent-project) | |
207 | { | |
208 | inherit $(parent-project) ; | |
209 | } | |
210 | } | |
211 | ||
212 | # This is needed only by the 'make' rule. Need to find a way to make 'make' | |
213 | # work without this method. | |
214 | # | |
215 | rule project-module ( ) | |
216 | { | |
217 | return $(self.project-module) ; | |
218 | } | |
219 | ||
220 | rule get ( attribute ) | |
221 | { | |
222 | return [ project.attribute $(self.project-module) $(attribute) ] ; | |
223 | } | |
224 | ||
225 | rule build-dir ( ) | |
226 | { | |
227 | if ! $(self.build-dir) | |
228 | { | |
229 | self.build-dir = [ get build-dir ] ; | |
230 | if ! $(self.build-dir) | |
231 | { | |
232 | local location = [ $(self.project).get location ] ; | |
233 | if $(location) | |
234 | { | |
235 | self.build-dir = [ path.join $(location) bin ] ; | |
236 | } | |
237 | else | |
238 | { | |
239 | local id = [ get id ] ; | |
240 | if $(id) | |
241 | { | |
242 | local rid = [ MATCH ^/(.*) : $(id) ] ; | |
243 | self.build-dir = [ path.join [ project.standalone-build-dir ] $(rid) ] ; | |
244 | } | |
245 | else | |
246 | { | |
247 | errors.error "Could not create build-dir for standalone project $(self.project-module:E=)." | |
248 | : "Missing project id" ; | |
249 | } | |
250 | } | |
251 | } | |
252 | } | |
253 | return $(self.build-dir) ; | |
254 | } | |
255 | ||
256 | # Generates all possible targets contained in this project. | |
257 | # | |
258 | rule generate ( property-set * ) | |
259 | { | |
260 | if [ modules.peek : .debug-building ] | |
261 | { | |
262 | ECHO [ targets.indent ] "building project" [ name ] | |
263 | " ('$(__name__)') with" [ $(property-set).raw ] ; | |
264 | targets.increase-indent ; | |
265 | } | |
266 | ||
267 | local usage-requirements = [ property-set.empty ] ; | |
268 | local targets ; | |
269 | ||
270 | for local t in [ targets-to-build ] | |
271 | { | |
272 | local g = [ $(t).generate $(property-set) ] ; | |
273 | usage-requirements = [ $(usage-requirements).add $(g[1]) ] ; | |
274 | targets += $(g[2-]) ; | |
275 | } | |
276 | targets.decrease-indent ; | |
277 | return $(usage-requirements) [ sequence.unique $(targets) ] ; | |
278 | } | |
279 | ||
280 | # Computes and returns a list of abstract-target instances which must be | |
281 | # built when this project is built. | |
282 | # | |
283 | rule targets-to-build ( ) | |
284 | { | |
285 | local result ; | |
286 | ||
287 | if ! $(self.built-main-targets) | |
288 | { | |
289 | build-main-targets ; | |
290 | } | |
291 | ||
292 | # Collect all main targets here, except for "explicit" ones. | |
293 | for local t in $(self.main-targets) | |
294 | { | |
295 | if ! [ $(t).name ] in $(self.explicit-targets) | |
296 | { | |
297 | result += $(t) ; | |
298 | } | |
299 | } | |
300 | ||
301 | # Collect all projects referenced via "projects-to-build" attribute. | |
302 | local self-location = [ get location ] ; | |
303 | for local pn in [ get projects-to-build ] | |
304 | { | |
305 | result += [ find $(pn)/ ] ; | |
306 | } | |
307 | ||
308 | return $(result) ; | |
309 | } | |
310 | ||
311 | # Add 'target' to the list of targets in this project that should be build | |
312 | # only by explicit request | |
313 | # | |
314 | rule mark-target-as-explicit ( target-name * ) | |
315 | { | |
316 | # Record the name of the target, not instance, since this rule is called | |
317 | # before main target instances are created. | |
318 | self.explicit-targets += $(target-name) ; | |
319 | } | |
320 | ||
321 | rule mark-target-as-always ( target-name * ) | |
322 | { | |
323 | # Record the name of the target, not instance, since this rule is called | |
324 | # before main target instances are created. | |
325 | self.always-targets += $(target-name) ; | |
326 | } | |
327 | ||
328 | # Add new target alternative | |
329 | # | |
330 | rule add-alternative ( target-instance ) | |
331 | { | |
332 | if $(self.built-main-targets) | |
333 | { | |
334 | import errors : error : errors.error ; | |
335 | errors.error add-alternative called when main targets are already | |
336 | created. : in project [ full-name ] ; | |
337 | } | |
338 | self.alternatives += $(target-instance) ; | |
339 | if ! ( [ $(target-instance).name ] in $(self.alternative-names) ) | |
340 | { | |
341 | self.alternative-names += [ $(target-instance).name ] ; | |
342 | } | |
343 | } | |
344 | ||
345 | # Checks if an alternative was declared for the target. | |
346 | # Unlike checking for a main target this does not require | |
347 | # building the main targets. And hence can be used in/directly | |
348 | # while loading a project. | |
349 | # | |
350 | rule has-alternative-for-target ( target-name ) | |
351 | { | |
352 | if $(target-name) in $(self.alternative-names) | |
353 | { | |
354 | return 1 ; | |
355 | } | |
356 | } | |
357 | ||
358 | # Returns a 'main-target' class instance corresponding to 'name'. | |
359 | # | |
360 | rule main-target ( name ) | |
361 | { | |
362 | if ! $(self.built-main-targets) | |
363 | { | |
364 | build-main-targets ; | |
365 | } | |
366 | return $(self.main-target.$(name)) ; | |
367 | } | |
368 | ||
369 | # Returns whether a main target with the specified name exists. | |
370 | # | |
371 | rule has-main-target ( name ) | |
372 | { | |
373 | if ! $(self.built-main-targets) | |
374 | { | |
375 | build-main-targets ; | |
376 | } | |
377 | ||
378 | if $(self.main-target.$(name)) | |
379 | { | |
380 | return true ; | |
381 | } | |
382 | } | |
383 | ||
384 | # Worker function for the find rule not implementing any caching and simply | |
385 | # returning nothing in case the target can not be found. | |
386 | # | |
387 | rule find-really ( id ) | |
388 | { | |
389 | local result ; | |
390 | local current-location = [ get location ] ; | |
391 | ||
392 | local split = [ MATCH ^(.*)//(.*)$ : $(id) ] ; | |
393 | local project-part = $(split[1]) ; | |
394 | local target-part = $(split[2]) ; | |
395 | ||
396 | local extra-error-message ; | |
397 | if $(project-part) | |
398 | { | |
399 | # There is an explicitly specified project part in id. Looks up the | |
400 | # project and passes the request to it. | |
401 | local pm = [ project.find $(project-part) : $(current-location) ] ; | |
402 | if $(pm) | |
403 | { | |
404 | project-target = [ project.target $(pm) ] ; | |
405 | result = [ $(project-target).find $(target-part) : no-error ] ; | |
406 | } | |
407 | else | |
408 | { | |
409 | extra-error-message = could not resolve project reference | |
410 | '$(project-part)' ; | |
411 | if ! [ path.is-rooted $(project-part) ] | |
412 | { | |
413 | local rooted = [ path.root $(project-part) / ] ; | |
414 | if $(rooted) && [ project.is-registered-id $(rooted) ] | |
415 | { | |
416 | extra-error-message += - possibly missing a leading | |
417 | slash ('/') character. ; | |
418 | } | |
419 | } | |
420 | } | |
421 | } | |
422 | else | |
423 | { | |
424 | # Interpret target-name as name of main target. Need to do this | |
425 | # before checking for file. Consider the following scenario with a | |
426 | # toolset not modifying its executable's names, e.g. gcc on | |
427 | # Unix-like platforms: | |
428 | # | |
429 | # exe test : test.cpp ; | |
430 | # install s : test : <location>. ; | |
431 | # | |
432 | # After the first build we would have a target named 'test' in the | |
433 | # Jamfile and a file named 'test' on the disk. We need the target to | |
434 | # override the file. | |
435 | result = [ main-target $(id) ] ; | |
436 | ||
437 | # Interpret id as an existing file reference. | |
438 | if ! $(result) | |
439 | { | |
440 | result = [ new file-reference [ path.make $(id) ] : | |
441 | $(self.project) ] ; | |
442 | if ! [ $(result).exists ] | |
443 | { | |
444 | result = ; | |
445 | } | |
446 | } | |
447 | ||
448 | # Interpret id as project-id. | |
449 | if ! $(result) | |
450 | { | |
451 | local project-module = [ project.find $(id) : | |
452 | $(current-location) ] ; | |
453 | if $(project-module) | |
454 | { | |
455 | result = [ project.target $(project-module) ] ; | |
456 | } | |
457 | } | |
458 | } | |
459 | ||
460 | return $(result:E="") $(extra-error-message) ; | |
461 | } | |
462 | ||
463 | # Find and return the target with the specified id, treated relative to | |
464 | # self. Id may specify either a target or a file name with the target taking | |
465 | # priority. May report an error or return nothing if the target is not found | |
466 | # depending on the 'no-error' parameter. | |
467 | # | |
468 | rule find ( id : no-error ? ) | |
469 | { | |
470 | local v = $(.id.$(id)) ; | |
471 | local extra-error-message ; | |
472 | if ! $(v) | |
473 | { | |
474 | local r = [ find-really $(id) ] ; | |
475 | v = $(r[1]) ; | |
476 | extra-error-message = $(r[2-]) ; | |
477 | if ! $(v) | |
478 | { | |
479 | v = none ; | |
480 | } | |
481 | .id.$(id) = $(v) ; | |
482 | } | |
483 | ||
484 | if $(v) != none | |
485 | { | |
486 | return $(v) ; | |
487 | } | |
488 | else if ! $(no-error) | |
489 | { | |
490 | local current-location = [ get location ] ; | |
491 | import errors : user-error : errors.user-error ; | |
492 | errors.user-error Unable to find file or target named | |
493 | : " " '$(id)' | |
494 | : referred to from project at | |
495 | : " " '$(current-location)' | |
496 | : $(extra-error-message) ; | |
497 | } | |
498 | } | |
499 | ||
500 | rule build-main-targets ( ) | |
501 | { | |
502 | self.built-main-targets = true ; | |
503 | for local a in $(self.alternatives) | |
504 | { | |
505 | local name = [ $(a).name ] ; | |
506 | local target = $(self.main-target.$(name)) ; | |
507 | if ! $(target) | |
508 | { | |
509 | local t = [ new main-target $(name) : $(self.project) ] ; | |
510 | self.main-target.$(name) = $(t) ; | |
511 | self.main-targets += $(t) ; | |
512 | target = $(self.main-target.$(name)) ; | |
513 | } | |
514 | ||
515 | if $(name) in $(self.always-targets) | |
516 | { | |
517 | $(a).always ; | |
518 | } | |
519 | ||
520 | $(target).add-alternative $(a) ; | |
521 | } | |
522 | } | |
523 | ||
524 | # Accessor, add a constant. | |
525 | # | |
526 | rule add-constant ( | |
527 | name # Variable name of the constant. | |
528 | : value + # Value of the constant. | |
529 | : type ? # Optional type of value. | |
530 | ) | |
531 | { | |
532 | switch $(type) | |
533 | { | |
534 | case path : | |
535 | local r ; | |
536 | for local v in $(value) | |
537 | { | |
538 | local l = $(self.location) ; | |
539 | if ! $(l) | |
540 | { | |
541 | # Project corresponding to config files do not have | |
542 | # 'location' attribute, but do have source location. It | |
543 | # might be more reasonable to make every project have a | |
544 | # location and use some other approach to prevent buildable | |
545 | # targets in config files, but that has been left for later. | |
546 | l = [ get source-location ] ; | |
547 | } | |
548 | v = [ path.root [ path.make $(v) ] $(l) ] ; | |
549 | # Now make the value absolute path. | |
550 | v = [ path.root $(v) [ path.pwd ] ] ; | |
551 | # Constants should be in platform-native form. | |
552 | v = [ path.native $(v) ] ; | |
553 | r += $(v) ; | |
554 | } | |
555 | value = $(r) ; | |
556 | } | |
557 | if ! $(name) in $(self.constants) | |
558 | { | |
559 | self.constants += $(name) ; | |
560 | } | |
561 | self.constant.$(name) = $(value) ; | |
562 | # Inject the constant in the scope of the Jamroot module. | |
563 | modules.poke $(self.project-module) : $(name) : $(value) ; | |
564 | } | |
565 | ||
566 | rule inherit ( parent ) | |
567 | { | |
568 | for local c in [ modules.peek $(parent) : self.constants ] | |
569 | { | |
570 | # No need to pass the type. Path constants were converted to | |
571 | # absolute paths already by parent. | |
572 | add-constant $(c) : [ modules.peek $(parent) : self.constant.$(c) ] | |
573 | ; | |
574 | } | |
575 | ||
576 | # Import rules from parent. | |
577 | local this-module = [ project-module ] ; | |
578 | local parent-module = [ $(parent).project-module ] ; | |
579 | # Do not import rules coming from 'project-rules' as they must be | |
580 | # imported localized. | |
581 | local user-rules = [ set.difference | |
582 | [ RULENAMES $(parent-module) ] : | |
583 | [ RULENAMES project-rules ] ] ; | |
584 | IMPORT $(parent-module) : $(user-rules) : $(this-module) : $(user-rules) | |
585 | ; | |
586 | EXPORT $(this-module) : $(user-rules) ; | |
587 | } | |
588 | } | |
589 | ||
590 | ||
591 | # Helper rules to detect cycles in main target references. | |
592 | # | |
593 | local rule start-building ( main-target-instance ) | |
594 | { | |
595 | if $(main-target-instance) in $(.targets-being-built) | |
596 | { | |
597 | local names ; | |
598 | for local t in $(.targets-being-built) $(main-target-instance) | |
599 | { | |
600 | names += [ $(t).full-name ] ; | |
601 | } | |
602 | ||
603 | import errors ; | |
604 | errors.error "Recursion in main target references" | |
605 | : "the following target are being built currently:" | |
606 | : $(names) ; | |
607 | } | |
608 | .targets-being-built += $(main-target-instance) ; | |
609 | } | |
610 | ||
611 | ||
612 | local rule end-building ( main-target-instance ) | |
613 | { | |
614 | .targets-being-built = $(.targets-being-built[1--2]) ; | |
615 | } | |
616 | ||
617 | ||
618 | # A named top-level target in Jamfile. | |
619 | # | |
620 | class main-target : abstract-target | |
621 | { | |
622 | import assert ; | |
623 | import feature ; | |
624 | import print ; | |
625 | import property-set ; | |
626 | import sequence ; | |
627 | import targets : start-building end-building ; | |
628 | ||
629 | rule __init__ ( name : project ) | |
630 | { | |
631 | abstract-target.__init__ $(name) : $(project) ; | |
632 | } | |
633 | ||
634 | # Add a new alternative for this target | |
635 | rule add-alternative ( target ) | |
636 | { | |
637 | local d = [ $(target).default-build ] ; | |
638 | if $(self.alternatives) && ( $(self.default-build) != $(d) ) | |
639 | { | |
640 | import errors : error : errors.error ; | |
641 | errors.error "default build must be identical in all alternatives" | |
642 | : "main target is" [ full-name ] | |
643 | : "with" [ $(d).raw ] | |
644 | : "differing from previous default build" | |
645 | [ $(self.default-build).raw ] ; | |
646 | } | |
647 | else | |
648 | { | |
649 | self.default-build = $(d) ; | |
650 | } | |
651 | self.alternatives += $(target) ; | |
652 | } | |
653 | ||
654 | # Returns the best viable alternative for this property-set. See the | |
655 | # documentation for selection rules. | |
656 | # | |
657 | rule select-alternatives ( property-set debug ? ) | |
658 | { | |
659 | # When selecting alternatives we have to consider defaults, for example: | |
660 | # lib l : l.cpp : <variant>debug ; | |
661 | # lib l : l_opt.cpp : <variant>release ; | |
662 | # will not work unless we add default value <variant>debug. | |
663 | property-set = [ $(property-set).add-defaults ] ; | |
664 | ||
665 | # The algorithm: we keep the current best viable alternative. When we | |
666 | # encounter a new best viable alternative, we compare it with the | |
667 | # current one. | |
668 | ||
669 | local best ; | |
670 | local best-properties ; | |
671 | ||
672 | if $(self.alternatives[2-]) | |
673 | { | |
674 | local bad ; | |
675 | local worklist = $(self.alternatives) ; | |
676 | while $(worklist) && ! $(bad) | |
677 | { | |
678 | local v = $(worklist[1]) ; | |
679 | local properties = [ $(v).match $(property-set) $(debug) ] ; | |
680 | ||
681 | if $(properties) != no-match | |
682 | { | |
683 | if ! $(best) | |
684 | { | |
685 | best = $(v) ; | |
686 | best-properties = $(properties) ; | |
687 | } | |
688 | else | |
689 | { | |
690 | if $(properties) = $(best-properties) | |
691 | { | |
692 | bad = true ; | |
693 | } | |
694 | else if $(properties) in $(best-properties) | |
695 | { | |
696 | # Do nothing, this alternative is worse | |
697 | } | |
698 | else if $(best-properties) in $(properties) | |
699 | { | |
700 | best = $(v) ; | |
701 | best-properties = $(properties) ; | |
702 | } | |
703 | else | |
704 | { | |
705 | bad = true ; | |
706 | } | |
707 | } | |
708 | } | |
709 | worklist = $(worklist[2-]) ; | |
710 | } | |
711 | if ! $(bad) | |
712 | { | |
713 | return $(best) ; | |
714 | } | |
715 | } | |
716 | else | |
717 | { | |
718 | return $(self.alternatives) ; | |
719 | } | |
720 | } | |
721 | ||
722 | rule apply-default-build ( property-set ) | |
723 | { | |
724 | return [ targets.apply-default-build $(property-set) : | |
725 | $(self.default-build) ] ; | |
726 | } | |
727 | ||
728 | # Select an alternative for this main target, by finding all alternatives | |
729 | # whose requirements are satisfied by 'properties' and picking the one with | |
730 | # the longest requirements set. Returns the result of calling 'generate' on | |
731 | # that alternative. | |
732 | # | |
733 | rule generate ( property-set ) | |
734 | { | |
735 | start-building $(__name__) ; | |
736 | ||
737 | # We want composite properties in the build request to act as if all the | |
738 | # properties they expand to have been explicitly specified. | |
739 | property-set = [ $(property-set).expand ] ; | |
740 | ||
741 | local all-property-sets = [ apply-default-build $(property-set) ] ; | |
742 | local usage-requirements = [ property-set.empty ] ; | |
743 | local result ; | |
744 | for local p in $(all-property-sets) | |
745 | { | |
746 | local r = [ generate-really $(p) ] ; | |
747 | if $(r) | |
748 | { | |
749 | usage-requirements = [ $(usage-requirements).add $(r[1]) ] ; | |
750 | result += $(r[2-]) ; | |
751 | } | |
752 | } | |
753 | end-building $(__name__) ; | |
754 | return $(usage-requirements) [ sequence.unique $(result) ] ; | |
755 | } | |
756 | ||
757 | # Generates the main target with the given property set and returns a list | |
758 | # which first element is property-set object containing usage-requirements | |
759 | # of generated target and with generated virtual target in other elements. | |
760 | # It is possible that no targets are generated. | |
761 | # | |
762 | local rule generate-really ( property-set ) | |
763 | { | |
764 | local best-alternatives = [ select-alternatives $(property-set) ] ; | |
765 | if ! $(best-alternatives) | |
766 | { | |
767 | ECHO "error: No best alternative for" [ full-name ] ; | |
768 | select-alternatives $(property-set) debug ; | |
769 | return [ property-set.empty ] ; | |
770 | } | |
771 | else | |
772 | { | |
773 | # Now return virtual targets for the only alternative. | |
774 | return [ $(best-alternatives).generate $(property-set) ] ; | |
775 | } | |
776 | } | |
777 | ||
778 | rule rename ( new-name ) | |
779 | { | |
780 | abstract-target.rename $(new-name) ; | |
781 | for local a in $(self.alternatives) | |
782 | { | |
783 | $(a).rename $(new-name) ; | |
784 | } | |
785 | } | |
786 | } | |
787 | ||
788 | ||
789 | # Abstract target referring to a source file. This is an artificial entity | |
790 | # allowing sources to a target to be represented using a list of abstract target | |
791 | # instances. | |
792 | # | |
793 | class file-reference : abstract-target | |
794 | { | |
795 | import virtual-target ; | |
796 | import property-set ; | |
797 | import path ; | |
798 | ||
799 | rule __init__ ( file : project ) | |
800 | { | |
801 | abstract-target.__init__ $(file) : $(project) ; | |
802 | } | |
803 | ||
804 | rule generate ( properties ) | |
805 | { | |
806 | return [ property-set.empty ] [ virtual-target.from-file $(self.name) : | |
807 | [ location ] : $(self.project) ] ; | |
808 | } | |
809 | ||
810 | # Returns true if the referred file really exists. | |
811 | rule exists ( ) | |
812 | { | |
813 | location ; | |
814 | return $(self.file-path) ; | |
815 | } | |
816 | ||
817 | # Returns the location of target. Needed by 'testing.jam'. | |
818 | rule location ( ) | |
819 | { | |
820 | if ! $(self.file-location) | |
821 | { | |
822 | local source-location = [ $(self.project).get source-location ] ; | |
823 | for local src-dir in $(source-location) | |
824 | { | |
825 | if ! $(self.file-location) | |
826 | { | |
827 | local location = [ path.root $(self.name) $(src-dir) ] ; | |
828 | if [ CHECK_IF_FILE [ path.native $(location) ] ] | |
829 | { | |
830 | self.file-location = $(src-dir) ; | |
831 | self.file-path = $(location) ; | |
832 | } | |
833 | } | |
834 | } | |
835 | } | |
836 | return $(self.file-location) ; | |
837 | } | |
838 | } | |
839 | ||
840 | ||
841 | # Given a target-reference, made in context of 'project', returns the | |
842 | # abstract-target instance that is referred to, as well as properties explicitly | |
843 | # specified for this reference. | |
844 | # | |
845 | rule resolve-reference ( target-reference : project ) | |
846 | { | |
847 | # Separate target name from properties override. | |
848 | local split = [ MATCH "^([^<]*)(/(<.*))?$" : $(target-reference) ] ; | |
849 | local id = $(split[1]) ; | |
850 | if ! $(split) || ! $(id) | |
851 | { | |
852 | error "Malformed target reference $(target-reference)" ; | |
853 | } | |
854 | local sproperties = ; | |
855 | if $(split[3]) | |
856 | { | |
857 | sproperties = [ property.make [ feature.split $(split[3]) ] ] ; | |
858 | sproperties = [ feature.expand-composites $(sproperties) ] ; | |
859 | } | |
860 | ||
861 | # Find the target. | |
862 | local target = [ $(project).find $(id) ] ; | |
863 | ||
864 | return $(target) [ property-set.create $(sproperties) ] ; | |
865 | } | |
866 | ||
867 | ||
868 | # Attempts to generate the target given by target reference, which can refer | |
869 | # both to a main target or to a file. Returns a list consisting of | |
870 | # - usage requirements | |
871 | # - generated virtual targets, if any | |
872 | # | |
873 | rule generate-from-reference ( | |
874 | target-reference # Target reference. | |
875 | : project # Project where the reference is made. | |
876 | : property-set # Properties of the main target that makes the reference. | |
877 | ) | |
878 | { | |
879 | local r = [ resolve-reference $(target-reference) : $(project) ] ; | |
880 | local target = $(r[1]) ; | |
881 | local sproperties = $(r[2]) ; | |
882 | ||
883 | # Take properties which should be propagated and refine them with | |
884 | # source-specific requirements. | |
885 | local propagated = [ $(property-set).propagated ] ; | |
886 | local rproperties = [ $(propagated).refine $(sproperties) ] ; | |
887 | if $(rproperties[1]) = "@error" | |
888 | { | |
889 | import errors ; | |
890 | errors.error | |
891 | "When building" [ full-name ] " with properties " $(properties) : | |
892 | "Invalid properties specified for " $(source) ":" | |
893 | $(rproperties[2-]) ; | |
894 | } | |
895 | return [ $(target).generate $(rproperties) ] ; | |
896 | } | |
897 | ||
898 | ||
899 | rule apply-default-build ( property-set : default-build ) | |
900 | { | |
901 | # 1. First, see what properties from default-build are already present in | |
902 | # property-set. | |
903 | ||
904 | local raw = [ $(property-set).raw ] ; | |
905 | local specified-features = $(raw:G) ; | |
906 | ||
907 | local defaults-to-apply ; | |
908 | for local d in [ $(default-build).raw ] | |
909 | { | |
910 | if ! $(d:G) in $(specified-features) | |
911 | { | |
912 | defaults-to-apply += $(d) ; | |
913 | } | |
914 | } | |
915 | ||
916 | # 2. If there are any defaults to be applied, form a new build request. Pass | |
917 | # it through to 'expand-no-defaults' since default-build might contain | |
918 | # "release debug" resulting in two property-sets. | |
919 | local result ; | |
920 | if $(defaults-to-apply) | |
921 | { | |
922 | # We have to compress subproperties here to prevent property lists like: | |
923 | # <toolset>msvc <toolset-msvc:version>7.1 <threading>multi | |
924 | # | |
925 | # from being expanded into: | |
926 | # <toolset-msvc:version>7.1/<threading>multi | |
927 | # <toolset>msvc/<toolset-msvc:version>7.1/<threading>multi | |
928 | # | |
929 | # due to a cross-product property combination. That may be an indication | |
930 | # that build-request.expand-no-defaults is the wrong rule to use here. | |
931 | properties = [ build-request.expand-no-defaults | |
932 | [ feature.compress-subproperties $(raw) ] $(defaults-to-apply) ] ; | |
933 | ||
934 | if $(properties) | |
935 | { | |
936 | for local p in $(properties) | |
937 | { | |
938 | result += [ property-set.create | |
939 | [ feature.expand [ feature.split $(p) ] ] ] ; | |
940 | } | |
941 | } | |
942 | else | |
943 | { | |
944 | result = [ property-set.empty ] ; | |
945 | } | |
946 | } | |
947 | else | |
948 | { | |
949 | result = $(property-set) ; | |
950 | } | |
951 | return $(result) ; | |
952 | } | |
953 | ||
954 | ||
955 | # Given a build request and requirements, return properties common to dependency | |
956 | # build request and target requirements. | |
957 | # | |
958 | # TODO: Document exactly what 'common properties' are, whether they should | |
959 | # include default property values, whether they should contain any conditional | |
960 | # properties or should those be already processed, etc. See whether there are | |
961 | # any differences between use cases with empty and non-empty build-request as | |
962 | # well as with requirements containing and those not containing any non-free | |
963 | # features. | |
964 | # | |
965 | rule common-properties ( build-request requirements ) | |
966 | { | |
967 | # For optimization, we add free requirements directly, without using a | |
968 | # complex algorithm. This gives the complex algorithm a better chance of | |
969 | # caching results. | |
970 | local free = [ $(requirements).free ] ; | |
971 | local non-free = [ property-set.create [ $(requirements).base ] | |
972 | [ $(requirements).incidental ] ] ; | |
973 | ||
974 | local key = .rp.$(build-request)-$(non-free) ; | |
975 | if ! $($(key)) | |
976 | { | |
977 | $(key) = [ common-properties2 $(build-request) $(non-free) ] ; | |
978 | } | |
979 | return [ $($(key)).add-raw $(free) ] ; | |
980 | } | |
981 | ||
982 | ||
983 | # Given a 'context' -- a set of already present properties, and 'requirements', | |
984 | # decide which extra properties should be applied to 'context'. For conditional | |
985 | # requirements, this means evaluating the condition. For indirect conditional | |
986 | # requirements, this means calling a rule. Ordinary requirements are always | |
987 | # applied. | |
988 | # | |
989 | # Handles the situation where evaluating one conditional requirement affects | |
990 | # conditions of another conditional requirements, such as: | |
991 | # <toolset>gcc:<variant>release <variant>release:<define>RELEASE | |
992 | # | |
993 | # If 'what' is 'refined' returns context refined with new requirements. If | |
994 | # 'what' is 'added' returns just the requirements to be applied. | |
995 | # | |
996 | rule evaluate-requirements ( requirements : context : what ) | |
997 | { | |
998 | # Apply non-conditional requirements. It is possible that further | |
999 | # conditional requirement change a value set by non-conditional | |
1000 | # requirements. For example: | |
1001 | # | |
1002 | # exe a : a.cpp : <threading>single <toolset>foo:<threading>multi ; | |
1003 | # | |
1004 | # I am not sure if this should be an error, or not, especially given that | |
1005 | # | |
1006 | # <threading>single | |
1007 | # | |
1008 | # might come from project's requirements. | |
1009 | ||
1010 | local unconditional = [ feature.expand [ $(requirements).non-conditional ] ] | |
1011 | ; | |
1012 | ||
1013 | local raw = [ $(context).raw ] ; | |
1014 | raw = [ property.refine $(raw) : $(unconditional) ] ; | |
1015 | ||
1016 | # We have collected properties that surely must be present in common | |
1017 | # properties. We now try to figure out what other properties should be added | |
1018 | # in order to satisfy rules (4)-(6) from the docs. | |
1019 | ||
1020 | local conditionals = [ $(requirements).conditional ] ; | |
1021 | # The 'count' variable has one element for each conditional feature and for | |
1022 | # each occurrence of '<indirect-conditional>' feature. It is used as a loop | |
1023 | # counter: for each iteration of the loop before we remove one element and | |
1024 | # the property set should stabilize before we are done. It is assumed that | |
1025 | # #conditionals iterations should be enough for properties to propagate | |
1026 | # along conditions in any direction. | |
1027 | local count = $(conditionals) [ $(requirements).get <conditional> ] | |
1028 | and-once-more ; | |
1029 | ||
1030 | local added-requirements ; | |
1031 | ||
1032 | local current = $(raw) ; | |
1033 | ||
1034 | # It is assumed that ordinary conditional requirements can not add | |
1035 | # <conditional> properties (a.k.a. indirect conditional properties), and | |
1036 | # that rules referred to by <conditional> properties can not add new | |
1037 | # <conditional> properties. So the list of indirect conditionals does not | |
1038 | # change. | |
1039 | local indirect = [ $(requirements).get <conditional> ] ; | |
1040 | indirect = [ MATCH ^@(.*) : $(indirect) ] ; | |
1041 | ||
1042 | local ok ; | |
1043 | while $(count) | |
1044 | { | |
1045 | # Evaluate conditionals in context of current properties. | |
1046 | local e = [ property.evaluate-conditionals-in-context $(conditionals) : | |
1047 | $(current) ] ; | |
1048 | ||
1049 | # Evaluate indirect conditionals. | |
1050 | for local i in $(indirect) | |
1051 | { | |
1052 | local t = [ current ] ; | |
1053 | local p = [ $(t).project ] ; | |
1054 | local new = [ indirect.call $(i) $(current) ] ; | |
1055 | e += [ property.translate-paths $(new) : [ $(p).location ] ] ; | |
1056 | } | |
1057 | ||
1058 | if $(e) = $(added-requirements) | |
1059 | { | |
1060 | # If we got the same result, we have found the final properties. | |
1061 | count = ; | |
1062 | ok = true ; | |
1063 | } | |
1064 | else | |
1065 | { | |
1066 | # Oops, conditional evaluation results have changed. Also 'current' | |
1067 | # contains leftovers from a previous evaluation. Recompute 'current' | |
1068 | # using initial properties and conditional requirements. | |
1069 | added-requirements = $(e) ; | |
1070 | current = [ property.refine $(raw) : [ feature.expand $(e) ] ] ; | |
1071 | } | |
1072 | count = $(count[2-]) ; | |
1073 | } | |
1074 | if ! $(ok) | |
1075 | { | |
1076 | import errors ; | |
1077 | errors.error Can not evaluate conditional properties $(conditionals) ; | |
1078 | } | |
1079 | ||
1080 | if $(what) = added | |
1081 | { | |
1082 | return [ property-set.create $(unconditional) $(added-requirements) ] ; | |
1083 | } | |
1084 | else if $(what) = refined | |
1085 | { | |
1086 | return [ property-set.create $(current) ] ; | |
1087 | } | |
1088 | else | |
1089 | { | |
1090 | import errors ; | |
1091 | errors.error "Invalid value of the 'what' parameter." ; | |
1092 | } | |
1093 | } | |
1094 | ||
1095 | ||
1096 | rule common-properties2 ( build-request requirements ) | |
1097 | { | |
1098 | # This guarantees that default properties are present in the result, unless | |
1099 | # they are overriden by some requirement. FIXME: There is a possibility that | |
1100 | # we have added <foo>bar, which is composite and expands to <foo2>bar2, but | |
1101 | # default value of <foo2> is not bar2, in which case it is not clear what to | |
1102 | # do. | |
1103 | # | |
1104 | build-request = [ $(build-request).add-defaults ] ; | |
1105 | # Features added by 'add-defaults' can be composite and expand to features | |
1106 | # without default values -- which therefore have not been added yet. It | |
1107 | # could be clearer/faster to expand only newly added properties but that is | |
1108 | # not critical. | |
1109 | build-request = [ $(build-request).expand ] ; | |
1110 | ||
1111 | return [ evaluate-requirements $(requirements) : $(build-request) : | |
1112 | refined ] ; | |
1113 | } | |
1114 | ||
1115 | ||
1116 | rule push-target ( target ) | |
1117 | { | |
1118 | .targets = $(target) $(.targets) ; | |
1119 | } | |
1120 | ||
1121 | rule pop-target ( ) | |
1122 | { | |
1123 | .targets = $(.targets[2-]) ; | |
1124 | } | |
1125 | ||
1126 | # Return the metatarget that is currently being generated. | |
1127 | rule current ( ) | |
1128 | { | |
1129 | return $(.targets[1]) ; | |
1130 | } | |
1131 | ||
1132 | ||
1133 | # Implements the most standard way of constructing main target alternative from | |
1134 | # sources. Allows sources to be either file or other main target and handles | |
1135 | # generation of those dependency targets. | |
1136 | # | |
1137 | class basic-target : abstract-target | |
1138 | { | |
1139 | import build-request ; | |
1140 | import build-system ; | |
1141 | import "class" : new ; | |
1142 | import feature ; | |
1143 | import property ; | |
1144 | import property-set ; | |
1145 | import sequence ; | |
1146 | import set ; | |
1147 | import targets ; | |
1148 | import virtual-target ; | |
1149 | ||
1150 | rule __init__ ( name : project : sources * : requirements * : | |
1151 | default-build * : usage-requirements * ) | |
1152 | { | |
1153 | abstract-target.__init__ $(name) : $(project) ; | |
1154 | ||
1155 | self.sources = $(sources) ; | |
1156 | if ! $(requirements) | |
1157 | { | |
1158 | requirements = [ property-set.empty ] ; | |
1159 | } | |
1160 | self.requirements = $(requirements) ; | |
1161 | if ! $(default-build) | |
1162 | { | |
1163 | default-build = [ property-set.empty ] ; | |
1164 | } | |
1165 | self.default-build = $(default-build) ; | |
1166 | if ! $(usage-requirements) | |
1167 | { | |
1168 | usage-requirements = [ property-set.empty ] ; | |
1169 | } | |
1170 | self.usage-requirements = $(usage-requirements) ; | |
1171 | ||
1172 | if $(sources:G) | |
1173 | { | |
1174 | import errors : user-error : errors.user-error ; | |
1175 | errors.user-error properties found "in" the 'sources' parameter | |
1176 | "for" [ full-name ] ; | |
1177 | } | |
1178 | } | |
1179 | ||
1180 | rule always ( ) | |
1181 | { | |
1182 | self.always = 1 ; | |
1183 | } | |
1184 | ||
1185 | # Returns the list of abstract-targets which are used as sources. The extra | |
1186 | # properties specified for sources are not represented. The only user for | |
1187 | # this rule at the moment is the "--dump-tests" feature of the test system. | |
1188 | # | |
1189 | rule sources ( ) | |
1190 | { | |
1191 | if ! $(self.source-targets) | |
1192 | { | |
1193 | for local s in $(self.sources) | |
1194 | { | |
1195 | self.source-targets += [ targets.resolve-reference $(s) : | |
1196 | $(self.project) ] ; | |
1197 | } | |
1198 | } | |
1199 | return $(self.source-targets) ; | |
1200 | } | |
1201 | ||
1202 | rule requirements ( ) | |
1203 | { | |
1204 | return $(self.requirements) ; | |
1205 | } | |
1206 | ||
1207 | rule default-build ( ) | |
1208 | { | |
1209 | return $(self.default-build) ; | |
1210 | } | |
1211 | ||
1212 | # Returns the alternative condition for this alternative, if the condition | |
1213 | # is satisfied by 'property-set'. | |
1214 | # | |
1215 | rule match ( property-set debug ? ) | |
1216 | { | |
1217 | # The condition is composed of all base non-conditional properties. It | |
1218 | # is not clear if we should expand 'self.requirements' or not. For one | |
1219 | # thing, it would be nice to be able to put | |
1220 | # <toolset>msvc-6.0 | |
1221 | # in requirements. On the other hand, if we have <variant>release as a | |
1222 | # condition it does not make sense to require <optimization>full to be | |
1223 | # in the build request just to select this variant. | |
1224 | local bcondition = [ $(self.requirements).base ] ; | |
1225 | local ccondition = [ $(self.requirements).conditional ] ; | |
1226 | local condition = [ set.difference $(bcondition) : $(ccondition) ] ; | |
1227 | if $(debug) | |
1228 | { | |
1229 | ECHO " next alternative: required properties:" | |
1230 | $(condition:E=(empty)) ; | |
1231 | } | |
1232 | ||
1233 | if $(condition) in [ $(property-set).raw ] | |
1234 | { | |
1235 | if $(debug) | |
1236 | { | |
1237 | ECHO " matched" ; | |
1238 | } | |
1239 | return $(condition) ; | |
1240 | } | |
1241 | else | |
1242 | { | |
1243 | if $(debug) | |
1244 | { | |
1245 | ECHO " not matched" ; | |
1246 | } | |
1247 | return no-match ; | |
1248 | } | |
1249 | } | |
1250 | ||
1251 | # Takes a target reference, which might be either target id or a dependency | |
1252 | # property, and generates that target using 'property-set' as a build | |
1253 | # request. | |
1254 | # | |
1255 | # The results are added to the variable called 'result-var'. Usage | |
1256 | # requirements are added to the variable called 'usage-requirements-var'. | |
1257 | # | |
1258 | rule generate-dependencies ( dependencies * : property-set : result-var | |
1259 | usage-requirements-var ) | |
1260 | { | |
1261 | for local dependency in $(dependencies) | |
1262 | { | |
1263 | local grist = $(dependency:G) ; | |
1264 | local id = $(dependency:G=) ; | |
1265 | local result = [ targets.generate-from-reference $(id) : | |
1266 | $(self.project) : $(property-set) ] ; | |
1267 | ||
1268 | $(result-var) += $(result[2-]:G=$(grist)) ; | |
1269 | $(usage-requirements-var) += [ $(result[1]).raw ] ; | |
1270 | } | |
1271 | } | |
1272 | ||
1273 | # Determines final build properties, generates sources, and calls | |
1274 | # 'construct'. This method should not be overridden. | |
1275 | # | |
1276 | rule generate ( property-set ) | |
1277 | { | |
1278 | if [ modules.peek : .debug-building ] | |
1279 | { | |
1280 | ECHO ; | |
1281 | local fn = [ full-name ] ; | |
1282 | ECHO [ targets.indent ] "Building target '$(fn)'" ; | |
1283 | targets.increase-indent ; | |
1284 | ECHO [ targets.indent ] Build request: $(property-set) | |
1285 | [ $(property-set).raw ] ; | |
1286 | local cf = [ build-system.command-line-free-features ] ; | |
1287 | ECHO [ targets.indent ] Command line free features: [ $(cf).raw ] ; | |
1288 | ECHO [ targets.indent ] Target requirements: | |
1289 | [ $(self.requirements).raw ] ; | |
1290 | } | |
1291 | targets.push-target $(__name__) ; | |
1292 | ||
1293 | # Apply free features from the command line. If user said | |
1294 | # define=FOO | |
1295 | # he most likely wants this define to be set for all compiles. | |
1296 | # Make it before check for already built. | |
1297 | property-set = [ $(property-set).refine | |
1298 | [ build-system.command-line-free-features ] ] ; | |
1299 | ||
1300 | if ! $(self.generated.$(property-set)) | |
1301 | { | |
1302 | local rproperties = [ targets.common-properties $(property-set) | |
1303 | $(self.requirements) ] ; | |
1304 | ||
1305 | if [ modules.peek : .debug-building ] | |
1306 | { | |
1307 | ECHO ; | |
1308 | ECHO [ targets.indent ] "Common properties: " | |
1309 | [ $(rproperties).raw ] ; | |
1310 | } | |
1311 | ||
1312 | if ( $(rproperties[1]) != "@error" ) && ( [ $(rproperties).get | |
1313 | <build> ] != no ) | |
1314 | { | |
1315 | local source-targets ; | |
1316 | local properties = [ $(rproperties).non-dependency ] ; | |
1317 | local usage-requirements ; | |
1318 | ||
1319 | generate-dependencies [ $(rproperties).dependency ] : | |
1320 | $(rproperties) : properties usage-requirements ; | |
1321 | ||
1322 | generate-dependencies $(self.sources) : $(rproperties) : | |
1323 | source-targets usage-requirements ; | |
1324 | ||
1325 | if [ modules.peek : .debug-building ] | |
1326 | { | |
1327 | ECHO ; | |
1328 | ECHO [ targets.indent ] "Usage requirements for" | |
1329 | $(self.name)": " $(usage-requirements) ; | |
1330 | } | |
1331 | ||
1332 | rproperties = [ property-set.create $(properties) | |
1333 | $(usage-requirements) ] ; | |
1334 | usage-requirements = [ property-set.create $(usage-requirements) | |
1335 | ] ; | |
1336 | ||
1337 | if [ modules.peek : .debug-building ] | |
1338 | { | |
1339 | ECHO [ targets.indent ] "Build properties: " | |
1340 | [ $(rproperties).raw ] ; | |
1341 | } | |
1342 | ||
1343 | local extra = [ $(rproperties).get <source> ] ; | |
1344 | source-targets += $(extra:G=) ; | |
1345 | # We might get duplicate sources, for example if we link to two | |
1346 | # libraries having the same <library> usage requirement. Use | |
1347 | # stable sort, since for some targets the order is important, | |
1348 | # e.g. RUN_PY targets need a python source to come first. | |
1349 | source-targets = [ sequence.unique $(source-targets) : stable ] | |
1350 | ; | |
1351 | ||
1352 | local result = [ construct $(self.name) : $(source-targets) : | |
1353 | $(rproperties) ] ; | |
1354 | ||
1355 | if $(result) | |
1356 | { | |
1357 | local gur = $(result[1]) ; | |
1358 | result = $(result[2-]) ; | |
1359 | ||
1360 | if $(self.always) | |
1361 | { | |
1362 | for local t in $(result) | |
1363 | { | |
1364 | $(t).always ; | |
1365 | } | |
1366 | } | |
1367 | ||
1368 | local s = [ create-subvariant $(result) | |
1369 | : [ virtual-target.recent-targets ] | |
1370 | : $(property-set) : $(source-targets) | |
1371 | : $(rproperties) : $(usage-requirements) ] ; | |
1372 | virtual-target.clear-recent-targets ; | |
1373 | ||
1374 | local ur = [ compute-usage-requirements $(s) ] ; | |
1375 | ur = [ $(ur).add $(gur) ] ; | |
1376 | $(s).set-usage-requirements $(ur) ; | |
1377 | if [ modules.peek : .debug-building ] | |
1378 | { | |
1379 | ECHO [ targets.indent ] "Usage requirements from" | |
1380 | $(self.name)": " [ $(ur).raw ] ; | |
1381 | } | |
1382 | ||
1383 | self.generated.$(property-set) = $(ur) $(result) ; | |
1384 | } | |
1385 | } | |
1386 | else | |
1387 | { | |
1388 | if $(rproperties[1]) = "@error" | |
1389 | { | |
1390 | ECHO [ targets.indent ] "Skipping build of:" [ full-name ] | |
1391 | "cannot compute common properties" ; | |
1392 | } | |
1393 | else if [ $(rproperties).get <build> ] = no | |
1394 | { | |
1395 | # If we just see <build>no, we cannot produce any reasonable | |
1396 | # diagnostics. The code that adds this property is expected | |
1397 | # to explain why a target is not built, for example using | |
1398 | # the configure.log-component-configuration function. | |
1399 | } | |
1400 | else | |
1401 | { | |
1402 | ECHO [ targets.indent ] "Skipping build of: " [ full-name ] | |
1403 | " unknown reason" ; | |
1404 | } | |
1405 | ||
1406 | # We are here either because there has been an error computing | |
1407 | # properties or there is <build>no in properties. In the latter | |
1408 | # case we do not want any diagnostic. In the former case, we | |
1409 | # need diagnostics. FIXME | |
1410 | ||
1411 | # If this target fails to build, add <build>no to properties to | |
1412 | # cause any parent target to fail to build. Except that it | |
1413 | # - does not work now, since we check for <build>no only in | |
1414 | # common properties, but not in properties that came from | |
1415 | # dependencies | |
1416 | # - it is not clear if that is a good idea anyway. The alias | |
1417 | # target, for example, should not fail to build if a | |
1418 | # dependency fails. | |
1419 | self.generated.$(property-set) = [ property-set.create <build>no | |
1420 | ] ; | |
1421 | } | |
1422 | } | |
1423 | else | |
1424 | { | |
1425 | if [ modules.peek : .debug-building ] | |
1426 | { | |
1427 | ECHO [ targets.indent ] "Already built" ; | |
1428 | local ur = $(self.generated.$(property-set)) ; | |
1429 | ur = $(ur[0]) ; | |
1430 | targets.increase-indent ; | |
1431 | ECHO [ targets.indent ] "Usage requirements from" | |
1432 | $(self.name)": " [ $(ur).raw ] ; | |
1433 | targets.decrease-indent ; | |
1434 | } | |
1435 | } | |
1436 | ||
1437 | targets.pop-target ; | |
1438 | targets.decrease-indent ; | |
1439 | return $(self.generated.$(property-set)) ; | |
1440 | } | |
1441 | ||
1442 | # Given the set of generated targets, and refined build properties, | |
1443 | # determines and sets appropriate usage requirements on those targets. | |
1444 | # | |
1445 | rule compute-usage-requirements ( subvariant ) | |
1446 | { | |
1447 | local rproperties = [ $(subvariant).build-properties ] ; | |
1448 | xusage-requirements = [ targets.evaluate-requirements | |
1449 | $(self.usage-requirements) : $(rproperties) : added ] ; | |
1450 | ||
1451 | # We generate all dependency properties and add them, as well as their | |
1452 | # usage requirements, to the result. | |
1453 | local extra ; | |
1454 | generate-dependencies [ $(xusage-requirements).dependency ] : | |
1455 | $(rproperties) : extra extra ; | |
1456 | ||
1457 | local result = [ property-set.create | |
1458 | [ $(xusage-requirements).non-dependency ] $(extra) ] ; | |
1459 | ||
1460 | # Propagate usage requirements we got from sources, except for the | |
1461 | # <pch-header> and <pch-file> features. | |
1462 | # | |
1463 | # That feature specifies which pch file to use, and should apply only to | |
1464 | # direct dependents. Consider: | |
1465 | # | |
1466 | # pch pch1 : ... | |
1467 | # lib lib1 : ..... pch1 ; | |
1468 | # pch pch2 : | |
1469 | # lib lib2 : pch2 lib1 ; | |
1470 | # | |
1471 | # Here, lib2 should not get <pch-header> property from pch1. | |
1472 | # | |
1473 | # Essentially, when those two features are in usage requirements, they | |
1474 | # are propagated only to direct dependents. We might need a more general | |
1475 | # mechanism, but for now, only those two features are special. | |
1476 | # | |
1477 | # TODO - Actually there are more possible candidates like for instance | |
1478 | # when listing static library X as a source for another static library. | |
1479 | # Then static library X will be added as a <source> property to the | |
1480 | # second library's usage requirements but those requirements should last | |
1481 | # only up to the first executable or shared library that actually links | |
1482 | # to it. | |
1483 | local raw = [ $(subvariant).sources-usage-requirements ] ; | |
1484 | raw = [ $(raw).raw ] ; | |
1485 | raw = [ property.change $(raw) : <pch-header> ] ; | |
1486 | raw = [ property.change $(raw) : <pch-file> ] ; | |
1487 | return [ $(result).add [ property-set.create $(raw) ] ] ; | |
1488 | } | |
1489 | ||
1490 | # Creates new subvariant instances for 'targets'. | |
1491 | # 'root-targets' - virtual targets to be returned to dependants | |
1492 | # 'all-targets' - virtual targets created while building this main target | |
1493 | # 'build-request' - property-set instance with requested build properties | |
1494 | # | |
1495 | local rule create-subvariant ( root-targets * : all-targets * : | |
1496 | build-request : sources * : rproperties : usage-requirements ) | |
1497 | { | |
1498 | for local e in $(root-targets) | |
1499 | { | |
1500 | $(e).root true ; | |
1501 | } | |
1502 | ||
1503 | # Process all virtual targets that will be created if this main target | |
1504 | # is created. | |
1505 | local s = [ new subvariant $(__name__) : $(build-request) : $(sources) : | |
1506 | $(rproperties) : $(usage-requirements) : $(all-targets) ] ; | |
1507 | for local v in $(all-targets) | |
1508 | { | |
1509 | if ! [ $(v).creating-subvariant ] | |
1510 | { | |
1511 | $(v).creating-subvariant $(s) ; | |
1512 | } | |
1513 | } | |
1514 | return $(s) ; | |
1515 | } | |
1516 | ||
1517 | # Constructs virtual targets for this abstract target and the dependency | |
1518 | # graph. Returns a usage-requirements property-set and a list of virtual | |
1519 | # targets. Should be overriden in derived classes. | |
1520 | # | |
1521 | rule construct ( name : source-targets * : properties * ) | |
1522 | { | |
1523 | import errors : error : errors.error ; | |
1524 | errors.error "method should be defined in derived classes" ; | |
1525 | } | |
1526 | } | |
1527 | ||
1528 | ||
1529 | class typed-target : basic-target | |
1530 | { | |
1531 | import generators ; | |
1532 | ||
1533 | rule __init__ ( name : project : type : sources * : requirements * : | |
1534 | default-build * : usage-requirements * ) | |
1535 | { | |
1536 | basic-target.__init__ $(name) : $(project) : $(sources) : | |
1537 | $(requirements) : $(default-build) : $(usage-requirements) ; | |
1538 | ||
1539 | self.type = $(type) ; | |
1540 | } | |
1541 | ||
1542 | rule type ( ) | |
1543 | { | |
1544 | return $(self.type) ; | |
1545 | } | |
1546 | ||
1547 | rule construct ( name : source-targets * : property-set ) | |
1548 | { | |
1549 | local r = [ generators.construct $(self.project) $(name:S=) | |
1550 | : $(self.type) | |
1551 | : [ property-set.create [ $(property-set).raw ] | |
1552 | <main-target-type>$(self.type) ] | |
1553 | : $(source-targets) : true ] ; | |
1554 | if ! $(r) | |
1555 | { | |
1556 | local viable-generators = [ generators.find-viable-generators | |
1557 | $(self.type) : $(property-set) ] ; | |
1558 | ECHO "WARNING: Unable to construct" [ full-name ] | |
1559 | "of type" $(self.type) | |
1560 | "with these properties:" [ $(property-set).raw ] ; | |
1561 | ECHO "WARNING: Considered these as possible generators:" ; | |
1562 | for local gen in $(viable-generators) | |
1563 | { | |
1564 | ECHO "WARNING:" [ $(gen).id ] | |
1565 | "with source types {" [ $(gen).source-types ] "}" | |
1566 | "and requirements {" [ $(gen).requirements ] "}" ; | |
1567 | } | |
1568 | ||
1569 | # Are there any top-level generators for this type/property set. | |
1570 | if ! [ generators.find-viable-generators $(self.type) : | |
1571 | $(property-set) ] | |
1572 | { | |
1573 | ECHO "error: no generators were found for type '$(self.type)'" ; | |
1574 | ECHO "error: and the requested properties" ; | |
1575 | ECHO "error: make sure you've configured the needed tools" ; | |
1576 | ECHO "See http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html" ; | |
1577 | EXIT "To debug this problem, try the --debug-generators option." | |
1578 | ; | |
1579 | } | |
1580 | } | |
1581 | return $(r) ; | |
1582 | } | |
1583 | } | |
1584 | ||
1585 | ||
1586 | # Return the list of sources to use, if main target rule is invoked with | |
1587 | # 'sources'. If there are any objects in 'sources', they are treated as main | |
1588 | # target instances, and the name of such targets are adjusted to be | |
1589 | # '<name_of_this_target>__<name_of_source_target>'. Such renaming is disabled if | |
1590 | # a non-empty value is passed as the 'no-renaming' parameter. | |
1591 | # | |
1592 | rule main-target-sources ( sources * : main-target-name : no-renaming ? ) | |
1593 | { | |
1594 | local result ; | |
1595 | for local t in $(sources) | |
1596 | { | |
1597 | if [ class.is-instance $(t) ] | |
1598 | { | |
1599 | local name = [ $(t).name ] ; | |
1600 | if ! $(no-renaming) | |
1601 | { | |
1602 | name = $(main-target-name)__$(name) ; | |
1603 | $(t).rename $(name) ; | |
1604 | } | |
1605 | # Inline targets are not built by default. | |
1606 | local p = [ $(t).project ] ; | |
1607 | $(p).mark-target-as-explicit $(name) ; | |
1608 | result += $(name) ; | |
1609 | } | |
1610 | else | |
1611 | { | |
1612 | result += $(t) ; | |
1613 | } | |
1614 | } | |
1615 | return $(result) ; | |
1616 | } | |
1617 | ||
1618 | ||
1619 | # Returns the requirements to use when declaring a main target, obtained by | |
1620 | # translating all specified property paths and refining project requirements | |
1621 | # with the ones specified for the target. | |
1622 | # | |
1623 | rule main-target-requirements ( | |
1624 | specification * # Properties explicitly specified for the main target. | |
1625 | : project # Project where the main target is to be declared. | |
1626 | ) | |
1627 | { | |
1628 | local requirements = [ property-set.refine-from-user-input | |
1629 | [ $(project).get requirements ] : $(specification) : | |
1630 | [ $(project).project-module ] : [ $(project).get location ] ] ; | |
1631 | if $(requirements[1]) = "@error" | |
1632 | { | |
1633 | import errors ; | |
1634 | errors.error "Conflicting requirements for target:" $(requirements) ; | |
1635 | } | |
1636 | return [ $(requirements).add [ toolset.requirements ] ] ; | |
1637 | } | |
1638 | ||
1639 | ||
1640 | # Returns the usage requirements to use when declaring a main target, which are | |
1641 | # obtained by translating all specified property paths and adding project's | |
1642 | # usage requirements. | |
1643 | # | |
1644 | rule main-target-usage-requirements ( | |
1645 | specification * # Use-properties explicitly specified for a main target. | |
1646 | : project # Project where the main target is to be declared. | |
1647 | ) | |
1648 | { | |
1649 | local project-usage-requirements = [ $(project).get usage-requirements ] ; | |
1650 | ||
1651 | # We do not use 'refine-from-user-input' because: | |
1652 | # - I am not sure if removing parent's usage requirements makes sense | |
1653 | # - refining usage requirements is not needed, since usage requirements are | |
1654 | # always free. | |
1655 | local usage-requirements = [ property-set.create-from-user-input | |
1656 | $(specification) | |
1657 | : [ $(project).project-module ] [ $(project).get location ] ] ; | |
1658 | ||
1659 | return [ $(project-usage-requirements).add $(usage-requirements) ] ; | |
1660 | } | |
1661 | ||
1662 | ||
1663 | # Return the default build value to use when declaring a main target, which is | |
1664 | # obtained by using the specified value if not empty and parent's default build | |
1665 | # attribute otherwise. | |
1666 | # | |
1667 | rule main-target-default-build ( | |
1668 | specification * # Default build explicitly specified for a main target. | |
1669 | : project # Project where the main target is to be declared. | |
1670 | ) | |
1671 | { | |
1672 | local result ; | |
1673 | if $(specification) | |
1674 | { | |
1675 | result = $(specification) ; | |
1676 | } | |
1677 | else | |
1678 | { | |
1679 | result = [ $(project).get default-build ] ; | |
1680 | } | |
1681 | return [ property-set.create-with-validation $(result) ] ; | |
1682 | } | |
1683 | ||
1684 | ||
1685 | # Registers the specified target as a main target alternative and returns it. | |
1686 | # | |
1687 | rule main-target-alternative ( target ) | |
1688 | { | |
1689 | local ptarget = [ $(target).project ] ; | |
1690 | $(ptarget).add-alternative $(target) ; | |
1691 | return $(target) ; | |
1692 | } | |
1693 | ||
1694 | ||
1695 | # Creates a metatarget with the specified properties, using 'klass' as the | |
1696 | # class. The 'name', 'sources', 'requirements', 'default-build' and | |
1697 | # 'usage-requirements' are assumed to be in the form specified by the user in | |
1698 | # the Jamfile corresponding to 'project'. | |
1699 | # | |
1700 | rule create-metatarget ( klass : project : name : sources * : requirements * : | |
1701 | default-build * : usage-requirements * ) | |
1702 | { | |
1703 | return [ targets.main-target-alternative [ new $(klass) $(name) : $(project) | |
1704 | : [ targets.main-target-sources $(sources) : $(name) ] | |
1705 | : [ targets.main-target-requirements $(requirements) : $(project) ] | |
1706 | : [ targets.main-target-default-build $(default-build) : $(project) ] | |
1707 | : [ targets.main-target-usage-requirements $(usage-requirements) : | |
1708 | $(project) ] ] ] ; | |
1709 | } | |
1710 | ||
1711 | ||
1712 | # Creates a typed-target with the specified properties. The 'name', 'sources', | |
1713 | # 'requirements', 'default-build' and 'usage-requirements' are assumed to be in | |
1714 | # the form specified by the user in the Jamfile corresponding to 'project'. | |
1715 | # | |
1716 | rule create-typed-target ( type : project : name : sources * : requirements * : | |
1717 | default-build * : usage-requirements * ) | |
1718 | { | |
1719 | return [ targets.main-target-alternative [ new typed-target $(name) : | |
1720 | $(project) : $(type) | |
1721 | : [ targets.main-target-sources $(sources) : $(name) ] | |
1722 | : [ targets.main-target-requirements $(requirements) : $(project) ] | |
1723 | : [ targets.main-target-default-build $(default-build) : $(project) ] | |
1724 | : [ targets.main-target-usage-requirements $(usage-requirements) : | |
1725 | $(project) ] ] ] ; | |
1726 | } |