]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | # Copyright 2003, 2005, 2007 Dave Abrahams |
2 | # Copyright 2006, 2007 Rene Rivera | |
3 | # Copyright 2003, 2004, 2005, 2006 Vladimir Prus | |
4 | # Distributed under the Boost Software License, Version 1.0. | |
1e59de90 TL |
5 | # (See accompanying file LICENSE.txt or copy at |
6 | # https://www.bfgroup.xyz/b2/LICENSE.txt) | |
7c673cae FG |
7 | |
8 | # This file is part of Boost Build version 2. You can think of it as forming the | |
9 | # main() routine. It is invoked by the bootstrapping code in bootstrap.jam. | |
10 | ||
11 | import build-request ; | |
12 | import builtin ; | |
13 | import "class" : new ; | |
14 | import configure ; | |
15 | import config-cache ; | |
16 | import feature ; | |
17 | import generators ; | |
11fdf7f2 | 18 | import indirect ; |
7c673cae FG |
19 | import make ; |
20 | import modules ; | |
21 | import os ; | |
22 | import path ; | |
23 | import project ; | |
24 | import property ; | |
25 | import property-set ; | |
26 | import regex ; | |
27 | import sequence ; | |
28 | import targets ; | |
29 | import toolset ; | |
30 | import utility ; | |
31 | import version ; | |
32 | import virtual-target ; | |
33 | ||
34 | ||
35 | ################################################################################ | |
36 | # | |
37 | # Module global data. | |
38 | # | |
39 | ################################################################################ | |
40 | ||
41 | # Shortcut used in this module for accessing used command-line parameters. | |
42 | .argv = [ modules.peek : ARGV ] ; | |
43 | ||
44 | # Flag indicating we should display additional debugging information related to | |
45 | # locating and loading Boost Build configuration files. | |
46 | .debug-config = [ MATCH ^(--debug-configuration)$ : $(.argv) ] ; | |
47 | ||
48 | # Virtual targets obtained when building main targets references on the command | |
49 | # line. When running 'bjam --clean main_target' we want to clean only files | |
50 | # belonging to that main target so we need to record which targets are produced | |
51 | # for it. | |
52 | .results-of-main-targets = ; | |
53 | ||
54 | # Was an XML dump requested? | |
55 | .out-xml = [ MATCH ^--out-xml=(.*)$ : $(.argv) ] ; | |
56 | ||
57 | # Default toolset & version to be used in case no other toolset has been used | |
58 | # explicitly by either the loaded configuration files, the loaded project build | |
59 | # scripts or an explicit toolset request on the command line. If not specified, | |
60 | # an arbitrary default will be used based on the current host OS. This value, | |
61 | # while not strictly necessary, has been added to allow testing Boost-Build's | |
62 | # default toolset usage functionality. | |
63 | .default-toolset = ; | |
64 | .default-toolset-version = ; | |
65 | ||
66 | ||
67 | ################################################################################ | |
68 | # | |
69 | # Public rules. | |
70 | # | |
71 | ################################################################################ | |
72 | ||
73 | # Returns the property set with the free features from the currently processed | |
74 | # build request. | |
75 | # | |
76 | rule command-line-free-features ( ) | |
77 | { | |
78 | return $(.command-line-free-features) ; | |
79 | } | |
80 | ||
81 | ||
82 | # Returns the location of the build system. The primary use case is building | |
83 | # Boost where it is sometimes needed to get the location of other components | |
84 | # (e.g. BoostBook files) and it is convenient to use locations relative to the | |
85 | # Boost Build path. | |
86 | # | |
87 | rule location ( ) | |
88 | { | |
89 | local r = [ modules.binding build-system ] ; | |
90 | return $(r:P) ; | |
91 | } | |
92 | ||
93 | ||
94 | # Sets the default toolset & version to be used in case no other toolset has | |
95 | # been used explicitly by either the loaded configuration files, the loaded | |
96 | # project build scripts or an explicit toolset request on the command line. For | |
97 | # more detailed information see the comment related to used global variables. | |
98 | # | |
99 | rule set-default-toolset ( toolset : version ? ) | |
100 | { | |
101 | .default-toolset = $(toolset) ; | |
102 | .default-toolset-version = $(version) ; | |
103 | } | |
104 | ||
11fdf7f2 | 105 | rule add-pre-build-hook ( function ) |
7c673cae | 106 | { |
11fdf7f2 | 107 | .pre-build-hook += [ indirect.make $(function) : [ CALLER_MODULE ] ] ; |
7c673cae FG |
108 | } |
109 | ||
11fdf7f2 | 110 | rule add-post-build-hook ( function ) |
7c673cae | 111 | { |
11fdf7f2 | 112 | .post-build-hook += [ indirect.make $(function) : [ CALLER_MODULE ] ] ; |
7c673cae FG |
113 | } |
114 | ||
11fdf7f2 TL |
115 | # Old names for backwards compatibility |
116 | IMPORT build-system : add-pre-build-hook : build-system : set-pre-build-hook ; | |
117 | IMPORT build-system : add-post-build-hook : build-system : set-post-build-hook ; | |
118 | EXPORT build-system : set-pre-build-hook set-post-build-hook ; | |
119 | ||
7c673cae FG |
120 | ################################################################################ |
121 | # | |
122 | # Local rules. | |
123 | # | |
124 | ################################################################################ | |
125 | ||
126 | # Returns actual Jam targets to be used for executing a clean request. | |
127 | # | |
128 | local rule actual-clean-targets ( ) | |
129 | { | |
130 | # The cleaning is tricky. Say, if user says 'bjam --clean foo' where 'foo' | |
131 | # is a directory, then we want to clean targets which are in 'foo' as well | |
132 | # as those in any children Jamfiles under foo but not in any unrelated | |
133 | # Jamfiles. To achieve this we first mark all projects explicitly detected | |
134 | # as targets for this build system run as needing to be cleaned. | |
135 | for local t in $(targets) | |
136 | { | |
137 | if [ class.is-a $(t) : project-target ] | |
138 | { | |
139 | local project = [ $(t).project-module ] ; | |
140 | .should-clean-project.$(project) = true ; | |
141 | } | |
142 | } | |
143 | ||
144 | # Construct a list of targets explicitly detected on this build system run | |
145 | # as a result of building main targets. | |
146 | local targets-to-clean ; | |
147 | for local t in $(.results-of-main-targets) | |
148 | { | |
149 | # Do not include roots or sources. | |
150 | targets-to-clean += [ virtual-target.traverse $(t) ] ; | |
151 | } | |
152 | targets-to-clean = [ sequence.unique $(targets-to-clean) ] ; | |
153 | ||
154 | local to-clean ; | |
155 | for local t in [ virtual-target.all-targets ] | |
156 | { | |
157 | # Remove only derived targets and only those asked to be cleaned, | |
158 | # whether directly or by belonging to one of the removed projects. | |
159 | local p = [ $(t).project ] ; | |
160 | if [ $(t).action ] && ( $(t) in $(targets-to-clean) || | |
161 | [ should-clean-project [ $(p).project-module ] ] ) | |
162 | { | |
163 | to-clean += $(t) ; | |
164 | } | |
165 | } | |
166 | ||
167 | local to-clean-actual ; | |
168 | for local t in $(to-clean) | |
169 | { | |
170 | to-clean-actual += [ $(t).actualize ] ; | |
171 | } | |
172 | return $(to-clean-actual) ; | |
173 | } | |
174 | ||
175 | ||
176 | # Given a target id, try to find and return the corresponding target. This is | |
177 | # only invoked when there is no Jamfile in ".". This code somewhat duplicates | |
178 | # code in project-target.find but we can not reuse that code without a | |
179 | # project-targets instance. | |
180 | # | |
181 | local rule find-target ( target-id ) | |
182 | { | |
183 | local split = [ MATCH (.*)//(.*) : $(target-id) ] ; | |
184 | ||
185 | local pm ; | |
186 | if $(split) | |
187 | { | |
188 | pm = [ project.find $(split[1]) : "." ] ; | |
189 | } | |
190 | else | |
191 | { | |
192 | pm = [ project.find $(target-id) : "." ] ; | |
193 | } | |
194 | ||
195 | local result ; | |
196 | if $(pm) | |
197 | { | |
198 | result = [ project.target $(pm) ] ; | |
199 | } | |
200 | ||
201 | if $(split) | |
202 | { | |
203 | result = [ $(result).find $(split[2]) ] ; | |
204 | } | |
205 | ||
206 | return $(result) ; | |
207 | } | |
208 | ||
209 | ||
210 | # Initializes a new configuration module. | |
211 | # | |
212 | local rule initialize-config-module ( module-name : location ? ) | |
213 | { | |
214 | project.initialize $(module-name) : $(location) ; | |
215 | if USER_MODULE in [ RULENAMES ] | |
216 | { | |
217 | USER_MODULE $(module-name) ; | |
218 | } | |
219 | } | |
220 | ||
221 | ||
222 | # Helper rule used to load configuration files. Loads the first configuration | |
223 | # file with the given 'filename' at 'path' into module with name 'module-name'. | |
224 | # Not finding the requested file may or may not be treated as an error depending | |
225 | # on the must-find parameter. Returns a normalized path to the loaded | |
226 | # configuration file or nothing if no file was loaded. | |
227 | # | |
228 | local rule load-config ( module-name : filename : path + : must-find ? ) | |
229 | { | |
230 | if $(.debug-config) | |
231 | { | |
232 | local path-string = $(path) ; | |
233 | if $(path-string) = "" { path-string = . ; } | |
11fdf7f2 | 234 | ECHO "notice:" Searching '$(path-string)' for $(module-name) |
7c673cae FG |
235 | configuration file '$(filename)'. ; |
236 | } | |
237 | local where = [ GLOB $(path) : $(filename) ] ; | |
238 | if $(where) | |
239 | { | |
240 | where = [ NORMALIZE_PATH $(where[1]) ] ; | |
241 | if $(.debug-config) | |
242 | { | |
243 | local where-string = $(where:D) ; | |
244 | if $(where-string) = "" { where-string = . ; } | |
245 | where-string = '$(where-string)' ; | |
11fdf7f2 | 246 | ECHO "notice:" Loading $(module-name) configuration file '$(filename)' |
7c673cae FG |
247 | from $(where-string:J=" "). ; |
248 | } | |
249 | ||
250 | # Set source location so that path-constant in config files with | |
251 | # relative paths work. This is of most importance for | |
252 | # project-config.jam, but may be used in other config files as well. | |
253 | local attributes = [ project.attributes $(module-name) ] ; | |
254 | $(attributes).set source-location : $(where:D) : exact ; | |
255 | modules.load $(module-name) : $(filename) : $(path) ; | |
256 | project.load-used-projects $(module-name) ; | |
257 | } | |
258 | else if $(must-find) || $(.debug-config) | |
259 | { | |
260 | local path-string = $(path) ; | |
261 | if $(path-string) = "" { path-string = . ; } | |
262 | path-string = '$(path-string)' ; | |
263 | path-string = $(path-string:J=" ") ; | |
264 | if $(must-find) | |
265 | { | |
266 | import errors ; | |
267 | errors.user-error Configuration file '$(filename)' not found "in" | |
268 | $(path-string). ; | |
269 | } | |
11fdf7f2 | 270 | ECHO "notice:" Configuration file '$(filename)' not found "in" |
7c673cae FG |
271 | $(path-string). ; |
272 | } | |
273 | return $(where) ; | |
274 | } | |
275 | ||
11fdf7f2 TL |
276 | # Parses options of the form --xxx-config=path/to/config.jam |
277 | # and environmental variables of the form BOOST_BUILD_XXX_CONFIG. | |
278 | # If not found, returns an empty list. The option may be | |
279 | # explicitly set to the empty string, in which case, handle-config-option | |
280 | # will return "". | |
281 | # | |
282 | local rule handle-config-option ( name : env ? ) | |
283 | { | |
284 | local result = [ MATCH ^--$(name)=(.*)$ : $(.argv) ] ; | |
285 | if ! $(result)-is-defined && $(env) | |
286 | { | |
287 | result = [ os.environ $(env) ] ; | |
288 | } | |
289 | # Special handling for the case when the OS does not strip the quotes | |
290 | # around the file name, as is the case when using Cygwin bash. | |
291 | result = [ utility.unquote $(result[-1]) ] ; | |
292 | if ! $(result) | |
293 | { | |
294 | return $(result) ; | |
295 | } | |
296 | # Treat explicitly entered user paths as native OS path | |
297 | # references and, if non-absolute, root them at the current | |
298 | # working directory. | |
299 | result = [ path.make $(result) ] ; | |
300 | result = [ path.root $(result) [ path.pwd ] ] ; | |
301 | result = [ path.native $(result) ] ; | |
302 | return $(result) ; | |
303 | } | |
304 | ||
7c673cae FG |
305 | |
306 | # Loads all the configuration files used by Boost Build in the following order: | |
307 | # | |
308 | # -- test-config -- | |
309 | # Loaded only if specified on the command-line using the --test-config | |
310 | # command-line parameter. It is ok for this file not to exist even if specified. | |
311 | # If this configuration file is loaded, regular site and user configuration | |
312 | # files will not be. If a relative path is specified, file is searched for in | |
313 | # the current folder. | |
314 | # | |
11fdf7f2 TL |
315 | # -- all-config -- |
316 | # Loaded only if specified on the command-line using the --config command | |
317 | # line option. If a file name is specified, it must exist and replaces all | |
318 | # other configuration files. If an empty file name is passed, no configuration | |
319 | # files will be loaded. | |
320 | # | |
7c673cae | 321 | # -- site-config -- |
11fdf7f2 TL |
322 | # Named site-config.jam by default or may be named explicitly using the |
323 | # --site-config command-line option. If named explicitly, the file is found | |
324 | # relative to the current working directory and must exist. If the default one | |
325 | # is used then it is searched for in the system root path (Windows), | |
326 | # /etc (non-Windows), user's home folder or the Boost Build path, in that | |
327 | # order. Not loaded in case the test-config configuration file is loaded, | |
328 | # the file is explicitly set to the empty string or the --ignore-site-config | |
329 | # command-line option is specified. | |
7c673cae FG |
330 | # |
331 | # -- user-config -- | |
332 | # Named user-config.jam by default or may be named explicitly using the | |
333 | # --user-config command-line option or the BOOST_BUILD_USER_CONFIG environment | |
334 | # variable. If named explicitly the file is looked for from the current working | |
335 | # directory and if the default one is used then it is searched for in the | |
336 | # user's home directory and the Boost Build path, in that order. Not loaded in | |
337 | # case either the test-config configuration file is loaded or an empty file name | |
338 | # is explicitly specified. If the file name has been given explicitly then the | |
339 | # file must exist. | |
340 | # | |
341 | # -- project-config -- | |
11fdf7f2 TL |
342 | # Named project-config.jam. Looked up in the current working folder and |
343 | # then upwards through its parents up to the root folder. It may also be | |
344 | # named explicitly using the --project-config command-line option. If a file | |
345 | # is specified explicitly, it is found relative to the current working | |
346 | # directory and must exist. If an empty file name is passed, project-config | |
347 | # will not be loaded. | |
7c673cae FG |
348 | # |
349 | # Test configurations have been added primarily for use by Boost Build's | |
350 | # internal unit testing system but may be used freely in other places as well. | |
351 | # | |
352 | local rule load-configuration-files | |
353 | { | |
354 | # Flag indicating that site configuration should not be loaded. | |
355 | local ignore-site-config = | |
356 | [ MATCH ^(--ignore-site-config)$ : $(.argv) ] ; | |
11fdf7f2 TL |
357 | local ignore-user-config ; |
358 | local ignore-project-config ; | |
7c673cae FG |
359 | |
360 | initialize-config-module test-config ; | |
11fdf7f2 | 361 | local test-config = [ handle-config-option test-config ] ; |
7c673cae FG |
362 | if $(test-config) |
363 | { | |
364 | local where = [ load-config test-config : $(test-config:BS) : | |
365 | $(test-config:D) ] ; | |
366 | if $(where) | |
367 | { | |
368 | if $(.debug-config) | |
369 | { | |
370 | ECHO "notice: Regular site and user configuration files will" ; | |
371 | ECHO "notice: be ignored due to the test configuration being" | |
372 | "loaded." ; | |
373 | } | |
11fdf7f2 TL |
374 | ignore-site-config = true ; |
375 | ignore-user-config = true ; | |
7c673cae | 376 | } |
11fdf7f2 TL |
377 | } |
378 | ||
379 | initialize-config-module all-config ; | |
380 | local all-config = [ handle-config-option config ] ; | |
381 | if $(all-config) | |
382 | { | |
383 | load-config all-config : $(all-config:D=) : $(all-config:D) : required ; | |
384 | if $(.debug-config) | |
7c673cae | 385 | { |
11fdf7f2 TL |
386 | ECHO "notice: Regular configuration files will be ignored due" ; |
387 | ECHO "notice: to the global configuration being loaded." ; | |
7c673cae FG |
388 | } |
389 | } | |
11fdf7f2 TL |
390 | if $(all-config)-is-defined |
391 | { | |
392 | if $(.debug-config) && ! $(all-config) | |
393 | { | |
394 | ECHO "notice: Configuration file loading explicitly disabled." ; | |
395 | } | |
396 | ignore-site-config = true ; | |
397 | ignore-user-config = true ; | |
398 | ignore-project-config = true ; | |
399 | } | |
7c673cae FG |
400 | |
401 | local user-path = [ os.home-directories ] [ os.environ BOOST_BUILD_PATH ] ; | |
402 | local site-path = /etc $(user-path) ; | |
403 | if [ os.name ] in NT CYGWIN | |
404 | { | |
405 | site-path = [ modules.peek : SystemRoot ] $(user-path) ; | |
406 | } | |
407 | ||
11fdf7f2 | 408 | if $(.debug-config) && $(ignore-site-config) = --ignore-site-config |
7c673cae FG |
409 | { |
410 | ECHO "notice: Site configuration files will be ignored due to the" ; | |
411 | ECHO "notice: --ignore-site-config command-line option." ; | |
412 | } | |
413 | ||
414 | initialize-config-module site-config ; | |
11fdf7f2 | 415 | if ! $(ignore-site-config) |
7c673cae | 416 | { |
11fdf7f2 TL |
417 | local site-config = [ handle-config-option site-config ] ; |
418 | if $(site-config) | |
419 | { | |
420 | load-config site-config : $(site-config:D=) : $(site-config:D) | |
421 | : must-exist ; | |
422 | } | |
423 | else if ! $(site-config)-is-defined | |
424 | { | |
425 | load-config site-config : site-config.jam : $(site-path) ; | |
426 | } | |
427 | else if $(.debug-config) | |
428 | { | |
429 | ECHO "notice:" Site configuration file loading explicitly disabled. ; | |
430 | } | |
7c673cae FG |
431 | } |
432 | ||
433 | initialize-config-module user-config ; | |
11fdf7f2 | 434 | if ! $(ignore-user-config) |
7c673cae | 435 | { |
11fdf7f2 TL |
436 | local user-config = |
437 | [ handle-config-option user-config : BOOST_BUILD_USER_CONFIG ] ; | |
7c673cae FG |
438 | |
439 | if $(user-config) | |
440 | { | |
11fdf7f2 | 441 | if $(.debug-config) |
7c673cae | 442 | { |
11fdf7f2 TL |
443 | ECHO "notice:" Loading explicitly specified user configuration |
444 | "file:" ; | |
445 | ECHO " $(user-config)" ; | |
7c673cae | 446 | } |
11fdf7f2 TL |
447 | |
448 | load-config user-config : $(user-config:D=) : $(user-config:D) | |
449 | : must-exist ; | |
450 | } | |
451 | else if ! $(user-config)-is-defined | |
452 | { | |
453 | load-config user-config : user-config.jam : $(user-path) ; | |
7c673cae FG |
454 | } |
455 | else if $(.debug-config) | |
456 | { | |
11fdf7f2 | 457 | ECHO "notice:" User configuration file loading explicitly disabled. ; |
7c673cae FG |
458 | } |
459 | } | |
460 | ||
461 | # We look for project-config.jam from "." upward. I am not sure this is 100% | |
462 | # right decision, we might as well check for it only alongside the Jamroot | |
463 | # file. However: | |
464 | # - We need to load project-config.jam before Jamroot | |
465 | # - We probably need to load project-config.jam even if there is no Jamroot | |
466 | # - e.g. to implement automake-style out-of-tree builds. | |
11fdf7f2 | 467 | if ! $(ignore-project-config) |
7c673cae | 468 | { |
11fdf7f2 TL |
469 | local project-config = [ handle-config-option project-config ] ; |
470 | if $(project-config) | |
471 | { | |
472 | initialize-config-module project-config : $(project-config:D=) ; | |
473 | load-config project-config : $(project-config:D=) | |
474 | : $(project-config:D) : must-exist ; | |
475 | } | |
476 | else if ! $(project-config)-is-defined | |
477 | { | |
478 | local file = [ path.glob "." : project-config.jam ] ; | |
479 | if ! $(file) | |
480 | { | |
481 | file = [ path.glob-in-parents "." : project-config.jam ] ; | |
482 | } | |
483 | if $(file) | |
484 | { | |
485 | initialize-config-module project-config : $(file:D) ; | |
486 | load-config project-config : project-config.jam : $(file:D) ; | |
487 | } | |
488 | } | |
489 | else if $(.debug-config) | |
490 | { | |
491 | ECHO "notice:" Project configuration file loading explicitly | |
492 | disabled. ; | |
493 | } | |
7c673cae FG |
494 | } |
495 | ||
496 | project.end-load ; | |
497 | } | |
498 | ||
499 | ||
500 | # Autoconfigure toolsets based on any instances of --toolset=xx,yy,...zz or | |
501 | # toolset=xx,yy,...zz in the command line. May return additional properties to | |
502 | # be processed as if they had been specified by the user. | |
503 | # | |
504 | local rule process-explicit-toolset-requests | |
505 | { | |
506 | local extra-properties ; | |
507 | ||
508 | local option-toolsets = [ regex.split-list [ MATCH ^--toolset=(.*)$ : $(.argv) ] : "," ] ; | |
509 | local feature-toolsets = [ regex.split-list [ MATCH ^toolset=(.*)$ : $(.argv) ] : "," ] ; | |
510 | ||
511 | for local t in $(option-toolsets) $(feature-toolsets) | |
512 | { | |
513 | # Parse toolset-version/properties. | |
11fdf7f2 | 514 | local toolset = [ MATCH "([^/]+)/?.*" : $(t) ] ; |
7c673cae FG |
515 | local properties = [ feature.expand-subfeatures <toolset>$(toolset) : true ] ; |
516 | local toolset-property = [ property.select <toolset> : $(properties) ] ; | |
517 | local known ; | |
518 | if $(toolset-property:G=) in [ feature.values <toolset> ] | |
519 | { | |
520 | known = true ; | |
521 | } | |
522 | ||
523 | # If the toolset is not known, configure it now. | |
524 | ||
525 | # TODO: we should do 'using $(toolset)' in case no version has been | |
526 | # specified and there are no versions defined for the given toolset to | |
527 | # allow the toolset to configure its default version. For this we need | |
528 | # to know how to detect whether a given toolset has any versions | |
529 | # defined. An alternative would be to do this whenever version is not | |
530 | # specified but that would require that toolsets correctly handle the | |
531 | # case when their default version is configured multiple times which | |
532 | # should be checked for all existing toolsets first. | |
533 | ||
534 | if ! $(known) | |
535 | { | |
536 | if $(.debug-config) | |
537 | { | |
538 | ECHO "notice: [cmdline-cfg] toolset $(toolset) not" | |
539 | "previously configured; attempting to auto-configure now" ; | |
540 | } | |
11fdf7f2 TL |
541 | local t,v = [ MATCH "([^-]+)-?(.+)?" : $(toolset) ] ; |
542 | project.push-current ; | |
7c673cae | 543 | toolset.using $(t,v[1]) : $(t,v[2]) ; |
11fdf7f2 | 544 | project.pop-current ; |
7c673cae FG |
545 | } |
546 | ||
547 | # Make sure we get an appropriate property into the build request in | |
548 | # case toolset has been specified using the "--toolset=..." command-line | |
549 | # option form. | |
550 | if ! $(t) in $(.argv) $(feature-toolsets) | |
551 | { | |
552 | if $(.debug-config) | |
553 | { | |
11fdf7f2 | 554 | ECHO "notice:" "[cmdline-cfg]" adding toolset=$(t) to the build |
7c673cae FG |
555 | request. ; |
556 | } | |
557 | extra-properties += toolset=$(t) ; | |
558 | } | |
559 | } | |
560 | ||
561 | return $(extra-properties) ; | |
562 | } | |
563 | ||
564 | ||
565 | # Returns whether the given project (identifed by its project module) should be | |
566 | # cleaned because it or any of its parent projects have already been marked as | |
567 | # needing to be cleaned in this build. As an optimization, will explicitly mark | |
568 | # all encountered project needing to be cleaned in case thay have not already | |
569 | # been marked so. | |
570 | # | |
571 | local rule should-clean-project ( project ) | |
572 | { | |
573 | if ! $(.should-clean-project.$(project))-is-defined | |
574 | { | |
575 | local r = "" ; | |
576 | if ! [ project.is-jamroot-module $(project) ] | |
577 | { | |
578 | local parent = [ project.attribute $(project) parent-module ] ; | |
579 | if $(parent) | |
580 | { | |
581 | r = [ should-clean-project $(parent) ] ; | |
582 | } | |
583 | } | |
584 | .should-clean-project.$(project) = $(r) ; | |
585 | } | |
586 | ||
587 | return $(.should-clean-project.$(project)) ; | |
588 | } | |
589 | ||
590 | ||
591 | ################################################################################ | |
592 | # | |
593 | # main() | |
594 | # ------ | |
595 | # | |
596 | ################################################################################ | |
597 | ||
598 | { | |
599 | if --version in $(.argv) | |
600 | { | |
601 | version.print ; | |
602 | EXIT ; | |
603 | } | |
604 | ||
605 | version.verify-engine-version ; | |
606 | ||
607 | load-configuration-files ; | |
608 | ||
609 | # Load explicitly specified toolset modules. | |
610 | local extra-properties = [ process-explicit-toolset-requests ] ; | |
611 | ||
612 | # Load the actual project build script modules. We always load the project | |
613 | # in the current folder so 'use-project' directives have any chance of being | |
614 | # seen. Otherwise, we would not be able to refer to subprojects using target | |
615 | # ids. | |
616 | local current-project ; | |
617 | { | |
618 | local current-module = [ project.find "." : "." ] ; | |
619 | if $(current-module) | |
620 | { | |
621 | current-project = [ project.target $(current-module) ] ; | |
622 | } | |
623 | } | |
624 | ||
625 | # Load the default toolset module if no other has already been specified. | |
626 | if ! [ feature.values <toolset> ] | |
627 | { | |
628 | local default-toolset = $(.default-toolset) ; | |
629 | local default-toolset-version = ; | |
630 | if $(default-toolset) | |
631 | { | |
632 | default-toolset-version = $(.default-toolset-version) ; | |
633 | } | |
634 | else | |
635 | { | |
636 | default-toolset = gcc ; | |
637 | if [ os.name ] = NT | |
638 | { | |
639 | default-toolset = msvc ; | |
640 | } | |
641 | else if [ os.name ] = VMS | |
642 | { | |
643 | default-toolset = vmsdecc ; | |
644 | } | |
645 | else if [ os.name ] = MACOSX | |
646 | { | |
1e59de90 | 647 | default-toolset = clang ; |
7c673cae FG |
648 | } |
649 | } | |
650 | ||
651 | ECHO "warning: No toolsets are configured." ; | |
652 | ECHO "warning: Configuring default toolset" \"$(default-toolset)\". ; | |
653 | ECHO "warning: If the default is wrong, your build may not work correctly." ; | |
654 | ECHO "warning: Use the \"toolset=xxxxx\" option to override our guess." ; | |
655 | ECHO "warning: For more configuration options, please consult" ; | |
1e59de90 | 656 | ECHO "warning: https://www.bfgroup.xyz/b2/manual/release/index.html#bbv2.overview.configuration" ; |
7c673cae FG |
657 | |
658 | toolset.using $(default-toolset) : $(default-toolset-version) ; | |
659 | } | |
660 | ||
661 | ||
662 | # Parse command line for targets and properties. Note that this requires | |
663 | # that all project files already be loaded. | |
664 | # FIXME: This is not entirely true. Additional project files may be loaded | |
665 | # only later via the project.find() rule when dereferencing encountered | |
666 | # target ids containing explicit project references. See what to do about | |
667 | # those as such 'lazy loading' may cause problems that are then extremely | |
668 | # difficult to debug. | |
669 | local build-request = [ build-request.from-command-line $(.argv) | |
670 | $(extra-properties) ] ; | |
671 | local target-ids = [ $(build-request).get-at 1 ] ; | |
672 | local properties = [ $(build-request).get-at 2 ] ; | |
673 | ||
674 | ||
675 | # Check that we actually found something to build. | |
676 | if ! $(current-project) && ! $(target-ids) | |
677 | { | |
678 | import errors ; | |
679 | errors.user-error no Jamfile "in" current directory found, and no target | |
680 | references specified. ; | |
681 | } | |
682 | ||
683 | ||
684 | # Flags indicating that this build system run has been started in order to | |
685 | # clean existing instead of create new targets. Note that these are not the | |
686 | # final flag values as they may get changed later on due to some special | |
687 | # targets being specified on the command line. | |
688 | local clean ; if "--clean" in $(.argv) { clean = true ; } | |
689 | local cleanall ; if "--clean-all" in $(.argv) { cleanall = true ; } | |
690 | ||
691 | ||
692 | # List of explicitly requested files to build. Any target references read | |
693 | # from the command line parameter not recognized as one of the targets | |
694 | # defined in the loaded Jamfiles will be interpreted as an explicitly | |
695 | # requested file to build. If any such files are explicitly requested then | |
696 | # only those files and the targets they depend on will be built and they | |
697 | # will be searched for among targets that would have been built had there | |
698 | # been no explicitly requested files. | |
699 | local explicitly-requested-files | |
700 | ||
701 | ||
702 | # List of Boost Build meta-targets, virtual-targets and actual Jam targets | |
703 | # constructed in this build system run. | |
704 | local targets ; | |
705 | local virtual-targets ; | |
706 | local actual-targets ; | |
707 | ||
708 | ||
709 | # Process each target specified on the command-line and convert it into | |
710 | # internal Boost Build target objects. Detect special clean target. If no | |
92f5a8d4 | 711 | # main Boost Build targets were explicitly requested use the current project |
7c673cae FG |
712 | # as the target. |
713 | for local id in $(target-ids) | |
714 | { | |
715 | if $(id) = clean | |
716 | { | |
717 | clean = true ; | |
718 | } | |
719 | else | |
720 | { | |
721 | local t ; | |
722 | if $(current-project) | |
723 | { | |
724 | t = [ $(current-project).find $(id) : no-error ] ; | |
725 | } | |
726 | else | |
727 | { | |
728 | t = [ find-target $(id) ] ; | |
729 | } | |
730 | ||
731 | if ! $(t) | |
732 | { | |
733 | ECHO "notice: could not find main target" $(id) ; | |
734 | ECHO "notice: assuming it is a name of file to create." ; | |
735 | explicitly-requested-files += $(id) ; | |
736 | } | |
737 | else | |
738 | { | |
739 | targets += $(t) ; | |
740 | } | |
741 | } | |
742 | } | |
743 | if ! $(targets) | |
744 | { | |
745 | targets += [ project.target [ project.module-name "." ] ] ; | |
746 | } | |
747 | ||
748 | if [ option.get dump-generators : : true ] | |
749 | { | |
750 | generators.dump ; | |
751 | } | |
752 | ||
753 | # We wish to put config.log in the build directory corresponding to Jamroot, | |
754 | # so that the location does not differ depending on the directory we run the | |
755 | # build from. The amount of indirection necessary here is scary. | |
756 | local first-project = [ $(targets[0]).project ] ; | |
757 | local first-project-root-location = [ $(first-project).get project-root ] ; | |
758 | local first-project-root-module = [ project.load | |
759 | $(first-project-root-location) ] ; | |
760 | local first-project-root = [ project.target $(first-project-root-module) ] ; | |
761 | local first-build-build-dir = [ $(first-project-root).build-dir ] ; | |
762 | configure.set-log-file $(first-build-build-dir)/config.log ; | |
763 | config-cache.load $(first-build-build-dir)/project-cache.jam ; | |
764 | ||
765 | # Expand properties specified on the command line into multiple property | |
766 | # sets consisting of all legal property combinations. Each expanded property | |
767 | # set will be used for a single build run. E.g. if multiple toolsets are | |
768 | # specified then requested targets will be built with each of them. | |
769 | # The expansion is being performed as late as possible so that the feature | |
770 | # validation is performed after all necessary modules (including project targets | |
771 | # on the command line) have been loaded. | |
772 | if $(properties) | |
773 | { | |
1e59de90 TL |
774 | local cli_properties = [ build-request.convert-command-line-elements $(properties) ] ; |
775 | if $(cli_properties) | |
7c673cae | 776 | { |
1e59de90 TL |
777 | expanded += $(cli_properties) ; |
778 | expanded = [ build-request.expand-no-defaults $(expanded) ] ; | |
779 | local xexpanded ; | |
780 | for local e in $(expanded) | |
781 | { | |
782 | xexpanded += [ property-set.create [ feature.split $(e) ] ] ; | |
783 | } | |
784 | expanded = $(xexpanded) ; | |
785 | } | |
786 | else | |
787 | { | |
788 | expanded = [ property-set.empty ] ; | |
7c673cae | 789 | } |
7c673cae FG |
790 | } |
791 | else | |
792 | { | |
793 | expanded = [ property-set.empty ] ; | |
794 | } | |
795 | ||
796 | # Now that we have a set of targets to build and a set of property sets to | |
797 | # build the targets with, we can start the main build process by using each | |
798 | # property set to generate virtual targets from all of our listed targets | |
799 | # and any of their dependants. | |
800 | for local p in $(expanded) | |
801 | { | |
802 | .command-line-free-features = [ property-set.create [ $(p).free ] ] ; | |
803 | for local t in $(targets) | |
804 | { | |
805 | local g = [ $(t).generate $(p) ] ; | |
806 | if ! [ class.is-a $(t) : project-target ] | |
807 | { | |
808 | .results-of-main-targets += $(g[2-]) ; | |
809 | } | |
810 | virtual-targets += $(g[2-]) ; | |
811 | } | |
812 | } | |
813 | ||
814 | ||
815 | # Convert collected virtual targets into actual raw Jam targets. | |
816 | for t in $(virtual-targets) | |
817 | { | |
818 | actual-targets += [ $(t).actualize ] ; | |
819 | } | |
820 | ||
821 | config-cache.save ; | |
822 | ||
823 | ||
824 | # If XML data output has been requested prepare additional rules and targets | |
825 | # so we can hook into Jam to collect build data while its building and have | |
826 | # it trigger the final XML report generation after all the planned targets | |
827 | # have been built. | |
828 | if $(.out-xml) | |
829 | { | |
830 | # Get a qualified virtual target name. | |
831 | rule full-target-name ( target ) | |
832 | { | |
833 | local name = [ $(target).name ] ; | |
834 | local project = [ $(target).project ] ; | |
835 | local project-path = [ $(project).get location ] ; | |
836 | return $(project-path)//$(name) ; | |
837 | } | |
838 | ||
839 | # Generate an XML file containing build statistics for each constituent. | |
840 | # | |
841 | rule out-xml ( xml-file : constituents * ) | |
842 | { | |
843 | # Prepare valid XML header and footer with some basic info. | |
844 | local nl = " | |
845 | " ; | |
846 | local os = [ modules.peek : OS OSPLAT JAMUNAME ] "" ; | |
847 | local timestamp = [ modules.peek : JAMDATE ] ; | |
848 | local cwd = [ PWD ] ; | |
849 | local command = $(.argv) ; | |
850 | local bb-version = [ version.boost-build ] ; | |
851 | .header on $(xml-file) = | |
852 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>" | |
853 | "$(nl)<build format=\"1.0\" version=\"$(bb-version)\">" | |
854 | "$(nl) <os name=\"$(os[1])\" platform=\"$(os[2])\"><![CDATA[$(os[3-]:J= )]]></os>" | |
855 | "$(nl) <timestamp><![CDATA[$(timestamp)]]></timestamp>" | |
856 | "$(nl) <directory><![CDATA[$(cwd)]]></directory>" | |
857 | "$(nl) <command><![CDATA[\"$(command:J=\" \")\"]]></command>" | |
858 | ; | |
859 | .footer on $(xml-file) = | |
860 | "$(nl)</build>" ; | |
861 | ||
862 | # Generate the target dependency graph. | |
863 | .contents on $(xml-file) += | |
864 | "$(nl) <targets>" ; | |
865 | for local t in [ virtual-target.all-targets ] | |
866 | { | |
867 | local action = [ $(t).action ] ; | |
868 | if $(action) | |
869 | # If a target has no action, it has no dependencies. | |
870 | { | |
871 | local name = [ full-target-name $(t) ] ; | |
872 | local sources = [ $(action).sources ] ; | |
873 | local dependencies ; | |
874 | for local s in $(sources) | |
875 | { | |
876 | dependencies += [ full-target-name $(s) ] ; | |
877 | } | |
878 | ||
879 | local path = [ $(t).path ] ; | |
880 | local jam-target = [ $(t).actual-name ] ; | |
881 | ||
882 | .contents on $(xml-file) += | |
883 | "$(nl) <target>" | |
884 | "$(nl) <name><![CDATA[$(name)]]></name>" | |
885 | "$(nl) <dependencies>" | |
886 | "$(nl) <dependency><![CDATA[$(dependencies)]]></dependency>" | |
887 | "$(nl) </dependencies>" | |
888 | "$(nl) <path><![CDATA[$(path)]]></path>" | |
889 | "$(nl) <jam-target><![CDATA[$(jam-target)]]></jam-target>" | |
890 | "$(nl) </target>" | |
891 | ; | |
892 | } | |
893 | } | |
894 | .contents on $(xml-file) += | |
895 | "$(nl) </targets>" ; | |
896 | ||
897 | # Build $(xml-file) after $(constituents). Do so even if a | |
898 | # constituent action fails and regenerate the xml on every bjam run. | |
899 | INCLUDES $(xml-file) : $(constituents) ; | |
900 | ALWAYS $(xml-file) ; | |
901 | __ACTION_RULE__ on $(xml-file) = | |
902 | build-system.out-xml.generate-action ; | |
903 | out-xml.generate $(xml-file) ; | |
904 | } | |
905 | ||
906 | # The actual build actions are here; if we did this work in the actions | |
907 | # clause we would have to form a valid command line containing the | |
908 | # result of @(...) below (the name of the XML file). | |
909 | # | |
910 | rule out-xml.generate-action ( args * : xml-file | |
911 | : command status start end user system : output ? ) | |
912 | { | |
913 | local contents = | |
914 | [ on $(xml-file) return $(.header) $(.contents) $(.footer) ] ; | |
915 | local f = @($(xml-file):E=$(contents)) ; | |
916 | } | |
917 | ||
918 | # Nothing to do here; the *real* actions happen in | |
919 | # out-xml.generate-action. | |
920 | actions quietly out-xml.generate { } | |
921 | ||
922 | # Define the out-xml file target, which depends on all the targets so | |
923 | # that it runs the collection after the targets have run. | |
924 | out-xml $(.out-xml) : $(actual-targets) ; | |
925 | ||
926 | # Set up a global __ACTION_RULE__ that records all the available | |
927 | # statistics about each actual target in a variable "on" the --out-xml | |
928 | # target. | |
929 | # | |
930 | rule out-xml.collect ( xml-file : target : command status start end user | |
931 | system : output ? ) | |
932 | { | |
933 | local nl = " | |
934 | " ; | |
935 | # Open the action with some basic info. | |
936 | .contents on $(xml-file) += | |
937 | "$(nl) <action status=\"$(status)\" start=\"$(start)\" end=\"$(end)\" user=\"$(user)\" system=\"$(system)\">" ; | |
938 | ||
939 | # If we have an action object we can print out more detailed info. | |
940 | local action = [ on $(target) return $(.action) ] ; | |
941 | if $(action) | |
942 | { | |
943 | local action-name = [ $(action).action-name ] ; | |
944 | local action-sources = [ $(action).sources ] ; | |
945 | local action-props = [ $(action).properties ] ; | |
946 | ||
947 | # The qualified name of the action which we created the target. | |
948 | .contents on $(xml-file) += | |
949 | "$(nl) <name><![CDATA[$(action-name)]]></name>" ; | |
950 | ||
951 | # The sources that made up the target. | |
952 | .contents on $(xml-file) += | |
953 | "$(nl) <sources>" ; | |
954 | for local source in $(action-sources) | |
955 | { | |
956 | local source-actual = [ $(source).actual-name ] ; | |
957 | .contents on $(xml-file) += | |
958 | "$(nl) <source><![CDATA[$(source-actual)]]></source>" ; | |
959 | } | |
960 | .contents on $(xml-file) += | |
961 | "$(nl) </sources>" ; | |
962 | ||
963 | # The properties that define the conditions under which the | |
964 | # target was built. | |
965 | .contents on $(xml-file) += | |
966 | "$(nl) <properties>" ; | |
967 | for local prop in [ $(action-props).raw ] | |
968 | { | |
969 | local prop-name = [ MATCH ^<(.*)>$ : $(prop:G) ] ; | |
970 | .contents on $(xml-file) += | |
971 | "$(nl) <property name=\"$(prop-name)\"><![CDATA[$(prop:G=)]]></property>" ; | |
972 | } | |
973 | .contents on $(xml-file) += | |
974 | "$(nl) </properties>" ; | |
975 | } | |
976 | ||
977 | local locate = [ on $(target) return $(LOCATE) ] ; | |
978 | locate ?= "" ; | |
979 | .contents on $(xml-file) += | |
980 | "$(nl) <jam-target><![CDATA[$(target)]]></jam-target>" | |
981 | "$(nl) <path><![CDATA[$(target:G=:R=$(locate))]]></path>" | |
982 | "$(nl) <command><![CDATA[$(command)]]></command>" | |
983 | "$(nl) <output><![CDATA[$(output)]]></output>" ; | |
984 | .contents on $(xml-file) += | |
985 | "$(nl) </action>" ; | |
986 | } | |
987 | ||
988 | # When no __ACTION_RULE__ is set "on" a target, the search falls back to | |
989 | # the global module. | |
990 | module | |
991 | { | |
992 | __ACTION_RULE__ = build-system.out-xml.collect | |
993 | [ modules.peek build-system : .out-xml ] ; | |
994 | } | |
995 | ||
996 | IMPORT | |
997 | build-system : | |
998 | out-xml.collect | |
999 | out-xml.generate-action | |
1000 | : : | |
1001 | build-system.out-xml.collect | |
1002 | build-system.out-xml.generate-action | |
1003 | ; | |
1004 | } | |
1005 | ||
1006 | local j = [ option.get jobs ] ; | |
1007 | if $(j) | |
1008 | { | |
1009 | modules.poke : PARALLELISM : $(j) ; | |
1010 | } | |
1011 | ||
1012 | local k = [ option.get keep-going : true : true ] ; | |
1013 | if $(k) in "on" "yes" "true" | |
1014 | { | |
1015 | modules.poke : KEEP_GOING : 1 ; | |
1016 | } | |
1017 | else if $(k) in "off" "no" "false" | |
1018 | { | |
1019 | modules.poke : KEEP_GOING : 0 ; | |
1020 | } | |
1021 | else | |
1022 | { | |
1023 | EXIT "error: Invalid value for the --keep-going option" ; | |
1024 | } | |
1025 | ||
1026 | # The 'all' pseudo target is not strictly needed expect in the case when we | |
1027 | # use it below but people often assume they always have this target | |
1028 | # available and do not declare it themselves before use which may cause | |
1029 | # build failures with an error message about not being able to build the | |
1030 | # 'all' target. | |
1031 | NOTFILE all ; | |
1032 | ||
1033 | # And now that all the actual raw Jam targets and all the dependencies | |
1034 | # between them have been prepared all that is left is to tell Jam to update | |
1035 | # those targets. | |
1036 | if $(explicitly-requested-files) | |
1037 | { | |
1038 | # Note that this case can not be joined with the regular one when only | |
1039 | # exact Boost Build targets are requested as here we do not build those | |
1040 | # requested targets but only use them to construct the dependency tree | |
1041 | # needed to build the explicitly requested files. | |
1042 | UPDATE $(explicitly-requested-files:G=e) $(.out-xml) ; | |
1043 | } | |
1044 | else if $(cleanall) | |
1045 | { | |
1046 | UPDATE clean-all ; | |
1047 | } | |
1048 | else if $(clean) | |
1049 | { | |
1050 | common.Clean clean : [ actual-clean-targets ] ; | |
1051 | UPDATE clean ; | |
1052 | } | |
1053 | else | |
1054 | { | |
1055 | configure.print-configure-checks-summary ; | |
1056 | ||
11fdf7f2 | 1057 | for local function in $(.pre-build-hook) |
7c673cae | 1058 | { |
11fdf7f2 | 1059 | indirect.call $(function) ; |
7c673cae FG |
1060 | } |
1061 | ||
1062 | DEPENDS all : $(actual-targets) ; | |
1063 | if UPDATE_NOW in [ RULENAMES ] | |
1064 | { | |
b32b8144 FG |
1065 | local ok = [ UPDATE_NOW all ] ; |
1066 | # Force sequence updating of regular targets, then the xml | |
1067 | # log output target. To ensure the output records all built | |
1068 | # as otherwise if could execute out-of-sequence when | |
1069 | # doing parallel builds. | |
1070 | if $(.out-xml) | |
1071 | { | |
1072 | UPDATE_NOW $(.out-xml) : : ignore-minus-n ; | |
1073 | } | |
11fdf7f2 | 1074 | for local function in $(.post-build-hook) |
7c673cae | 1075 | { |
11fdf7f2 | 1076 | indirect.call $(function) $(ok) ; |
7c673cae FG |
1077 | } |
1078 | # Prevent automatic update of the 'all' target, now that we have | |
1079 | # explicitly updated what we wanted. | |
1080 | UPDATE ; | |
1081 | } | |
1082 | else | |
1083 | { | |
1084 | UPDATE all $(.out-xml) ; | |
1085 | } | |
1086 | } | |
1087 | } |