blob: ce212212fd2714a8f240d53cd6e5ce7ebb34f114 [file]
# Copyright 2025 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
All build variables we create for various `CppCompileAction`s
"""
load("//cc/common:cc_helper_internal.bzl", "extensions", _PRIVATE_STARLARKIFICATION_ALLOWLIST = "PRIVATE_STARLARKIFICATION_ALLOWLIST")
load("//cc/private:cc_internal.bzl", _cc_internal = "cc_internal")
load("//cc/private/rules_impl:native_cc_common.bzl", _cc_common_internal = "native_cc_common")
# deliberately short name for less clutter while using in this file, we can have
# a different symbol with a more descriptive name if we ever export this
_VARS = struct(
# Variable for the collection of framework include paths.
FRAMEWORK_PATHS = "framework_include_paths",
# Variable for the path to the source file being compiled.
SOURCE_FILE = "source_file",
# Variable for all flags coming from copt rule attribute, and from --copt,
# --cxxopt, or --conlyopt options.
USER_COMPILE_FLAGS = "user_compile_flags",
# Variable for the path to the compilation output file.
OUTPUT_FILE = "output_file",
# Variable for the dependency file path
DEPENDENCY_FILE = "dependency_file",
# Variable for the serialized diagnostics file path
SERIALIZED_DIAGNOSTICS_FILE = "serialized_diagnostics_file",
# Variable for the module file name.
MODULE_NAME = "module_name",
# Variable for the collection of include paths.
INCLUDE_PATHS = "include_paths",
# Variable for the collection of quote include paths.
QUOTE_INCLUDE_PATHS = "quote_include_paths",
# Variable for the collection of system include paths.
SYSTEM_INCLUDE_PATHS = "system_include_paths",
# Variable for the collection of external include paths.
EXTERNAL_INCLUDE_PATHS = "external_include_paths",
# Variable for the module map file name.
MODULE_MAP_FILE = "module_map_file",
# Variable for the dependent module map file name.
DEPENDENT_MODULE_MAP_FILES = "dependent_module_map_files",
# Variable for the collection of module files.
MODULE_FILES = "module_files",
# Variable for the collection of macros defined for preprocessor. */
PREPROCESSOR_DEFINES = "preprocessor_defines",
# Variable for the gcov coverage file path.
GCOV_GCNO_FILE = "gcov_gcno_file",
# Variable for the minimized LTO indexing bitcode file, used by the LTO
# indexing action. This file was generated by CppCompile actions. For
# efficiency, it contains minimal information that is required by the LTO
# indexing action.
LTO_INDEXING_BITCODE_FILE = "lto_indexing_bitcode_file",
# Variable for the LTO index file, used by the LTO backend action. This file
# was generated by the LTO indexing action.
THINLTO_INDEX = "thinlto_index",
# Variable for the bitcode file that is input to LTO backend.
THINLTO_INPUT_BITCODE_FILE = "thinlto_input_bitcode_file",
# Variable for the object file that is output by LTO backend.
THINLTO_OUTPUT_OBJECT_FILE = "thinlto_output_object_file",
# Variable marking fission is used.
IS_USING_FISSION = "is_using_fission",
# Variable for the per object debug info file.
PER_OBJECT_DEBUG_INFO_FILE = "per_object_debug_info_file",
# Variable present when the output is compiled as position independent.
PIC = "pic",
# Variable marking memprof profile is being used
IS_USING_MEMPROF = "is_using_memprof",
# Variable for includes that compiler needs to include into sources.
INCLUDES = "includes",
)
# buildifier: disable=name-conventions
_UnboundValueProviderDoNotUse = provider("This provider is used as an unique symbol to distinguish between bound and unbound Starlark values, to avoid using kwargs.", fields = [])
_UNBOUND = _UnboundValueProviderDoNotUse()
# IMPORTANT: This function is public API exposed on cc_common module!
def create_compile_variables(
*,
cc_toolchain,
feature_configuration,
source_file = None,
output_file = None,
user_compile_flags = None,
includes = None,
include_directories = None,
quote_include_directories = None,
system_include_directories = None,
framework_include_directories = None,
preprocessor_defines = None,
thinlto_index = None,
thinlto_input_bitcode_file = None,
thinlto_output_object_file = None,
use_pic = False,
# TODO(b/65151735): Remove once we migrate crosstools to features
add_legacy_cxx_options = False, # buildifier: disable=unused-variable
variables_extension = {},
strip_opts = _UNBOUND,
input_file = _UNBOUND):
"""Returns variables used for compilation actions.
Args:
cc_toolchain: cc_toolchain for which we are creating build variables.
feature_configuration: Feature configuration to be queried.
source_file: Optional source file path for the compilation. Please prefer passing source_file
here over appending it to the end of the command line generated from
cc_common.get_memory_inefficient_command_line, as then it's in the power of
the toolchain author to properly specify and position compiler flags.
output_file: Optional output file path of the compilation. Please prefer passing output_file
here over appending it to the end of the command line generated from
cc_common.get_memory_inefficient_command_line, as then it's in the power of
the toolchain author to properly specify and position compiler flags.
includes: paths to headers that should be included using -include
user_compile_flags: List of additional compilation flags (copts).
include_directories: Depset of include directories.
quote_include_directories: Depset of quote include directories.
system_include_directories: Depset of system include directories.
framework_include_directories: Depset of framework include directories.
preprocessor_defines: Depset of preprocessor defines.
thinlto_index: LTO index file path.
thinlto_input_bitcode_file: Bitcode file that is input to LTO backend.
thinlto_output_object_file: Object file that is output by LTO backend.
use_pic: When true the compilation will generate position independent code.
add_legacy_cxx_options: Unused.
variables_extension: A dictionary of additional variables used by compile actions.
strip_opts: (Private API)
input_file: (Private API)
Returns:
(CcToolchainVariables) common compile build variables
"""
if strip_opts != _UNBOUND or input_file != _UNBOUND:
_cc_internal.check_private_api(allowlist = _PRIVATE_STARLARKIFICATION_ALLOWLIST)
if strip_opts == _UNBOUND:
strip_opts = []
if input_file == _UNBOUND:
input_file = None
if (use_pic and not feature_configuration.is_enabled("pic") and not feature_configuration.is_enabled("supports_pic")):
fail("PIC compilation is requested but the toolchain does not support it " +
"(feature named 'supports_pic' is not enabled)")
cpp_configuration = cc_toolchain._cpp_configuration
fdo_context = cc_toolchain._fdo_context
common_vars = _setup_common_compile_build_variables_internal(
feature_configuration = feature_configuration,
is_using_memprof = getattr(fdo_context, "memprof_profile_artifact", None) != None,
fdo_build_stamp = _get_fdo_build_stamp(cpp_configuration, fdo_context, feature_configuration),
variables_extension = variables_extension,
includes = includes or [],
include_dirs = include_directories or depset(),
quote_include_dirs = quote_include_directories or depset(),
system_include_dirs = system_include_directories or depset(),
framework_include_dirs = framework_include_directories or depset(),
defines = preprocessor_defines or depset(),
)
additional_build_variables = {}
additional_build_variables["stripopts"] = strip_opts
if input_file:
additional_build_variables["input_file"] = input_file
variables = get_specific_compile_build_variables(
feature_configuration,
use_pic = use_pic,
source_file = source_file,
output_file = output_file,
thinlto_index = thinlto_index,
thinlto_bitcode_file = thinlto_input_bitcode_file,
thinlto_output_object_file = thinlto_output_object_file,
additional_build_variables = additional_build_variables,
user_compile_flags = user_compile_flags or [],
)
return _cc_internal.combine_cc_toolchain_variables(cc_toolchain._build_variables, common_vars, variables)
# buildifier: disable=function-docstring
def setup_common_compile_build_variables(
*,
cc_compilation_context,
cc_toolchain,
cpp_configuration,
fdo_context,
feature_configuration,
variables_extension):
common_vars = _setup_common_compile_build_variables_internal(
feature_configuration = feature_configuration,
is_using_memprof = getattr(fdo_context, "memprof_profile_artifact", None) != None,
fdo_build_stamp = _get_fdo_build_stamp(cpp_configuration, fdo_context, feature_configuration),
variables_extension = variables_extension,
include_dirs = cc_compilation_context.includes,
quote_include_dirs = cc_compilation_context.quote_includes,
system_include_dirs = cc_compilation_context.system_includes,
framework_include_dirs = cc_compilation_context.framework_includes,
defines = cc_compilation_context.defines,
local_defines = cc_compilation_context.local_defines,
external_include_dirs = cc_compilation_context.external_includes,
)
return _cc_internal.combine_cc_toolchain_variables(cc_toolchain._build_variables, common_vars)
def _setup_common_compile_build_variables_internal(
*,
feature_configuration,
fdo_build_stamp, # str
is_using_memprof, # bool
includes = [], # [str]
variables_extension = [], # [dict{str,object}]
additional_build_variables = {}, # dict{str,str}
include_dirs = depset(),
quote_include_dirs = depset(),
system_include_dirs = depset(),
framework_include_dirs = depset(),
defines = depset(),
local_defines = depset(),
external_include_dirs = depset()):
result = {}
if feature_configuration.is_enabled("use_header_modules"):
result[_VARS.MODULE_FILES] = []
result[_VARS.INCLUDE_PATHS] = include_dirs
result[_VARS.QUOTE_INCLUDE_PATHS] = quote_include_dirs
result[_VARS.SYSTEM_INCLUDE_PATHS] = system_include_dirs
if includes:
result[_VARS.INCLUDES] = _cc_internal.intern_string_sequence_variable_value(includes)
result[_VARS.FRAMEWORK_PATHS] = framework_include_dirs
if is_using_memprof:
result[_VARS.IS_USING_MEMPROF] = "1"
# Stamp FDO builds with FDO subtype string
all_defines = defines.to_list() + local_defines.to_list() + (
["BUILD_FDO_TYPE=\"" + fdo_build_stamp + "\""] if fdo_build_stamp else []
)
result[_VARS.PREPROCESSOR_DEFINES] = _cc_internal.intern_string_sequence_variable_value(all_defines)
result = result | additional_build_variables
for key, value in variables_extension.items():
if type(value) == type([]):
result[key] = _cc_internal.intern_string_sequence_variable_value(value)
elif type(value) == type(""):
result[key] = value
elif type(value) == type(depset()):
for e in value.to_list():
if type(e) != type(""):
fail("for string_sequence_variables_extension, got element of type " + type(e) + ", want string")
result[key] = value
else:
fail("for variable extension key:" + key + ", got element of type " + type(value) + ", want string")
if external_include_dirs:
result[_VARS.EXTERNAL_INCLUDE_PATHS] = external_include_dirs
return _cc_internal.cc_toolchain_variables(vars = result)
def _get_fdo_build_stamp(cpp_configuration, fdo_context, feature_configuration):
branch_fdo_profile = getattr(fdo_context, "branch_fdo_profile", None)
if branch_fdo_profile:
branch_fdo_mode = branch_fdo_profile.branch_fdo_mode
if branch_fdo_mode == "auto_fdo":
return "AFDO" if feature_configuration.is_enabled("autofdo") else None
if branch_fdo_mode == "xbinary_fdo":
return "XFDO" if feature_configuration.is_enabled("xbinaryfdo") else None
if branch_fdo_mode == "llvm_cs_fdo" or cpp_configuration.cs_fdo_instrument():
return "CSFDO"
if branch_fdo_profile or cpp_configuration.fdo_instrument():
return "FDO"
return None
# Note: this method is side-effect free, callers should add fdo inputs to
# cc_compile_action_builder themselves
def get_specific_compile_build_variables(
feature_configuration,
use_pic,
source_file = None,
output_file = None,
dotd_file = None,
diagnostics_file = None,
gcno_file = None,
dwo_file = None,
lto_indexing_file = None,
thinlto_index = None,
thinlto_bitcode_file = None,
thinlto_output_object_file = None,
using_fission = False,
code_coverage_enabled = False,
cpp_module_map = None,
direct_module_maps = depset(),
user_compile_flags = [],
additional_build_variables = {},
fdo_build_variables = {}):
"""Creates a CcToolchainVariables instance
Args:
feature_configuration: (FeatureConfiguration)
use_pic: (bool)
source_file: (File)
output_file: (File)
dotd_file: (File)
diagnostics_file: (File)
gcno_file: (File)
dwo_file: (File)
lto_indexing_file: (File)
thinlto_index: (File)
thinlto_bitcode_file: (File)
thinlto_output_object_file: (File)
using_fission: (bool)
code_coverage_enabled: (bool)
cpp_module_map: (File)
direct_module_maps: (depset[File])
user_compile_flags: (list[str])
additional_build_variables: (dict{str,str})
fdo_build_variables: (dict{str,str})
Returns:
(Variables)
"""
result = {}
if feature_configuration.is_enabled("module_maps") and cpp_module_map:
result[_VARS.MODULE_NAME] = cpp_module_map.name
result[_VARS.MODULE_MAP_FILE] = cpp_module_map.file
result[_VARS.DEPENDENT_MODULE_MAP_FILES] = direct_module_maps
result[_VARS.USER_COMPILE_FLAGS] = _cc_internal.intern_string_sequence_variable_value(user_compile_flags)
if source_file:
result[_VARS.SOURCE_FILE] = source_file
if output_file:
result[_VARS.OUTPUT_FILE] = output_file
# Set dependency_file to enable <object>.d file generation.
if dotd_file:
result[_VARS.DEPENDENCY_FILE] = dotd_file
# Set diagnostics_file to enable <object>.dia file generation.
if diagnostics_file:
result[_VARS.SERIALIZED_DIAGNOSTICS_FILE] = diagnostics_file
if gcno_file:
result[_VARS.GCOV_GCNO_FILE] = gcno_file
elif code_coverage_enabled:
# TODO: Blaze currently uses `gcov_gcno_file` to detect if code coverage
# is enabled. It should use a different signal.
result[_VARS.GCOV_GCNO_FILE] = ""
if dwo_file:
result[_VARS.PER_OBJECT_DEBUG_INFO_FILE] = dwo_file
if using_fission:
result[_VARS.IS_USING_FISSION] = ""
if lto_indexing_file:
result[_VARS.LTO_INDEXING_BITCODE_FILE] = lto_indexing_file
if thinlto_index:
result[_VARS.THINLTO_INDEX] = thinlto_index
if thinlto_bitcode_file:
result[_VARS.THINLTO_INPUT_BITCODE_FILE] = thinlto_bitcode_file
if thinlto_output_object_file:
result[_VARS.THINLTO_OUTPUT_OBJECT_FILE] = thinlto_output_object_file
if use_pic:
result[_VARS.PIC] = ""
result = result | additional_build_variables
result = result | fdo_build_variables
return _cc_internal.cc_toolchain_variables(vars = result)
_SOURCE_TYPES_FOR_CXXOPTS = set(
extensions.CC_SOURCE +
extensions.CC_HEADER +
extensions.CLIF_INPUT_PROTO +
extensions.CPP_MODULE_MAP +
extensions.OBJCPP_SOURCE,
)
# buildifier: disable=function-docstring
def get_copts(
language,
cpp_configuration,
source_file,
conlyopts,
copts,
cxxopts,
label,
override_extension = None):
if override_extension != None:
extension = override_extension
elif source_file.extension:
extension = "." + source_file.extension
else:
extension = ""
result = []
result.extend(_copts_from_options(language, cpp_configuration, extension))
result.extend(copts)
if extension in extensions.C_SOURCE:
result.extend(conlyopts)
if extension in _SOURCE_TYPES_FOR_CXXOPTS:
result.extend(cxxopts)
if label:
result.extend(_cc_internal.per_file_copts(cpp_configuration, source_file, label))
return result
def _copts_from_options(language, cpp_configuration, extension):
result = []
result.extend(cpp_configuration.copts)
if extension in extensions.C_SOURCE:
result.extend(cpp_configuration.conlyopts)
if extension in _SOURCE_TYPES_FOR_CXXOPTS:
result.extend(cpp_configuration.cxxopts)
if extension in extensions.OBJC_SOURCE or extension in extensions.OBJCPP_SOURCE or (
language == "objc" and extension in extensions.CC_HEADER
):
result.extend(cpp_configuration.objccopts)
return result
def get_fdo_variables_and_inputs(
cc_toolchain,
fdo_context,
feature_configuration,
cpp_configuration):
auxiliary_fdo_inputs_list = _get_auxiliary_fdo_inputs_list(
cc_toolchain = cc_toolchain,
fdo_context = fdo_context,
feature_configuration = feature_configuration,
)
fdo_build_variables = _setup_fdo_build_variables(
cc_toolchain = cc_toolchain,
fdo_context = fdo_context,
auxiliary_fdo_inputs_list = auxiliary_fdo_inputs_list,
feature_configuration = feature_configuration,
fdo_instrument = cpp_configuration.fdo_instrument(),
cs_fdo_instrument = cpp_configuration.cs_fdo_instrument(),
)
# TODO(b/396122076): once starlarkification of CcStaticCompilationHelper is done, check whether
# we can change auxiliary_fdo_inputs from depset to list.
auxiliary_fdo_inputs = depset(direct = auxiliary_fdo_inputs_list)
return fdo_build_variables, auxiliary_fdo_inputs
def _setup_fdo_build_variables(
cc_toolchain,
fdo_context,
auxiliary_fdo_inputs_list,
feature_configuration,
fdo_instrument,
cs_fdo_instrument):
"""Populates FDO build variables."""
variables = {}
if feature_configuration.is_enabled("fdo_instrument"):
variables["fdo_instrument_path"] = fdo_instrument
if feature_configuration.is_enabled("cs_fdo_instrument"):
variables["cs_fdo_instrument_path"] = cs_fdo_instrument
if not (getattr(fdo_context, "branch_fdo_profile", None) or
getattr(fdo_context, "prefetch_hints_artifact", None) or
getattr(fdo_context, "propeller_optimize_info", None) or
getattr(fdo_context, "memprof_profile_artifact", None)):
return variables
prefetch_hints_artifact = getattr(fdo_context, "prefetch_hints_artifact", None)
if prefetch_hints_artifact:
variables["fdo_prefetch_hints_path"] = prefetch_hints_artifact.path
if _should_pass_propeller_profiles(cc_toolchain, fdo_context, feature_configuration):
# _should_pass_propeller_profiles() ensures that fdo_context.propeller_optimize_info
# is not None.
cc_artifact = getattr(fdo_context.propeller_optimize_info, "cc_profile", None)
if cc_artifact:
variables["propeller_optimize_cc_path"] = cc_artifact.path
ld_artifact = getattr(fdo_context.propeller_optimize_info, "ld_profile", None)
if ld_artifact:
variables["propeller_optimize_ld_path"] = ld_artifact.path
memprof_profile_artifact = getattr(fdo_context, "memprof_profile_artifact", None)
if memprof_profile_artifact:
variables["memprof_profile_path"] = memprof_profile_artifact.path
branch_fdo_profile = getattr(fdo_context, "branch_fdo_profile", None)
if (branch_fdo_profile and
auxiliary_fdo_inputs_list and
(
feature_configuration.is_enabled("autofdo") or
feature_configuration.is_enabled("xbinaryfdo") or
(
feature_configuration.is_enabled("fdo_optimize") and
branch_fdo_profile.branch_fdo_mode in ("llvm_fdo", "llvm_cs_fdo")
)
)):
variables["fdo_profile_path"] = branch_fdo_profile.profile_artifact.path
return variables
def _get_auxiliary_fdo_inputs_list(
cc_toolchain,
fdo_context,
feature_configuration):
"""Returns the auxiliary files that need to be added to CppCompileAction."""
auxiliary_inputs = []
prefetch_hints_artifact = getattr(fdo_context, "prefetch_hints_artifact", None)
if prefetch_hints_artifact:
auxiliary_inputs.append(prefetch_hints_artifact)
if _should_pass_propeller_profiles(cc_toolchain, fdo_context, feature_configuration):
# _should_pass_propeller_profiles() ensures that fdo_context.propeller_optimize_info
# is not None.
cc_artifact = getattr(fdo_context.propeller_optimize_info, "cc_profile", None)
if cc_artifact:
auxiliary_inputs.append(cc_artifact)
ld_artifact = getattr(fdo_context.propeller_optimize_info, "ld_profile", None)
if ld_artifact:
auxiliary_inputs.append(ld_artifact)
memprof_profile_artifact = getattr(fdo_context, "memprof_profile_artifact", None)
if memprof_profile_artifact:
auxiliary_inputs.append(memprof_profile_artifact)
branch_fdo_profile = getattr(fdo_context, "branch_fdo_profile", None)
# If --fdo_optimize was not specified, we don't have any additional inputs.
if branch_fdo_profile:
auxiliary_inputs.append(branch_fdo_profile.profile_artifact)
return auxiliary_inputs
def _should_pass_propeller_profiles(
cc_toolchain,
fdo_context,
feature_configuration):
"""Returns whether Propeller profiles should be passed to a compile action."""
if cc_toolchain._is_tool_configuration:
# Propeller doesn't make much sense for host builds.
return False
if getattr(fdo_context, "propeller_optimize_info", None) == None:
# No Propeller profiles to pass.
return False
# Don't pass Propeller input files if they have no effect (i.e. for ThinLTO).
return (not feature_configuration.is_enabled("thin_lto") or
feature_configuration.is_enabled("propeller_optimize_thinlto_compile_actions"))
def get_linkstamp_compile_variables(
source_file,
output_file,
label_replacement,
output_replacement,
additional_linkstamp_defines,
build_info_header_artifacts,
feature_configuration,
cc_toolchain,
needs_pic):
"""Returns variables for linkstamp compilation.
Args:
source_file: The linkstamp source file to be compiled.
output_file: The output object file.
label_replacement: String to replace ${LABEL} in linkstamp defines.
output_replacement: String to replace ${OUTPUT_PATH} in linkstamp defines.
additional_linkstamp_defines: A list of additional defines for linkstamp compilation.
build_info_header_artifacts: A list of build info header artifacts.
feature_configuration: The feature configuration.
cc_toolchain: The C++ toolchain provider.
needs_pic: Whether PIC compilation is needed.
Returns:
CcToolchainVariables for linkstamp compilation.
"""
if not _cc_common_internal.action_is_enabled(
feature_configuration = feature_configuration,
action_name = "linkstamp-compile",
):
fail("Action 'linkstamp-compile' is not configured.")
fdo_build_stamp = _get_fdo_build_stamp(
cc_toolchain._cpp_configuration,
cc_toolchain._fdo_context,
feature_configuration,
)
code_coverage_enabled = feature_configuration.is_enabled("coverage")
copts = get_copts(
language = "c++", # The only language that receives special treatment is "objc".
cpp_configuration = cc_toolchain._cpp_configuration,
source_file = source_file,
conlyopts = [],
copts = [],
cxxopts = [],
label = None,
)
defines = _compute_all_linkstamp_defines(
label_replacement = label_replacement,
output_replacement = output_replacement,
additional_linkstamp_defines = additional_linkstamp_defines,
cc_toolchain = cc_toolchain,
fdo_build_stamp = fdo_build_stamp,
code_coverage_enabled = code_coverage_enabled,
)
return create_compile_variables(
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
source_file = source_file,
output_file = output_file,
use_pic = needs_pic,
preprocessor_defines = depset(defines),
includes = [a.path for a in build_info_header_artifacts],
include_directories = depset(["."]),
user_compile_flags = copts,
)
def _compute_all_linkstamp_defines(
label_replacement,
output_replacement,
additional_linkstamp_defines,
cc_toolchain,
fdo_build_stamp,
code_coverage_enabled):
"""Computes defines for linkstamp compilation."""
defines = [
'GPLATFORM="' + cc_toolchain.toolchain_id + '"',
"BUILD_COVERAGE_ENABLED=" + ("1" if code_coverage_enabled else "0"),
# G3_TARGET_NAME is a C string literal that normally contain the label of the target
# being linked. However, they are set differently when using shared native deps. In
# that case, a single .so file is shared by multiple targets, and its contents cannot
# depend on which target(s) were specified on the command line. So in that case we
# have to use the (obscure) name of the .so file instead, or more precisely the path of
# the .so file relative to the workspace root.
'G3_TARGET_NAME="${LABEL}"',
# G3_BUILD_TARGET is a C string literal containing the output of this
# link. (An undocumented and untested invariant is that G3_BUILD_TARGET is the
# location of the executable, either absolutely, or relative to the directory part of
# BUILD_INFO.)
'G3_BUILD_TARGET="${OUTPUT_PATH}"',
]
if additional_linkstamp_defines:
defines.extend(additional_linkstamp_defines)
if fdo_build_stamp:
defines.append('BUILD_FDO_TYPE="' + fdo_build_stamp + '"')
return [
define.replace("${LABEL}", label_replacement).replace("${OUTPUT_PATH}", output_replacement)
for define in defines
]