Index: build/engine.py =================================================================== --- build/engine.py (revision 75643) +++ build/engine.py (working copy) @@ -20,14 +20,14 @@ self.function = function def __call__(self, targets, sources, property_set): - if self.function: - self.function(targets, sources, property_set) # Bjam actions defined from Python have only the command # to execute, and no associated jam procedural code. So # passing 'property_set' to it is not necessary. bjam_interface.call("set-update-action", self.action_name, targets, sources, []) + if self.function: + self.function(targets, sources, property_set) class BjamNativeAction: """Class representing bjam action defined by Jam code. @@ -132,7 +132,12 @@ bjam_flags = reduce(operator.or_, (action_modifiers[flag] for flag in flags), 0) - bjam_interface.define_action(action_name, command, bound_list, bjam_flags) + # We allow command to be empty so that we can define 'action' as pure + # python function that would do some conditional logic and then relay + # to other actions. + assert command or function + if command: + bjam_interface.define_action(action_name, command, bound_list, bjam_flags) self.actions[action_name] = BjamAction(action_name, function) Index: build/property.py =================================================================== --- build/property.py (revision 75643) +++ build/property.py (working copy) @@ -63,7 +63,7 @@ (other._feature, other._value, other._condition)) -def create_from_string(s, allow_condition=False): +def create_from_string(s, allow_condition=False,allow_missing_value=False): condition = [] import types @@ -92,7 +92,7 @@ f = feature.get(feature_name) value = get_value(s) - if not value: + if not value and not allow_missing_value: get_manager().errors()("Invalid property '%s' -- no value specified" % s) Index: build/toolset.py =================================================================== --- build/toolset.py (revision 75643) +++ build/toolset.py (working copy) @@ -144,7 +144,7 @@ transformed = [] for c in condition: # FIXME: 'split' might be a too raw tool here. - pl = [property.create_from_string(s) for s in c.split('/')] + pl = [property.create_from_string(s,False,True) for s in c.split('/')] pl = feature.expand_subfeatures(pl); transformed.append(property_set.create(pl)) condition = transformed @@ -380,7 +380,6 @@ #if ! $(.ignore-requirements) #{ - print "XXXX", requirements __requirements.extend(requirements) #} Index: tools/builtin.py =================================================================== --- tools/builtin.py (revision 75643) +++ tools/builtin.py (working copy) @@ -144,11 +144,20 @@ feature.feature ('threading', ['single', 'multi'], ['propagated']) feature.feature ('rtti', ['on', 'off'], ['propagated']) feature.feature ('exception-handling', ['on', 'off'], ['propagated']) + + # Whether there is support for asynchronous EH (e.g. catching SEGVs). + feature.feature ('asynch-exceptions', ['on', 'off'], ['propagated']) + + # Whether all extern "C" functions are considered nothrow by default. + feature.feature ('extern-c-nothrow', ['off', 'on'], ['propagated']) + feature.feature ('debug-symbols', ['on', 'off'], ['propagated']) feature.feature ('define', [], ['free']) + feature.feature ('undef', [], ['free']) feature.feature ('include', [], ['free', 'path']) #order-sensitive feature.feature ('cflags', [], ['free']) feature.feature ('cxxflags', [], ['free']) + feature.feature ('asmflags', [], ['free']) feature.feature ('linkflags', [], ['free']) feature.feature ('archiveflags', [], ['free']) feature.feature ('version', [], ['free']) @@ -309,10 +318,6 @@ variant ('release', ['speed', 'off', 'full', 'off', 'NDEBUG']) variant ('profile', ['release'], ['on', 'on']) - - type.register ('H', ['h']) - type.register ('HPP', ['hpp'], 'H') - type.register ('C', ['c']) reset () @@ -700,21 +705,11 @@ return result -### rule register-archiver ( id composing ? : source_types + : target_types + : -### requirements * ) -### { -### local g = [ new ArchiveGenerator $(id) $(composing) : $(source_types) -### : $(target_types) : $(requirements) ] ; -### generators.register $(g) ; -### } -### -### -### IMPORT $(__name__) : register-linker register-archiver -### : : generators.register-linker generators.register-archiver ; -### -### -### +def register_archiver(id, source_types, target_types, requirements): + g = ArchiveGenerator(id, True, source_types, target_types, requirements) + generators.register(g) + class DummyGenerator(generators.Generator): """Generator that accepts everything and produces nothing. Useful as a general fallback for toolset-specific actions like PCH generation. Index: tools/common.py =================================================================== --- tools/common.py (revision 75643) +++ tools/common.py (working copy) @@ -349,7 +349,9 @@ programs = path.programs_path() m = path.glob(programs, [command, command + '.exe' ]) if not len(m): - print "Could not find:", command, "in", programs + if __debug_configuration: + print "Could not find:", command, "in", programs + return None return os.path.dirname(m[0]) # ported from trunk@47174 Index: tools/mc.py =================================================================== --- tools/mc.py (revision 0) +++ tools/mc.py (working copy) @@ -0,0 +1,46 @@ +# Copyright (c) 2005 Alexey Pakhunov. +# Copyright (c) 2011 Juraj Ivancic +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE_1_0.txt or +# http://www.boost.org/LICENSE_1_0.txt) + +# Support for Microsoft message compiler tool. +# Notes: +# - there's just message compiler tool, there's no tool for +# extracting message strings from sources +# - This file allows to use Microsoft message compiler +# with any toolset. In msvc.jam, there's more specific +# message compiling action. + +import bjam + +from b2.tools import common, rc +from b2.build import generators, type +from b2.build.toolset import flags +from b2.build.feature import feature +from b2.manager import get_manager + +def init(): + pass + +type.register('MC', ['mc']) + + +# Command line options +feature('mc-input-encoding', ['ansi', 'unicode'], ['free']) +feature('mc-output-encoding', ['unicode', 'ansi'], ['free']) +feature('mc-set-customer-bit', ['no', 'yes'], ['free']) + +flags('mc.compile', 'MCFLAGS', ['ansi'], ['-a']) +flags('mc.compile', 'MCFLAGS', ['unicode'], ['-u']) +flags('mc.compile', 'MCFLAGS', ['ansi'], '-A') +flags('mc.compile', 'MCFLAGS', ['unicode'], ['-U']) +flags('mc.compile', 'MCFLAGS', ['no'], []) +flags('mc.compile', 'MCFLAGS', ['yes'], ['-c']) + +generators.register_standard('mc.compile', ['MC'], ['H', 'RC']) + +get_manager().engine().register_action( + 'mc.compile', + 'mc $(MCFLAGS) -h "$(<[1]:DW)" -r "$(<[2]:DW)" "$(>:W)"') Index: tools/midl.py =================================================================== --- tools/midl.py (revision 0) +++ tools/midl.py (working copy) @@ -0,0 +1,134 @@ +# Copyright (c) 2005 Alexey Pakhunov. +# Copyright (c) 2011 Juraj Ivancic +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE_1_0.txt or +# http://www.boost.org/LICENSE_1_0.txt) + +# Microsoft Interface Definition Language (MIDL) related routines +from b2.build import scanner, type +from b2.build.toolset import flags +from b2.build.feature import feature +from b2.manager import get_manager +from b2.tools import builtin, common +from b2.util import regex + +def init(): + pass + +type.register('IDL', ['idl']) + +# A type library (.tlb) is generated by MIDL compiler and can be included +# to resources of an application (.rc). In order to be found by a resource +# compiler its target type should be derived from 'H' - otherwise +# the property '' will be ignored. +type.register('MSTYPELIB', 'tlb', 'H') + +# Register scanner for MIDL files +class MidlScanner(scanner.Scanner): + def __init__ (self, includes=[]): + scanner.Scanner.__init__(self) + self.includes = includes + + # List of quoted strings + re_strings = "[ \t]*\"([^\"]*)\"([ \t]*,[ \t]*\"([^\"]*)\")*[ \t]*" ; + + # 'import' and 'importlib' directives + self.re_import = "import" + re_strings + "[ \t]*;" ; + self.re_importlib = "importlib[ \t]*[(]" + re_strings + "[)][ \t]*;" ; + + # C preprocessor 'include' directive + self.re_include_angle = "#[ \t]*include[ \t]*<(.*)>" ; + self.re_include_quoted = "#[ \t]*include[ \t]*\"(.*)\"" ; + + def pattern(): + # Match '#include', 'import' and 'importlib' directives + return "((#[ \t]*include|import(lib)?).+(<(.*)>|\"(.*)\").+)" + + def process(self, target, matches, binding): + included_angle = regex.transform(matches, self.re_include_angle) + included_quoted = regex.transform(matches, self.re_include_quoted) + imported = regex.transform(matches, self.re_import, [1, 3]) + imported_tlbs = regex.transform(matches, self.re_importlib, [1, 3]) + + # CONSIDER: the new scoping rule seem to defeat "on target" variables. + g = bjam.call('get-target-variable', target, 'HDRGRIST') + b = os.path.normalize_path(os.path.dirname(binding)) + + # Attach binding of including file to included targets. + # When target is directly created from virtual target + # this extra information is unnecessary. But in other + # cases, it allows to distinguish between two headers of the + # same name included from different places. + g2 = g + "#" + b + + g = "<" + g + ">" + g2 = "<" + g2 + ">" + + included_angle = [ g + x for x in included_angle ] + included_quoted = [ g + x for x in included_quoted ] + imported = [ g + x for x in imported ] + imported_tlbs = [ g + x for x in imported_tlbs ] + + all = included_angle + included_quoted + imported + + bjam.call('INCLUDES', [target], all) + bjam.call('DEPENDS', [target], imported_tlbs) + bjam.call('NOCARE', all + imported_tlbs) + engine.set_target_variable(included_angle , 'SEARCH', ungrist(self.includes)) + engine.set_target_variable(included_quoted, 'SEARCH', b + ungrist(self.includes)) + engine.set_target_variable(imported , 'SEARCH', b + ungrist(self.includes)) + engine.set_target_variable(imported_tlbs , 'SEARCH', b + ungrist(self.includes)) + + get_manager().scanners().propagate(type.get_scanner('CPP', PropertySet(self.includes)), included_angle + included_quoted) + get_manager().scanners().propagate(self, imported) + +scanner.register(MidlScanner, 'include') +type.set_scanner('IDL', MidlScanner) + + +# Command line options +feature('midl-stubless-proxy', ['yes', 'no'], ['propagated'] ) +feature('midl-robust', ['yes', 'no'], ['propagated'] ) + +flags('midl.compile.idl', 'MIDLFLAGS', ['yes'], ['/Oicf' ]) +flags('midl.compile.idl', 'MIDLFLAGS', ['no' ], ['/Oic' ]) +flags('midl.compile.idl', 'MIDLFLAGS', ['yes' ], ['/robust' ]) +flags('midl.compile.idl', 'MIDLFLAGS', ['no' ], ['/no_robust']) + +# Architecture-specific options +architecture_x86 = ['' , 'x86'] +address_model_32 = ['', '32'] +address_model_64 = ['', '64'] + +flags('midl.compile.idl', 'MIDLFLAGS', [ar + '/' + m for ar in architecture_x86 for m in address_model_32 ], ['/win32']) +flags('midl.compile.idl', 'MIDLFLAGS', [ar + '/64' for ar in architecture_x86], ['/x64']) +flags('midl.compile.idl', 'MIDLFLAGS', ['ia64/' + m for m in address_model_64], ['/ia64']) + +flags('midl.compile.idl', 'DEFINES', [], ['']) +flags('midl.compile.idl', 'UNDEFS', [], ['']) +flags('midl.compile.idl', 'INCLUDES', [], ['']) + + +builtin.register_c_compiler('midl.compile.idl', ['IDL'], ['MSTYPELIB', 'H', 'C(%_i)', 'C(%_proxy)', 'C(%_dlldata)'], []) + + +# MIDL does not always generate '%_proxy.c' and '%_dlldata.c'. This behavior +# depends on contents of the source IDL file. Calling TOUCH_FILE below ensures +# that both files will be created so bjam will not try to recreate them +# constantly. +get_manager().engine().register_action( + 'midl.compile.idl', + '''midl /nologo @"@($(<[1]:W).rsp:E= +"$(>:W)" +-D$(DEFINES) +"-I$(INCLUDES)" +-U$(UNDEFS) +$(MIDLFLAGS) +/tlb "$(<[1]:W)" +/h "$(<[2]:W)" +/iid "$(<[3]:W)" +/proxy "$(<[4]:W)" +/dlldata "$(<[5]:W)")" +{touch} "$(<[4]:W)" +{touch} "$(<[5]:W)"'''.format(touch=common.file_creation_command())) Index: tools/msvc.py =================================================================== --- tools/msvc.py (revision 0) +++ tools/msvc.py (working copy) @@ -0,0 +1,1193 @@ +# Copyright (c) 2003 David Abrahams. +# Copyright (c) 2005 Vladimir Prus. +# Copyright (c) 2005 Alexey Pakhunov. +# Copyright (c) 2006 Bojan Resnik. +# Copyright (c) 2006 Ilya Sokolov. +# Copyright (c) 2007 Rene Rivera +# Copyright (c) 2008 Jurko Gospodnetic +# Copyright (c) 2011 Juraj Ivancic +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE_1_0.txt or +# http://www.boost.org/LICENSE_1_0.txt) + +################################################################################ +# +# MSVC Boost Build toolset module. +# -------------------------------- +# +# All toolset versions need to have their location either auto-detected or +# explicitly specified except for the special 'default' version that expects the +# environment to find the needed tools or report an error. +# +################################################################################ + +from os import environ +import os.path +import re +import _winreg + +import bjam + +from b2.tools import common, rc, pch, builtin, mc, midl +from b2.build import feature, type, toolset, generators, property_set +from b2.build.property import Property +from b2.util import path +from b2.manager import get_manager +from b2.build.generators import Generator +from b2.build.toolset import flags +from b2.util.utility import to_seq, on_windows +from b2.tools.common import Configurations + +__debug = None + +def debug(): + global __debug + if __debug is None: + __debug = "--debug-configuration" in bjam.variable("ARGV") + return __debug + + +# It is not yet clear what to do with Cygwin on python port. +def on_cygwin(): + return False + + +type.register('MANIFEST', ['manifest']) +feature.feature('embed-manifest',['on','off'], ['incidental', 'propagated']) ; + +type.register('PDB',['pdb']) + +################################################################################ +# +# Public rules. +# +################################################################################ + +# Initialize a specific toolset version configuration. As the result, path to +# compiler and, possible, program names are set up, and will be used when that +# version of compiler is requested. For example, you might have: +# +# using msvc : 6.5 : cl.exe ; +# using msvc : 7.0 : Y:/foo/bar/cl.exe ; +# +# The version parameter may be ommited: +# +# using msvc : : Z:/foo/bar/cl.exe ; +# +# The following keywords have special meanings when specified as versions: +# - all - all detected but not yet used versions will be marked as used +# with their default options. +# - default - this is an equivalent to an empty version. +# +# Depending on a supplied version, detected configurations and presence 'cl.exe' +# in the path different results may be achieved. The following table describes +# the possible scenarios: +# +# Nothing "x.y" +# Passed Nothing "x.y" detected, detected, +# version detected detected cl.exe in path cl.exe in path +# +# default Error Use "x.y" Create "default" Use "x.y" +# all None Use all None Use all +# x.y - Use "x.y" - Use "x.y" +# a.b Error Error Create "a.b" Create "a.b" +# +# "x.y" - refers to a detected version; +# "a.b" - refers to an undetected version. +# +# FIXME: Currently the command parameter and the property parameter +# seem to overlap in duties. Remove this duplication. This seems to be related +# to why someone started preparing to replace init with configure rules. + +def init(version = None, command = None, options = None): + # When initialized from + # using msvc : x.0 ; + # we get version as a single element list i.e. ['x.0'], + # but when specified from the command line we get a string i.e. 'x.0'. + # We want to work with a string, so unpack the list if needed. + is_single_element_list = (isinstance(version,list) and len(version) == 1) + assert(version==None or isinstance(version,str) or is_single_element_list) + if is_single_element_list: + version = version[0] + + options = to_seq(options) + command = to_seq(command) + + if command: + options.append(""+command) + configure(version,options) + +def configure(version=None, options=None): + if version == "all": + if options: + raise RuntimeError("MSVC toolset configuration: options should be empty when '{}' is specified.".format(version)) + + # Configure (i.e. mark as used) all registered versions. + all_versions = __versions.all() + if not all_versions: + if debug(): + print "notice: [msvc-cfg] Asked to configure all registered" \ + "msvc toolset versions when there are none currently" \ + "registered." ; + else: + for v in all_versions: + # Note that there is no need to skip already configured + # versions here as this will request configure-really rule + # to configure the version using default options which will + # in turn cause it to simply do nothing in case the version + # has already been configured. + configure_really(v) + elif version == "default": + configure_really(None,options) + else: + configure_really(version, options) + +def extend_conditions(conditions,exts): + return [ cond + '/' + ext for cond in conditions for ext in exts ] + +def configure_version_specific(toolset_arg, version, conditions): + # Starting with versions 7.0, the msvc compiler have the /Zc:forScope and + # /Zc:wchar_t options that improve C++ standard conformance, but those + # options are off by default. If we are sure that the msvc version is at + # 7.*, add those options explicitly. We can be sure either if user specified + # version 7.* explicitly or if we auto-detected the version ourselves. + if not re.match('^6\\.', version): + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS',conditions, ['/Zc:forScope','/Zc:wchar_t']) + toolset.flags('{}.compile.c++'.format(toolset_arg), 'C++FLAGS',conditions, ['/wd4675']) + + # Explicitly disable the 'function is deprecated' warning. Some msvc + # versions have a bug, causing them to emit the deprecation warning even + # with /W0. + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS',extend_conditions(conditions,['off']), ['/wd4996']) + if re.match('^[78]\\.', version): + # 64-bit compatibility warning deprecated since 9.0, see + # http://msdn.microsoft.com/en-us/library/yt4xw8fh.aspx + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS',extend_conditions(conditions,['all']), ['/Wp64']) + + # + # Processor-specific optimization. + # + if re.match('^[67]', version ): + # 8.0 deprecates some of the options. + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(conditions,['speed','space']), ['/Ogiy', '/Gs']) + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(conditions,['speed']), ['/Ot']) + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(conditions,['space']), ['/Os']) + + cpu_arch_i386_cond = extend_conditions(conditions, __cpu_arch_i386) + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(cpu_arch_i386_cond,['']),['/GB']) + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(cpu_arch_i386_cond,['i386']),['/G3']) + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(cpu_arch_i386_cond,['i486']),['/G4']) + + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(cpu_arch_i386_cond,['' + t for t in __cpu_type_g5]), ['/G5']) + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(cpu_arch_i386_cond,['' + t for t in __cpu_type_g6]), ['/G6']) + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(cpu_arch_i386_cond,['' + t for t in __cpu_type_g7]), ['/G7']) + + # Improve floating-point accuracy. Otherwise, some of C++ Boost's "math" + # tests will fail. + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', conditions, ['/Op']) + + # 7.1 and below have single-threaded static RTL. + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(conditions,['off/static/single']), ['/ML']) + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(conditions,['on/static/single']), ['/MLd']) + else: + # 8.0 and above adds some more options. + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(conditions, [a + '/' for a in __cpu_arch_amd64]), ['/favor:blend']) + + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(conditions, [a + '/' + t for a in __cpu_arch_amd64 for t in __cpu_type_em64t]), ['/favor:EM64T']) + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(conditions, [a + '/' + t for a in __cpu_arch_amd64 for t in __cpu_type_amd64]), ['/favor:AMD64']) + + # 8.0 and above only has multi-threaded static RTL. + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(conditions,['off/static/single']), ['/MT']) + toolset.flags('{}.compile'.format(toolset_arg), 'CFLAGS', extend_conditions(conditions,['on/static/single']), ['/MTd']) + + # Specify target machine type so the linker will not need to guess. + toolset.flags('{}.link'.format(toolset_arg), 'LINKFLAGS', extend_conditions(conditions, __cpu_arch_amd64), ['/MACHINE:X64']) + toolset.flags('{}.link'.format(toolset_arg), 'LINKFLAGS', extend_conditions(conditions, __cpu_arch_i386), ['/MACHINE:X86']) + toolset.flags('{}.link'.format(toolset_arg), 'LINKFLAGS', extend_conditions(conditions, __cpu_arch_ia64), ['/MACHINE:IA64']) + + # Make sure that manifest will be generated even if there is no + # dependencies to put there. + toolset.flags('{}.link'.format(toolset_arg), 'LINKFLAGS', extend_conditions(conditions,["off"]), ['/MANIFEST']) + + +# Registers this toolset including all of its flags, features & generators. Does +# nothing on repeated calls. + +def register_toolset(): + if not 'msvc' in feature.values('toolset'): + register_toolset_really() + + +engine = get_manager().engine() + +# this rule sets up the pdb file that will be used when generating static +# libraries and the debug-store option is database, so that the compiler +# puts all debug info into a single .pdb file named after the library +# +# Poking at source targets this way is probably not clean, but it's the +# easiest approach. +def archive(targets, sources=None, properties=None): + bjam.call('set-target-variable',targets,'PDB_NAME', os.path.splitext(targets[0])[0] + '.pdb') + +# Declare action for creating static libraries. If library exists, remove it +# before adding files. See +# http://article.gmane.org/gmane.comp.lib.boost.build/4241 for rationale. +if not on_cygwin(): + engine.register_action( + 'msvc.archive', + '''if exist "$(<[1])" DEL "$(<[1])" + $(.LD) $(AROPTIONS) /out:"$(<[1])" @"@($(<[1]:W).rsp:E= +"$(>)" +$(LIBRARIES_MENTIONED_BY_FILE) +"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" +"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")"''', + function=archive) +else: + engine.register_action( + 'msvc.archive', + '''{rm} "$(<[1])" + $(.LD) $(AROPTIONS) /out:"$(<[1])" @"@($(<[1]:W).rsp:E= +"$(>)" +$(LIBRARIES_MENTIONED_BY_FILE) +"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" +"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")"'''.format(rm=common.rm_command()), + function=archive) + +# For the assembler the following options are turned on by default: +# +# -Zp4 align structures to 4 bytes +# -Cp preserve case of user identifiers +# -Cx preserve case in publics, externs +# +engine.register_action( + 'msvc.compile.asm', + '$(.ASM) -c -Zp4 -Cp -Cx -D$(DEFINES) $(ASMFLAGS) $(USER_ASMFLAGS) -Fo "$(<:W)" "$(>:W)"' ) + + +# Equivalent to [ on $(target) return $(prefix)$(var)$(suffix) ]. Note that $(var) can be a list. +def expand_target_variable(target,var,prefix=None,suffix=None): + list = bjam.call( 'get-target-variable', target, var ) + return " ".join([ ("" if prefix is None else prefix) + elem + ("" if suffix is None else suffix) for elem in list ]) + + +compile_c_cpp_pch = '''$(.CC) @"@($(<[1]:W).rsp:E="$(>[2]:W)" -Fo"$(<[2]:W)" -Yc"$(>[1]:D=)" $(YLOPTION)"__bjam_pch_symbol_$(>[1]:D=)" -Fp"$(<[1]:W)" $(CC_RSPLINE))" "@($(<[1]:W).cpp:E=#include $(.escaped-double-quote)$(>[1]:D=)$(.escaped-double-quote)$(.nl))" $(.CC.FILTER)''' +# Action for running the C/C++ compiler using precompiled headers. An already +# built source file for compiling the precompiled headers is expected to be +# given as one of the source parameters. +compile_c_cpp_pch_s = '''$(.CC) @"@($(<[1]:W).rsp:E="$(>[2]:W)" -Fo"$(<[2]:W)" -Yc"$(>[1]:D=)" $(YLOPTION)"__bjam_pch_symbol_$(>[1]:D=)" -Fp"$(<[1]:W)" $(CC_RSPLINE))" $(.CC.FILTER)''' + +def get_rspline(targets, lang_opt): + result = lang_opt + ' ' + \ + expand_target_variable(targets, 'UNDEFS', '-U' ) + ' ' + \ + expand_target_variable(targets, 'CFLAGS' ) + ' ' + \ + expand_target_variable(targets, 'C++FLAGS' ) + ' ' + \ + expand_target_variable(targets, 'OPTIONS' ) + ' -c ' + \ + expand_target_variable(targets, 'DEFINES', '\n-D' ) + ' ' + \ + expand_target_variable(targets, 'INCLUDES', '\n"-I', '"' ) + bjam.call('set-target-variable', targets, 'CC_RSPLINE', result) + +def compile_c(targets, sources = [], properties = None): + get_manager().engine().set_target_variable( targets[1], 'C++FLAGS', '' ) + get_rspline(targets, '-TC') + sources += bjam.call('get-target-variable',targets,'PCH_FILE') + sources += bjam.call('get-target-variable',targets,'PCH_HEADER') + compile_c_cpp(targets,sources) + +def compile_c_preprocess(targets, sources = [], properties = None): + get_manager().engine().set_target_variable( target[1], 'C++FLAGS', '' ) + get_rspline(targets, '-TC') + sources += bjam.call('get-target-variable',targets,'PCH_FILE') + sources += bjam.call('get-target-variable',targets,'PCH_HEADER') + preprocess_c_cpp(targets,sources) + +def compile_c_pch(targets, sources = [], properties = []): + get_manager().engine().set_target_variable( target[1], 'C++FLAGS', '' ) + get_rspline([targets[1]], '-TC') + get_rspline([targets[2]], '-TC') + pch_source = bjam.call('get-target-variable', targets, 'PCH_SOURCE') + sources += pch_source + if pch_source: + get_manager().engine().set_update_action('compile-c-c++-pch-s', targets, sources, properties) + get_manager().engine().add_dependency(targets,pch_source) + compile_c_cpp_pch_s(targets,sources) + else: + get_manager().engine().set_update_action('compile-c-c++-pch', targets, sources, properties) + compile_c_cpp_pch(targets,sources) + +toolset.flags( 'msvc', 'YLOPTION', [], ['-Yl'] ) + +def compile_cpp(targets,sources=[],properties=None): + get_rspline(targets,'-TP') + sources += bjam.call('get-target-variable',targets,'PCH_FILE') + sources += bjam.call('get-target-variable',targets,'PCH_HEADER') + compile_c_cpp(targets,sources) + +def compile_cpp_preprocess(targets,sources=[],properties=None): + get_rspline(targets,'-TP') + sources += bjam.call('get-target-variable',targets,'PCH_FILE') + sources += bjam.call('get-target-variable',targets,'PCH_HEADER') + preprocess_c_cpp(targets,sources) + +def compile_cpp_pch(targets,sources=[],properties=None): + get_rspline([targets[1]], '-TP') + get_rspline([targets[2]], '-TP') + pch_source = bjam.call('get-target-variable', targets, 'PCH_SOURCE') + sources += pch_source + if pch_source: + get_manager().engine().set_update_action('compile-c-c++-pch-s', targets, sources, properties) + get_manager().engine().add_dependency(targets,pch_source) + compile_c_cpp_pch_s(targets,sources) + else: + get_manager().engine().set_update_action('compile-c-c++-pch', targets, sources, properties) + compile_c_cpp_pch(targets,sources) + + +# Action for running the C/C++ compiler without using precompiled headers. +# +# WARNING: Synchronize any changes this in action with intel-win +# +# Notes regarding PDB generation, for when we use on/database +# +# 1. PDB_CFLAG is only set for on/database, ensuring that the /Fd flag is dropped if PDB_CFLAG is empty +# +# 2. When compiling executables's source files, PDB_NAME is set on a per-source file basis by rule compile-c-c++. +# The linker will pull these into the executable's PDB +# +# 3. When compiling library's source files, PDB_NAME is updated to .pdb for each source file by rule archive, +# as in this case the compiler must be used to create a single PDB for our library. +# + +compile_action = '$(.CC) @"@($(<[1]:W).rsp:E="$(>[1]:W)" -Fo"$(<[1]:W)" $(PDB_CFLAG)"$(PDB_NAME)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE))" $(.CC.FILTER)' +engine.register_action( + 'msvc.compile.c', + compile_action, + function=compile_c, + bound_list=['PDB_NAME']) + +engine.register_action( + 'msvc.compile.c++', + compile_action, + function=compile_cpp, + bound_list=['PDB_NAME']) + + +preprocess_action = '$(.CC) @"@($(<[1]:W).rsp:E="$(>[1]:W)" -E $(PDB_CFLAG)"$(PDB_NAME)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE))" >"$(<[1]:W)"' + +engine.register_action( + 'msvc.preprocess.c', + preprocess_action, + function=compile_c_preprocess, + bound_list=['PDB_NAME']) + +engine.register_action( + 'msvc.preprocess.c++', + preprocess_action, + function=compile_cpp_preprocess, + bound_list=['PDB_NAME']) + +def compile_c_cpp(targets,sources=None): + pch_header = bjam.call('get-target-variable',targets[0],'PCH_HEADER') + pch_file = bjam.call('get-target-variable',targets[0],'PCH_FILE') + if pch_header: get_manager().engine().add_dependency(targets[0],pch_header) + if pch_file: get_manager().engine().add_dependency(targets[0],pch_file) + bjam.call('set-target-variable',targets,'PDB_NAME', os.path.splitext(targets[0])[0] + '.pdb') + +def preprocess_c_cpp(targets,sources=None): + #same as above + return compile_c_cpp(targets,sources) + +# Action for running the C/C++ compiler using precompiled headers. In addition +# to whatever else it needs to compile, this action also adds a temporary source +# .cpp file used to compile the precompiled headers themselves. + +engine.register_action( + 'msvc.compile.c.pch', + None, # action set by the function + function=compile_c_pch) + +engine.register_action( + 'msvc.compile.c++.pch', + None, # action set by the function + function=compile_cpp_pch) + + +# See midl.py for details. +# +engine.register_action( + 'msvc.compile.idl', + '''$(.IDL) /nologo @"@($(<[1]:W).rsp:E= +"$(>:W)" +-D$(DEFINES) +"-I$(INCLUDES:W)" +-U$(UNDEFS) +$(MIDLFLAGS) +/tlb "$(<[1]:W)" +/h "$(<[2]:W)" +/iid "$(<[3]:W)" +/proxy "$(<[4]:W)" +/dlldata "$(<[5]:W)")" + {touch} "$(<[4]:W)" + {touch} "$(<[5]:W)"'''.format(touch=common.file_creation_command())) + +engine.register_action( + 'msvc.compile.mc', + '$(.MC) $(MCFLAGS) -h "$(<[1]:DW)" -r "$(<[2]:DW)" "$(>:W)"') + +engine.register_action( + 'msvc.compile.rc', + '$(.RC) -l 0x409 -U$(UNDEFS) -D$(DEFINES) -I"$(INCLUDES:W)" -fo "$(<:W)" "$(>:W)"') + +def link_dll(targets,sources=None,properties=None): + get_manager().engine().add_dependency(targets,bjam.call('get-target-variable',targets,'DEF_FILE')) + manifest(targets, sources, properties) + +def manifest(targets,sources=None,properties=None): + if 'on' in properties.get(''): + get_manager().engine().set_update_action('msvc.manifest', targets, sources, properties) + + +# Incremental linking a DLL causes no end of problems: if the actual exports do +# not change, the import .lib file is never updated. Therefore, the .lib is +# always out-of-date and gets rebuilt every time. I am not sure that incremental +# linking is such a great idea in general, but in this case I am sure we do not +# want it. + +# Windows manifest is a new way to specify dependencies on managed DotNet +# assemblies and Windows native DLLs. The manifests are embedded as resources +# and are useful in any PE target (both DLL and EXE). + +if not on_cygwin(): + engine.register_action( + 'msvc.link', + '''$(.LD) $(LINKFLAGS) /out:"$(<[1]:W)" /LIBPATH:"$(LINKPATH:W)" $(OPTIONS) @"@($(<[1]:W).rsp:E= +"$(>)" +$(LIBRARIES_MENTIONED_BY_FILE) +$(LIBRARIES) +"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" +"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" +if %ERRORLEVEL% NEQ 0 EXIT %ERRORLEVEL%''', + function=manifest, + bound_list=['PDB_NAME','DEF_FILE','LIBRARIES_MENTIONED_BY_FILE']) + + engine.register_action( + 'msvc.manifest', + '''if exist "$(<[1]).manifest" ( + $(.MT) -manifest "$(<[1]).manifest" "-outputresource:$(<[1]);1" + )''') + + engine.register_action( + 'msvc.link.dll', + '''$(.LD) /DLL $(LINKFLAGS) /out:"$(<[1]:W)" /IMPLIB:"$(<[2]:W)" /LIBPATH:"$(LINKPATH:W)" /def:"$(DEF_FILE)" $(OPTIONS) @"@($(<[1]:W).rsp:E= +"$(>)" +$(LIBRARIES_MENTIONED_BY_FILE) +$(LIBRARIES) +"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" +"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" +if %ERRORLEVEL% NEQ 0 EXIT %ERRORLEVEL%''', + function=link_dll, + bound_list=['DEF_FILE','LIBRARIES_MENTIONED_BY_FILE']) + + engine.register_action( + 'msvc.manifest.dll', + '''if exist "$(<[1]).manifest" ( + $(.MT) -manifest "$(<[1]).manifest" "-outputresource:$(<[1]);2" + )''') +else: + engine.register_action( + 'msvc.link', + '''$(.LD) $(LINKFLAGS) /out:"$(<[1]:W)" /LIBPATH:"$(LINKPATH:W)" $(OPTIONS) @"@($(<[1]:W).rsp:E= +"$(>)" +$(LIBRARIES_MENTIONED_BY_FILE) +$(LIBRARIES) +"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" +"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")"''', + function=manifest, + bound_list=['PDB_NAME','DEF_FILE','LIBRARIES_MENTIONED_BY_FILE']) + + engine.register_action( + 'msvc.manifest', + '''if test -e "$(<[1]).manifest"; then + $(.MT) -manifest "$(<[1]).manifest" "-outputresource:$(<[1]);1" + fi''') + + engine.register_action( + 'msvc.link.dll', + '''$(.LD) /DLL $(LINKFLAGS) /out:"$(<[1]:W)" /IMPLIB:"$(<[2]:W)" /LIBPATH:"$(LINKPATH:W)" /def:"$(DEF_FILE)" $(OPTIONS) @"@($(<[1]:W).rsp:E= +"$(>)" +$(LIBRARIES_MENTIONED_BY_FILE) +$(LIBRARIES) +"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" +"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")"''', + function=link_dll, + bound_list=['DEF_FILE','LIBRARIES_MENTIONED_BY_FILE']) + + engine.register_action( + 'msvc.manifest.dll', + '''if test -e "$(<[1]).manifest"; then + $(.MT) -manifest "$(<[1]).manifest" "-outputresource:$(<[1]);2" + fi''') + + +################################################################################ +# +# Classes. +# +################################################################################ + +class MsvcPchGenerator(pch.PchGenerator): + + # Inherit the __init__ method + + def run_pch(self, project, name, prop_set, sources): + # Find the header in sources. Ignore any CPP sources. + pch_header = None + pch_source = None + for s in sources: + if type.is_derived(s.type(), 'H'): + pch_header = s + elif type.is_derived(s.type(), 'CPP') or type.is_derived(s.type(), 'C'): + pch_source = s + + if not pch-header: + raise RuntimeError( "can not build pch without pch-header" ) + + # If we do not have the PCH source - that is fine. We will just create a + # temporary .cpp file in the action. + temp_prop_set = property_set.create([Property('pch-source',pch_source)]+prop_set.all()) + generated = Generator.run(project,name,temp_prop_set,pch_header) + pch_file = None + for g in generated: + if type.is_derived(g.type(), 'PCH'): + pch_file = g + return property_set.create([Property('pch-header',pch_header),Property('pch-file',pch_file)]+generated) + + +################################################################################ +# +# Local rules. +# +################################################################################ + +# Detects versions listed as '_known_versions' by checking registry information, +# environment variables & default paths. Supports both native Windows and +# Cygwin. +def auto_detect_toolset_versions(): + if on_windows() or on_cygwin(): + for version in _known_versions: + versionVarName = '__version_{}_reg'.format(version.replace('.','_')) + if versionVarName in globals(): + vc_path = None + for x in [ '', 'Wow6432Node\\' ]: + try: + with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\{}{}'.format(x, globals()[versionVarName])) as reg_key: + vc_path = _winreg.QueryValueEx(reg_key, "ProductDir")[0] + except: + pass + if vc_path: + vc_path = os.path.join(vc_path,'bin') + register_configuration(version,os.path.normpath(vc_path)) + + for i in _known_versions: + if not i in __versions.all(): + register_configuration(i,default_path(i)) + + +# Worker rule for toolset version configuration. Takes an explicit version id or +# nothing in case it should configure the default toolset version (the first +# registered one or a new 'default' one in case no toolset versions have been +# registered yet). +# + +def configure_really(version=None, options=[]): + v = version + if not v: + # Take the first registered (i.e. auto-detected) version. + version = __versions.first() + + # Note: 'version' can still be empty at this point if no versions have + # been auto-detected. + if not version: + version = "default" + + # Version alias -> real version number. + version = globals().get("__version_alias_{}".format(version), version) + + # Check whether the selected configuration is already in use. + if version in __versions.used(): + # Allow multiple 'toolset.using' calls for the same configuration if the + # identical sets of options are used. + if options and options != __versions.get(version,'options'): + raise RuntimeError("MSVC toolset configuration: Toolset version '$(version)' already configured.".format(version)) + else: + # Register a new configuration. + __versions.register(version) + + # Add user-supplied to auto-detected options. + version_opts = __versions.get(version, 'options') + if (version_opts): + options = version_opts + options + + # Mark the configuration as 'used'. + __versions.use(version) + # Generate conditions and save them. + conditions = common.check_init_parameters('msvc', None, ('version', v)) + __versions.set(version, 'conditions', conditions) + command = feature.get_values('', options) + + # If version is specified, we try to search first in default paths, and + # only then in PATH. + command = common.get_invocation_command('msvc', 'cl.exe', command, default_paths(version)) + common.handle_options('msvc', conditions, command, options) + + if not version: + # Even if version is not explicitly specified, try to detect the + # version from the path. + # FIXME: We currently detect both Microsoft Visual Studio 9.0 and + # 9.0express as 9.0 here. + if re.search("Microsoft Visual Studio 10", command): + version = '10.0' + elif re.search("Microsoft Visual Studio 9", command): + version = '9.0' + elif re.search("Microsoft Visual Studio 8", command): + version = '8.0' + elif re.search("NET 2003[\/\\]VC7", command): + version = '7.1' + elif re.search("Microsoft Visual C\\+\\+ Toolkit 2003", command): + version = '7.1toolkit' + elif re.search(".NET[\/\\]VC7", command): + version = '7.0' + else: + version = '6.0' + + # Generate and register setup command. + + below_8_0 = re.search("^[67]\\.",version) != None + + if below_8_0: + cpu = ['i386'] + else: + cpu = ['i386', 'amd64', 'ia64'] + + setup_scripts = {} + + if command: + # TODO: Note that if we specify a non-existant toolset version then + # this rule may find and use a corresponding compiler executable + # belonging to an incorrect toolset version. For example, if you + # have only MSVC 7.1 installed, have its executable on the path and + # specify you want Boost Build to use MSVC 9.0, then you want Boost + # Build to report an error but this may cause it to silently use the + # MSVC 7.1 compiler even though it thinks it is using the msvc-9.0 + # toolset version. + command = common.get_absolute_tool_path(command) + + if command: + parent = os.path.dirname(os.path.normpath(command)) + # Setup will be used if the command name has been specified. If + # setup is not specified explicitly then a default setup script will + # be used instead. Setup scripts may be global or arhitecture/ + # /platform/cpu specific. Setup options are used only in case of + # global setup scripts. + + # Default setup scripts provided with different VC distributions: + # + # VC 7.1 had only the vcvars32.bat script specific to 32 bit i386 + # builds. It was located in the bin folder for the regular version + # and in the root folder for the free VC 7.1 tools. + # + # Later 8.0 & 9.0 versions introduce separate platform specific + # vcvars*.bat scripts (e.g. 32 bit, 64 bit AMD or 64 bit Itanium) + # located in or under the bin folder. Most also include a global + # vcvarsall.bat helper script located in the root folder which runs + # one of the aforementioned vcvars*.bat scripts based on the options + # passed to it. So far only the version coming with some PlatformSDK + # distributions does not include this top level script but to + # support those we need to fall back to using the worker scripts + # directly in case the top level script can not be found. + + global_setup = feature.get_values('',options) + if global_setup: + global_setup = global_setup[0] + else: + global_setup = None + + if not below_8_0 and not global_setup: + global_setup = locate_default_setup(command,parent,'vcvarsall.bat') + + + default_setup = { + 'amd64' : 'vcvarsx86_amd64.bat', + 'i386' : 'vcvars32.bat', + 'ia64' : 'vcvarsx86_ia64.bat' } + + # http://msdn2.microsoft.com/en-us/library/x4d2c09s(VS.80).aspx and + # http://msdn2.microsoft.com/en-us/library/x4d2c09s(vs.90).aspx + # mention an x86_IPF option, that seems to be a documentation bug + # and x86_ia64 is the correct option. + default_global_setup_options = { + 'amd64' : 'x86_amd64', + 'i386' : 'x86', + 'ia64' : 'x86_ia64' } + + somehow_detect_the_itanium_platform = None + # When using 64-bit Windows, and targeting 64-bit, it is possible to + # use a native 64-bit compiler, selected by the "amd64" & "ia64" + # parameters to vcvarsall.bat. There are two variables we can use -- + # PROCESSOR_ARCHITECTURE and PROCESSOR_IDENTIFIER. The first is + # 'x86' when running 32-bit Windows, no matter which processor is + # used, and 'AMD64' on 64-bit windows on x86 (either AMD64 or EM64T) + # Windows. + # + if re.search( 'AMD64', environ[ "PROCESSOR_ARCHITECTURE" ] ) != None: + default_global_setup_options[ 'amd64' ] = 'amd64' + # TODO: The same 'native compiler usage' should be implemented for + # the Itanium platform by using the "ia64" parameter. For this + # though we need someone with access to this platform who can find + # out how to correctly detect this case. + elif somehow_detect_the_itanium_platform: + default_global_setup_options[ 'ia64' ] = 'ia64' + + setup_prefix = "call " + setup_suffix = """ >nul\n""" + if on_cygwin(): + setup_prefix = "cmd.exe /S /C call " + setup_suffix = " \">nul\" \"&&\" " + + for c in cpu: + setup_options = None + setup_cpu = feature.get_values(''.format(c),options) + + if not setup_cpu: + if global_setup: + setup_cpu = global_setup + # If needed we can easily add using configuration flags + # here for overriding which options get passed to the + # global setup command for which target platform: + # setup_options = feature.get_values(''.format(c),options) + if not setup_options: + setup_options = default_global_setup_options[ c ] + else: + setup_cpu = locate_default_setup(command, parent, default_setup[ c ]) + + # Cygwin to Windows path translation. + # setup-$(c) = "\""$(setup-$(c):W)"\"" ; + + # Append setup options to the setup name and add the final setup + # prefix & suffix. + setup_scripts[ c ] = '{}"{}" {}{}'.format(setup_prefix, setup_cpu, setup_options, setup_suffix) + + # Get tool names (if any) and finish setup. + compiler = feature.get_values("", options) + if not compiler: + compiler = "cl" + + linker = feature.get_values("", options) + if not linker: + linker = "link" + + resource_compiler = feature.get_values("", options) + if not resource_compiler: + resource_compiler = "rc" + + # Turn on some options for i386 assembler + # -coff generate COFF format object file (compatible with cl.exe output) + default_assembler_amd64 = 'ml64' + default_assembler_i386 = 'ml -coff' + default_assembler_ia64 = 'ias' + + assembler = feature.get_values('',options) + + idl_compiler = feature.get_values('',options) + if not idl_compiler: + idl_compiler = 'midl' + + mc_compiler = feature.get_values('',options) + if not mc_compiler: + mc_compiler = 'mc' + + manifest_tool = feature.get_values('',options) + if not manifest_tool: + manifest_tool = 'mt' + + cc_filter = feature.get_values('',options) + + for c in cpu: + cpu_conditions = [ condition + '/' + arch for arch in globals()['__cpu_arch_{}'.format(c)] for condition in conditions ] + + setup_script = setup_scripts.get(c, '') + + if debug(): + for cpu_condition in cpu_conditions: + print "notice: [msvc-cfg] condition: '{}', setup: '{}'".format(cpu_condition,setup_script) + + cpu_assembler = assembler + if not cpu_assembler: + cpu_assembler = locals()['default_assembler_{}'.format(c)] + + toolset.flags('msvc.compile', '.CC' , cpu_conditions, ['{}{} /Zm800 -nologo' .format(setup_script, compiler)]) + toolset.flags('msvc.compile', '.RC' , cpu_conditions, ['{}{} -nologo' .format(setup_script, resource_compiler)]) + toolset.flags('msvc.compile', '.ASM', cpu_conditions, ['{}{} ' .format(setup_script, cpu_assembler)]) + toolset.flags('msvc.link' , '.LD' , cpu_conditions, ['{}{} /NOLOGO /INCREMENTAL:NO'.format(setup_script, linker)]) + toolset.flags('msvc.archive', '.LD' , cpu_conditions, ['{}{} /lib /NOLOGO' .format(setup_script, linker)]) + toolset.flags('msvc.compile', '.IDL', cpu_conditions, ['{}{} ' .format(setup_script, idl_compiler)]) + toolset.flags('msvc.compile', '.MC' , cpu_conditions, ['{}{} ' .format(setup_script, mc_compiler)]) + toolset.flags('msvc.link' , '.MT' , cpu_conditions, ['{}{} -nologo' .format(setup_script, manifest_tool)]) + + if cc_filter: + toolset.flags('msvc', '.CC.FILTER', cpu_conditions, ['"|" {}'.format(cc_filter)]) + + # Set version-specific flags. + configure_version_specific('msvc', version, conditions) + + +# Returns the default installation path for the given version. +# +def default_path(version): + # Use auto-detected path if possible. + options = __versions.get(version, 'options') + tmp_path = None + if options: + tmp_path = feature.get_values('', options) + + if tmp_path: + tmp_path="".join(tmp_path) + tmp_path=os.path.dirname(tmp_path) + else: + env_var_var_name = '__version_{}_env'.format(version.replace('.','_')) + vc_path = None + if env_var_var_name in globals(): + env_var_name = globals()[env_var_var_name] + if env_var_name in os.environ: + vc_path = environ[env_var_name] + if vc_path: + vc_path = os.path.join(vc_path,globals()['__version_{}_envpath'.format(version.replace('.','_'))]) + tmp_path = os.path.normpath(vc_path) + + var_name = '__version_{}_path'.format(version.replace('.','_')) + if not tmp_path and var_name in globals(): + tmp_path = os.path.normpath(os.path.join(common.get_program_files_dir(), globals()[var_name])) + return tmp_path + + +# Returns either the default installation path (if 'version' is not empty) or +# list of all known default paths (if no version is given) +# +def default_paths(version = None): + possible_paths = [] + if version: + path = default_path(version) + if path: + possible_paths.append(path) + else: + for i in _known_versions: + path = default_path(i) + if path: + possible_paths.append(path) + return possible_paths + + +class MsvcLinkingGenerator(builtin.LinkingGenerator): + # Calls the base version. If necessary, also create a target for the + # manifest file.specifying source's name as the name of the created + # target. As result, the PCH will be named whatever.hpp.gch, and not + # whatever.gch. + def generated_targets(self, sources, prop_set, project, name): + result = builtin.LinkingGenerator.generated_targets(self, sources, prop_set, project, name) + if result: + name_main = result[0].name() + action = result[0].action() + + if prop_set.get('') == 'on': + # We force exact name on PDB. The reason is tagging -- the tag rule may + # reasonably special case some target types, like SHARED_LIB. The tag rule + # will not catch PDB, and it cannot even easily figure if PDB is paired with + # SHARED_LIB or EXE or something else. Because PDB always get the + # same name as the main target, with .pdb as extension, just force it. + target = FileTarget(name_main.split_ext()[0]+'.pdb','PDB',project,action,True) + registered_target = virtual_target.register(target) + if target != registered_target: + action.replace_targets(target,registered_target) + result.append(registered_target) + if prop_set.get('') == 'off': + # Manifest is evil target. It has .manifest appened to the name of + # main target, including extension. E.g. a.exe.manifest. We use 'exact' + # name because to achieve this effect. + target = FileTarget(name_main+'.manifest', 'MANIFEST', project, action, True) + registered_target = virtual_target.register(target) + if target != registered_target: + action.replace_targets(target,registered_target) + result.append(registered_target) + return result + + +# Unsafe worker rule for the register-toolset() rule. Must not be called +# multiple times. + +def register_toolset_really(): + feature.extend('toolset', ['msvc']) + + # Intel and msvc supposedly have link-compatible objects. + feature.subfeature( 'toolset', 'msvc', 'vendor', 'intel', ['propagated', 'optional']) + + # Inherit MIDL flags. + toolset.inherit_flags('msvc', 'midl') + + # Inherit MC flags. + toolset.inherit_flags('msvc','mc') + + # Dynamic runtime comes only in MT flavour. + toolset.add_requirements(['msvc,shared:multi']) + + # Declare msvc toolset specific features. + feature.feature('debug-store', ['object', 'database'], ['propagated']) + feature.feature('pch-source', [], ['dependency', 'free']) + + # Declare generators. + + # TODO: Is it possible to combine these? Make the generators + # non-composing so that they do not convert each source into a separate + # .rsp file. + generators.register(MsvcLinkingGenerator('msvc.link', True, ['OBJ', 'SEARCHED_LIB', 'STATIC_LIB', 'IMPORT_LIB'], ['EXE'], ['msvc'])) + generators.register(MsvcLinkingGenerator('msvc.link.dll', True, ['OBJ', 'SEARCHED_LIB', 'STATIC_LIB', 'IMPORT_LIB'], ['SHARED_LIB','IMPORT_LIB'], ['msvc'])) + + builtin.register_archiver('msvc.archive', ['OBJ'], ['STATIC_LIB'], ['msvc']) + builtin.register_c_compiler('msvc.compile.c++', ['CPP'], ['OBJ'], ['msvc']) + builtin.register_c_compiler('msvc.compile.c', ['C'], ['OBJ'], ['msvc']) + builtin.register_c_compiler('msvc.compile.c++.preprocess', ['CPP'], ['PREPROCESSED_CPP'], ['msvc']) + builtin.register_c_compiler('msvc.compile.c.preprocess', ['C'], ['PREPROCESSED_C'], ['msvc']) + + # Using 'register-c-compiler' adds the build directory to INCLUDES. + builtin.register_c_compiler('msvc.compile.rc', ['RC'], ['OBJ(%_res)'], ['msvc']) + generators.override('msvc.compile.rc', 'rc.compile.resource') + generators.register_standard('msvc.compile.asm', ['ASM'], ['OBJ'], ['msvc']) + + builtin.register_c_compiler('msvc.compile.idl', ['IDL'], ['MSTYPELIB', 'H', 'C(%_i)', 'C(%_proxy)', 'C(%_dlldata)'], ['msvc']) + generators.override('msvc.compile.idl', 'midl.compile.idl') + + generators.register_standard('msvc.compile.mc', ['MC'], ['H','RC'], ['msvc']) + generators.override('msvc.compile.mc', 'mc.compile') + + # Note: the 'H' source type will catch both '.h' and '.hpp' headers as + # the latter have their HPP type derived from H. The type of compilation + # is determined entirely by the destination type. + generators.register(MsvcPchGenerator('msvc.compile.c.pch', False, ['H'], ['C_PCH','OBJ'], ['on', 'msvc'])) + generators.register(MsvcPchGenerator('msvc.compile.c++.pch', False, ['H'], ['CPP_PCH','OBJ'], ['on', 'msvc'])) + + generators.override('msvc.compile.c.pch', 'pch.default-c-pch-generator') + generators.override('msvc.compile.c++.pch', 'pch.default-cpp-pch-generator') + + toolset.flags('msvc.compile', 'PCH_FILE' , ['on'], ['' ]) + toolset.flags('msvc.compile', 'PCH_SOURCE', ['on'], ['']) + toolset.flags('msvc.compile', 'PCH_HEADER', ['on'], ['']) + + # + # Declare flags for compilation. + # + toolset.flags('msvc.compile', 'CFLAGS', ['speed'], ['/O2']) + toolset.flags('msvc.compile', 'CFLAGS', ['space'], ['/O1']) + + toolset.flags('msvc.compile', 'CFLAGS', [ a + '/' + t for a in __cpu_arch_ia64 for t in __cpu_type_itanium ], ['/G1']) + toolset.flags('msvc.compile', 'CFLAGS', [ a + '/' + t for a in __cpu_arch_ia64 for t in __cpu_type_itanium2 ], ['/G2']) + + toolset.flags('msvc.compile', 'CFLAGS', ['on/object'], ['/Z7']) + toolset.flags('msvc.compile', 'CFLAGS', ['on/database'], ['/Zi']) + toolset.flags('msvc.compile', 'CFLAGS', ['off'], ['/Od']) + toolset.flags('msvc.compile', 'CFLAGS', ['off'], ['/Ob0']) + toolset.flags('msvc.compile', 'CFLAGS', ['on'], ['/Ob1']) + toolset.flags('msvc.compile', 'CFLAGS', ['full'], ['/Ob2']) + + toolset.flags('msvc.compile', 'CFLAGS', ['on'], ['/W3']) + toolset.flags('msvc.compile', 'CFLAGS', ['off'], ['/W0']) + toolset.flags('msvc.compile', 'CFLAGS', ['all'], ['/W4']) + toolset.flags('msvc.compile', 'CFLAGS', ['on'], ['/WX']) + + toolset.flags('msvc.compile', 'C++FLAGS', ['on/off/off'], ['/EHs']) + toolset.flags('msvc.compile', 'C++FLAGS', ['on/off/on'], ['/EHsc']) + toolset.flags('msvc.compile', 'C++FLAGS', ['on/on/off'], ['/EHa']) + toolset.flags('msvc.compile', 'C++FLAGS', ['on/on/on'], ['/EHac']) + + # By default 8.0 enables rtti support while prior versions disabled it. We + # simply enable or disable it explicitly so we do not have to depend on this + # default behaviour. + toolset.flags('msvc.compile', 'CFLAGS', ['on'], ['/GR']) + toolset.flags('msvc.compile', 'CFLAGS', ['off'], ['/GR-']) + toolset.flags('msvc.compile', 'CFLAGS', ['off/shared'], ['/MD']) + toolset.flags('msvc.compile', 'CFLAGS', ['on/shared'], ['/MDd']) + + toolset.flags('msvc.compile', 'CFLAGS', ['off/static/multi'], ['/MT']) + toolset.flags('msvc.compile', 'CFLAGS', ['on/static/multi'], ['/MTd']) + + toolset.flags('msvc.compile', 'OPTIONS', [], ['']) + toolset.flags('msvc.compile.c++', 'OPTIONS', [], ['']) + + toolset.flags('msvc.compile', 'PDB_CFLAG', ['on/database'],['/Fd']) + + toolset.flags('msvc.compile', 'DEFINES', [], ['']) + toolset.flags('msvc.compile', 'UNDEFS', [], ['']) + toolset.flags('msvc.compile', 'INCLUDES', [], ['']) + + # Declare flags for the assembler. + toolset.flags('msvc.compile.asm', 'USER_ASMFLAGS', [], ['']) + + toolset.flags('msvc.compile.asm', 'ASMFLAGS', ['on'], ['/Zi', '/Zd']) + + toolset.flags('msvc.compile.asm', 'ASMFLAGS', ['on'], ['/W3']) + toolset.flags('msvc.compile.asm', 'ASMFLAGS', ['off'], ['/W0']) + toolset.flags('msvc.compile.asm', 'ASMFLAGS', ['all'], ['/W4']) + toolset.flags('msvc.compile.asm', 'ASMFLAGS', ['on'], ['/WX']) + + toolset.flags('msvc.compile.asm', 'DEFINES', [], ['']) + + # Declare flags for linking. + toolset.flags('msvc.link', 'PDB_LINKFLAG', ['on/database'], ['/PDB']) # not used yet + toolset.flags('msvc.link', 'LINKFLAGS', ['on'], ['/DEBUG']) + toolset.flags('msvc.link', 'DEF_FILE', [], ['']) + + # The linker disables the default optimizations when using /DEBUG so we + # have to enable them manually for release builds with debug symbols. + toolset.flags('msvc', 'LINKFLAGS', ['on/off'], ['/OPT:REF,ICF']) + + toolset.flags('msvc', 'LINKFLAGS', ['console'], ['/subsystem:console']) + toolset.flags('msvc', 'LINKFLAGS', ['gui'], ['/subsystem:windows']) + toolset.flags('msvc', 'LINKFLAGS', ['wince'], ['/subsystem:windowsce']) + toolset.flags('msvc', 'LINKFLAGS', ['native'], ['/subsystem:native']) + toolset.flags('msvc', 'LINKFLAGS', ['auto'], ['/subsystem:posix']) + + toolset.flags('msvc.link', 'OPTIONS', [], ['']) + toolset.flags('msvc.link', 'LINKPATH', [], ['']) + + toolset.flags('msvc.link', 'FINDLIBS_ST', ['']) + toolset.flags('msvc.link', 'FINDLIBS_SA', ['']) + toolset.flags('msvc.link', 'LIBRARY_OPTION', ['msvc']) + toolset.flags('msvc.link', 'LIBRARIES_MENTIONED_BY_FILE', ['']) + + toolset.flags('msvc.archive', 'AROPTIONS', [], ['']) + + +# Locates the requested setup script under the given folder and returns its full +# path or nothing in case the script can not be found. In case multiple scripts +# are found only the first one is returned. +# +# TODO: There used to exist a code comment for the msvc.init rule stating that +# we do not correctly detect the location of the vcvars32.bat setup script for +# the free VC7.1 tools in case user explicitly provides a path. This should be +# tested or simply remove this whole comment in case this toolset version is no +# longer important. +# +def locate_default_setup(command, parent, setup_name): + for setup in [os.path.join(dir,setup_name) for dir in [command,parent]]: + if os.path.exists(setup): + return setup + return None + + +# Validates given path, registers found configuration and prints debug +# information about it. +# +def register_configuration(version, path=None): + if path: + command = os.path.join(path, 'cl.exe') + if os.path.exists(command): + if debug(): + print "notice: [msvc-cfg] msvc-$(version) detected, command: ''".format(version,command) + __versions.register(version) + __versions.set(version,'options',['{}'.format(command)]) + + +################################################################################ +# +# Startup code executed when loading this module. +# +################################################################################ + +# Similar to Configurations, but remembers the first registered configuration. +class MSVCConfigurations(Configurations): + def __init__(self): + Configurations.__init__(self) + self.first_ = None + + def register(self, id): + Configurations.register(self,id) + if not self.first_: + self.first_ = id + + def first(self): + return self.first_ + + +# List of all registered configurations. +__versions = MSVCConfigurations() + +# Supported CPU architectures. +__cpu_arch_i386 = [ + '/', + '/32', + 'x86/', + 'x86/32'] + +__cpu_arch_amd64 = [ + '/64', + 'x86/64'] + +__cpu_arch_ia64 = [ + 'ia64/', + 'ia64/64'] + + +# Supported CPU types (only Itanium optimization options are supported from +# VC++ 2005 on). See +# http://msdn2.microsoft.com/en-us/library/h66s5s0e(vs.90).aspx for more +# detailed information. +__cpu_type_g5 = ['i586', 'pentium', 'pentium-mmx' ] +__cpu_type_g6 = ['i686', 'pentiumpro', 'pentium2', 'pentium3', 'pentium3m', 'pentium-m', 'k6', + 'k6-2', 'k6-3', 'winchip-c6', 'winchip2', 'c3', 'c3-2' ] +__cpu_type_em64t = ['prescott', 'nocona', 'conroe', 'conroe-xe', 'conroe-l', 'allendale', 'mermon', + 'mermon-xe', 'kentsfield', 'kentsfield-xe', 'penryn', 'wolfdale', + 'yorksfield', 'nehalem' ] +__cpu_type_amd64 = ['k8', 'opteron', 'athlon64', 'athlon-fx'] +__cpu_type_g7 = ['pentium4', 'pentium4m', 'athlon', 'athlon-tbird', 'athlon-4', 'athlon-xp' + 'athlon-mp'] + __cpu_type_em64t + __cpu_type_amd64 +__cpu_type_itanium = ['itanium', 'itanium1', 'merced'] +__cpu_type_itanium2 = ['itanium2', 'mckinley'] + + +# Known toolset versions, in order of preference. +_known_versions = ['10.0', '10.0express', '9.0', '9.0express', '8.0', '8.0express', '7.1', '7.1toolkit', '7.0', '6.0'] + +# Version aliases. +__version_alias_6 = '6.0' +__version_alias_6_5 = '6.0' +__version_alias_7 = '7.0' +__version_alias_8 = '8.0' +__version_alias_9 = '9.0' +__version_alias_10 = '10.0' + +# Names of registry keys containing the Visual C++ installation path (relative +# to "HKEY_LOCAL_MACHINE\SOFTWARE\\Microsoft"). +__version_6_0_reg = "VisualStudio\\6.0\\Setup\\Microsoft Visual C++" +__version_7_0_reg = "VisualStudio\\7.0\\Setup\\VC" +__version_7_1_reg = "VisualStudio\\7.1\\Setup\\VC" +__version_8_0_reg = "VisualStudio\\8.0\\Setup\\VC" +__version_8_0express_reg = "VCExpress\\8.0\\Setup\\VC" +__version_9_0_reg = "VisualStudio\\9.0\\Setup\\VC" +__version_9_0express_reg = "VCExpress\\9.0\\Setup\\VC" +__version_10_0_reg = "VisualStudio\\10.0\\Setup\\VC" +__version_10_0express_reg = "VCExpress\\10.0\\Setup\\VC" + +# Visual C++ Toolkit 2003 does not store its installation path in the registry. +# The environment variable 'VCToolkitInstallDir' and the default installation +# path will be checked instead. +__version_7_1toolkit_path = 'Microsoft Visual C++ Toolkit 2003\\bin' +__version_7_1toolkit_env = 'VCToolkitInstallDir' + +# Path to the folder containing "cl.exe" relative to the value of the +# corresponding environment variable. +__version_7_1toolkit_envpath = 'bin' ; +# +# +# Auto-detect all the available msvc installations on the system. +auto_detect_toolset_versions() + +# And finally trigger the actual Boost Build toolset registration. +register_toolset() Index: tools/types/__init__.py =================================================================== --- tools/types/__init__.py (revision 75643) +++ tools/types/__init__.py (working copy) @@ -5,6 +5,7 @@ 'html', 'lib', 'obj', + 'preprocessed', 'rsp', ] Index: tools/types/cpp.py =================================================================== --- tools/types/cpp.py (revision 75643) +++ tools/types/cpp.py (working copy) @@ -5,6 +5,9 @@ from b2.build import type def register (): - type.register_type ('CPP', ['cpp', 'cxx', 'cc']) + type.register_type('CPP', ['cpp', 'cxx', 'cc']) + type.register_type('H', ['h']) + type.register_type('HPP', ['hpp'], 'H') + type.register_type('C', ['c']) register () Index: tools/types/preprocessed.py =================================================================== --- tools/types/preprocessed.py (revision 0) +++ tools/types/preprocessed.py (working copy) @@ -0,0 +1,11 @@ +# Copyright David Abrahams 2004. Distributed under the Boost +# Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +from b2.build import type + +def register (): + type.register_type('PREPROCESSED_C', ['i'], 'C') + type.register_type('PREPROCESSED_CPP', ['ii'], 'CPP') + +register ()