Ticket #5943: common.jam

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