# Copyright 2021 Nikita Kniazev
# Copyright 2001 David Abrahams
# Copyright 2002-2017 Rene Rivera
# Copyright 2002-2003 Vladimir Prus
# Copyright 2005 Reece H. Dunn
# Copyright 2006 Ilya Sokolov
# Copyright 2007 Roland Schwarz
# Copyright 2007 Boris Gubenko
#
# Distributed under the Boost Software License, Version 1.0.
#    (See accompanying file LICENSE.txt or copy at
#          https://www.bfgroup.xyz/b2/LICENSE.txt)

#| tag::doc[]

[[b2.reference.tools.compiler.gcc]]
= GCC - GNU Compiler Collection

The `gcc` module supports the http://gcc.gnu.org[GNU C++ compiler] on
Linux, a number of Unix-like system including SunOS and on Windows
(either http://www.cygwin.com[Cygwin] or http://www.mingw.org[MinGW]).

The `gcc` module is initialized using the following syntax:

----
using gcc : [version] : [c++-compile-command] : [compiler options] ;
----

This statement may be repeated several times, if you want to configure
several versions of the compiler.

If the version is not explicitly specified, it will be automatically
detected by running the compiler with the `-v` option. If the command is
not specified, the `g++` binary will be searched in PATH.

The following options can be provided, using
_`<option-name>option-value syntax`_:

`asmflags`::
Specifies additional compiler flags that will be used when compiling assembler
sources.

`cflags`::
Specifies additional compiler flags that will be used when compiling C
sources.

`cxxflags`::
Specifies additional compiler flags that will be used when compiling C++
sources.

`fflags`::
Specifies additional compiler flags that will be used when compiling Fortran
sources.

`mflags`::
Specifies additional compiler flags that will be used when compiling
Objective-C sources.

`mmflags`::
Specifies additional compiler flags that will be used when compiling
Objective-C++ sources.

`compileflags`::
Specifies additional compiler flags that will be used when compiling any
language sources.

`linkflags`::
Specifies additional command line options that will be passed to the linker.

`root`::
Specifies root directory of the compiler installation. This option is
necessary only if it is not possible to detect this information from the
compiler command--for example if the specified compiler command is a user
script.

`archiver`::
Specifies the archiver command that is used to produce static
libraries. Normally, it is autodetected using gcc `-print-prog-name`
option or defaulted to `ar`, but in some cases you might want to
override it, for example to explicitly use a system version instead of
one included with gcc.

`rc`::
Specifies the resource compiler command that will be used with the
version of gcc that is being configured. This setting makes sense only
for Windows and only if you plan to use resource files. By default
`windres` will be used.

`rc-type`::
Specifies the type of resource compiler. The value can be either
`windres` for msvc resource compiler, or `rc` for borland's resource
compiler.

In order to compile 64-bit applications, you have to specify
`address-model=64`, and the `instruction-set` feature should refer to a 64
bit processor. Currently, those include `nocona`, `opteron`, `athlon64` and
`athlon-fx`.

|# # end::doc[]

import "class" : new ;
import common ;
import cygwin ;
import feature ;
import fortran ;
import generators ;
import os ;
import pch ;
import property ;
import property-set ;
import rc ;
import regex ;
import sequence ;
import set ;
import toolset ;
import type ;
import unix ;
import virtual-target ;
import errors ;
import args ;


# Used in actions for multi argument options
_ = " " ;

.debug-configuration = [ args.get-arg debug-configuration ] ;


feature.extend toolset : gcc ;

toolset.inherit-generators gcc : unix : unix.link unix.link.dll ;
toolset.inherit-flags gcc : unix ;
toolset.inherit-rules gcc : unix ;

# Make gcc toolset object files use the "o" suffix on all platforms.
type.set-generated-target-suffix OBJ : <toolset>gcc : o ;
type.set-generated-target-suffix OBJ : <toolset>gcc <target-os>windows : o ;
type.set-generated-target-suffix OBJ : <toolset>gcc <target-os>cygwin : o ;


# Initializes the gcc toolset for the given version. If necessary, command may
# be used to specify where the compiler is located. The parameter 'options' is a
# space-delimited list of options, each one specified as
# <option-name>option-value. Valid option names are: cxxflags, linkflags and
# linker-type. Accepted linker-type values are aix, darwin, gnu, hpux, osf or
# sun and the default value will be selected based on the current OS.
# Example:
#   using gcc : 3.4 : : <cxxflags>foo <linkflags>bar <linker-type>sun ;
#
# The compiler command to use is detected in three steps:
# 1) If an explicit command is specified by the user, it will be used and must
#    be available.
# 2) If only a certain version is specified, it is enforced:
#    - either the 'g++-VERSION' command must be available
#    - or the default command 'g++' must be available and match the exact
#      version.
# 3) Without user-provided restrictions use default 'g++'.
#
rule init ( version ? : command * : options * : requirement * )
{
    # Information about the gcc command...
    #   The command.
    command = [ common.find-compiler gcc : g++ : $(version) : $(command) ] ;
    #   The 'command' variable can have multiple elements but when calling the
    # SHELL builtin we need a single string, and we need to quote elements
    # with spaces.
    local command-string = [ common.make-command-string $(command) ] ;
    #   The root directory of the tool install.
    local root = [ feature.get-values <root> : $(options) ] ;
    #   The bin directory where to find the command to execute.
    local bin ;
    #   The compiler flavor.
    local flavor = [ feature.get-values <flavor> : $(options) ] ;
    #   vxworks build on windows uses csh that is neither mingw or cygwin
    if [ feature.get-values <target-os> : $(options) ] = vxworks
    {
        flavor ?= vxworks ;
    }
    #   Autodetect the root and bin dir if not given.
    if $(command)
    {
        bin ?= [ common.get-absolute-tool-path $(command[-1]) ] ;
        root ?= $(bin:D) ;
    }
    local machine ;
    local target-os ;
    #   Autodetect the version and flavor if not given.
    if $(command)
    {
        machine = [ MATCH "^([^ ]+)" :
            [ SHELL "$(command-string) -dumpmachine" ] ] ;
        if ! $(version) { # ?= operator does not short-circuit
        version ?= [ get-short-version $(command-string) ] ;
        }
        switch $(machine:L)
        {
            case *mingw* : flavor ?= mingw ;
            case *cygwin* : flavor ?= cygwin ;
        }
        switch $(machine:L)
        {
            case *mingw*  : target-os ?= windows ;
            case *cygwin* : target-os ?= cygwin ;
            case *linux*  : target-os ?= linux ;
            case *aix*    : target-os ?= aix ;
            case *hpux*   : target-os ?= hpux ;
            # TODO: finish this list.
        }
    }

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

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

    # Set the default target-os for this toolset.
    if $(target-os) && ! [ feature.get-values <target-os> : $(requirement) ]
    {
        local conditionx = [ regex.replace $(condition) "/" "," ] ;
        toolset.add-defaults $(conditionx)\:<target-os>$(target-os) ;
    }

    # If gcc is installed in a non-standard location, we would need to add
    # LD_LIBRARY_PATH when running programs created with it (for unit-test/run
    # rules).
    if $(command)
    {
        # On multilib 64-bit boxes, there are both 32-bit and 64-bit libraries
        # and all must be added to LD_LIBRARY_PATH. The linker will pick the
        # right onces. Note that we do not provide a clean way to build a 32-bit
        # binary using a 64-bit compiler, but user can always pass -m32
        # manually.
        local lib_path = $(root)/bin $(root)/lib $(root)/lib32 $(root)/lib64 ;
        if $(.debug-configuration)
        {
            ECHO "notice:" using gcc libraries "::" $(condition) "::" $(lib_path) ;
        }
        toolset.flags gcc.link RUN_PATH $(condition) : $(lib_path) ;
    }

    # If we are not using a system gcc installation we should adjust the various
    # programs as needed to prefer using their installation specific versions.
    # This is essential for correct use of MinGW and for cross-compiling.

    # - Archive builder.
    local archiver = [ common.get-invocation-command gcc
        : [ .get-prog-name $(command-string) : ar : $(flavor) ]
        : [ feature.get-values <archiver> : $(options) ]
        : $(bin)
        : search-path ] ;
    toolset.flags gcc.archive .AR $(condition) : $(archiver[1]) ;
    if $(.debug-configuration)
    {
        ECHO "notice:" using gcc archiver "::" $(condition) "::" $(archiver[1]) ;
    }
    local arflags = [ feature.get-values <arflags> : $(options) ] ;
    toolset.flags gcc.archive .ARFLAGS $(condition) : $(arflags) ;

    # - Resource compiler.
    local rc = [ common.get-invocation-command-nodefault gcc : windres :
        [ feature.get-values <rc> : $(options) ] : $(bin) : search-path ] ;
    local rc-type = [ feature.get-values <rc-type> : $(options) ] ;
    rc-type ?= windres ;
    if ! $(rc)
    {
        # If we can not find an RC compiler we fallback to a null one that
        # creates empty object files. This allows the same Jamfiles to work
        # across the board. The null RC uses assembler to create the empty
        # objects, so configure that.
        rc = [ common.get-invocation-command gcc : as : : $(bin) : search-path ]
            ;
        rc-type = null ;
    }
    rc.configure $(rc) : $(condition) : <rc-type>$(rc-type) ;

    toolset.flags gcc VERSION $(condition) : [ regex.split $(version) "[.]" ] ;

    init-cxxstd-flags $(condition) : $(version) ;

    if ! $(target-os) in aix hpux
    {
        if [ MATCH "^(i.86|x86|powerpc|ppc|sparc)" : $(machine:L) ]
        {
            compile-link-flags $(condition)/<architecture>/<address-model>32 : -m32 ;
            compile-link-flags $(condition)/<architecture>/<address-model>64 : -m64 ;
        }
        if [ MATCH "^(s390)" : $(machine:L) ]
        {
            compile-link-flags $(condition)/<architecture>/<address-model>32 : -m31 ;
            compile-link-flags $(condition)/<architecture>/<address-model>64 : -m64 ;
        }
    }
}

if [ os.name ] = NT
{
    # This causes single-line command invocation to not go through .bat files,
    # thus avoiding command-line length limitations.
    JAMSHELL = [ modules.peek : JAMSHELL ] ;
    JAMSHELL ?= % ;
}

rule get-full-version ( command-string )
{
    # -dumpfullversion is only supported for gcc 7+.
    # Passing both options works, as the first one that's
    # recognized will be used.
    return [ common.match-command-output version  : "^([0-9.]+)"
           : "$(command-string) -dumpfullversion -dumpversion" ] ;
}

rule get-short-version ( command-string : single-digit-since ? )
{
    local version = [ get-full-version $(command-string) ] ;
    version = [ SPLIT_BY_CHARACTERS $(version) : . ] ;

    import version ;
    if [ version.version-less $(version) : $(single-digit-since:E=5) ]
    {
        return $(version[1-2]:J=.) ;
    }

    return $(version[1]) ;
}

# Uses -print-prog-name to get the name of the tool.
# Converts the path to native form if using cygwin.
rule .get-prog-name ( command-string : tool : flavor ? )
{
    local prog-name = [ NORMALIZE_PATH [ MATCH "(.*)[\n]+" :
        [ SHELL "$(command-string) -print-prog-name=$(tool)" ] ] ] ;

    if $(flavor) = cygwin && [ os.name ] = NT
    {
        prog-name = [ cygwin.cygwin-to-windows-path $(prog-name) ] ;
    }
    return $(prog-name) ;
}

###
### Functions that set options on the targets.
###

local all-os = [ feature.values <target-os> ] ;

local rule compile-link-flags ( * )
{
    toolset.flags gcc.compile OPTIONS $(1) : $(2) ;
    toolset.flags gcc.link OPTIONS $(1) : $(2) ;
}

{
    # This logic will add -fPIC for all compilations:
    #
    # lib a : a.cpp b ;
    # obj b : b.cpp ;
    # exe c : c.cpp a d ;
    # obj d : d.cpp ;
    #
    # This all is fine, except that 'd' will be compiled with -fPIC even
    # though it is not needed, as 'd' is used only in exe. However, it is
    # hard to detect where a target is going to be used. Alternatively, we
    # can set -fPIC only when main target type is LIB but than 'b' would be
    # compiled without -fPIC which would lead to link errors on x86-64. So,
    # compile everything with -fPIC.
    #
    # Yet another alternative would be to create a propagated <sharedable>
    # feature and set it when building shared libraries, but that would be
    # hard to implement and would increase the target path length even more.

    # On Windows, fPIC is the default, and specifying -fPIC explicitly leads
    # to a warning.
    local non-windows = [ set.difference $(all-os) : cygwin windows ] ;
    compile-link-flags <link>shared/<target-os>$(non-windows) : -fPIC ;
}

{
    # Handle address-model
    compile-link-flags <target-os>aix/<address-model>32 : -maix32 ;
    compile-link-flags <target-os>aix/<address-model>64 : -maix64 ;

    compile-link-flags <target-os>hpux/<address-model>32 : -milp32 ;
    compile-link-flags <target-os>hpux/<address-model>64 : -mlp64 ;

    local generic-os = [ set.difference $(all-os) : aix hpux ] ;
    compile-link-flags <target-os>$(generic-os)/<architecture>s390x/<address-model>32 : -m31 ;
    compile-link-flags <target-os>$(generic-os)/<architecture>s390x/<address-model>64 : -m64 ;
    local arch = power sparc x86 ;
    compile-link-flags <target-os>$(generic-os)/<architecture>$(arch)/<address-model>32 : -m32 ;
    compile-link-flags <target-os>$(generic-os)/<architecture>$(arch)/<address-model>64 : -m64 ;
}

{
    # Handle threading
    local rule threading-flags ( * )
    {
        compile-link-flags <threading>multi/$(1) : $(2) ;
        if $(3)
        {
            toolset.flags gcc.link FINDLIBS-SA <threading>multi/$(1) : $(3) ;
        }
    }

    threading-flags <target-os>windows/<threadapi>pthread : -pthread ;
    threading-flags <target-os>cygwin/<threadapi>pthread : -pthread ;
    threading-flags <target-os>solaris  : -pthreads : rt ;
    threading-flags <target-os>qnx : -pthread ;

    local bsd = [ MATCH ^(.*bsd)$ : $(all-os) ] ;
    threading-flags <target-os>$(bsd) : -pthread ;

    # iOS doesn't need pthread flag according to the https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/pthread.3.html
    # The default system libraries include pthread functions.  No additional libraries or CFLAGS are necessary to use this API.
    local no-threading = android beos haiku sgi darwin vxworks iphone appletv ;
    local threading-generic-os = [ set.difference $(all-os) : $(no-threading) $(bsd) windows cygwin solaris qnx ] ;
    threading-flags <target-os>$(threading-generic-os) : -pthread : rt ;
}

{
    local rule cxxstd-flags ( * )
    {
        toolset.flags gcc.compile.c++ OPTIONS $(1) : $(2) ;
        toolset.flags gcc.link OPTIONS $(1) : $(2) ;
    }

    local cxxstd = [ feature.values <cxxstd> ] ;
    local dialects = [ feature.values <cxxstd-dialect> ] ;
    .cxxstd-dialects = [ set.difference $(dialects) : gnu iso ] ;
    # C++ latest needs to be set up on a per-toolset basis
    for local std in [ set.difference $(cxxstd) : latest ]
    {
        cxxstd-flags <cxxstd>$(std)/<cxxstd-dialect>iso : -std=c++$(std) ;
        cxxstd-flags <cxxstd>$(std)/<cxxstd-dialect>gnu : -std=gnu++$(std) ;
        # If we see this it's probably a mistake, but
        # toolset.flags has no way to set up diagnostics.
        cxxstd-flags <cxxstd>$(std)/<cxxstd-dialect>$(.cxxstd-dialects) : -std=c++$(std) ;
    }

    local rule version-ge ( lhs : rhs )
    {
        lhs = [ regex.split $(lhs) "[.]" ] ;
        rhs = [ regex.split $(rhs) "[.]" ] ;
        return [ sequence.compare $(rhs) : $(lhs) : numbers.less ] ;
    }
    # Version specific flags
    local rule init-cxxstd-flags ( condition * : version )
    {
        local std ;
        if [ version-ge $(version) : 14 ] { std = 26 ; }
        else if [ version-ge $(version) : 11 ] { std = 23 ; }
        else if [ version-ge $(version) : 10 ] { std = 20 ; }
        else if [ version-ge $(version) : 8 ] { std = 2a ; }
        else if [ version-ge $(version) : 6 ] { std = 17 ; }
        else if [ version-ge $(version) : 5 ] { std = 1z ; }
        else if [ version-ge $(version) : 4.9 ] { std = 14 ; }
        else if [ version-ge $(version) : 4.8 ] { std = 1y ; }
        else if [ version-ge $(version) : 4.7 ] { std = 11 ; }
        else if [ version-ge $(version) : 3.3 ] { std = 98 ; }
        if $(std)
        {
            cxxstd-flags $(condition)/<cxxstd>latest/<cxxstd-dialect>iso : -std=c++$(std) ;
            cxxstd-flags $(condition)/<cxxstd>latest/<cxxstd-dialect>gnu : -std=gnu++$(std) ;
            cxxstd-flags $(condition)/<cxxstd>latest/<cxxstd-dialect>$(.cxxstd-dialects) : -std=c++$(std) ;
        }
    }
}

generators.register-c-compiler gcc.compile.c++.preprocess : CPP : PREPROCESSED_CPP : <toolset>gcc ;
generators.register-c-compiler gcc.compile.c.preprocess   : C   : PREPROCESSED_C   : <toolset>gcc ;
generators.register-c-compiler gcc.compile.c++ : CPP : OBJ : <toolset>gcc ;
generators.register-c-compiler gcc.compile.c   : C   : OBJ : <toolset>gcc ;
generators.register-c-compiler gcc.compile.asm : ASM_CPP : OBJ : <toolset>gcc ;
generators.register-c-compiler gcc.compile.m   : OBJECTIVE_C   : OBJ : <toolset>gcc ;
generators.register-c-compiler gcc.compile.mm  : OBJECTIVE_CPP : OBJ : <toolset>gcc ;

generators.register [ new fortran-compiling-generator
    gcc.compile.fortran : FORTRAN FORTRAN90 : OBJ : <toolset>gcc ] ;

rule set_command_db ( targets + )
{
    local config_command = [ regex.escape
        [ on $(targets[1]) return $(CONFIG_COMMAND:E="") ]
        : "()[]\\+.*^$\"" : "\\" ] ;
    COMMAND_DATABASE on $(targets) = "(\"$(config_command)\"[^\n]*)" ;
}

rule compile.c++.preprocess ( targets * : sources * : properties * )
{
    # Some extensions are compiled as C++ by default. For others, we need to
    # pass -x c++. We could always pass -x c++ but distcc does not work with it.
    if ! $(>:S) in .cc .cp .cxx .cpp .c++ .C
    {
        LANG on $(<) = "-x c++" ;
    }
    gcc.set_command_db $(targets) ;
    DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ;
}

rule compile.c.preprocess ( targets * : sources * : properties * )
{
    # If we use the name g++ then default file suffix -> language mapping does
    # not work. So have to pass -x option. Maybe, we can work around this by
    # allowing the user to specify both C and C++ compiler names.
    #if $(>:S) != .c
    #{
        LANG on $(<) = "-x c" ;
    #}
    gcc.set_command_db $(targets) ;
    DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ;
}

rule compile.c++ ( targets * : sources * : properties * )
{
    # Some extensions are compiled as C++ by default. For others, we need to
    # pass -x c++. We could always pass -x c++ but distcc does not work with it.
    if ! $(>:S) in .cc .cp .cxx .cpp .c++ .C
    {
        LANG on $(<) = "-x c++" ;
    }
    gcc.set_command_db $(targets) ;
    DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ;
}

rule compile.c ( targets * : sources * : properties * )
{
    # If we use the name g++ then default file suffix -> language mapping does
    # not work. So have to pass -x option. Maybe, we can work around this by
    # allowing the user to specify both C and C++ compiler names.
    #if $(>:S) != .c
    #{
        LANG on $(<) = "-x c" ;
    #}
    gcc.set_command_db $(targets) ;
    DEPENDS $(<) : [ on $(<) return $(PCH_FILE) ] ;
}

rule compile.fortran ( targets * : sources * : properties * )
{
    gcc.set_command_db $(targets) ;
}

actions compile.c++ bind PCH_FILE
{
    "$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) $(INCLUDE-GCH)$(_)"$(PCH_FILE:S=)" $(INCLUDE-PCH)$(_)"$(PCH_FILE)" -I"$(INCLUDES)" -include$(_)"$(FORCE_INCLUDES)" -c -o "$(<)" "$(>:T)"
}

actions compile.c bind PCH_FILE
{
    "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) $(INCLUDE-GCH)$(_)"$(PCH_FILE:S=)" $(INCLUDE-PCH)$(_)"$(PCH_FILE)" -I"$(INCLUDES)" -include$(_)"$(FORCE_INCLUDES)" -c -o "$(<)" "$(>:T)"
}

actions compile.c++.preprocess bind PCH_FILE
{
    "$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) $(INCLUDE-GCH)$(_)"$(PCH_FILE:S=)" $(INCLUDE-PCH)$(_)"$(PCH_FILE)" -I"$(INCLUDES)" -include$(_)"$(FORCE_INCLUDES)" "$(>:T)" -E >"$(<)"
}

actions compile.c.preprocess bind PCH_FILE
{
    "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) $(INCLUDE-GCH)$(_)"$(PCH_FILE:S=)" $(INCLUDE-PCH)$(_)"$(PCH_FILE)" -I"$(INCLUDES)" -include$(_)"$(FORCE_INCLUDES)" "$(>:T)" -E >$(<)
}

actions compile.fortran
{
    "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) $(INCLUDE-GCH)$(_)"$(PCH_FILE:S=)" $(INCLUDE-PCH)$(_)"$(PCH_FILE)" -I"$(INCLUDES)" -include$(_)"$(FORCE_INCLUDES)" -c -o "$(<)" "$(>)"
}

rule compile.asm ( targets * : sources * : properties * )
{
    gcc.set_command_db $(targets) ;
    LANG on $(<) = "-x assembler-with-cpp" ;
}

actions compile.asm
{
    "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)"
}

actions compile.m
{
    "$(CONFIG_COMMAND)" -x objective-c $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)"
}

actions compile.mm
{
    "$(CONFIG_COMMAND)" -x objective-c++ $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)"
}

###
### Precompiled header use and generation.
###

# The compiler looks for a precompiled header in each directory just before it
# looks for the include file in that directory. The name searched for is the
# name specified in the #include directive with ".gch" suffix appended. The
# logic in gcc-pch-generator will make sure that the BASE_PCH suffix is appended
# to the full header name.

type.set-generated-target-suffix PCH : <toolset>gcc : gch ;

# GCC-specific pch generator.
class gcc-pch-generator : pch-generator
{
    import project ;
    import property-set ;
    import type ;

    rule run-pch ( project name ? : property-set : sources + )
    {
        # Find the header in sources. Ignore any CPP sources.
        local header ;
        for local s in $(sources)
        {
            if [ type.is-derived [ $(s).type ] H ]
            {
                header = $(s) ;
            }
        }

        local pch-file = [ generator.run $(project) $(name) : $(property-set)
            : $(header) ] ;

        # Return result of base class and pch-file property as
        # usage-requirements.
        return
            [ $(pch-file[1]).add-raw <pch-file>$(pch-file[2-]) <cflags>-Winvalid-pch ]
            $(pch-file[2-])
          ;
    }
}

# Note: the 'H' source type will catch both '.h' header and '.hpp' header. The
# latter have HPP type, but HPP type is derived from H. The type of compilation
# is determined entirely by the destination type.
generators.register [ new gcc-pch-generator gcc.compile.c.pch   : H :   C_PCH : <pch>on <toolset>gcc ] ;
generators.register [ new gcc-pch-generator gcc.compile.c++.pch : H : CPP_PCH : <pch>on <toolset>gcc ] ;

# Override default do-nothing generators.
generators.override gcc.compile.c.pch   : pch.default-c-pch-generator   ;
generators.override gcc.compile.c++.pch : pch.default-cpp-pch-generator ;

toolset.flags gcc.compile PCH_FILE <pch>on : <pch-file> ;
toolset.flags gcc.compile INCLUDE-GCH : -include ;

actions compile.c++.pch
{
    "$(CONFIG_COMMAND)" -x c++-header -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -include$(_)"$(FORCE_INCLUDES)" -c -o "$(<)" "$(>:T)"
}

actions compile.c.pch
{
    "$(CONFIG_COMMAND)" -x c-header $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -include$(_)"$(FORCE_INCLUDES)" -c -o "$(<)" "$(>:T)"
}

###
### General options, like optimization.
###

# Declare flags and action for compilation.
toolset.flags gcc.compile OPTIONS <optimization>off   : -O0 ;
toolset.flags gcc.compile OPTIONS <optimization>speed : -O3 ;
toolset.flags gcc.compile OPTIONS <optimization>space : -Os ;
toolset.flags gcc.compile OPTIONS <optimization>minimal : -O1 ;
toolset.flags gcc.compile OPTIONS <optimization>debug : -Og ;

toolset.flags gcc.compile OPTIONS <inlining>off  : -fno-inline ;
toolset.flags gcc.compile OPTIONS <inlining>on   : -Wno-inline ;
toolset.flags gcc.compile OPTIONS <inlining>full : -finline-functions -Wno-inline ;

toolset.flags gcc.compile OPTIONS <warnings>off : -w ;
toolset.flags gcc.compile OPTIONS <warnings>on  : -Wall ;
toolset.flags gcc.compile OPTIONS <warnings>all : -Wall ;
toolset.flags gcc.compile OPTIONS <warnings>extra : -Wall -Wextra ;
toolset.flags gcc.compile OPTIONS <warnings>pedantic : -Wall -Wextra -pedantic ;
toolset.flags gcc.compile OPTIONS <warnings-as-errors>on : -Werror ;

toolset.flags gcc.compile OPTIONS <debug-symbols>on : -g ;
toolset.flags gcc.compile OPTIONS <profiling>on : -pg ;

toolset.flags gcc.compile OPTIONS <local-visibility>hidden : -fvisibility=hidden ;
toolset.flags gcc.compile.c++ OPTIONS <local-visibility>hidden : -fvisibility-inlines-hidden ;
toolset.flags gcc.compile OPTIONS <local-visibility>protected : -fvisibility=protected ;
toolset.flags gcc.compile OPTIONS <local-visibility>protected/<target-os>darwin : ;
toolset.flags gcc.compile OPTIONS <local-visibility>global : -fvisibility=default ;

toolset.flags gcc.compile.c++ OPTIONS <exception-handling>off : -fno-exceptions ;
toolset.flags gcc.compile.c++ OPTIONS <rtti>off : -fno-rtti ;

toolset.flags gcc.compile.c.preprocess OPTIONS <linemarkers>off : -P ;
toolset.flags gcc.compile.c++.preprocess OPTIONS <linemarkers>off : -P ;

# sanitizers
toolset.flags gcc.compile.c++ OPTIONS <address-sanitizer>on : -fsanitize=address -fno-omit-frame-pointer ;
toolset.flags gcc.compile.c++ OPTIONS <address-sanitizer>norecover : -fsanitize=address -fno-sanitize-recover=address -fno-omit-frame-pointer ;
toolset.flags gcc.compile.c++ OPTIONS <leak-sanitizer>on : -fsanitize=leak -fno-omit-frame-pointer ;
toolset.flags gcc.compile.c++ OPTIONS <leak-sanitizer>norecover : -fsanitize=leak -fno-sanitize-recover=leak -fno-omit-frame-pointer ;
toolset.flags gcc.compile.c++ OPTIONS <thread-sanitizer>on : -fsanitize=thread -fno-omit-frame-pointer ;
toolset.flags gcc.compile.c++ OPTIONS <thread-sanitizer>norecover : -fsanitize=thread -fno-sanitize-recover=thread -fno-omit-frame-pointer ;
toolset.flags gcc.compile.c++ OPTIONS <undefined-sanitizer>on : -fsanitize=undefined -fno-omit-frame-pointer ;
toolset.flags gcc.compile.c++ OPTIONS <undefined-sanitizer>norecover : -fsanitize=undefined -fno-sanitize-recover=undefined -fno-omit-frame-pointer ;

toolset.flags gcc.compile.c++ OPTIONS <coverage>on : --coverage ;

# configure Dinkum STL to match compiler options
toolset.flags gcc.compile.c++ DEFINES <rtti>off/<target-os>vxworks : _NO_RTTI ;
toolset.flags gcc.compile.c++ DEFINES <exception-handling>off/<target-os>vxworks : _NO_EX=1 ;

# LTO
toolset.flags gcc.compile OPTIONS <lto>on/<lto-mode>full : -flto ;
toolset.flags gcc.link OPTIONS <lto>on/<lto-mode>full : -flto ;

toolset.flags gcc.compile OPTIONS <lto>on/<lto-mode>fat : -flto -ffat-lto-objects ;
toolset.flags gcc.link OPTIONS <lto>on/<lto-mode>fat : -flto ;

# ABI selection
toolset.flags gcc.compile.c++ DEFINES <stdlib>gnu : _GLIBCXX_USE_CXX11_ABI=0 ;
toolset.flags gcc.compile.c++ DEFINES <stdlib>gnu11 : _GLIBCXX_USE_CXX11_ABI=1 ;

###
### User free feature options.
###

toolset.flags gcc.compile USER_OPTIONS <cflags> ;
toolset.flags gcc.compile.c++ USER_OPTIONS <cxxflags> ;
toolset.flags gcc.compile.asm USER_OPTIONS <asmflags> ;
toolset.flags gcc.compile DEFINES <define> ;
toolset.flags gcc.compile INCLUDES <include> ;
toolset.flags gcc.compile FORCE_INCLUDES <force-include> ;
toolset.flags gcc.compile.c++ TEMPLATE_DEPTH <c++-template-depth> ;
toolset.flags gcc.compile.fortran USER_OPTIONS <fflags> ;
toolset.flags gcc.compile.m USER_OPTIONS <mflags> ;
toolset.flags gcc.compile.mm USER_OPTIONS <mmflags> ;

###
### Linking generators and actions.
###

# Class checking that we do not try to use the <runtime-link>static property
# while creating or using a shared library, since it is not supported by
# gcc/libc.
class gcc-linking-generator : unix-linking-generator
{
    rule run ( project name ? : property-set : sources + )
    {
        local target-os = [ $(property-set).get <target-os> ] ;
        local no-static-link = true ;
        switch $(target-os)
        {
            case vms : no-static-link = ;
            case windows : no-static-link = ;
        }

        local properties = [ $(property-set).raw ] ;
        local reason ;
        if $(no-static-link) && <runtime-link>static in $(properties)
        {
            if <link>shared in $(properties)
            {
                reason = On gcc, DLLs can not be built with
                    '<runtime-link>static'. ;
            }
            else if [ type.is-derived $(self.target-types[1]) EXE ]
            {
                for local s in $(sources)
                {
                    local type = [ $(s).type ] ;
                    if $(type) && [ type.is-derived $(type) SHARED_LIB ]
                    {
                        reason = On gcc, using DLLs together with the
                            '<runtime-link>static' option is not possible. ;
                    }
                }
            }
        }
        if $(reason)
        {
            ECHO "warning:" $(reason) ;
            ECHO "warning:" It is suggested to use '<runtime-link>static' together
                with '<link>static'. ;
        }
        else
        {
            return [ unix-linking-generator.run $(project) $(name) :
                $(property-set) : $(sources) ] ;
        }
    }
}

# The set of permissible input types is different on mingw. So, define two sets
# of generators, with mingw generators selected when target-os=windows.

local g ;
g = [ new gcc-linking-generator gcc.mingw.link
      : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB
      : EXE
      : <toolset>gcc <target-os>windows ] ;
$(g).set-rule-name gcc.link ;
generators.register $(g) ;

g = [ new gcc-linking-generator gcc.mingw.link.dll
      : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB
      : SHARED_LIB IMPORT_LIB
      : <toolset>gcc <target-os>windows ] ;
$(g).set-rule-name gcc.link.dll ;
generators.register $(g) ;

generators.register
  [ new gcc-linking-generator gcc.link
      : LIB OBJ
      : EXE
      : <toolset>gcc ] ;
generators.register
  [ new gcc-linking-generator gcc.link.dll
      : LIB OBJ
      : SHARED_LIB
      : <toolset>gcc ] ;

generators.override gcc.mingw.link : gcc.link ;
generators.override gcc.mingw.link.dll : gcc.link.dll ;

# Cygwin is similar to msvc and mingw in that it uses import libraries. While in
# simple cases, it can directly link to a shared library, it is believed to be
# slower, and not always possible. Define cygwin-specific generators here.

g = [ new gcc-linking-generator gcc.cygwin.link
      : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB
      : EXE
      : <toolset>gcc <target-os>cygwin ] ;
$(g).set-rule-name gcc.link ;
generators.register $(g) ;

g = [ new gcc-linking-generator gcc.cygwin.link.dll
      : OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB
      : SHARED_LIB IMPORT_LIB
      : <toolset>gcc <target-os>cygwin ] ;
$(g).set-rule-name gcc.link.dll ;
generators.register $(g) ;

generators.override gcc.cygwin.link : gcc.link ;
generators.override gcc.cygwin.link.dll : gcc.link.dll ;

# Declare flags for linking.
# First, the common flags.
toolset.flags gcc.link OPTIONS <debug-symbols>on : -g ;
toolset.flags gcc.link OPTIONS <profiling>on : -pg ;
toolset.flags gcc.link USER_OPTIONS <linkflags> ;
toolset.flags gcc.link LINKPATH <library-path> ;
toolset.flags gcc.link FINDLIBS-ST <find-static-library> ;
toolset.flags gcc.link FINDLIBS-SA <find-shared-library> ;
toolset.flags gcc.link LIBRARIES <library-file> ;

# Specify compile flags for linker as well as they may be needed for LTO
toolset.flags gcc.link OPTIONS <local-visibility>hidden : -fvisibility=hidden -fvisibility-inlines-hidden ;
toolset.flags gcc.link OPTIONS <local-visibility>protected : -fvisibility=protected ;
toolset.flags gcc.link OPTIONS <local-visibility>protected/<target-os>darwin : ;
toolset.flags gcc.link OPTIONS <local-visibility>global : -fvisibility=default ;

# sanitizers
toolset.flags gcc.link OPTIONS <address-sanitizer>on : -fsanitize=address -fno-omit-frame-pointer ;
toolset.flags gcc.link OPTIONS <address-sanitizer>norecover : -fsanitize=address -fno-sanitize-recover=address -fno-omit-frame-pointer ;
toolset.flags gcc.link OPTIONS <leak-sanitizer>on : -fsanitize=leak -fno-omit-frame-pointer ;
toolset.flags gcc.link OPTIONS <leak-sanitizer>norecover : -fsanitize=leak -fno-sanitize-recover=leak -fno-omit-frame-pointer ;
toolset.flags gcc.link OPTIONS <thread-sanitizer>on : -fsanitize=thread -fno-omit-frame-pointer ;
toolset.flags gcc.link OPTIONS <thread-sanitizer>norecover : -fsanitize=thread -fno-sanitize-recover=thread -fno-omit-frame-pointer ;
toolset.flags gcc.link OPTIONS <undefined-sanitizer>on : -fsanitize=undefined -fno-omit-frame-pointer ;
toolset.flags gcc.link OPTIONS <undefined-sanitizer>norecover : -fsanitize=undefined -fno-sanitize-recover=undefined -fno-omit-frame-pointer ;

toolset.flags gcc.link OPTIONS <coverage>on : --coverage ;

# target specific link flags
{
    # aix

    # On AIX we *have* to use the native linker.
    #
    # The -bnoipath strips the prepending (relative) path of libraries from
    # the loader section in the target library or executable. Hence, during
    # load-time LIBPATH (identical to LD_LIBRARY_PATH) or a hard-coded
    # -blibpath (*similar* to -lrpath/-lrpath-link) is searched. Without
    # this option, the prepending (relative) path + library name is
    # hard-coded in the loader section, causing *only* this path to be
    # searched during load-time. Note that the AIX linker does not have an
    # -soname equivalent, this is as close as it gets.
    #
    # The -bbigtoc option instrcuts the linker to create a TOC bigger than 64k.
    # This is necessary for some submodules such as math, but it does make running
    # the tests a tad slower.
    #
    # The above options are definitely for AIX 5.x, and most likely also for
    # AIX 4.x and AIX 6.x. For details about the AIX linker see:
    # http://download.boulder.ibm.com/ibmdl/pub/software/dw/aix/es-aix_ll.pdf
    #
    toolset.flags gcc.link OPTIONS <target-os>aix : -Wl,-bnoipath -Wl,-bbigtoc ;

    # See note [1]
    toolset.flags gcc.link OPTIONS <target-os>aix/<runtime-link>static : -static ;

    # darwin

    # On Darwin, the -s option to ld does not work unless we pass -static,
    # and passing -static unconditionally is a bad idea. So, do not pass -s
    # at all and darwin.jam will use a separate 'strip' invocation.
    toolset.flags gcc.link RPATH <target-os>darwin : <dll-path> ;
    # This does not support -R.
    toolset.flags gcc.link RPATH_OPTION <target-os>darwin : -rpath ;
    # -rpath-link is not supported at all.
    toolset.flags gcc.link SHARED_OPTION <target-os>darwin : -dynamiclib -single_module ;
    toolset.flags gcc.link HAVE_SONAME <target-os>darwin : "" ;
    toolset.flags gcc.link SONAME_OPTION <target-os>darwin : -install_name ;
    toolset.flags gcc.link SONAME_PREFIX <target-os>darwin : /usr/pkg/lib/ ;

    # See note [1]
    toolset.flags gcc.link OPTIONS <target-os>darwin/<runtime-link>static : -static ;

    # vxworks
    # On VxWorks we want to reflect what ever special flags have been set in the
    # environment for the CPU we are targeting in the cross build
    toolset.flags gcc.link     OPTIONS     <target-os>vxworks/<strip>on    : -Wl,--strip-all                ;
    toolset.flags gcc.link     OPTIONS     <target-os>vxworks/<link>static : [ os.environ LDFLAGS_STATIC ]  ;
    toolset.flags gcc.link.dll OPTIONS     <target-os>vxworks              : [ os.environ LDFLAGS_SO ]      ;
    toolset.flags gcc.link     OPTIONS     <target-os>vxworks/<link>shared : [ os.environ LDFLAGS_DYNAMIC ] ;

    # default

    local generic-os = [ set.difference $(all-os) : aix darwin vxworks solaris osf hpux iphone appletv ] ;
    # Strip the binary when no debugging is needed. We use --strip-all flag
    # as opposed to -s since icc (intel's compiler) is generally
    # option-compatible with and inherits from the gcc toolset, but does not
    # support -s.
    toolset.flags gcc.link OPTIONS <target-os>$(generic-os)/<strip>on :
        -Wl,--strip-all ;
    toolset.flags gcc.link START-GROUP <target-os>$(generic-os) :
        -Wl,--start-group ;
    toolset.flags gcc.link END-GROUP <target-os>$(generic-os) : -Wl,--end-group ;

    local rpath-os = [ set.difference $(all-os) : aix darwin vxworks solaris osf hpux windows ] ;
    toolset.flags gcc.link RPATH <target-os>$(rpath-os) : <dll-path> ;
    toolset.flags gcc.link RPATH_OPTION <target-os>$(rpath-os) : -rpath ;
    toolset.flags gcc.link RPATH_LINK <target-os>$(rpath-os) : <xdll-path> ;

    # gnu ld has the ability to change the search behaviour for libraries
    # referenced by the -l switch. These modifiers are -Bstatic and
    # -Bdynamic and change search for -l switches that follow them. The
    # following list shows the tried variants. Search stops at the first
    # variant that has a match.
    #
    # *nix: -Bstatic -lxxx
    #    libxxx.a
    #
    # *nix: -Bdynamic -lxxx
    #    libxxx.so
    #    libxxx.a
    #
    # windows (mingw, cygwin) -Bstatic -lxxx
    #    libxxx.a
    #    xxx.lib
    #
    # windows (mingw, cygwin) -Bdynamic -lxxx
    #    libxxx.dll.a
    #    xxx.dll.a
    #    libxxx.a
    #    xxx.lib
    #    cygxxx.dll (*)
    #    libxxx.dll
    #    xxx.dll
    #    libxxx.a
    #
    # (*) This is for cygwin
    # Please note that -Bstatic and -Bdynamic are not a guarantee that a
    # static or dynamic lib indeed gets linked in. The switches only change
    # search patterns!

    # On *nix mixing shared libs with static runtime is not a good idea.
    toolset.flags gcc.link FINDLIBS-ST-PFX <target-os>$(generic-os)/<runtime-link>shared : -Wl,-Bstatic ;
    toolset.flags gcc.link FINDLIBS-SA-PFX <target-os>$(generic-os)/<runtime-link>shared : -Wl,-Bdynamic ;

    # On windows allow mixing of static and dynamic libs with static
    # runtime is not a good idea.
    toolset.flags gcc.link FINDLIBS-ST-PFX <target-os>windows/<runtime-link>static : -Wl,-Bstatic ;
    toolset.flags gcc.link FINDLIBS-SA-PFX <target-os>windows/<runtime-link>static : -Wl,-Bdynamic ;
    toolset.flags gcc.link OPTIONS <target-os>windows/<runtime-link>static : -Wl,-Bstatic ;

    local soname-os = [ set.difference $(generic-os) : windows ] ;
    toolset.flags gcc.link HAVE_SONAME <target-os>$(soname-os) : "" ;
    toolset.flags gcc.link SONAME_OPTION <target-os>$(soname-os) : -soname ;

    # See note [1]
    toolset.flags gcc.link OPTIONS <target-os>$(generic-os)/<runtime-link>static : -static ;

    # hpux

    toolset.flags gcc.link OPTIONS <target-os>hpux/<strip>on : -Wl,-s ;

    toolset.flags gcc.link HAVE_SONAME <target-os>hpux : "" ;
    toolset.flags gcc.link SONAME_OPTION <target-os>hpux : +h ;

    # osf

    # No --strip-all, just -s.
    toolset.flags gcc.link OPTIONS <target-os>osf/<strip>on : -Wl,-s ;
    toolset.flags gcc.link RPATH <target-os>osf : <dll-path> ;
    # This does not support -R.
    toolset.flags gcc.link RPATH_OPTION <target-os>osf : -rpath ;
    # -rpath-link is not supported at all.

    # See note [1]
    toolset.flags gcc.link OPTIONS <target-os>osf/<runtime-link>static : -static ;

    # sun

    toolset.flags gcc.link OPTIONS <target-os>solaris/<strip>on : -Wl,-s ;

    toolset.flags gcc.link HAVE_SONAME <target-os>solaris : "" ;
    toolset.flags gcc.link SONAME_OPTION <target-os>solaris : -h ;

    toolset.flags gcc.link RPATH <target-os>solaris : <dll-path> ;
    # Solaris linker does not have a separate -rpath-link, but allows using
    # -L for the same purpose.
    toolset.flags gcc.link LINKPATH <target-os>solaris : <xdll-path> ;

    # This permits shared libraries with non-PIC code on Solaris.
    # VP, 2004/09/07: Now that we have -fPIC hardcode in link.dll, the
    # following is not needed. Whether -fPIC should be hardcoded, is a
    # separate question.
    # AH, 2004/10/16: it is still necessary because some tests link against
    # static libraries that were compiled without PIC.
    toolset.flags gcc.link OPTIONS <target-os>solaris : -mimpure-text ;

    # See note [1]
    toolset.flags gcc.link OPTIONS <target-os>solaris/<runtime-link>static : -static ;

    # [1]
    # For <runtime-link>static we made sure there are no dynamic libraries in the
    # link. On HP-UX not all system libraries exist as archived libraries (for
    # example, there is no libunwind.a), so, on this platform, the -static option
    # cannot be specified.
}


# Enclose the RPATH variable on 'targets' in double quotes, unless it is already
# enclosed in single quotes. This special casing is done because it is common to
# pass '$ORIGIN' to linker -- and it has to have single quotes to prevent shell
# expansion -- and if we add double quotes then the preventing properties of
# single quotes disappear.
#
rule quote-rpath ( targets * )
{
    local r = [ on $(targets[1]) return $(RPATH) ] ;
    if ! [ MATCH ('.*') : $(r) ]
    {
        r = \"$(r)\" ;
    }
    RPATH on $(targets) = $(r) ;
}

# Declare actions for linking.
rule link ( targets * : sources * : properties * )
{
    SPACE on $(targets) = " " ;
    quote-rpath $(targets) ;
}

rule link.dll ( targets * : sources * : properties * )
{
    SPACE on $(targets) = " " ;
    quote-rpath $(targets) ;
}

actions link bind LIBRARIES
{
    "$(CONFIG_COMMAND)" @($(<[1]:T).rsp:O=FC:<=@":>=":E=-L"$(LINKPATH)" -Wl,$(RPATH_OPTION)$(SPACE)-Wl,$(RPATH) -Wl,-rpath-link$(SPACE)-Wl,"$(RPATH_LINK)" -o "$(<:T)" $(START-GROUP) "$(>:T)" "$(LIBRARIES)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS))
}

actions link.dll bind LIBRARIES
{
    "$(CONFIG_COMMAND)" @($(<[1]:T).rsp:O=FC:<=@":>=":E=-L"$(LINKPATH)" -Wl,$(RPATH_OPTION)$(SPACE)-Wl,$(RPATH) -Wl,$(IMPLIB_OPTION:E=--out-implib),"$(<[2]:T)" -o "$(<[1]:T)" $(HAVE_SONAME)-Wl,$(SONAME_OPTION)$(SPACE)-Wl,"$(SONAME_PREFIX:E=)$(<[1]:D=)" $(SHARED_OPTION:E=-shared) $(START-GROUP) "$(>:T)" "$(LIBRARIES)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS))
}

###
### Archive library generation.
###

# Default value. Mostly for the sake of intel-linux that inherits from gcc, but
# does not have the same logic to set the .AR variable. We can put the same
# logic in intel-linux, but that is hardly worth the trouble as on Linux, 'ar'
# is always available.
.AR = ar ;
.ARFLAGS = rsc ;

toolset.flags gcc.archive AROPTIONS <archiveflags> ;

rule archive ( targets * : sources * : properties * )
{
    # Always remove archive and start again. Here is the rationale from
    #
    # Andre Hentz:
    #
    # I had a file, say a1.c, that was included into liba.a. I moved a1.c to
    # a2.c, updated my Jamfiles and rebuilt. My program was crashing with absurd
    # errors. After some debugging I traced it back to the fact that a1.o was
    # *still* in liba.a
    #
    # Rene Rivera:
    #
    # Originally removing the archive was done by splicing an RM onto the
    # archive action. That makes archives fail to build on NT when they have
    # many files because it will no longer execute the action directly and blow
    # the line length limit. Instead we remove the file in a different action,
    # just before building the archive.
    #
    local clean.a = $(targets[1])(clean) ;
    TEMPORARY $(clean.a) ;
    NOCARE $(clean.a) ;
    LOCATE on $(clean.a) = [ on $(targets[1]) return $(LOCATE) ] ;
    DEPENDS $(clean.a) : $(sources) ;
    DEPENDS $(targets) : $(clean.a) ;
    common.RmTemps $(clean.a) : $(targets) ;
}

# Declare action for creating static libraries.
# The letter 'r' means to add files to the archive with replacement. Since we
# remove archive, we do not care about replacement, but there is no option "add
# without replacement".
# The letter 'c' suppresses the warning in case the archive does not exists yet.
# That warning is produced only on some platforms, for whatever reasons.
#
actions piecemeal archive
{
    "$(.AR)" $(AROPTIONS) $(.ARFLAGS) "$(<)" @($(<[-1]:T).rsp:O=FC:<=@":>=":E="$(>:T)")
}

###
### CPU architecture and instruction set options.
###

local rule cpu-flags ( toolset variable : architecture : instruction-set + :
    values + : default ? )
{
    if $(default)
    {
        toolset.flags $(toolset) $(variable)
            <architecture>$(architecture)/<instruction-set> : $(values) ;
    }
    toolset.flags $(toolset) $(variable)
        <architecture>/<instruction-set>$(instruction-set)
        <architecture>$(architecture)/<instruction-set>$(instruction-set)
        : $(values) ;
}


# Set architecture/instruction-set options.
#
# x86 and compatible
# The 'native' option appeared in gcc 4.2 so we cannot safely use it as default.
# Use i686 instead for 32-bit.
toolset.flags gcc OPTIONS <architecture>x86/<address-model>32/<instruction-set> : -march=i686 ;
cpu-flags gcc OPTIONS : x86 : native : -march=native ;
cpu-flags gcc OPTIONS : x86 : i486 : -march=i486 ;
cpu-flags gcc OPTIONS : x86 : i586 : -march=i586 ;
cpu-flags gcc OPTIONS : x86 : i686 : -march=i686 ;
cpu-flags gcc OPTIONS : x86 : pentium : -march=pentium ;
cpu-flags gcc OPTIONS : x86 : pentium-mmx : -march=pentium-mmx ;
cpu-flags gcc OPTIONS : x86 : pentiumpro : -march=pentiumpro ;
cpu-flags gcc OPTIONS : x86 : pentium2 : -march=pentium2 ;
cpu-flags gcc OPTIONS : x86 : pentium3 : -march=pentium3 ;
cpu-flags gcc OPTIONS : x86 : pentium3m : -march=pentium3m ;
cpu-flags gcc OPTIONS : x86 : pentium-m : -march=pentium-m ;
cpu-flags gcc OPTIONS : x86 : pentium4 : -march=pentium4 ;
cpu-flags gcc OPTIONS : x86 : pentium4m : -march=pentium4m ;
cpu-flags gcc OPTIONS : x86 : prescott : -march=prescott ;
cpu-flags gcc OPTIONS : x86 : nocona : -march=nocona ;
cpu-flags gcc OPTIONS : x86 : core2 : -march=core2 ;
cpu-flags gcc OPTIONS : x86 : conroe : -march=core2 ;
cpu-flags gcc OPTIONS : x86 : conroe-xe : -march=core2 ;
cpu-flags gcc OPTIONS : x86 : conroe-l : -march=core2 ;
cpu-flags gcc OPTIONS : x86 : allendale : -march=core2 ;
cpu-flags gcc OPTIONS : x86 : wolfdale : -march=core2 -msse4.1 ;
cpu-flags gcc OPTIONS : x86 : merom : -march=core2 ;
cpu-flags gcc OPTIONS : x86 : merom-xe : -march=core2 ;
cpu-flags gcc OPTIONS : x86 : kentsfield : -march=core2 ;
cpu-flags gcc OPTIONS : x86 : kentsfield-xe : -march=core2 ;
cpu-flags gcc OPTIONS : x86 : yorksfield : -march=core2 ;
cpu-flags gcc OPTIONS : x86 : penryn : -march=core2 ;
cpu-flags gcc OPTIONS : x86 : corei7 : -march=corei7 ;
cpu-flags gcc OPTIONS : x86 : nehalem : -march=corei7 ;
cpu-flags gcc OPTIONS : x86 : corei7-avx : -march=corei7-avx ;
cpu-flags gcc OPTIONS : x86 : sandy-bridge : -march=corei7-avx ;
cpu-flags gcc OPTIONS : x86 : core-avx-i : -march=core-avx-i ;
cpu-flags gcc OPTIONS : x86 : ivy-bridge : -march=core-avx-i ;
cpu-flags gcc OPTIONS : x86 : haswell : -march=core-avx-i -mavx2 -mfma -mbmi -mbmi2 -mlzcnt ;
cpu-flags gcc OPTIONS : x86 : broadwell : -march=broadwell ;
cpu-flags gcc OPTIONS : x86 : skylake : -march=skylake ;
cpu-flags gcc OPTIONS : x86 : skylake-avx512 : -march=skylake-avx512 ;
cpu-flags gcc OPTIONS : x86 : cannonlake : -march=skylake-avx512 -mavx512vbmi -mavx512ifma -msha ;
cpu-flags gcc OPTIONS : x86 : icelake-client : -march=icelake-client ;
cpu-flags gcc OPTIONS : x86 : icelake-server : -march=icelake-server ;
cpu-flags gcc OPTIONS : x86 : cascadelake : -march=skylake-avx512 -mavx512vnni ;
cpu-flags gcc OPTIONS : x86 : cooperlake : -march=cooperlake ;
cpu-flags gcc OPTIONS : x86 : tigerlake : -march=tigerlake ;
cpu-flags gcc OPTIONS : x86 : rocketlake : -march=rocketlake ;
cpu-flags gcc OPTIONS : x86 : alderlake : -march=alderlake ;
cpu-flags gcc OPTIONS : x86 : sapphirerapids : -march=sapphirerapids ;
cpu-flags gcc OPTIONS : x86 : k6 : -march=k6 ;
cpu-flags gcc OPTIONS : x86 : k6-2 : -march=k6-2 ;
cpu-flags gcc OPTIONS : x86 : k6-3 : -march=k6-3 ;
cpu-flags gcc OPTIONS : x86 : athlon : -march=athlon ;
cpu-flags gcc OPTIONS : x86 : athlon-tbird : -march=athlon-tbird ;
cpu-flags gcc OPTIONS : x86 : athlon-4 : -march=athlon-4 ;
cpu-flags gcc OPTIONS : x86 : athlon-xp : -march=athlon-xp ;
cpu-flags gcc OPTIONS : x86 : athlon-mp : -march=athlon-mp ;
##
cpu-flags gcc OPTIONS : x86 : k8 : -march=k8 ;
cpu-flags gcc OPTIONS : x86 : opteron : -march=opteron ;
cpu-flags gcc OPTIONS : x86 : athlon64 : -march=athlon64 ;
cpu-flags gcc OPTIONS : x86 : athlon-fx : -march=athlon-fx ;
cpu-flags gcc OPTIONS : x86 : k8-sse3 : -march=k8-sse3 ;
cpu-flags gcc OPTIONS : x86 : opteron-sse3 : -march=opteron-sse3 ;
cpu-flags gcc OPTIONS : x86 : athlon64-sse3 : -march=athlon64-sse3 ;
cpu-flags gcc OPTIONS : x86 : amdfam10 : -march=amdfam10 ;
cpu-flags gcc OPTIONS : x86 : barcelona : -march=barcelona ;
cpu-flags gcc OPTIONS : x86 : bdver1 : -march=bdver1 ;
cpu-flags gcc OPTIONS : x86 : bdver2 : -march=bdver2 ;
cpu-flags gcc OPTIONS : x86 : bdver3 : -march=bdver3 ;
cpu-flags gcc OPTIONS : x86 : bdver4 : -march=bdver4 ;
cpu-flags gcc OPTIONS : x86 : btver1 : -march=btver1 ;
cpu-flags gcc OPTIONS : x86 : btver2 : -march=btver2 ;
cpu-flags gcc OPTIONS : x86 : znver1 : -march=znver1 ;
cpu-flags gcc OPTIONS : x86 : znver2 : -march=znver2 ;
cpu-flags gcc OPTIONS : x86 : znver3 : -march=znver3 ;
cpu-flags gcc OPTIONS : x86 : winchip-c6 : -march=winchip-c6 ;
cpu-flags gcc OPTIONS : x86 : winchip2 : -march=winchip2 ;
cpu-flags gcc OPTIONS : x86 : c3 : -march=c3 ;
cpu-flags gcc OPTIONS : x86 : c3-2 : -march=c3-2 ;
cpu-flags gcc OPTIONS : x86 : c7 : -march=c7 ;
##
cpu-flags gcc OPTIONS : x86 : atom : -march=atom ;
# Sparc
cpu-flags gcc OPTIONS : sparc : v7 : -mcpu=v7 : default ;
cpu-flags gcc OPTIONS : sparc : cypress : -mcpu=cypress ;
cpu-flags gcc OPTIONS : sparc : v8 : -mcpu=v8 ;
cpu-flags gcc OPTIONS : sparc : supersparc : -mcpu=supersparc ;
cpu-flags gcc OPTIONS : sparc : sparclite : -mcpu=sparclite ;
cpu-flags gcc OPTIONS : sparc : hypersparc : -mcpu=hypersparc ;
cpu-flags gcc OPTIONS : sparc : sparclite86x : -mcpu=sparclite86x ;
cpu-flags gcc OPTIONS : sparc : f930 : -mcpu=f930 ;
cpu-flags gcc OPTIONS : sparc : f934 : -mcpu=f934 ;
cpu-flags gcc OPTIONS : sparc : sparclet : -mcpu=sparclet ;
cpu-flags gcc OPTIONS : sparc : tsc701 : -mcpu=tsc701 ;
cpu-flags gcc OPTIONS : sparc : v9 : -mcpu=v9 ;
cpu-flags gcc OPTIONS : sparc : ultrasparc : -mcpu=ultrasparc ;
cpu-flags gcc OPTIONS : sparc : ultrasparc3 : -mcpu=ultrasparc3 ;
# RS/6000 & PowerPC
cpu-flags gcc OPTIONS : power : 403 : -mcpu=403 ;
cpu-flags gcc OPTIONS : power : 505 : -mcpu=505 ;
cpu-flags gcc OPTIONS : power : 601 : -mcpu=601 ;
cpu-flags gcc OPTIONS : power : 602 : -mcpu=602 ;
cpu-flags gcc OPTIONS : power : 603 : -mcpu=603 ;
cpu-flags gcc OPTIONS : power : 603e : -mcpu=603e ;
cpu-flags gcc OPTIONS : power : 604 : -mcpu=604 ;
cpu-flags gcc OPTIONS : power : 604e : -mcpu=604e ;
cpu-flags gcc OPTIONS : power : 620 : -mcpu=620 ;
cpu-flags gcc OPTIONS : power : 630 : -mcpu=630 ;
cpu-flags gcc OPTIONS : power : 740 : -mcpu=740 ;
cpu-flags gcc OPTIONS : power : 7400 : -mcpu=7400 ;
cpu-flags gcc OPTIONS : power : 7450 : -mcpu=7450 ;
cpu-flags gcc OPTIONS : power : 750 : -mcpu=750 ;
cpu-flags gcc OPTIONS : power : 801 : -mcpu=801 ;
cpu-flags gcc OPTIONS : power : 821 : -mcpu=821 ;
cpu-flags gcc OPTIONS : power : 823 : -mcpu=823 ;
cpu-flags gcc OPTIONS : power : 860 : -mcpu=860 ;
cpu-flags gcc OPTIONS : power : 970 : -mcpu=970 ;
cpu-flags gcc OPTIONS : power : 8540 : -mcpu=8540 ;
cpu-flags gcc OPTIONS : power : power : -mcpu=power ;
cpu-flags gcc OPTIONS : power : power2 : -mcpu=power2 ;
cpu-flags gcc OPTIONS : power : power3 : -mcpu=power3 ;
cpu-flags gcc OPTIONS : power : power4 : -mcpu=power4 ;
cpu-flags gcc OPTIONS : power : power5 : -mcpu=power5 ;
cpu-flags gcc OPTIONS : power : powerpc : -mcpu=powerpc ;
cpu-flags gcc OPTIONS : power : powerpc64 : -mcpu=powerpc64 ;
cpu-flags gcc OPTIONS : power : rios : -mcpu=rios ;
cpu-flags gcc OPTIONS : power : rios1 : -mcpu=rios1 ;
cpu-flags gcc OPTIONS : power : rios2 : -mcpu=rios2 ;
cpu-flags gcc OPTIONS : power : rsc : -mcpu=rsc ;
cpu-flags gcc OPTIONS : power : rs64a : -mcpu=rs64 ;
cpu-flags gcc OPTIONS : s390x : z196 : -march=z196 ;
cpu-flags gcc OPTIONS : s390x : zEC12 : -march=zEC12 ;
cpu-flags gcc OPTIONS : s390x : z13 : -march=z13 ;
cpu-flags gcc OPTIONS : s390x : z14 : -march=z14 ;
cpu-flags gcc OPTIONS : s390x : z15 : -march=z15 ;
# ARM
cpu-flags gcc OPTIONS : arm : cortex-a9+vfpv3 : -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=hard ;
cpu-flags gcc OPTIONS : arm : cortex-a53 : -mcpu=cortex-a53 ;
cpu-flags gcc OPTIONS : arm : cortex-r5 : -mcpu=cortex-r5 ;
cpu-flags gcc OPTIONS : arm : cortex-r5+vfpv3-d16 : -mcpu=cortex-r5 -mfpu=vfpv3-d16 -mfloat-abi=hard ;
# AIX variant of RS/6000 & PowerPC
toolset.flags gcc AROPTIONS <address-model>64/<target-os>aix : "-X64" ;

# Enable response file control
toolset.flags gcc RESPONSE_FILE_SUB <response-file>auto : a ;
toolset.flags gcc RESPONSE_FILE_SUB <response-file>file : f ;
toolset.flags gcc RESPONSE_FILE_SUB <response-file>contents : c ;
