]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/build/src/tools/common.jam
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / tools / build / src / tools / common.jam
1 # Copyright 2003, 2005 Dave Abrahams
2 # Copyright 2005, 2006 Rene Rivera
3 # Copyright 2005 Toon Knapen
4 # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
5 # Distributed under the Boost Software License, Version 1.0.
6 # (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
7
8 # Provides actions common to all toolsets, such as creating directories and
9 # removing files.
10
11 import os ;
12 import modules ;
13 import utility ;
14 import print ;
15 import type ;
16 import feature ;
17 import errors ;
18 import path ;
19 import sequence ;
20 import toolset ;
21 import virtual-target ;
22 import numbers ;
23
24 if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ]
25 {
26 .debug-configuration = true ;
27 }
28 if [ MATCH (--show-configuration) : [ modules.peek : ARGV ] ]
29 {
30 .show-configuration = true ;
31 }
32
33 # Configurations
34 #
35 # The following class helps to manage toolset configurations. Each configuration
36 # has a unique ID and one or more parameters. A typical example of a unique ID
37 # is a condition generated by 'common.check-init-parameters' rule. Other kinds
38 # of IDs can be used. Parameters may include any details about the configuration
39 # like 'command', 'path', etc.
40 #
41 # A toolset configuration may be in one of the following states:
42 #
43 # - registered
44 # Configuration has been registered (e.g. explicitly or by auto-detection
45 # code) but has not yet been marked as used, i.e. 'toolset.using' rule has
46 # not yet been called for it.
47 # - used
48 # Once called 'toolset.using' rule marks the configuration as 'used'.
49 #
50 # The main difference between the states above is that while a configuration is
51 # 'registered' its options can be freely changed. This is useful in particular
52 # for autodetection code - all detected configurations may be safely overwritten
53 # by user code.
54
55 class configurations
56 {
57 import errors ;
58
59 rule __init__ ( )
60 {
61 }
62
63 # Registers a configuration.
64 #
65 # Returns 'true' if the configuration has been added and an empty value if
66 # it already exists. Reports an error if the configuration is 'used'.
67 #
68 rule register ( id )
69 {
70 if $(id) in $(self.used)
71 {
72 errors.error "common: the configuration '$(id)' is in use" ;
73 }
74
75 local retval ;
76
77 if ! $(id) in $(self.all)
78 {
79 self.all += $(id) ;
80
81 # Indicate that a new configuration has been added.
82 retval = true ;
83 }
84
85 return $(retval) ;
86 }
87
88 # Mark a configuration as 'used'.
89 #
90 # Returns 'true' if the state of the configuration has been changed to
91 # 'used' and an empty value if it the state has not been changed. Reports an
92 # error if the configuration is not known.
93 #
94 rule use ( id )
95 {
96 if ! $(id) in $(self.all)
97 {
98 errors.error "common: the configuration '$(id)' is not known" ;
99 }
100
101 local retval ;
102
103 if ! $(id) in $(self.used)
104 {
105 self.used += $(id) ;
106
107 # Indicate that the configuration has been marked as 'used'.
108 retval = true ;
109 }
110
111 return $(retval) ;
112 }
113
114 # Return all registered configurations.
115 #
116 rule all ( )
117 {
118 return $(self.all) ;
119 }
120
121 # Return all used configurations.
122 #
123 rule used ( )
124 {
125 return $(self.used) ;
126 }
127
128 # Returns the value of a configuration parameter.
129 #
130 rule get ( id : param )
131 {
132 return $(self.$(param).$(id)) ;
133 }
134
135 # Sets the value of a configuration parameter.
136 #
137 rule set ( id : param : value * )
138 {
139 self.$(param).$(id) = $(value) ;
140 }
141 }
142
143
144 # The rule for checking toolset parameters. Trailing parameters should all be
145 # parameter name/value pairs. The rule will check that each parameter either has
146 # a value in each invocation or has no value in each invocation. Also, the rule
147 # will check that the combination of all parameter values is unique in all
148 # invocations.
149 #
150 # Each parameter name corresponds to a subfeature. This rule will declare a
151 # subfeature the first time a non-empty parameter value is passed and will
152 # extend it with all the values.
153 #
154 # The return value from this rule is a condition to be used for flags settings.
155 #
156 rule check-init-parameters ( toolset requirement * : * )
157 {
158 local sig = $(toolset) ;
159 local condition = <toolset>$(toolset) ;
160 local subcondition ;
161 for local index in 2 3 4 5 6 7 8 9
162 {
163 local name = $($(index)[1]) ;
164 local value = $($(index)[2]) ;
165
166 if $(value)-is-not-empty
167 {
168 condition = $(condition)-$(value) ;
169 if $(.had-unspecified-value.$(toolset).$(name))
170 {
171 errors.user-error
172 "$(toolset) initialization: parameter '$(name)'"
173 "inconsistent" : "no value was specified in earlier"
174 "initialization" : "an explicit value is specified now" ;
175 }
176 # The below logic is for intel compiler. It calls this rule with
177 # 'intel-linux' and 'intel-win' as toolset, so we need to get the
178 # base part of toolset name. We can not pass 'intel' as toolset
179 # because in that case it will be impossible to register versionless
180 # intel-linux and intel-win toolsets of a specific version.
181 local t = $(toolset) ;
182 local m = [ MATCH "([^-]*)-" : $(toolset) ] ;
183 if $(m)
184 {
185 t = $(m[1]) ;
186 }
187 if ! $(.had-value.$(toolset).$(name))
188 {
189 if ! $(.declared-subfeature.$(t).$(name))
190 {
191 feature.subfeature toolset $(t) : $(name) : : propagated ;
192 .declared-subfeature.$(t).$(name) = true ;
193 }
194 .had-value.$(toolset).$(name) = true ;
195 }
196 feature.extend-subfeature toolset $(t) : $(name) : $(value) ;
197 subcondition += <toolset-$(t):$(name)>$(value) ;
198 }
199 else
200 {
201 if $(.had-value.$(toolset).$(name))
202 {
203 errors.user-error
204 "$(toolset) initialization: parameter '$(name)'"
205 "inconsistent" : "an explicit value was specified in an"
206 "earlier initialization" : "no value is specified now" ;
207 }
208 .had-unspecified-value.$(toolset).$(name) = true ;
209 }
210 sig = $(sig)$(value:E="")- ;
211 }
212 # We also need to consider requirements on the toolset as we can
213 # configure the same toolset multiple times with different options that
214 # are selected with the requirements.
215 if $(requirement)
216 {
217 sig = $(sig)$(requirement:J=,) ;
218 }
219 if $(sig) in $(.all-signatures)
220 {
221 local message =
222 "duplicate initialization of $(toolset) with the following parameters: " ;
223 for local index in 2 3 4 5 6 7 8 9
224 {
225 local p = $($(index)) ;
226 if $(p)
227 {
228 message += "$(p[1]) = $(p[2]:E=<unspecified>)" ;
229 }
230 }
231 message += "previous initialization at $(.init-loc.$(sig))" ;
232 errors.user-error
233 $(message[1]) : $(message[2]) : $(message[3]) : $(message[4]) :
234 $(message[5]) : $(message[6]) : $(message[7]) : $(message[8]) ;
235 }
236 .all-signatures += $(sig) ;
237 .init-loc.$(sig) = [ errors.nearest-user-location ] ;
238
239 # If we have a requirement, this version should only be applied under that
240 # condition. To accomplish this we add a toolset requirement that imposes
241 # the toolset subcondition, which encodes the version.
242 if $(requirement)
243 {
244 local r = <toolset>$(toolset) $(requirement) ;
245 r = $(r:J=,) ;
246 toolset.add-requirements "$(r):$(subcondition)" ;
247 }
248
249 # We add the requirements, if any, to the condition to scope the toolset
250 # variables and options to this specific version.
251 condition += $(requirement) ;
252
253 if $(.show-configuration)
254 {
255 ECHO "notice:" $(condition) ;
256 }
257 return $(condition:J=/) ;
258 }
259
260
261 # A helper rule to get the command to invoke some tool. If
262 # 'user-provided-command' is not given, tries to find binary named 'tool' in
263 # PATH and in the passed 'additional-path'. Otherwise, verifies that the first
264 # element of 'user-provided-command' is an existing program.
265 #
266 # This rule returns the command to be used when invoking the tool. If we can not
267 # find the tool, a warning is issued. If 'path-last' is specified, PATH is
268 # checked after 'additional-paths' when searching for 'tool'.
269 #
270 rule get-invocation-command-nodefault ( toolset : tool :
271 user-provided-command * : additional-paths * : path-last ? )
272 {
273 local command ;
274 if ! $(user-provided-command)
275 {
276 command = [ find-tool $(tool) : $(additional-paths) : $(path-last) ] ;
277 if ! $(command) && $(.debug-configuration)
278 {
279 ECHO "warning:" toolset $(toolset) "initialization:" can not find tool
280 $(tool) ;
281 ECHO "warning:" initialized from [ errors.nearest-user-location ] ;
282 }
283 }
284 else
285 {
286 command = [ check-tool $(user-provided-command) ] ;
287 if ! $(command) && $(.debug-configuration)
288 {
289 ECHO "warning:" toolset $(toolset) "initialization:" ;
290 ECHO "warning:" can not find user-provided command
291 '$(user-provided-command)' ;
292 ECHO "warning:" initialized from [ errors.nearest-user-location ] ;
293 }
294 }
295
296 return $(command) ;
297 }
298
299
300 # Same as get-invocation-command-nodefault, except that if no tool is found,
301 # returns either the user-provided-command, if present, or the 'tool' parameter.
302 #
303 rule get-invocation-command ( toolset : tool : user-provided-command * :
304 additional-paths * : path-last ? )
305 {
306 local result = [ get-invocation-command-nodefault $(toolset) : $(tool) :
307 $(user-provided-command) : $(additional-paths) : $(path-last) ] ;
308
309 if ! $(result)
310 {
311 if $(user-provided-command)
312 {
313 result = $(user-provided-command) ;
314 }
315 else
316 {
317 result = $(tool) ;
318 }
319 }
320 return $(result) ;
321 }
322
323
324 # Given an invocation command return the absolute path to the command. This
325 # works even if command has no path element and was found on the PATH.
326 #
327 rule get-absolute-tool-path ( command )
328 {
329 if $(command:D)
330 {
331 return $(command:D) ;
332 }
333 else
334 {
335 local m = [ GLOB [ modules.peek : PATH Path path ] : $(command)
336 $(command).exe ] ;
337 return $(m[1]:D) ;
338 }
339 }
340
341
342 # Attempts to find tool (binary) named 'name' in PATH and in 'additional-paths'.
343 # If found in PATH, returns 'name' and if found in additional paths, returns
344 # absolute name. If the tool is found in several directories, returns the first
345 # path found. Otherwise, returns an empty string. If 'path-last' is specified,
346 # PATH is searched after 'additional-paths'.
347 #
348 rule find-tool ( name : additional-paths * : path-last ? )
349 {
350 if $(name:D)
351 {
352 return [ check-tool-aux $(name) ] ;
353 }
354 local path = [ path.programs-path ] ;
355 local match = [ path.glob $(path) : $(name) $(name).exe ] ;
356 local additional-match = [ path.glob $(additional-paths) : $(name)
357 $(name).exe ] ;
358
359 local result ;
360 if $(path-last)
361 {
362 result = $(additional-match) ;
363 if ! $(result) && $(match)
364 {
365 result = $(name) ;
366 }
367 }
368 else
369 {
370 if $(match)
371 {
372 result = $(name) ;
373 }
374 else
375 {
376 result = $(additional-match) ;
377 }
378 }
379 if $(result)
380 {
381 return [ path.native $(result[1]) ] ;
382 }
383 }
384
385 # Checks if 'command' can be found either in path or is a full name to an
386 # existing file.
387 #
388 local rule check-tool-aux ( command )
389 {
390 if $(command:D)
391 {
392 if [ path.exists $(command) ]
393 # Both NT and Cygwin will run .exe files by their unqualified names.
394 || ( [ os.on-windows ] && [ path.exists $(command).exe ] )
395 # Only NT will run .bat & .cmd files by their unqualified names.
396 || ( ( [ os.name ] = NT ) && ( [ path.exists $(command).bat ] ||
397 [ path.exists $(command).cmd ] ) )
398 {
399 return $(command) ;
400 }
401 }
402 else
403 {
404 if [ GLOB [ modules.peek : PATH Path path ] : $(command) ]
405 {
406 return $(command) ;
407 }
408 }
409 }
410
411
412 # Checks that a tool can be invoked by 'command'. If command is not an absolute
413 # path, checks if it can be found in 'path'. If command is an absolute path,
414 # check that it exists. Returns 'command' if ok or empty string otherwise.
415 #
416 local rule check-tool ( xcommand + )
417 {
418 if [ check-tool-aux $(xcommand[1]) ] ||
419 [ check-tool-aux $(xcommand[-1]) ]
420 {
421 return $(xcommand) ;
422 }
423 }
424
425
426 # Handle common options for toolset, specifically sets the following flag
427 # variables:
428 # - CONFIG_COMMAND to $(command)
429 # - OPTIONS for compile to the value of <compileflags> in $(options)
430 # - OPTIONS for compile.c to the value of <cflags> in $(options)
431 # - OPTIONS for compile.c++ to the value of <cxxflags> in $(options)
432 # - OPTIONS for compile.asm to the value of <asmflags> in $(options)
433 # - OPTIONS for compile.fortran to the value of <fflags> in $(options)
434 # - OPTIONS for link to the value of <linkflags> in $(options)
435 #
436 rule handle-options ( toolset : condition * : command * : options * )
437 {
438 if $(.debug-configuration)
439 {
440 ECHO "notice:" will use '$(command)' for $(toolset), condition
441 $(condition:E=(empty)) ;
442 }
443
444 # The last parameter ('unchecked') says it is OK to set flags for another
445 # module.
446 toolset.flags $(toolset) CONFIG_COMMAND $(condition) : $(command)
447 : unchecked ;
448
449 toolset.flags $(toolset).compile OPTIONS $(condition) :
450 [ feature.get-values <compileflags> : $(options) ] : unchecked ;
451
452 toolset.flags $(toolset).compile.c OPTIONS $(condition) :
453 [ feature.get-values <cflags> : $(options) ] : unchecked ;
454
455 toolset.flags $(toolset).compile.c++ OPTIONS $(condition) :
456 [ feature.get-values <cxxflags> : $(options) ] : unchecked ;
457
458 toolset.flags $(toolset).compile.asm OPTIONS $(condition) :
459 [ feature.get-values <asmflags> : $(options) ] : unchecked ;
460
461 toolset.flags $(toolset).compile.fortran OPTIONS $(condition) :
462 [ feature.get-values <fflags> : $(options) ] : unchecked ;
463
464 toolset.flags $(toolset).link OPTIONS $(condition) :
465 [ feature.get-values <linkflags> : $(options) ] : unchecked ;
466 }
467
468
469 # Returns the location of the "program files" directory on a Windows platform.
470 #
471 rule get-program-files-dir ( )
472 {
473 local ProgramFiles = [ modules.peek : ProgramFiles ] ;
474 if $(ProgramFiles)
475 {
476 ProgramFiles = "$(ProgramFiles:J= )" ;
477 }
478 else
479 {
480 ProgramFiles = "c:\\Program Files" ;
481 }
482 return $(ProgramFiles) ;
483 }
484
485
486 if [ os.name ] = NT
487 {
488 NULL_DEVICE = "NUL" ;
489 IGNORE = "2>$(NULL_DEVICE) >$(NULL_DEVICE) & setlocal" ;
490 RM = del /f /q ;
491 CP = copy /b ;
492 LN ?= $(CP) ;
493 # Ugly hack to convince copy to set the timestamp of the destination to the
494 # current time by concatenating the source with a nonexistent file. Note
495 # that this requires /b (binary) as the default when concatenating files is
496 # /a (ascii).
497 WINDOWS-CP-HACK = "+ this-file-does-not-exist-A698EE7806899E69" ;
498 }
499 else if [ os.name ] = VMS
500 {
501 NULL_DEVICE = "NL:" ;
502 PIPE = PIPE ;
503 IGNORE = "2>$(NULL_DEVICE) >$(NULL_DEVICE)" ;
504 RM = DELETE /NOCONF ;
505 CP = COPY /OVERWRITE ;
506 LN = $(CP) ;
507 }
508 else
509 {
510 NULL_DEVICE = "/dev/null" ;
511 IGNORE = "2>$(NULL_DEVICE) >$(NULL_DEVICE)" ;
512 RM = rm -f ;
513 CP = cp ;
514 LN = ln ;
515 }
516
517 NULL_OUT = ">$(NULL_DEVICE)" ;
518
519 rule null-device ( )
520 {
521 return $(NULL_DEVICE) ;
522 }
523
524
525 rule rm-command ( )
526 {
527 return $(RM) ;
528 }
529
530
531 rule copy-command ( )
532 {
533 return $(CP) ;
534 }
535
536
537 if "\n" = "n"
538 {
539 # Escape characters not supported so use ugly hacks. Will not work on Cygwin
540 # - see below.
541 nl = "
542 " ;
543 q = "" ;
544 }
545 else
546 {
547 nl = "\n" ;
548 q = "\"" ;
549 }
550
551
552 rule newline-char ( )
553 {
554 return $(nl) ;
555 }
556
557
558 # Returns the command needed to set an environment variable on the current
559 # platform. The variable setting persists through all following commands and is
560 # visible in the environment seen by subsequently executed commands. In other
561 # words, on Unix systems, the variable is exported, which is consistent with the
562 # only possible behavior on Windows systems.
563 #
564 rule variable-setting-command ( variable : value )
565 {
566 if [ os.name ] = NT
567 {
568 return "set $(variable)=$(value)$(nl)" ;
569 }
570 else if [ os.name ] = VMS
571 {
572 return "$(variable) == $(q)$(value)$(q)$(nl)" ;
573 }
574 else
575 {
576 # If we do not have escape character support in bjam, the cod below
577 # blows up on CYGWIN, since the $(nl) variable holds a Windows new-line
578 # \r\n sequence that messes up the executed export command which then
579 # reports that the passed variable name is incorrect.
580 # But we have a check for cygwin in kernel/bootstrap.jam already.
581 return "$(variable)=$(q)$(value)$(q)$(nl)export $(variable)$(nl)" ;
582 }
583 }
584
585
586 # Returns a command to sets a named shell path variable to the given NATIVE
587 # paths on the current platform.
588 #
589 rule path-variable-setting-command ( variable : paths * )
590 {
591 local sep = [ os.path-separator ] ;
592 return [ variable-setting-command $(variable) : $(paths:J=$(sep)) ] ;
593 }
594
595
596 # Returns a command that prepends the given paths to the named path variable on
597 # the current platform.
598 #
599 rule prepend-path-variable-command ( variable : paths * )
600 {
601 return [ path-variable-setting-command $(variable)
602 : $(paths) [ os.expand-variable $(variable) ] ] ;
603 }
604
605
606 # Return a command which can create a file. If 'r' is result of invocation, then
607 # 'r foobar' will create foobar with unspecified content. What happens if file
608 # already exists is unspecified.
609 #
610 rule file-creation-command ( )
611 {
612 if [ os.name ] = NT
613 {
614 # A few alternative implementations on Windows:
615 #
616 # 'type NUL >> '
617 # That would construct an empty file instead of a file containing
618 # a space and an end-of-line marker but it would also not change
619 # the target's timestamp in case the file already exists.
620 #
621 # 'type NUL > '
622 # That would construct an empty file instead of a file containing
623 # a space and an end-of-line marker but it would also destroy an
624 # already existing file by overwriting it with an empty one.
625 #
626 # I guess the best solution would be to allow Boost Jam to define
627 # built-in functions such as 'create a file', 'touch a file' or 'copy a
628 # file' which could be used from inside action code. That would allow
629 # completely portable operations without this kind of kludge.
630 # (22.02.2009.) (Jurko)
631 return "echo. > " ;
632 }
633 else if [ os.name ] = VMS
634 {
635 return "APPEND /NEW NL: " ;
636 }
637 else
638 {
639 return "touch " ;
640 }
641 }
642
643
644 # Returns a command that may be used for 'touching' files. It is not a real
645 # 'touch' command on NT because it adds an empty line at the end of file but it
646 # works with source files.
647 #
648 rule file-touch-command ( )
649 {
650 if [ os.name ] = NT
651 {
652 return "echo. >> " ;
653 }
654 else if [ os.name ] = VMS
655 {
656 return "APPEND /NEW NL: " ;
657 }
658 else
659 {
660 return "touch " ;
661 }
662 }
663
664
665 rule MkDir
666 {
667 # If dir exists, do not update it. Do this even for $(DOT).
668 NOUPDATE $(<) ;
669
670 if $(<) != $(DOT) && ! $($(<)-mkdir)
671 {
672 # Cheesy gate to prevent multiple invocations on same dir.
673 $(<)-mkdir = true ;
674
675 # Schedule the mkdir build action.
676 common.mkdir $(<) ;
677
678 # Prepare a Jam 'dirs' target that can be used to make the build only
679 # construct all the target directories.
680 DEPENDS dirs : $(<) ;
681
682 # Recursively create parent directories. $(<:P) = $(<)'s parent & we
683 # recurse until root.
684
685 local s = $(<:P) ;
686 if [ os.name ] = NT
687 {
688 switch $(s)
689 {
690 case "*:" : s = ;
691 case "*:\\" : s = ;
692 }
693 }
694
695 if $(s)
696 {
697 if $(s) != $(<)
698 {
699 DEPENDS $(<) : $(s) ;
700 MkDir $(s) ;
701 }
702 else
703 {
704 NOTFILE $(s) ;
705 }
706 }
707 }
708 }
709
710
711 #actions MkDir1
712 #{
713 # mkdir "$(<)"
714 #}
715
716 # The following quick-fix actions should be replaced using the original MkDir1
717 # action once Boost Jam gets updated to correctly detect different paths leading
718 # up to the same filesystem target and triggers their build action only once.
719 # (todo) (04.07.2008.) (Jurko)
720
721 if [ os.name ] = NT
722 {
723 actions quietly mkdir
724 {
725 if not exist "$(<)\\" mkdir "$(<)"
726 }
727 }
728 else
729 {
730 actions quietly mkdir
731 {
732 mkdir -p "$(<)"
733 }
734 }
735
736
737 actions piecemeal together existing Clean
738 {
739 $(RM) "$(>)"
740 }
741
742
743 rule copy
744 {
745 }
746
747
748 actions copy
749 {
750 $(CP) "$(>)" $(WINDOWS-CP-HACK) "$(<)"
751 }
752
753
754 rule RmTemps
755 {
756 }
757
758
759 actions quietly updated piecemeal together RmTemps
760 {
761 $(RM) "$(>)" $(IGNORE)
762 }
763
764
765 actions hard-link
766 {
767 $(RM) "$(<)" 2$(NULL_OUT) $(NULL_OUT)
768 $(LN) "$(>)" "$(<)" $(NULL_OUT)
769 }
770
771
772 if [ os.name ] = VMS
773 {
774 actions mkdir
775 {
776 IF F$PARSE("$(<:W)") .EQS. "" THEN CREATE /DIR $(<:W)
777 }
778
779 actions piecemeal together existing Clean
780 {
781 $(RM) $(>:WJ=;*,);*
782 }
783
784 actions copy
785 {
786 $(CP) $(>:WJ=,) $(<:W)
787 }
788
789 actions quietly updated piecemeal together RmTemps
790 {
791 $(PIPE) $(RM) $(>:WJ=;*,);* $(IGNORE)
792 }
793
794 actions hard-link
795 {
796 $(PIPE) $(RM) $(>[1]:W);* $(IGNORE)
797 $(PIPE) $(LN) $(>[1]:W) $(<:W) $(NULL_OUT)
798 }
799 }
800
801 # Given a target, as given to a custom tag rule, returns a string formatted
802 # according to the passed format. Format is a list of properties that is
803 # represented in the result. For each element of format the corresponding target
804 # information is obtained and added to the result string. For all, but the
805 # literal, the format value is taken as the as string to prepend to the output
806 # to join the item to the rest of the result. If not given "-" is used as a
807 # joiner.
808 #
809 # The format options can be:
810 #
811 # <base>[joiner]
812 # :: The basename of the target name.
813 # <toolset>[joiner]
814 # :: The abbreviated toolset tag being used to build the target.
815 # <threading>[joiner]
816 # :: Indication of a multi-threaded build.
817 # <runtime>[joiner]
818 # :: Collective tag of the build runtime.
819 # <version:/version-feature | X.Y[.Z]/>[joiner]
820 # :: Short version tag taken from the given "version-feature" in the
821 # build properties. Or if not present, the literal value as the
822 # version number.
823 # <property:/property-name/>[joiner]
824 # :: Direct lookup of the given property-name value in the build
825 # properties. /property-name/ is a regular expression. E.g.
826 # <property:toolset-.*:flavor> will match every toolset.
827 # /otherwise/
828 # :: The literal value of the format argument.
829 #
830 # For example this format:
831 #
832 # boost_ <base> <toolset> <threading> <runtime> <version:boost-version>
833 #
834 # Might return:
835 #
836 # boost_thread-vc80-mt-gd-1_33.dll, or
837 # boost_regex-vc80-gd-1_33.dll
838 #
839 # The returned name also has the target type specific prefix and suffix which
840 # puts it in a ready form to use as the value from a custom tag rule.
841 #
842 rule format-name ( format * : name : type ? : property-set )
843 {
844 local result = "" ;
845 for local f in $(format)
846 {
847 switch $(f:G)
848 {
849 case <base> :
850 result += $(name:B) ;
851
852 case <toolset> :
853 result += [ join-tag $(f:G=) : [ toolset-tag $(name) : $(type) :
854 $(property-set) ] ] ;
855
856 case <threading> :
857 result += [ join-tag $(f:G=) : [ threading-tag $(name) : $(type)
858 : $(property-set) ] ] ;
859
860 case <runtime> :
861 result += [ join-tag $(f:G=) : [ runtime-tag $(name) : $(type) :
862 $(property-set) ] ] ;
863
864 case <qt> :
865 result += [ join-tag $(f:G=) : [ qt-tag $(name) : $(type) :
866 $(property-set) ] ] ;
867
868 case <address-model> :
869 result += [ join-tag $(f:G=) : [ address-model-tag $(name) :
870 $(type) : $(property-set) ] ] ;
871
872 case <arch-and-model> :
873 result += [ join-tag $(f:G=) : [ arch-and-model-tag $(name) :
874 $(type) : $(property-set) ] ] ;
875
876 case <version:*> :
877 local key = [ MATCH <version:(.*)> : $(f:G) ] ;
878 local version = [ $(property-set).get <$(key)> ] ;
879 version ?= $(key) ;
880 version = [ MATCH "^([^.]+)[.]([^.]+)[.]?([^.]*)" : $(version) ] ;
881 result += [ join-tag $(f:G=) : $(version[1])_$(version[2]) ] ;
882
883 case <property:*> :
884 local key = [ MATCH <property:(.*)> : $(f:G) ] ;
885 local p0 = [ MATCH <($(key))> : [ $(property-set).raw ] ] ;
886 if $(p0)
887 {
888 local p = [ $(property-set).get <$(p0)> ] ;
889 if $(p)
890 {
891 result += [ join-tag $(f:G=) : $(p) ] ;
892 }
893 }
894
895 case * :
896 result += $(f:G=) ;
897 }
898 }
899 return [ virtual-target.add-prefix-and-suffix $(result:J=) : $(type) :
900 $(property-set) ] ;
901 }
902
903
904 local rule join-tag ( joiner ? : tag ? )
905 {
906 if ! $(joiner) { joiner = - ; }
907 return $(joiner)$(tag) ;
908 }
909
910
911 local rule toolset-tag ( name : type ? : property-set )
912 {
913 local tag = ;
914
915 local properties = [ $(property-set).raw ] ;
916 switch [ $(property-set).get <toolset> ]
917 {
918 case borland* : tag += bcb ;
919 case clang* :
920 {
921 switch [ $(property-set).get <toolset-clang:platform> ]
922 {
923 case darwin : tag += clang-darwin ;
924 case linux : tag += clang ;
925 case win : tag += clangw ;
926 }
927 }
928 case como* : tag += como ;
929 case cw : tag += cw ;
930 case darwin* : tag += xgcc ;
931 case edg* : tag += edg ;
932 case embarcadero* : tag += embtc ;
933 case gcc* :
934 {
935 switch [ $(property-set).get <target-os> ]
936 {
937 case *windows* : tag += mgw ;
938 case * : tag += gcc ;
939 }
940 }
941 case intel :
942 if [ $(property-set).get <toolset-intel:platform> ] = win
943 {
944 tag += iw ;
945 }
946 else
947 {
948 tag += il ;
949 }
950 case kcc* : tag += kcc ;
951 case kylix* : tag += bck ;
952 #case metrowerks* : tag += cw ;
953 #case mingw* : tag += mgw ;
954 case mipspro* : tag += mp ;
955 case msvc* : tag += vc ;
956 case qcc* : tag += qcc ;
957 case sun* : tag += sw ;
958 case tru64cxx* : tag += tru ;
959 case vacpp* : tag += xlc ;
960 }
961 local version = [ MATCH "<toolset.*version>([0123456789]+)[.]?([0123456789]*)"
962 : $(properties) ] ;
963 # For historical reasons, vc6.0 and vc7.0 use different naming.
964 if $(tag) = vc
965 {
966 if $(version[1]) = 6
967 {
968 # Cancel minor version.
969 version = 6 ;
970 }
971 else if $(version[1]) = 7 && $(version[2]) = 0
972 {
973 version = 7 ;
974 }
975 }
976
977 # From GCC 5, versioning changes and minor becomes patch
978 if ( $(tag) = gcc || $(tag) = mgw ) && $(version[1]) && [ numbers.less 4 $(version[1]) ]
979 {
980 version = $(version[1]) ;
981 }
982
983 # Ditto, from Clang 4
984 if ( $(tag) = clang || $(tag) = clangw ) && $(version[1]) && [ numbers.less 3 $(version[1]) ]
985 {
986 version = $(version[1]) ;
987 }
988
989 # On intel, version is not added, because it does not matter and it is the
990 # version of vc used as backend that matters. Ideally, we should encode the
991 # backend version but that would break compatibility with V1.
992 if $(tag) = iw
993 {
994 version = ;
995 }
996
997 # On borland, version is not added for compatibility with V1.
998 if $(tag) = bcb
999 {
1000 version = ;
1001 }
1002
1003 # Use un-versioned toolset name for Embarcadero, to avoid having to update
1004 # the auto-linking logic on every release.
1005 if $(tag) = embtc
1006 {
1007 version = ;
1008 }
1009
1010 tag += $(version) ;
1011
1012 return $(tag:J=) ;
1013 }
1014
1015
1016 local rule threading-tag ( name : type ? : property-set )
1017 {
1018 if <threading>multi in [ $(property-set).raw ]
1019 {
1020 return mt ;
1021 }
1022 }
1023
1024
1025 local rule runtime-tag ( name : type ? : property-set )
1026 {
1027 local tag = ;
1028
1029 local properties = [ $(property-set).raw ] ;
1030 if <runtime-link>static in $(properties) { tag += s ; }
1031
1032 # This is an ugly thing. In V1, there is code to automatically detect which
1033 # properties affect a target. So, if <runtime-debugging> does not affect gcc
1034 # toolset, the tag rules will not even see <runtime-debugging>. Similar
1035 # functionality in V2 is not implemented yet, so we just check for toolsets
1036 # known to care about runtime debugging.
1037 if ( <toolset>msvc in $(properties) ) ||
1038 ( <stdlib>stlport in $(properties) ) ||
1039 ( <toolset-intel:platform>win in $(properties) ) ||
1040 ( <toolset-clang:platform>win in $(properties) )
1041 {
1042 if <runtime-debugging>on in $(properties) { tag += g ; }
1043 }
1044
1045 if <python-debugging>on in $(properties) { tag += y ; }
1046 if <variant>debug in $(properties) { tag += d ; }
1047 if <stdlib>stlport in $(properties) { tag += p ; }
1048 if <stdlib-stlport:iostream>hostios in $(properties) { tag += n ; }
1049
1050 return $(tag:J=) ;
1051 }
1052
1053
1054 # Create a tag for the Qt library version
1055 # "<qt>4.6.0" will result in tag "qt460"
1056 local rule qt-tag ( name : type ? : property-set )
1057 {
1058 local v = [ MATCH "([0123456789]+)[.]?([0123456789]*)[.]?([0123456789]*)" :
1059 [ $(property-set).get <qt> ] ] ;
1060 return qt$(v:J=) ;
1061 }
1062
1063
1064 # Create a tag for the address-model
1065 # <address-model>64 will simply generate "64"
1066 local rule address-model-tag ( name : type ? : property-set )
1067 {
1068 return [ $(property-set).get <address-model> ] ;
1069 }
1070
1071 # Create a tag for the architecture and model
1072 # <architecture>x86 <address-model>32 would generate "x32"
1073 # This relies on the fact that all architectures start with
1074 # unique letters.
1075 local rule arch-and-model-tag ( name : type ? : property-set )
1076 {
1077 local architecture = [ $(property-set).get <architecture> ] ;
1078 local address-model = [ $(property-set).get <address-model> ] ;
1079
1080 local arch = [ MATCH ^(.) : $(architecture) ] ;
1081
1082 return $(arch)$(address-model) ;
1083 }
1084
1085 # TODO: probably needs to escape '"' in command parts
1086 # TODO: if part does not contain whitespaces it does not require escaping
1087 rule make-command-string ( command + )
1088 {
1089 local command-string = \"$(command)\" ;
1090 return $(command-string:J=" ") ;
1091 }
1092
1093 rule find-compiler ( toolset : tool : version ? : command * : additional-paths * )
1094 {
1095 #1): use user-provided command
1096 if $(command)
1097 {
1098 if ! [ get-invocation-command-nodefault $(toolset) : $(tool)
1099 : $(command) : $(additional-paths) ]
1100 {
1101 local command-string = [ make-command-string $(command) ] ;
1102 errors.error toolset $(toolset) "initialization:"
1103 : provided command '$(command-string)' not found
1104 : initialized from [ errors.nearest-user-location ] ;
1105 }
1106 }
1107 #2): enforce user-provided version
1108 else if $(version)
1109 {
1110 command = [ get-invocation-command-nodefault $(toolset) : $(tool)-$(version)
1111 : : $(additional-paths) ] ;
1112
1113 #2.1) fallback: check whether "$(tool)" reports the requested version
1114 if ! $(command) { # ?= operator does not short-circuit
1115 command ?= [ get-invocation-command-nodefault $(toolset) : $(tool)
1116 : : $(additional-paths) ] ;
1117 }
1118
1119 if ! $(command)
1120 {
1121 errors.error toolset $(toolset) "initialization:"
1122 : version '$(version)' requested but neither
1123 '$(tool)-$(version)' nor default '$(tool)' found
1124 : initialized from [ errors.nearest-user-location ] ;
1125 }
1126
1127 import $(toolset) ;
1128 local tool-version = [ $(toolset).get-full-version $(command) ] ;
1129
1130 import version ;
1131 if ! [ version.version-compatible [ SPLIT_BY_CHARACTERS $(version) : . ]
1132 : [ SPLIT_BY_CHARACTERS $(tool-version) : . ] ]
1133 {
1134 errors.error toolset $(toolset) "initialization:"
1135 : version '$(version)' requested but
1136 '$(tool)-$(version)' not found and version
1137 '$(tool-version:J=.)' of default '$(command)'
1138 does not match
1139 : initialized from [ errors.nearest-user-location ]
1140 ;
1141 }
1142 }
1143 #3) default: no command and no version specified, try using "$(tool)"
1144 else
1145 {
1146 command = [ get-invocation-command-nodefault $(toolset) : $(tool)
1147 : : $(additional-paths) ] ;
1148 if ! $(command)
1149 {
1150 errors.error toolset $(toolset) "initialization:"
1151 : no command provided, default command '$(tool)' not found
1152 : initialized from [ errors.nearest-user-location ] ;
1153 }
1154 }
1155 return $(command) ;
1156 }
1157
1158 rule match-command-output ( kind ? : pattern : command-string )
1159 {
1160 local output = [ SHELL $(command-string) : exit-status ] ;
1161 if 0 != $(output[2])
1162 {
1163 errors.error '$(command-string)'
1164 exited with error code $(output[2]) ;
1165 }
1166
1167 local match = [ MATCH $(pattern) : $(output[1]) ] ;
1168 if ! $(match)
1169 {
1170 errors.error '$(command-string)'
1171 returned an invalid $kind string '$(output[1])' ;
1172 }
1173
1174 return $(match) ;
1175 }
1176
1177 rule __test__ ( )
1178 {
1179 import assert ;
1180
1181 local save-os = [ modules.peek os : .name ] ;
1182
1183 modules.poke os : .name : LINUX ;
1184 assert.result "PATH=\"foo:bar:baz\"\nexport PATH\n"
1185 : path-variable-setting-command PATH : foo bar baz ;
1186 assert.result "PATH=\"foo:bar:$PATH\"\nexport PATH\n"
1187 : prepend-path-variable-command PATH : foo bar ;
1188
1189 modules.poke os : .name : NT ;
1190 assert.result "set PATH=foo;bar;baz\n"
1191 : path-variable-setting-command PATH : foo bar baz ;
1192 assert.result "set PATH=foo;bar;%PATH%\n"
1193 : prepend-path-variable-command PATH : foo bar ;
1194
1195 modules.poke os : .name : $(save-os) ;
1196 }