1# Utility functions for packaging an LLVM distribution. See the 2# BuildingADistribution documentation for more details. 3 4# These functions assume a number of conventions that are common across all LLVM 5# subprojects: 6# - The generated CMake exports file for ${project} is called ${project}Targets 7# (except for LLVM where it's called ${project}Exports for legacy reasons). 8# - The build target for the CMake exports is called ${project}-cmake-exports 9# (except LLVM where it's just cmake-exports). 10# - The ${PROJECT}${distribution}_HAS_EXPORTS global property holds whether a 11# 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 to 14# hold the path of the installed CMake modules directory. 15# - The ${PROJECT}_INSTALL_PACKAGE_DIR variable contains the install destination 16# 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 the 31# distribution for a target later. The distribution for ${target} is stored in 32# 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 and 36 # properties that are empty (you have to do a second get_property call with 37 # the SET option, which is unergonomic), so just use a special marker to 38 # 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 use 45 # the last one to determine its export set. We disallow this in strict 46 # 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_ERRORS 51 "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 file 62# 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's 68# scope to indicate if the target is in any distribution. If no distributions 69# have been configured, this will always be set to true. 70# - distribution_var: The variable with this name is set in the caller's scope 71# to indicate the distribution name for the target. If the target belongs to 72# the default (unnamed) distribution, or if no distributions have been 73# configured, it's set to the empty string. 74# - UMBRELLA: The (optional) umbrella target that the target is a part of. For 75# 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 AND 88 NOT distribution STREQUAL umbrella_distribution) 89 set_property(GLOBAL APPEND_STRING PROPERTY LLVM_DISTRIBUTION_ERRORS 90 "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 a 111# project. As explained at the top of the file, the project export set for a 112# distribution is named ${project}{distribution}Targets (except for LLVM where 113# it's named ${project}{distribution}Exports for legacy reasons). Also set the 114# ${PROJECT}_${DISTRIBUTION}_HAS_EXPORTS global property to mark the project as 115# 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 of 118# this argument should match the casing used by the project's Config.cmake 119# 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 to 122# the EXPORT argument for the target for the project. 123# - UMBRELLA: The (optional) umbrella target that the target is a part of. For 124# 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") # legacy 129 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 targets 149# files for all distributions. See the comment at the top of this file for 150# various assumptions made. 151# - project: The project to produce the commands for. IMPORTANT: See the comment 152# 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 to 154# 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") # legacy 160 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 called 178# ${project}-${distribution}-cmake-exports, where ${project} is the project name 179# in lowercase and ${distribution} is the distribution name in lowercase, except 180# for LLVM, where the target is just called ${distribution}-cmake-exports. See 181# the comment at the top of this file for various assumptions made. 182# - project: The project. IMPORTANT: See the comment for get_target_export_arg 183# 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") # legacy 190 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. The 223# ${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 all 229 # 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 errors 234 "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 list 243 # containing one element which is the empty string, so just use a special 244 # marker to denote the default (unnamed) distribution and fix it in the 245 # 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}-stripped 270 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_ERROR 295 "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