mirror of
https://github.com/boostorg/build.git
synced 2026-02-14 00:32:11 +00:00
595 lines
16 KiB
Plaintext
595 lines
16 KiB
Plaintext
# Copyright (C) Vladimir Prus 2002. 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.
|
|
|
|
# Provides actions common to all toolsets, for as making directoies and
|
|
# removing files.
|
|
|
|
import os ;
|
|
import modules ;
|
|
import utility ;
|
|
import print ;
|
|
import type ;
|
|
import feature ;
|
|
import errors ;
|
|
import path ;
|
|
import sequence ;
|
|
import toolset ;
|
|
|
|
if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ]
|
|
{
|
|
.debug-configuration = true ;
|
|
}
|
|
|
|
# Configurations
|
|
#
|
|
# The following class helps to manage toolset configurations. Each configuration
|
|
# has unique ID and one or more parameters. A typical example of unique ID is
|
|
# a condition generated by 'common.check-init-parameters' rule. Other kinds of
|
|
# ID can be used. Parameters may include any details about the configuration like
|
|
# 'command', 'path', etc.
|
|
#
|
|
# A configuration may be in one of two states:
|
|
#
|
|
# - registered - a toolset configuration is registered (by autodetection code
|
|
# for instance) but is not used. I.e. 'toolset.using' wasn't yet been called
|
|
# for this configuration.
|
|
# - used - once called 'toolset.using' marks the configuration as 'used'.
|
|
#
|
|
# The main difference between the states is that while a configuration is
|
|
# 'registered' its options can be freely changed. This is useful in particular
|
|
# for autodetection code - all detected configurations may be safely overwritten
|
|
# by a user.
|
|
|
|
class configurations
|
|
{
|
|
import errors : error ;
|
|
|
|
rule __init__ ( )
|
|
{
|
|
}
|
|
|
|
# Registers a configuration.
|
|
#
|
|
# Returns 'true' if the configuration has been added and an empty value if
|
|
# it already exists. Reports an error if the configuration is 'used'.
|
|
rule register ( id )
|
|
{
|
|
if $(id) in $(self.used)
|
|
{
|
|
error "common: the configuration '$(id)' is in use" ;
|
|
}
|
|
|
|
local retval ;
|
|
|
|
if ! $(id) in $(self.all)
|
|
{
|
|
self.all += $(id) ;
|
|
|
|
# indicate that a new configuration has been added
|
|
retval = true ;
|
|
}
|
|
|
|
return $(retval) ;
|
|
}
|
|
|
|
# Mark a configuration as 'used'.
|
|
#
|
|
# Returns 'true' if the state of the configuration has been changed to
|
|
# 'used' and an empty value if it the state wasn't changed. Reports an error
|
|
# if the configuration isn't known.
|
|
rule use ( id )
|
|
{
|
|
if ! $(id) in $(self.all)
|
|
{
|
|
error "common: the configuration '$(id)' is not known" ;
|
|
}
|
|
|
|
local retval ;
|
|
|
|
if ! $(id) in $(self.used)
|
|
{
|
|
self.used += $(id) ;
|
|
|
|
# indicate that the configuration has been marked as 'used'
|
|
retval = true ;
|
|
}
|
|
|
|
return $(retval) ;
|
|
}
|
|
|
|
# Return all registered configurations.
|
|
rule all ( )
|
|
{
|
|
return $(self.all) ;
|
|
}
|
|
|
|
# Return all used configurations.
|
|
rule used ( )
|
|
{
|
|
return $(self.used) ;
|
|
}
|
|
|
|
# Returns the value of a configuration parameter.
|
|
rule get ( id : param )
|
|
{
|
|
return $(self.$(param).$(id)) ;
|
|
}
|
|
|
|
# Sets the value of a configuration parameter.
|
|
rule set ( id : param : value * )
|
|
{
|
|
self.$(param).$(id) = $(value) ;
|
|
}
|
|
}
|
|
|
|
|
|
# The rule checks toolset parameters. Each trailing parameter
|
|
# should be a pair of parameter name and parameter value.
|
|
# The rule will check that each parameter either has value in
|
|
# each invocation, or has no value in each invocation. Also,
|
|
# the rule will check that the combination of all parameter values is
|
|
# unique in all invocations.
|
|
#
|
|
# Each parameter name corresponds to subfeature. This rule will declare subfeature
|
|
# the first time non-empty parameter value is passed, and will extend it with
|
|
# all the values.
|
|
#
|
|
# The return value from this rule is a condition to be used for flags settings.
|
|
rule check-init-parameters ( toolset : * )
|
|
{
|
|
local sig = $(toolset) ;
|
|
local condition = <toolset>$(toolset) ;
|
|
for local index in 2 3 4 5 6 7 8 9
|
|
{
|
|
local name = $($(index)[1]) ;
|
|
local value = $($(index)[2]) ;
|
|
|
|
if $(value)-is-specified
|
|
{
|
|
condition = $(condition)-$(value) ;
|
|
if $(.had-unspecified-value.$(toolset).$(name))
|
|
{
|
|
errors.user-error
|
|
"$(toolset) initialization: parameter '$(name)' inconsistent" :
|
|
"no value was specified in earlier initialization" :
|
|
"an explicit value is specified now" ;
|
|
}
|
|
# The below logic is for intel compiler. It calls this rule
|
|
# with 'intel-linux' and 'intel-win' as toolset, so we need to
|
|
# get the base part of toolset name.
|
|
# We can't pass 'intel' as toolset, because it that case it will
|
|
# be impossible to register versionles intel-linux and
|
|
# intel-win of specific version.
|
|
local t = $(toolset) ;
|
|
local m = [ MATCH ([^-]*)- : $(toolset) ] ;
|
|
if $(m)
|
|
{
|
|
t = $(m[1]) ;
|
|
}
|
|
if ! $(.had-value.$(toolset).$(name))
|
|
{
|
|
if ! $(.declared-subfeature.$(t).$(name))
|
|
{
|
|
feature.subfeature toolset $(t) : $(name) : : propagated ;
|
|
.declared-subfeature.$(t).$(name) = true ;
|
|
}
|
|
.had-value.$(toolset).$(name) = true ;
|
|
}
|
|
feature.extend-subfeature toolset $(t) : $(name) : $(value) ;
|
|
}
|
|
else
|
|
{
|
|
if $(.had-value.$(toolset).$(name))
|
|
{
|
|
errors.user-error
|
|
"$(toolset) initialization: parameter '$(name)' inconsistent" :
|
|
"an explicit value was specified in an earlier initialization" :
|
|
"no value is specified now" ;
|
|
}
|
|
.had-unspecified-value.$(toolset).$(name) = true ;
|
|
}
|
|
sig = $(sig)$(value:E="")- ;
|
|
}
|
|
if $(sig) in $(.all-signatures)
|
|
{
|
|
local message =
|
|
"duplicate initialization of $(toolset) with the following parameters: " ;
|
|
for local index in 2 3 4 5 6 7 8 9
|
|
{
|
|
local p = $($(index)) ;
|
|
if $(p)
|
|
{
|
|
message += "$(p[1]) = $(p[2]:E=<unspecified>)" ;
|
|
}
|
|
}
|
|
message += "previous initialization at $(.init-loc.$(sig))" ;
|
|
errors.user-error $(message[1]) : $(message[2]) : $(message[3]) : $(message[4])
|
|
: $(message[5]) : $(message[6]) : $(message[7]) : $(message[8]) ;
|
|
}
|
|
.all-signatures += $(sig) ;
|
|
.init-loc.$(sig) = [ errors.nearest-user-location ] ;
|
|
|
|
return $(condition) ;
|
|
}
|
|
|
|
# A helper rule to get the command to invoke some tool.
|
|
# In 'user-provided-command' is not given, tries to find binary
|
|
# named 'tool' in PATH and in the passed 'additional-path'. Otherwise,
|
|
# verified that the first element of 'user-provided-command' is an
|
|
# existing program.
|
|
#
|
|
#
|
|
# This rule returns the command to be used when invoking the tool. If we can't
|
|
# find the tool, a warning is issued.
|
|
# If 'path-last' is specified, PATH is checked after 'additional-paths' when
|
|
# searching to 'tool'.
|
|
rule get-invocation-command (
|
|
toolset : tool : user-provided-command * : additional-paths * : path-last ? )
|
|
{
|
|
local command ;
|
|
if ! $(user-provided-command)
|
|
{
|
|
command = [ common.find-tool $(tool) : $(additional-paths) : $(path-last) ] ;
|
|
if ! $(command)
|
|
{
|
|
if $(.debug-configuration)
|
|
{
|
|
ECHO "warning: toolset $(toolset) initialization: can't find tool $(tool)" ;
|
|
ECHO "warning: initialized from" [ errors.nearest-user-location ] ;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
command = [ common.check-tool $(user-provided-command) ] ;
|
|
if ! $(command)
|
|
{
|
|
if $(.debug-configuration)
|
|
{
|
|
ECHO "warning: toolset $(toolset) initialization: " ;
|
|
ECHO "warning: can't find user-provided command " '$(user-provided-command)' ;
|
|
ECHO "warning: initialized from" [ errors.nearest-user-location ] ;
|
|
}
|
|
# It's possible, in theory, that user-provided command is OK, but we're
|
|
# not smart enough to understand that.
|
|
command = $(user-provided-command) ;
|
|
}
|
|
}
|
|
if ! $(command)
|
|
{
|
|
command = $(user-provided-command) ;
|
|
}
|
|
return $(command) ;
|
|
}
|
|
|
|
# Given an invocation command,
|
|
# return the absolute path to the command. This works even if commnad
|
|
# has not path element and is present in PATH.
|
|
rule get-absolute-tool-path ( command )
|
|
{
|
|
if $(command:D)
|
|
{
|
|
return $(command:D) ;
|
|
}
|
|
else
|
|
{
|
|
local m = [ GLOB [ modules.peek : PATH Path path ] : $(command) $(command).exe ] ;
|
|
return $(m[1]:D) ;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
# Attempts to find tool (binary) named 'name' in PATH and in 'additional-paths'.
|
|
# If found in PATH, returns 'name'.
|
|
# If found in additional paths, returns absolute name. If the tool is found
|
|
# in several directories, return all paths.
|
|
# Otherwise, returns empty string.
|
|
# If 'path-last' is specified, PATH is searched after 'additional-paths'.
|
|
rule find-tool ( name : additional-paths * : path-last ? )
|
|
{
|
|
local path = [ path.programs-path ] ;
|
|
local match = [ path.glob $(path) : $(name) $(name).exe ] ;
|
|
local additional-match = [ path.glob $(additional-paths) : $(name) $(name).exe ] ;
|
|
|
|
local result ;
|
|
if $(path-last)
|
|
{
|
|
result = $(additional-match) ;
|
|
if ! $(result) && $(match)
|
|
{
|
|
result = $(name) ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if $(match)
|
|
{
|
|
result = $(name) ;
|
|
}
|
|
else
|
|
{
|
|
result = $(additional-match) ;
|
|
}
|
|
}
|
|
if $(result)
|
|
{
|
|
return [ path.native $(result[1]) ] ;
|
|
}
|
|
}
|
|
|
|
# Checks if 'command' can be found either in path
|
|
# or is a full name to an existing file.
|
|
rule check-tool-aux ( command )
|
|
{
|
|
if $(command:D)
|
|
{
|
|
if [ path.exists $(command) ]
|
|
{
|
|
return $(command) ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if [ GLOB [ modules.peek : PATH Path path ] : $(command) ]
|
|
{
|
|
return $(command) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Checks that a tool can be invoked by 'command'.
|
|
# If command is not an absolute path, checks if it can be found in 'path'.
|
|
# If comand is absolute path, check that it exists. Returns 'command'
|
|
# if ok and empty string otherwise.
|
|
rule check-tool ( xcommand + )
|
|
{
|
|
if [ check-tool-aux $(xcommand[1]) ]
|
|
|| [ check-tool-aux $(xcommand[-1]) ]
|
|
{
|
|
return $(xcommand) ;
|
|
}
|
|
}
|
|
|
|
# Handle common options for toolset, specifically sets the following
|
|
# flag variables:
|
|
# - CONFIG_COMMAND to 'command'
|
|
# - OPTIONS for compile.c to the value of <cflags> in options
|
|
# - OPTIONS for compile.c++ to the value of <cxxflags> in options
|
|
# - OPTIOns for compile to the value of <compileflags> in options
|
|
# - OPTIONs for link to the value of <linkflags> in options
|
|
rule handle-options ( toolset : condition * : command * : options * )
|
|
{
|
|
# The last parameter ('true') says it's OK to set flags for another
|
|
# module,
|
|
toolset.flags $(toolset) CONFIG_COMMAND $(condition) : $(command) : unchecked ;
|
|
toolset.flags $(toolset).compile OPTIONS $(condition) :
|
|
[ feature.get-values <compileflags> : $(options) ] : unchecked ;
|
|
toolset.flags $(toolset).compile.c OPTIONS $(condition) :
|
|
[ feature.get-values <cflags> : $(options) ] : unchecked ;
|
|
toolset.flags $(toolset).compile.c++ OPTIONS $(condition) :
|
|
[ feature.get-values <cxxflags> : $(options) ] : unchecked ;
|
|
toolset.flags $(toolset).compile.fortran OPTIONS $(condition) :
|
|
[ feature.get-values <fflags> : $(options) ] : unchecked ;
|
|
toolset.flags $(toolset).link OPTIONS $(condition) :
|
|
[ feature.get-values <linkflags> : $(options) ] : unchecked ;
|
|
}
|
|
|
|
|
|
# returns the location of the "program files" directory on a windows
|
|
# platform
|
|
rule get-program-files-dir ( )
|
|
{
|
|
local ProgramFiles = [ modules.peek : ProgramFiles ] ;
|
|
if $(ProgramFiles)
|
|
{
|
|
ProgramFiles = "$(ProgramFiles:J= )" ;
|
|
}
|
|
else
|
|
{
|
|
ProgramFiles = "c:\\Program Files" ;
|
|
}
|
|
return $(ProgramFiles) ;
|
|
}
|
|
|
|
if [ os.name ] = NT
|
|
{
|
|
RM = del /f /q ;
|
|
CP = copy ;
|
|
IGNORE = "2>nul >nul & setlocal" ;
|
|
}
|
|
else
|
|
{
|
|
RM = rm -f ;
|
|
CP = cp ;
|
|
}
|
|
|
|
nl = "
|
|
" ;
|
|
|
|
rule rm-command ( )
|
|
{
|
|
return $(RM) ;
|
|
}
|
|
|
|
rule copy-command ( )
|
|
{
|
|
return $(CP) ;
|
|
}
|
|
|
|
|
|
# Returns the command needed to set the environment variable on the
|
|
# current platform. The variable setting persists through all
|
|
# following commands and is visible in the environment seen by
|
|
# subsequently executed commands. In other words, on Unix systems,
|
|
# the variable is exported, which is consistent with the only possible
|
|
# behavior on Windows systems.
|
|
rule variable-setting-command ( variable : value )
|
|
{
|
|
if [ os.name ] = NT
|
|
{
|
|
return "set $(variable)=$(value)$(nl)" ;
|
|
}
|
|
else
|
|
{
|
|
return "$(variable)=$(value)$(nl)export $(variable)$(nl)" ;
|
|
}
|
|
}
|
|
|
|
# Returns a command that sets the named shell path variable to the
|
|
# given NATIVE paths to on the current platform.
|
|
rule path-variable-setting-command ( variable : paths * )
|
|
{
|
|
local sep = [ os.path-separator ] ;
|
|
return [ variable-setting-command $(variable) : $(paths:J=$(sep)) ] ;
|
|
}
|
|
|
|
# Returns a command that prepends the given paths to the named path
|
|
# variable on the current platform.
|
|
rule prepend-path-variable-command ( variable : paths * )
|
|
{
|
|
return [
|
|
path-variable-setting-command $(variable)
|
|
: $(paths) [ os.expand-variable $(variable) ]
|
|
] ;
|
|
}
|
|
|
|
|
|
# Return a command which can create a file. If 'r' is result of invocation,
|
|
# then
|
|
# r foobar
|
|
# will create foobar with unspecified content. What happens if file already
|
|
# exists is unspecified.
|
|
rule file-creation-command ( )
|
|
{
|
|
if [ modules.peek : NT ]
|
|
{
|
|
return "echo. > " ;
|
|
}
|
|
else
|
|
{
|
|
return "touch " ;
|
|
}
|
|
}
|
|
|
|
|
|
# Returns a command that may be used for 'touching' files.
|
|
# It is not a real 'touch' command on NT because it adds an empty line at
|
|
# the end of file but it works with source files
|
|
rule file-touch-command ( )
|
|
{
|
|
if [ os.name ] in NT
|
|
{
|
|
return "echo. >> " ;
|
|
}
|
|
else
|
|
{
|
|
return "touch " ;
|
|
}
|
|
}
|
|
|
|
|
|
rule MkDir
|
|
{
|
|
# If dir exists, don't update it
|
|
# Do this even for $(DOT).
|
|
|
|
NOUPDATE $(<) ;
|
|
|
|
if $(<) != $(DOT) && ! $($(<)-mkdir)
|
|
{
|
|
local s ;
|
|
|
|
# Cheesy gate to prevent multiple invocations on same dir
|
|
# MkDir1 has the actions
|
|
# Arrange for jam dirs
|
|
|
|
$(<)-mkdir = true ;
|
|
MkDir1 $(<) ;
|
|
Depends dirs : $(<) ;
|
|
|
|
# Recursively make parent directories.
|
|
# $(<:P) = $(<)'s parent, & we recurse until root
|
|
|
|
s = $(<:P) ;
|
|
|
|
if $(NT)
|
|
{
|
|
switch $(s)
|
|
{
|
|
case *: : s = ;
|
|
case *:\\ : s = ;
|
|
}
|
|
}
|
|
|
|
if $(s) && $(s) != $(<)
|
|
{
|
|
Depends $(<) : $(s) ;
|
|
MkDir $(s) ;
|
|
}
|
|
else if $(s)
|
|
{
|
|
NOTFILE $(s) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
actions MkDir1
|
|
{
|
|
mkdir "$(<)"
|
|
}
|
|
|
|
actions piecemeal together existing Clean
|
|
{
|
|
$(RM) "$(>)"
|
|
}
|
|
|
|
rule copy
|
|
{
|
|
}
|
|
|
|
|
|
actions copy
|
|
{
|
|
$(CP) "$(>)" "$(<)"
|
|
}
|
|
|
|
rule RmTemps
|
|
{
|
|
}
|
|
actions quietly updated piecemeal together RmTemps
|
|
{
|
|
$(RM) "$(>)" $(IGNORE)
|
|
}
|
|
|
|
rule __test__ ( ) {
|
|
|
|
import assert ;
|
|
|
|
local save-os = [ modules.peek os : name ] ;
|
|
|
|
modules.poke os : .name : LINUX ;
|
|
|
|
local nl = "
|
|
" ;
|
|
|
|
assert.result "PATH=foo:bar:baz$(nl)export PATH$(nl)"
|
|
: path-variable-setting-command PATH : foo bar baz ;
|
|
|
|
assert.result "PATH=foo:bar:$PATH$(nl)export PATH$(nl)"
|
|
: prepend-path-variable-command PATH : foo bar ;
|
|
|
|
modules.poke os : .name : NT ;
|
|
|
|
assert.result "set PATH=foo;bar;baz$(nl)"
|
|
: path-variable-setting-command PATH : foo bar baz ;
|
|
|
|
assert.result "set PATH=foo;bar;%PATH%$(nl)"
|
|
: prepend-path-variable-command PATH : foo bar ;
|
|
|
|
modules.poke os : .name : $(save-os) ;
|
|
}
|