Boost logo

Boost-Build :

From: David Abrahams (dave_at_[hidden])
Date: 2005-05-17 14:35:14


> I attach a prototype implementation that implements this idea for
> msvc.compile.c++ action, and would appreciate if you try it.

Doesn't work at all. Did you test it? It creates circular
dependencies, among other things. The enclosed works... sort of.

It's really inconvenient and IMO senseless to use response files for
compiles of single source files. The only purpose is to shorten
command lines, and when the response file name is likely to be as long
as the input file name you don't gain anything.

 --=-=-= Content-Disposition: inline; filename=msvc.jam

# Copyright David Abrahams 2003. Permission to copy, use,
# modify, sell and distribute this software is granted provided this
# copyright notice appears in all copies. This software is provided
# "as is" without express or implied warranty, and with no claim as
# to its suitability for any purpose.
import property ;
import generators ;
import os ;
import type ;
import toolset : flags ;
import errors : error ;
import feature : feature get-values ;
import path ;
import sequence : unique ;
import common ;

import rc ;

if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ]
{
.debug-configuration = true ;
}

feature.extend toolset : msvc ;

feature.subfeature toolset msvc : vendor
: intel
: propagated optional
# intel and msvc supposedly have link-compatible objects... remains
# to be seen, though ;-)
;

RM = [ modules.peek common : RM ] ;

# Initialize the toolset for a specific version. 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 : X:/some_dir ;
# using msvc : 7.0 : Y:/some_dir ;
# using msvc : : Z:/some_dir
#
# If you have "msvc-6.5" in build request, the version from X: drive will be used,
# and if you put only "msvc", then drive "Z:" will be used. Note that it's not possible
# the specify that by default, version 7.0 must be used --- you should use 'using'
# without version number for that effect.
#
# version --
# path --
#
# When invoking tools, we'll first run vcvars32.bat from the configured path and
# then cl/link, without path.
#
# Note: for free VC7.1 tools, we don't correctly find vcvars32.bar when user
# explicitly provides a path.
rule init (
version ? # the msvc version which is being configured. When omitted
# the tools invoked when no explicit version is given will be configured.
: command *
# the command to invoke the compiler. If not specified:
# - if version is given, default location for that version will be searched
#
# - if version is not given, default locations for 7.1, 7.0 and 6.* will
# be searched
#
# - if compiler is not found in default locations, PATH will be searched.
: options *
# options can include <setup>, <compiler>, <linker> and <resource-compiler>
)
{
# setup will be used iff a path has been specified. If setup is
# not specified, vcvars32.bat will be used instead.
setup = [ get-values <setup> : $(options) ] ;
setup ?= vcvars32.bat ;
compiler = [ get-values <compiler> : $(options) ] ;
compiler ?= cl ;
linker = [ get-values <linker> : $(options) ] ;
linker ?= link ;
resource-compiler = [ get-values <resource-compiler> : $(options) ] ;
resource-compiler ?= rc ;

local condition = [ common.check-init-parameters msvc :
version $(version) ] ;

# 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) ] : $(version) ] ;

common.handle-options msvc : $(condition) : $(command) : $(options) ;

if $(command)
{
command = [ common.get-absolute-tool-path $(command[-1]) ] ;
}
local root = $(command:D) ;

setup = $(root)\\bin\\$(setup) ;

# CONSIDER: What's the point of 'call'. Can we invoke the script directly?
setup = "call \""$(setup)"\" > nul " ;

if [ os.name ] = NT
{
setup = $(setup)"
" ;
}
else
{
setup = "cmd /S /C "$(setup)" \"&&\" " ;
}

# prefix with setup, or quoted path if any
local prefix = $(setup) ;

flags msvc.compile .CC $(condition) : $(prefix)$(compiler) ;
flags msvc.compile .RC $(condition) : $(prefix)$(resource-compiler) ;
flags msvc.link .LD $(condition) : $(prefix)$(linker) ;
flags msvc.archive .LD $(condition) : $(prefix)$(linker) ;

if ! $(version)
{
# Even if version is not explicitly specified, try to detect the version
# from the path.
if [ MATCH "(Microsoft Visual Studio 8)" : $(command) ]
{
version = 8.0 ;
}
else if [ MATCH "(\\.NET 2003\\VC7)" : $(command) ] ||
[ MATCH "(Microsoft Visual C\\+\\+ Toolkit 2003)" : $(command) ]
{
version = 7.1 ;
}
else if [ MATCH "(.NET\\VC7)" : $(command) ]
{
version = 7.0 ;
}
else
{
version = 6.0 ;
}
}

# The following options work only for 7* versions. It means
# that if the user has not specified a version he gets 6.0 compatible
# behavious (i.e. a bit buggy)
if [ MATCH ^(7\..*) : $(version) ]
{
flags msvc.compile CFLAGS $(condition) : /Zc:forScope /Zc:wchar_t ;
}

# 8.0 deprecates some of the options
if [ MATCH ^(8) : $(version) ]
{
flags msvc.compile CFLAGS $(condition)/<optimization>speed : /O2 ;
flags msvc.compile CFLAGS $(condition)/<optimization>space : /O1 ;
}
else
{
flags msvc.compile CFLAGS $(condition)/<optimization>speed : /Ogity /O2 /Gs ;
flags msvc.compile CFLAGS $(condition)/<optimization>space : /Ogisy /O1 /Gs ;
}
}

rule default-paths ( version ? )
{
local possible-paths ;

local ProgramFiles = [ modules.peek : ProgramFiles ] ;
if $(ProgramFiles)
{
ProgramFiles = "$(ProgramFiles:J= )" ;
}
else
{
ProgramFiles = "c:\\Program Files" ;
}

local version-6-path = $(ProgramFiles)"\\Microsoft Visual Studio\\VC98" ;
local version-7-path = $(ProgramFiles)"\\Microsoft Visual Studio .NET\\VC7" ;
local version-7.0-path = $(version-7-path) ;
local version-7.1-path = $(ProgramFiles)"\\Microsoft Visual Studio .NET 2003\\VC7" ;
local version-8.0-path = $(ProgramFiles)"\\Microsoft Visual Studio 8" ;

local VS71COMNTOOLS = [ modules.peek : VS71COMNTOOLS ] ;
if $(VS71COMNTOOLS)
{
# VS71COMNTOOLS is set by VS .NET 2003 to <VSDIR>\Common7\Tools
version-7.1-path = [ path.make "$(VS71COMNTOOLS:J= )" ] ;
version-7.1-path = [ path.parent $(version-7.1-path) ] ;
version-7.1-path = [ path.parent $(version-7.1-path) ] ;
version-7.1-path = [ path.join $(version-7.1-path) "VC7" ] ;
version-7.1-path = [ path.native $(version-7.1-path) ] ;
}

local VCToolkitInstallDir = [ modules.peek : VCToolkitInstallDir ] ;
if $(VCToolkitInstallDir)
{
version-7.1-path = [ path.make "$(VCToolkitInstallDir:J= )" ] ;
}

if $(version)
{
local v = [ MATCH ^(6|[^6].*) : $(version) ] ;
possible-paths += $(version-$(v)-path) ;
}
else
{
possible-paths += $(version-7.1-path) $(version-7.0-path) $(version-6-path) ;
}
# The vccars32.bat is actually in "bin" directory.
# (except for free VC7.1 tools)
possible-paths = $(possible-paths)\\bin $(possible-paths) ;

return $(possible-paths) ;
}

# Declare generators

# is it possible to combine these?
# make the generators non-composing, so that they don't convert each source
# into separate rsp file.
generators.register-linker msvc.link : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB : EXE RSP : <toolset>msvc ;
generators.register-linker msvc.link.dll : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB : SHARED_LIB IMPORT_LIB RSP : <toolset>msvc ;

generators.register-archiver msvc.archive : OBJ : STATIC_LIB RSP : <toolset>msvc ;
generators.register-c-compiler msvc.compile.c++ : CPP : OBJ : <toolset>msvc ;
generators.register-c-compiler msvc.compile.c : C : OBJ RSP(%_cpp) : <toolset>msvc ;
generators.register-standard msvc.compile.rc : RC : OBJ(%_res) : <toolset>msvc ;
generators.override msvc.compile.rc : rc.resource-compile ;

#
# Declare flags and action for compilation
#
feature.feature debug-store : object database : propagated ;
flags msvc.compile CFLAGS <debug-symbols>on/<debug-store>object : /Z7 ;
flags msvc.compile CFLAGS <debug-symbols>on/<debug-store>database : /Zi ;
flags msvc.compile CFLAGS <optimization>off : /Od ;
flags msvc.compile CFLAGS <inlining>off : /Ob0 ;
flags msvc.compile CFLAGS <inlining>on : /Ob1 ;
flags msvc.compile CFLAGS <inlining>full : /Ob2 ;
flags msvc.compile CFLAGS <exception-handling>on : /EHsc ;
flags msvc.compile CFLAGS <rtti>on : /GR ;
flags msvc.compile CFLAGS <runtime-debugging>off/<link-runtime>shared : /MD ;
flags msvc.compile CFLAGS <runtime-debugging>on/<link-runtime>shared : /MDd ;

flags msvc.compile CFLAGS <runtime-debugging>off/<link-runtime>static/<threading>single : /ML ;
flags msvc.compile CFLAGS <runtime-debugging>on/<link-runtime>static/<threading>single : /MLd ;
flags msvc.compile CFLAGS <runtime-debugging>off/<link-runtime>static/<threading>multi : /MT ;
flags msvc.compile CFLAGS <runtime-debugging>on/<link-runtime>static/<threading>multi : /MTd ;
flags msvc.compile CFLAGS <base-target-type>CPP : /EHsc ;

flags msvc.compile USER_CFLAGS <cflags> : ;
flags msvc.compile.c++ USER_CFLAGS <cxxflags> : ;

flags msvc.compile PDB_CFLAG <debug-symbols>on/<debug-store>database : /Fd ; # not used yet

flags msvc.compile DEFINES <define> ;
flags msvc.compile UNDEFS <undef> ;
flags msvc.compile INCLUDES <include> ;

flags msvc WHATEVER <toolset-msvc:version> ;

# The actions differ only by explicit selection of input language
actions compile.c
{
$(.CC) /Zm800 -nologo -TC -U$(UNDEFS) $(CFLAGS) $(USER_CFLAGS) @"$(<[2]:W)" -c -Fo"$(<[1]:W)"
}
actions compile.c++ bind RSP
{
$(.CC) /Zm800 -nologo -TP -U$(UNDEFS) $(CFLAGS) $(USER_CFLAGS) @"$(RSP:W)" -c -Fo"$(<[1]:W)" && del "$(RSP)"
}

actions compile.rc
{
$(.RC) -l 0x409 -U$(UNDEFS) -D$(DEFINES) -I"$(INCLUDES)" -fo "$(<:W)" "$(>:W)"
}

# Declare flags and action for linking
flags msvc.link PDB_LINKFLAG <debug-symbols>on/<debug-store>database : /PDB: ; # not used yet
flags msvc.link LINKFLAGS <debug-symbols>on : /DEBUG ;
flags msvc.link DEF_FILE <def-file> ;
# The linker disables the default optimizations when using /DEBUG. Whe have
# to enable them manually for release builds with debug symbols.
flags msvc LINKFLAGS <debug-symbols>on/<runtime-debugging>off : /OPT:REF,ICF ;

flags msvc LINKFLAGS <user-interface>console : /subsystem:console ;
flags msvc LINKFLAGS <user-interface>gui : /subsystem:windows ;
flags msvc LINKFLAGS <user-interface>wince : /subsystem:windowsce ;
flags msvc LINKFLAGS <user-interface>native : /subsystem:native ;
flags msvc LINKFLAGS <user-interface>auto : /subsystem:posix ;

flags msvc LINKFLAGS <main-target-type>LIB/<link>shared : /DLL ;

toolset.flags msvc.link USER_LINKFLAGS <linkflags> ;
toolset.flags msvc.link LINKPATH <library-path> ;

flags msvc.link FINDLIBS_ST <find-static-library> ;
flags msvc.link FINDLIBS_SA <find-shared-library> ;
flags msvc.link LIBRARY_OPTION <toolset>msvc : "" : unchecked ;

rule archive ( targets + : sources * : properties * )
{
common.response-file $(targets) : $(sources) : $(targets[2]) : $(properties) ;
}

rule link ( targets + : sources * : properties * )
{
common.response-file $(targets) : $(sources) : $(targets[2])
: $(properties) ;
}

rule link.dll ( targets + : sources * : properties * )
{
common.response-file $(targets) : $(sources) : $(targets[3]) : $(properties) ;
DEPENDS $(<) : [ on $(<) return $(DEF_FILE) ] ;
}

# 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 [ os.name ] in NT
{
# The 'DEL' command would issue a message to stdout
# if the file does not exist, so need a check.
actions archive
{
if exist "$(<[1])" DEL "$(<[1])"
$(.LD) /lib /NOLOGO /out:"$(<[1])" @"$(<[2])"
}
}
else
{
actions archive
{
$(RM) "$(<[1])"
$(.LD) /lib /NOLOGO /out:"$(<[1])" @"$(<[2])"
}
}

# incremental linking a DLL causes no end of problems: if the
# actual exports don't change, the import .lib file is never
# updated. Therefore, the .lib is always out-of-date and gets
# rebuilt every time. I'm not sure that incremental linking is
# such a great idea in general, but in this case I'm sure we
# don't want it.
actions link bind DEF_FILE
{
$(.LD) /NOLOGO $(LINKFLAGS) /out:"$(<[1]:W)" /INCREMENTAL:NO /LIBPATH:"$(LINKPATH:W)" $(USER_LINKFLAGS) @"$(<[2]:W)"
}

actions link.dll bind DEF_FILE
{
$(.LD) /NOLOGO $(LINKFLAGS) /out:"$(<[1]:W)" /INCREMENTAL:NO /IMPLIB:"$(<[2]:W)" /LIBPATH:"$(LINKPATH:W)" /def:$(DEF_FILE) $(USER_LINKFLAGS) @"$(<[3]:W)"
}

rule compile.c++ ( targets + : sources * : properties * )
{
local response-file = $(targets[1]).rsp ;
ECHO targets= $(targets) ;
ECHO sources= $(sources) ;
ECHO response-file= $(response-file) ;

RSP on $(targets) = $(response-file) ;
# DEPENDS $(targets) : $(response-file) ;
LOCATE on $(response-file) = [ on $(targets[1]) return $(LOCATE) ] ;
common.response-file $(targets) : $(sources) : $(response-file) : $(properties) ;
}

rule compile.c ( targets + : sources * : properties * )
{
common.response-file $(targets) : $(sources) : $(targets[2]) : $(properties) ;
}

 --=-=-=

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com
 --=-=-=-- 

Boost-Build list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk