302 lines · plain
1# Utility functions for packaging an LLVM distribution. See the2# BuildingADistribution documentation for more details.3 4# These functions assume a number of conventions that are common across all LLVM5# subprojects:6# - The generated CMake exports file for ${project} is called ${project}Targets7# (except for LLVM where it's called ${project}Exports for legacy reasons).8# - The build target for the CMake exports is called ${project}-cmake-exports9# (except LLVM where it's just cmake-exports).10# - The ${PROJECT}${distribution}_HAS_EXPORTS global property holds whether a11# project has any exports for a particular ${distribution} (where ${PROJECT}12# is the project name in uppercase).13# - The ${PROJECT}_CMAKE_DIR variable is computed by ${project}Config.cmake to14# hold the path of the installed CMake modules directory.15# - The ${PROJECT}_INSTALL_PACKAGE_DIR variable contains the install destination16# for the project's CMake modules.17 18include_guard(GLOBAL)19 20if(LLVM_DISTRIBUTION_COMPONENTS AND LLVM_DISTRIBUTIONS)21 message(FATAL_ERROR "LLVM_DISTRIBUTION_COMPONENTS and LLVM_DISTRIBUTIONS cannot be specified together")22endif()23 24if(LLVM_DISTRIBUTION_COMPONENTS OR LLVM_DISTRIBUTIONS)25 if(LLVM_ENABLE_IDE)26 message(FATAL_ERROR "LLVM_DISTRIBUTION_COMPONENTS cannot be specified with multi-configuration generators (i.e. Xcode or Visual Studio)")27 endif()28endif()29 30# Build the map of targets to distributions that's used to look up the31# distribution for a target later. The distribution for ${target} is stored in32# the global property LLVM_DISTRIBUTION_FOR_${target}.33function(llvm_distribution_build_target_map)34 foreach(target ${LLVM_DISTRIBUTION_COMPONENTS})35 # CMake doesn't easily distinguish between properties that are unset and36 # properties that are empty (you have to do a second get_property call with37 # the SET option, which is unergonomic), so just use a special marker to38 # denote the default (unnamed) distribution.39 set_property(GLOBAL PROPERTY LLVM_DISTRIBUTION_FOR_${target} "<DEFAULT>")40 endforeach()41 42 foreach(distribution ${LLVM_DISTRIBUTIONS})43 foreach(target ${LLVM_${distribution}_DISTRIBUTION_COMPONENTS})44 # By default, we allow a target to be in multiple distributions, and use45 # the last one to determine its export set. We disallow this in strict46 # mode, emitting a single error at the end for readability.47 if(LLVM_STRICT_DISTRIBUTIONS)48 get_property(current_distribution GLOBAL PROPERTY LLVM_DISTRIBUTION_FOR_${target})49 if(current_distribution AND NOT current_distribution STREQUAL distribution)50 set_property(GLOBAL APPEND_STRING PROPERTY LLVM_DISTRIBUTION_ERRORS51 "Target ${target} cannot be in multiple distributions \52 ${distribution} and ${current_distribution}\n"53 )54 endif()55 endif()56 set_property(GLOBAL PROPERTY LLVM_DISTRIBUTION_FOR_${target} ${distribution})57 endforeach()58 endforeach()59endfunction()60 61# The include guard ensures this will only be called once. The rest of this file62# only defines other functions (i.e. it doesn't execute any more code directly).63llvm_distribution_build_target_map()64 65# Look up the distribution a particular target belongs to.66# - target: The target to look up.67# - in_distribution_var: The variable with this name is set in the caller's68# scope to indicate if the target is in any distribution. If no distributions69# have been configured, this will always be set to true.70# - distribution_var: The variable with this name is set in the caller's scope71# to indicate the distribution name for the target. If the target belongs to72# the default (unnamed) distribution, or if no distributions have been73# configured, it's set to the empty string.74# - UMBRELLA: The (optional) umbrella target that the target is a part of. For75# example, all LLVM libraries have the umbrella target llvm-libraries.76function(get_llvm_distribution target in_distribution_var distribution_var)77 if(NOT LLVM_DISTRIBUTION_COMPONENTS AND NOT LLVM_DISTRIBUTIONS)78 set(${in_distribution_var} YES PARENT_SCOPE)79 set(${distribution_var} "" PARENT_SCOPE)80 return()81 endif()82 83 cmake_parse_arguments(ARG "" UMBRELLA "" ${ARGN})84 get_property(distribution GLOBAL PROPERTY LLVM_DISTRIBUTION_FOR_${target})85 if(ARG_UMBRELLA)86 get_property(umbrella_distribution GLOBAL PROPERTY LLVM_DISTRIBUTION_FOR_${ARG_UMBRELLA})87 if(LLVM_STRICT_DISTRIBUTIONS AND distribution AND umbrella_distribution AND88 NOT distribution STREQUAL umbrella_distribution)89 set_property(GLOBAL APPEND_STRING PROPERTY LLVM_DISTRIBUTION_ERRORS90 "Target ${target} has different distribution ${distribution} from its \91 umbrella target's (${ARG_UMBRELLA}) distribution ${umbrella_distribution}\n"92 )93 endif()94 if(NOT distribution)95 set(distribution ${umbrella_distribution})96 endif()97 endif()98 99 if(distribution)100 set(${in_distribution_var} YES PARENT_SCOPE)101 if(distribution STREQUAL "<DEFAULT>")102 set(distribution "")103 endif()104 set(${distribution_var} "${distribution}" PARENT_SCOPE)105 else()106 set(${in_distribution_var} NO PARENT_SCOPE)107 endif()108endfunction()109 110# Get the EXPORT argument to use for an install command for a target in a111# project. As explained at the top of the file, the project export set for a112# distribution is named ${project}{distribution}Targets (except for LLVM where113# it's named ${project}{distribution}Exports for legacy reasons). Also set the114# ${PROJECT}_${DISTRIBUTION}_HAS_EXPORTS global property to mark the project as115# having exports for the distribution.116# - target: The target to get the EXPORT argument for.117# - project: The project to produce the argument for. IMPORTANT: The casing of118# this argument should match the casing used by the project's Config.cmake119# file. The correct casing for the LLVM projects is Clang, Flang, LLD, LLVM,120# and MLIR.121# - export_arg_var The variable with this name is set in the caller's scope to122# the EXPORT argument for the target for the project.123# - UMBRELLA: The (optional) umbrella target that the target is a part of. For124# example, all LLVM libraries have the umbrella target llvm-libraries.125function(get_target_export_arg target project export_arg_var)126 string(TOUPPER "${project}" project_upper)127 if(project STREQUAL "LLVM")128 set(suffix "Exports") # legacy129 else()130 set(suffix "Targets")131 endif()132 133 get_llvm_distribution(${target} in_distribution distribution ${ARGN})134 135 if(in_distribution)136 set(${export_arg_var} EXPORT ${project}${distribution}${suffix} PARENT_SCOPE)137 if(distribution)138 string(TOUPPER "${distribution}" distribution_upper)139 set_property(GLOBAL PROPERTY ${project_upper}_${distribution_upper}_HAS_EXPORTS True)140 else()141 set_property(GLOBAL PROPERTY ${project_upper}_HAS_EXPORTS True)142 endif()143 else()144 set(${export_arg_var} "" PARENT_SCOPE)145 endif()146endfunction()147 148# Produce a string of CMake include() commands to include the exported targets149# files for all distributions. See the comment at the top of this file for150# various assumptions made.151# - project: The project to produce the commands for. IMPORTANT: See the comment152# for get_target_export_arg above for the correct casing of this argument.153# - includes_var: The variable with this name is set in the caller's scope to154# the string of include commands.155function(get_config_exports_includes project includes_var)156 string(TOUPPER "${project}" project_upper)157 set(prefix "\${${project_upper}_CMAKE_DIR}/${project}")158 if(project STREQUAL "LLVM")159 set(suffix "Exports.cmake") # legacy160 else()161 set(suffix "Targets.cmake")162 endif()163 164 if(NOT LLVM_DISTRIBUTIONS)165 set(${includes_var} "include(\"${prefix}${suffix}\")" PARENT_SCOPE)166 else()167 set(includes)168 foreach(distribution ${LLVM_DISTRIBUTIONS})169 list(APPEND includes "include(\"${prefix}${distribution}${suffix}\" OPTIONAL)")170 endforeach()171 string(REPLACE ";" "\n" includes "${includes}")172 set(${includes_var} "${includes}" PARENT_SCOPE)173 endif()174endfunction()175 176# Create the install commands and targets for the distributions' CMake exports.177# The target to install ${distribution} for a project is called178# ${project}-${distribution}-cmake-exports, where ${project} is the project name179# in lowercase and ${distribution} is the distribution name in lowercase, except180# for LLVM, where the target is just called ${distribution}-cmake-exports. See181# the comment at the top of this file for various assumptions made.182# - project: The project. IMPORTANT: See the comment for get_target_export_arg183# above for the correct casing of this argument.184function(install_distribution_exports project)185 string(TOUPPER "${project}" project_upper)186 string(TOLOWER "${project}" project_lower)187 if(project STREQUAL "LLVM")188 set(prefix "")189 set(suffix "Exports") # legacy190 else()191 set(prefix "${project_lower}-")192 set(suffix "Targets")193 endif()194 set(destination "${${project_upper}_INSTALL_PACKAGE_DIR}")195 196 if(NOT LLVM_DISTRIBUTIONS)197 get_property(has_exports GLOBAL PROPERTY ${project_upper}_HAS_EXPORTS)198 if(has_exports)199 install(EXPORT ${project}${suffix} DESTINATION "${destination}"200 COMPONENT ${prefix}cmake-exports)201 endif()202 else()203 foreach(distribution ${LLVM_DISTRIBUTIONS})204 string(TOUPPER "${distribution}" distribution_upper)205 get_property(has_exports GLOBAL PROPERTY ${project_upper}_${distribution_upper}_HAS_EXPORTS)206 if(has_exports)207 string(TOLOWER "${distribution}" distribution_lower)208 set(target ${prefix}${distribution_lower}-cmake-exports)209 install(EXPORT ${project}${distribution}${suffix} DESTINATION "${destination}"210 COMPONENT ${target})211 if(NOT LLVM_ENABLE_IDE)212 add_custom_target(${target})213 get_subproject_title(subproject_title)214 set_target_properties(${target} PROPERTIES FOLDER "${subproject_title}/Distribution")215 add_llvm_install_targets(install-${target} COMPONENT ${target})216 endif()217 endif()218 endforeach()219 endif()220endfunction()221 222# Create the targets for installing the configured distributions. The223# ${distribution} target builds the distribution, install-${distribution}224# installs it, and install-${distribution}-stripped installs a stripped version,225# where ${distribution} is the distribution name in lowercase, or "distribution"226# for the default distribution.227function(llvm_distribution_add_targets)228 # This function is called towards the end of LLVM's CMakeLists.txt, so all229 # errors will have been seen by now.230 if(LLVM_STRICT_DISTRIBUTIONS)231 get_property(errors GLOBAL PROPERTY LLVM_DISTRIBUTION_ERRORS)232 if(errors)233 string(PREPEND errors234 "Strict distribution errors (turn off LLVM_STRICT_DISTRIBUTIONS to bypass):\n"235 )236 message(FATAL_ERROR "${errors}")237 endif()238 endif()239 240 set(distributions "${LLVM_DISTRIBUTIONS}")241 if(NOT distributions)242 # CMake seemingly doesn't distinguish between an empty list and a list243 # containing one element which is the empty string, so just use a special244 # marker to denote the default (unnamed) distribution and fix it in the245 # loop.246 set(distributions "<DEFAULT>")247 endif()248 249 get_property(LLVM_DRIVER_TOOL_SYMLINKS GLOBAL PROPERTY LLVM_DRIVER_TOOL_SYMLINKS)250 251 foreach(distribution ${distributions})252 if(distribution STREQUAL "<DEFAULT>")253 set(distribution_target distribution)254 # Preserve legacy behavior for LLVM_DISTRIBUTION_COMPONENTS.255 set(distribution_components ${LLVM_DISTRIBUTION_COMPONENTS} ${LLVM_RUNTIME_DISTRIBUTION_COMPONENTS})256 else()257 string(TOLOWER "${distribution}" distribution_lower)258 set(distribution_target ${distribution_lower}-distribution)259 set(distribution_components ${LLVM_${distribution}_DISTRIBUTION_COMPONENTS})260 endif()261 262 add_custom_target(${distribution_target})263 add_custom_target(install-${distribution_target})264 add_custom_target(install-${distribution_target}-stripped)265 get_subproject_title(subproject_title)266 set_target_properties(267 ${distribution_target} 268 install-${distribution_target}269 install-${distribution_target}-stripped270 PROPERTIES 271 FOLDER "${subproject_title}/Distribution"272 )273 274 foreach(target ${distribution_components})275 # Note that some distribution components may not have an actual target, but only an install-FOO target.276 # This happens for example if a target is an INTERFACE target.277 if(TARGET ${target})278 add_dependencies(${distribution_target} ${target})279 endif()280 281 if(TARGET install-${target})282 add_dependencies(install-${distribution_target} install-${target})283 elseif(TARGET install-llvm-driver AND ${target} IN_LIST LLVM_DRIVER_TOOL_SYMLINKS)284 add_dependencies(install-${distribution_target} install-llvm-driver)285 else()286 message(SEND_ERROR "Specified distribution component '${target}' doesn't have an install target")287 endif()288 289 if(TARGET install-${target}-stripped)290 add_dependencies(install-${distribution_target}-stripped install-${target}-stripped)291 elseif(TARGET install-llvm-driver-stripped AND ${target} IN_LIST LLVM_DRIVER_TOOL_SYMLINKS)292 add_dependencies(install-${distribution_target}-stripped install-llvm-driver-stripped)293 else()294 message(SEND_ERROR295 "Specified distribution component '${target}' doesn't have an install-stripped target."296 " Its installation target creation should be changed to use add_llvm_install_targets,"297 " or you should manually create the 'install-${target}-stripped' target.")298 endif()299 endforeach()300 endforeach()301endfunction()302