# Utility functions for packaging an LLVM distribution. See the
# BuildingADistribution documentation for more details.
# These functions assume a number of conventions that are common across all LLVM
# subprojects:
# - The generated CMake exports file for ${project} is called ${project}Targets
# (except for LLVM where it's called ${project}Exports for legacy reasons).
# - The build target for the CMake exports is called ${project}-cmake-exports
# (except LLVM where it's just cmake-exports).
# - The ${PROJECT}${distribution}_HAS_EXPORTS global property holds whether a
# project has any exports for a particular ${distribution} (where ${PROJECT}
# is the project name in uppercase).
# - The ${PROJECT}_CMAKE_DIR variable is computed by ${project}Config.cmake to
# hold the path of the installed CMake modules directory.
# - The ${PROJECT}_INSTALL_PACKAGE_DIR variable contains the install destination
# for the project's CMake modules.
include_guard(GLOBAL)
if(LLVM_DISTRIBUTION_COMPONENTS
AND LLVM_DISTRIBUTIONS
)
message(FATAL_ERROR "LLVM_DISTRIBUTION_COMPONENTS and LLVM_DISTRIBUTIONS cannot be specified together")
if(LLVM_DISTRIBUTION_COMPONENTS
OR LLVM_DISTRIBUTIONS
)
message(FATAL_ERROR "LLVM_DISTRIBUTION_COMPONENTS cannot be specified with multi-configuration generators (i.e. Xcode or Visual Studio)")
# Build the map of targets to distributions that's used to look up the
# distribution for a target later. The distribution for ${target} is stored in
# the global property LLVM_DISTRIBUTION_FOR_${target}.
function(llvm_distribution_build_target_map
)
foreach(target
${LLVM_DISTRIBUTION_COMPONENTS})
# CMake doesn't easily distinguish between properties that are unset and
# properties that are empty (you have to do a second get_property call with
# the SET option, which is unergonomic), so just use a special marker to
# denote the default (unnamed) distribution.
set_property(GLOBAL PROPERTY LLVM_DISTRIBUTION_FOR_
${target} "<DEFAULT>")
foreach(distribution
${LLVM_DISTRIBUTIONS})
foreach(target
${LLVM_${distribution}_DISTRIBUTION_COMPONENTS}
)
# By default, we allow a target to be in multiple distributions, and use
# the last one to determine its export set. We disallow this in strict
# mode, emitting a single error at the end for readability.
if(LLVM_STRICT_DISTRIBUTIONS
)
get_property(current_distribution
GLOBAL PROPERTY LLVM_DISTRIBUTION_FOR_
${target})
if(current_distribution
AND NOT current_distribution
STREQUAL distribution
)
set_property(GLOBAL APPEND_STRING
PROPERTY LLVM_DISTRIBUTION_ERRORS
"Target ${target} cannot be in multiple distributions \
${distribution} and ${current_distribution}\n"
)
set_property(GLOBAL PROPERTY LLVM_DISTRIBUTION_FOR_
${target} ${distribution})
# The include guard ensures this will only be called once. The rest of this file
# only defines other functions (i.e. it doesn't execute any more code directly).
llvm_distribution_build_target_map()
# Look up the distribution a particular target belongs to.
# - target: The target to look up.
# - in_distribution_var: The variable with this name is set in the caller's
# scope to indicate if the target is in any distribution. If no distributions
# have been configured, this will always be set to true.
# - distribution_var: The variable with this name is set in the caller's scope
# to indicate the distribution name for the target. If the target belongs to
# the default (unnamed) distribution, or if no distributions have been
# configured, it's set to the empty string.
# - UMBRELLA: The (optional) umbrella target that the target is a part of. For
# example, all LLVM libraries have the umbrella target llvm-libraries.
function(get_llvm_distribution target in_distribution_var distribution_var
)
if(NOT LLVM_DISTRIBUTION_COMPONENTS
AND NOT LLVM_DISTRIBUTIONS
)
set(${in_distribution_var} YES
PARENT_SCOPE)
set(${distribution_var} "" PARENT_SCOPE)
cmake_parse_arguments(ARG "" UMBRELLA "" ${ARGN})
get_property(distribution
GLOBAL PROPERTY LLVM_DISTRIBUTION_FOR_
${target})
get_property(umbrella_distribution
GLOBAL PROPERTY LLVM_DISTRIBUTION_FOR_
${ARG_UMBRELLA})
if(LLVM_STRICT_DISTRIBUTIONS
AND distribution
AND umbrella_distribution
AND
NOT distribution STREQUAL umbrella_distribution)
set_property(GLOBAL APPEND_STRING
PROPERTY LLVM_DISTRIBUTION_ERRORS
"Target ${target} has different distribution ${distribution} from its \
umbrella target's (${ARG_UMBRELLA}) distribution ${umbrella_distribution}\n"
)
set(distribution
${umbrella_distribution})
set(${in_distribution_var} YES
PARENT_SCOPE)
if(distribution
STREQUAL "<DEFAULT>")
set(${distribution_var} "${distribution}" PARENT_SCOPE)
set(${in_distribution_var} NO
PARENT_SCOPE)
# Get the EXPORT argument to use for an install command for a target in a
# project. As explained at the top of the file, the project export set for a
# distribution is named ${project}{distribution}Targets (except for LLVM where
# it's named ${project}{distribution}Exports for legacy reasons). Also set the
# ${PROJECT}_${DISTRIBUTION}_HAS_EXPORTS global property to mark the project as
# having exports for the distribution.
# - target: The target to get the EXPORT argument for.
# - project: The project to produce the argument for. IMPORTANT: The casing of
# this argument should match the casing used by the project's Config.cmake
# file. The correct casing for the LLVM projects is Clang, Flang, LLD, LLVM,
# and MLIR.
# - export_arg_var The variable with this name is set in the caller's scope to
# the EXPORT argument for the target for the project.
# - UMBRELLA: The (optional) umbrella target that the target is a part of. For
# example, all LLVM libraries have the umbrella target llvm-libraries.
function(get_target_export_arg target project export_arg_var
)
string(TOUPPER "${project}" project_upper
)
if(project
STREQUAL "LLVM")
set(suffix
"Exports") # legacy
get_llvm_distribution(${target} in_distribution distribution ${ARGN})
set(${export_arg_var} EXPORT
${project}${distribution}${suffix} PARENT_SCOPE)
string(TOUPPER "${distribution}" distribution_upper
)
set_property(GLOBAL PROPERTY ${project_upper}_
${distribution_upper}_HAS_EXPORTS True
)
set_property(GLOBAL PROPERTY ${project_upper}_HAS_EXPORTS True
)
set(${export_arg_var} "" PARENT_SCOPE)
# Produce a string of CMake include() commands to include the exported targets
# files for all distributions. See the comment at the top of this file for
# various assumptions made.
# - project: The project to produce the commands for. IMPORTANT: See the comment
# for get_target_export_arg above for the correct casing of this argument.
# - includes_var: The variable with this name is set in the caller's scope to
# the string of include commands.
function(get_config_exports_includes project includes_var
)
string(TOUPPER "${project}" project_upper
)
set(prefix
"\${${project_upper}_CMAKE_DIR}/${project}")
if(project
STREQUAL "LLVM")
set(suffix
"Exports.cmake") # legacy
set(suffix
"Targets.cmake")
if(NOT LLVM_DISTRIBUTIONS
)
set(${includes_var} "include(\"${prefix}${suffix}\")" PARENT_SCOPE)
foreach(distribution
${LLVM_DISTRIBUTIONS})
list(APPEND includes
"include(\"${prefix}${distribution}${suffix}\" OPTIONAL)")
string(REPLACE ";" "\n" includes
"${includes}")
set(${includes_var} "${includes}" PARENT_SCOPE)
# Create the install commands and targets for the distributions' CMake exports.
# The target to install ${distribution} for a project is called
# ${project}-${distribution}-cmake-exports, where ${project} is the project name
# in lowercase and ${distribution} is the distribution name in lowercase, except
# for LLVM, where the target is just called ${distribution}-cmake-exports. See
# the comment at the top of this file for various assumptions made.
# - project: The project. IMPORTANT: See the comment for get_target_export_arg
# above for the correct casing of this argument.
function(install_distribution_exports project
)
string(TOUPPER "${project}" project_upper
)
string(TOLOWER "${project}" project_lower
)
if(project
STREQUAL "LLVM")
set(suffix
"Exports") # legacy
set(prefix
"${project_lower}-")
set(destination
"${${project_upper}_INSTALL_PACKAGE_DIR}")
if(NOT LLVM_DISTRIBUTIONS
)
get_property(has_exports
GLOBAL PROPERTY ${project_upper}_HAS_EXPORTS
)
install(EXPORT
${project}${suffix} DESTINATION
"${destination}"
COMPONENT ${prefix}cmake-exports)
foreach(distribution
${LLVM_DISTRIBUTIONS})
string(TOUPPER "${distribution}" distribution_upper
)
get_property(has_exports
GLOBAL PROPERTY ${project_upper}_
${distribution_upper}_HAS_EXPORTS
)
string(TOLOWER "${distribution}" distribution_lower
)
set(target
${prefix}${distribution_lower}-cmake-exports
)
install(EXPORT
${project}${distribution}${suffix} DESTINATION
"${destination}"
COMPONENT ${target})
add_llvm_install_targets(install-${target} COMPONENT ${target})
# Create the targets for installing the configured distributions. The
# ${distribution} target builds the distribution, install-${distribution}
# installs it, and install-${distribution}-stripped installs a stripped version,
# where ${distribution} is the distribution name in lowercase, or "distribution"
# for the default distribution.
# This function is called towards the end of LLVM's CMakeLists.txt, so all
# errors will have been seen by now.
if(LLVM_STRICT_DISTRIBUTIONS
)
get_property(errors
GLOBAL PROPERTY LLVM_DISTRIBUTION_ERRORS
)
"Strict distribution errors (turn off LLVM_STRICT_DISTRIBUTIONS to bypass):\n"
)
set(distributions
"${LLVM_DISTRIBUTIONS}")
# CMake seemingly doesn't distinguish between an empty list and a list
# containing one element which is the empty string, so just use a special
# marker to denote the default (unnamed) distribution and fix it in the
# loop.
set(distributions
"<DEFAULT>")
get_property(LLVM_DRIVER_TOOL_SYMLINKS
GLOBAL PROPERTY LLVM_DRIVER_TOOL_SYMLINKS
)
foreach(distribution
${distributions})
if(distribution
STREQUAL "<DEFAULT>")
set(distribution_target distribution
)
# Preserve legacy behavior for LLVM_DISTRIBUTION_COMPONENTS.
set(distribution_components
${LLVM_DISTRIBUTION_COMPONENTS} ${LLVM_RUNTIME_DISTRIBUTION_COMPONENTS})
string(TOLOWER "${distribution}" distribution_lower
)
set(distribution_target
${distribution_lower}-distribution
)
set(distribution_components
${LLVM_${distribution}_DISTRIBUTION_COMPONENTS}
)
foreach(target
${distribution_components})
# Note that some distribution components may not have an actual target, but only an install-FOO target.
# This happens for example if a target is an INTERFACE target.
if(TARGET install-
${target})
elseif(TARGET install-llvm-driver
AND ${target} IN_LIST LLVM_DRIVER_TOOL_SYMLINKS
)
message(SEND_ERROR "Specified distribution component '${target}' doesn't have an install target")
if(TARGET install-
${target}-stripped
)
add_dependencies(install-
${distribution_target}-stripped install-
${target}-stripped
)
elseif(TARGET install-llvm-driver-stripped
AND ${target} IN_LIST LLVM_DRIVER_TOOL_SYMLINKS
)
add_dependencies(install-
${distribution_target}-stripped install-llvm-driver-stripped
)
"Specified distribution component '${target}' doesn't have an install-stripped target."
" Its installation target creation should be changed to use add_llvm_install_targets,"
" or you should manually create the 'install-${target}-stripped' target.")