1# Distributed under the OSI-approved BSD 3-Clause License. See accompanying 2# file Copyright.txt or https://cmake.org/licensing for details. 3 4# Author: Alex Turbov 5 6if(CMAKE_BINARY_DIR) 7 message(FATAL_ERROR "CPackNuGet.cmake may only be used by CPack internally.") 8endif() 9 10function(_cpack_nuget_debug) 11 if(CPACK_NUGET_PACKAGE_DEBUG) 12 message("CPackNuGet:Debug: " ${ARGN}) 13 endif() 14endfunction() 15 16function(_cpack_nuget_debug_var NAME) 17 if(CPACK_NUGET_PACKAGE_DEBUG) 18 message("CPackNuGet:Debug: ${NAME}=`${${NAME}}`") 19 endif() 20endfunction() 21 22function(_cpack_nuget_variable_fallback OUTPUT_VAR_NAME NUGET_VAR_NAME) 23 if(ARGN) 24 list(JOIN ARGN "`, `" _va_args) 25 set(_va_args ", ARGN: `${_va_args}`") 26 endif() 27 _cpack_nuget_debug( 28 "_cpack_nuget_variable_fallback: " 29 "OUTPUT_VAR_NAME=`${OUTPUT_VAR_NAME}`, " 30 "NUGET_VAR_NAME=`${NUGET_VAR_NAME}`" 31 "${_va_args}" 32 ) 33 34 set(_options USE_CDATA) 35 set(_one_value_args LIST_GLUE) 36 set(_multi_value_args FALLBACK_VARS) 37 cmake_parse_arguments(PARSE_ARGV 0 _args "${_options}" "${_one_value_args}" "${_multi_value_args}") 38 39 if(CPACK_NUGET_PACKAGE_COMPONENT) 40 string( 41 TOUPPER "${CPACK_NUGET_PACKAGE_COMPONENT}" 42 CPACK_NUGET_PACKAGE_COMPONENT_UPPER 43 ) 44 endif() 45 46 if(CPACK_NUGET_PACKAGE_COMPONENT 47 AND CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT}_PACKAGE_${NUGET_VAR_NAME} 48 ) 49 set( 50 _result 51 "${CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT}_PACKAGE_${NUGET_VAR_NAME}}" 52 ) 53 _cpack_nuget_debug( 54 " CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT}_PACKAGE_${NUGET_VAR_NAME}: " 55 "OUTPUT_VAR_NAME->${OUTPUT_VAR_NAME}=`${_result}`" 56 ) 57 58 elseif(CPACK_NUGET_PACKAGE_COMPONENT_UPPER 59 AND CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT_UPPER}_PACKAGE_${NUGET_VAR_NAME} 60 ) 61 set( 62 _result 63 "${CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT_UPPER}_PACKAGE_${NUGET_VAR_NAME}}" 64 ) 65 _cpack_nuget_debug( 66 " CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT_UPPER}_PACKAGE_${NUGET_VAR_NAME}: " 67 "OUTPUT_VAR_NAME->${OUTPUT_VAR_NAME}=`${_result}`" 68 ) 69 70 elseif(CPACK_NUGET_PACKAGE_${NUGET_VAR_NAME}) 71 set(_result "${CPACK_NUGET_PACKAGE_${NUGET_VAR_NAME}}") 72 _cpack_nuget_debug( 73 " CPACK_NUGET_PACKAGE_${NUGET_VAR_NAME}: " 74 "OUTPUT_VAR_NAME->${OUTPUT_VAR_NAME}=`${_result}`" 75 ) 76 77 else() 78 foreach(_var IN LISTS _args_FALLBACK_VARS) 79 _cpack_nuget_debug(" Fallback: ${_var} ...") 80 if(${_var}) 81 _cpack_nuget_debug(" ${_var}=`${${_var}}`") 82 set(_result "${${_var}}") 83 _cpack_nuget_debug( 84 " ${_var}: OUTPUT_VAR_NAME->${OUTPUT_VAR_NAME}=`${_result}`" 85 ) 86 break() 87 endif() 88 endforeach() 89 endif() 90 91 if(_result) 92 if(_args_USE_CDATA) 93 set(_value_before "<![CDATA[") 94 set(_value_after "]]>") 95 endif() 96 97 list(LENGTH _result _result_len) 98 if(_result_len GREATER 1 AND _args_LIST_GLUE) 99 list(JOIN _result "${_args_LIST_GLUE}" _result) 100 endif() 101 102 set(${OUTPUT_VAR_NAME} "${_value_before}${_result}${_value_after}" PARENT_SCOPE) 103 endif() 104 105endfunction() 106 107function(_cpack_nuget_variable_fallback_and_wrap_into_element ELEMENT NUGET_VAR_NAME) 108 set(_options) 109 set(_one_value_args) 110 set(_multi_value_args FALLBACK_VARS ATTRIBUTES) 111 cmake_parse_arguments(PARSE_ARGV 2 _args "${_options}" "${_one_value_args}" "${_multi_value_args}") 112 113 if(_args_ATTRIBUTES) 114 list(JOIN _args_ATTRIBUTES " " _attributes) 115 string(PREPEND _attributes " ") 116 endif() 117 118 _cpack_nuget_variable_fallback(_value ${NUGET_VAR_NAME} ${ARGN} USE_CDATA) 119 120 string(TOUPPER "${ELEMENT}" _ELEMENT_UP) 121 if(_value) 122 set( 123 _CPACK_NUGET_${_ELEMENT_UP}_TAG 124 "<${ELEMENT}${_attributes}>${_value}</${ELEMENT}>" 125 PARENT_SCOPE 126 ) 127 elseif(_attributes) 128 set( 129 _CPACK_NUGET_${_ELEMENT_UP}_TAG 130 "<${ELEMENT}${_attributes} />" 131 PARENT_SCOPE 132 ) 133 endif() 134endfunction() 135 136# Warn of obsolete nuspec fields, referencing CMake variables and suggested 137# replacement, if any 138function(_cpack_nuget_deprecation_warning NUGET_ELEMENT VARNAME REPLACEMENT) 139 if(${VARNAME}) 140 if(REPLACEMENT) 141 message(DEPRECATION "nuspec element `${NUGET_ELEMENT}` is deprecated in NuGet; consider replacing `${VARNAME}` with `${REPLACEMENT}`") 142 else() 143 message(DEPRECATION "nuspec element `${NUGET_ELEMENT}` is deprecated in NuGet; consider removing `${VARNAME}`") 144 endif() 145 endif() 146endfunction() 147 148# Print some debug info 149_cpack_nuget_debug("---[CPack NuGet Input Variables]---") 150_cpack_nuget_debug_var(CPACK_PACKAGE_NAME) 151_cpack_nuget_debug_var(CPACK_PACKAGE_VERSION) 152_cpack_nuget_debug_var(CPACK_TOPLEVEL_TAG) 153_cpack_nuget_debug_var(CPACK_TOPLEVEL_DIRECTORY) 154_cpack_nuget_debug_var(CPACK_TEMPORARY_DIRECTORY) 155_cpack_nuget_debug_var(CPACK_NUGET_GROUPS) 156if(CPACK_NUGET_GROUPS) 157 foreach(_group IN LISTS CPACK_NUGET_GROUPS) 158 string(MAKE_C_IDENTIFIER "${_group}" _group_up) 159 string(TOUPPER "${_group_up}" _group_up) 160 _cpack_nuget_debug_var(CPACK_NUGET_${_group_up}_GROUP_COMPONENTS) 161 endforeach() 162endif() 163_cpack_nuget_debug_var(CPACK_NUGET_COMPONENTS) 164_cpack_nuget_debug_var(CPACK_NUGET_ALL_IN_ONE) 165_cpack_nuget_debug_var(CPACK_NUGET_ORDINAL_MONOLITIC) 166_cpack_nuget_debug("-----------------------------------") 167 168function(_cpack_nuget_render_spec) 169 # Make a variable w/ upper-cased component name 170 if(CPACK_NUGET_PACKAGE_COMPONENT) 171 string(TOUPPER "${CPACK_NUGET_PACKAGE_COMPONENT}" CPACK_NUGET_PACKAGE_COMPONENT_UPPER) 172 endif() 173 174 # Set mandatory variables (not wrapped into XML elements) 175 # https://docs.microsoft.com/en-us/nuget/reference/nuspec#required-metadata-elements 176 if(CPACK_NUGET_PACKAGE_COMPONENT) 177 if(CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT_UPPER}_PACKAGE_NAME) 178 set( 179 CPACK_NUGET_PACKAGE_NAME 180 "${CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT_UPPER}_PACKAGE_NAME}" 181 ) 182 elseif(NOT CPACK_NUGET_PACKAGE_COMPONENT STREQUAL "Unspecified") 183 set( 184 CPACK_NUGET_PACKAGE_NAME 185 "${CPACK_PACKAGE_NAME}.${CPACK_NUGET_PACKAGE_COMPONENT}" 186 ) 187 else() 188 set(CPACK_NUGET_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") 189 endif() 190 elseif(NOT CPACK_NUGET_PACKAGE_NAME) 191 set(CPACK_NUGET_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") 192 endif() 193 194 # Warn about deprecated nuspec elements; warnings only display if 195 # variable is set 196 # Note that while nuspec's "summary" element is deprecated, there 197 # is no suggested replacement so (for now) no deprecation warning 198 # is shown for `CPACK_NUGET_*_DESCRIPTION_SUMMARY` 199 _cpack_nuget_deprecation_warning("licenseUrl" CPACK_NUGET_PACKAGE_LICENSEURL 200 "CPACK_NUGET_PACKAGE_LICENSE_FILE_NAME or CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION") 201 _cpack_nuget_deprecation_warning("licenseUrl" CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT}_LICENSEURL 202 "CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT}_LICENSE_FILE_NAME or CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT}_LICENSE_EXPRESSION") 203 _cpack_nuget_deprecation_warning("iconUrl" CPACK_NUGET_PACKAGE_ICONURL 204 "CPACK_NUGET_PACKAGE_ICON") 205 _cpack_nuget_deprecation_warning("iconUrl" CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT}_ICONURL 206 "CPACK_NUGET_${CPACK_NUGET_PACKAGE_COMPONENT}_ICON") 207 208 # Set nuspec fields 209 _cpack_nuget_variable_fallback( 210 CPACK_NUGET_PACKAGE_VERSION VERSION 211 FALLBACK_VARS 212 CPACK_PACKAGE_VERSION 213 ) 214 _cpack_nuget_variable_fallback( 215 CPACK_NUGET_PACKAGE_DESCRIPTION DESCRIPTION 216 FALLBACK_VARS 217 CPACK_COMPONENT_${CPACK_NUGET_PACKAGE_COMPONENT}_DESCRIPTION 218 CPACK_COMPONENT_${CPACK_NUGET_PACKAGE_COMPONENT_UPPER}_DESCRIPTION 219 CPACK_COMPONENT_GROUP_${CPACK_NUGET_PACKAGE_COMPONENT_UPPER}_DESCRIPTION 220 CPACK_PACKAGE_DESCRIPTION 221 USE_CDATA 222 ) 223 _cpack_nuget_variable_fallback( 224 CPACK_NUGET_PACKAGE_AUTHORS AUTHORS 225 FALLBACK_VARS 226 CPACK_PACKAGE_VENDOR 227 USE_CDATA 228 LIST_GLUE "," 229 ) 230 231 # Set optional variables (wrapped into XML elements) 232 # https://docs.microsoft.com/en-us/nuget/reference/nuspec#optional-metadata-elements 233 _cpack_nuget_variable_fallback_and_wrap_into_element( 234 title 235 TITLE 236 FALLBACK_VARS 237 CPACK_COMPONENT_${CPACK_NUGET_PACKAGE_COMPONENT}_DISPLAY_NAME 238 CPACK_COMPONENT_${CPACK_NUGET_PACKAGE_COMPONENT_UPPER}_DISPLAY_NAME 239 CPACK_COMPONENT_GROUP_${CPACK_NUGET_PACKAGE_COMPONENT_UPPER}_DISPLAY_NAME 240 ) 241 _cpack_nuget_variable_fallback_and_wrap_into_element(owners OWNERS LIST_GLUE ",") 242 _cpack_nuget_variable_fallback_and_wrap_into_element( 243 projectUrl 244 HOMEPAGE_URL 245 FALLBACK_VARS 246 CPACK_PACKAGE_HOMEPAGE_URL 247 ) 248 249 # "licenseUrl" is deprecated in favor of "license" 250 _cpack_nuget_variable_fallback_and_wrap_into_element(licenseUrl LICENSEURL) 251 252 # "iconUrl" is deprecated in favor of "icon" 253 _cpack_nuget_variable_fallback_and_wrap_into_element(iconUrl ICONURL) 254 255 # "license" takes a "type" attribute of either "file" or "expression" 256 # "file" refers to a file path of a .txt or .md file relative to the installation root 257 # "expression" refers to simple or compound expression of license identifiers 258 # listed at https://spdx.org/licenses/ 259 # Note that only one of CPACK_NUGET_PACKAGE_LICENSE_FILE_NAME and 260 # CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION may be specified. If both are specified, 261 # CPACK_NUGET_PACKAGE_LICENSE_FILE_NAME takes precedence and CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION is ignored. 262 if(CPACK_NUGET_PACKAGE_LICENSE_FILE_NAME) 263 _cpack_nuget_variable_fallback_and_wrap_into_element( 264 license LICENSE_FILE_NAME 265 ATTRIBUTES [[type="file"]] 266 ) 267 elseif(CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION) 268 _cpack_nuget_variable_fallback_and_wrap_into_element( 269 license LICENSE_EXPRESSION 270 ATTRIBUTES [[type="expression"]] 271 ) 272 endif() 273 274 # "icon" refers to a file path relative to the installation root 275 _cpack_nuget_variable_fallback_and_wrap_into_element(icon ICON) 276 # "summary" is deprecated in favor of "description" 277 _cpack_nuget_variable_fallback_and_wrap_into_element( 278 summary DESCRIPTION_SUMMARY 279 FALLBACK_VARS 280 CPACK_PACKAGE_DESCRIPTION_SUMMARY 281 ) 282 if(CPACK_NUGET_PACKAGE_REQUIRE_LICENSE_ACCEPTANCE) 283 set( 284 _CPACK_NUGET_REQUIRELICENSEACCEPTANCE_TAG 285 "<requireLicenseAcceptance>true</requireLicenseAcceptance>" 286 ) 287 endif() 288 _cpack_nuget_variable_fallback_and_wrap_into_element(releaseNotes RELEASE_NOTES) 289 _cpack_nuget_variable_fallback_and_wrap_into_element(copyright COPYRIGHT) 290 # "language" is a locale identifier such as "en_CA" 291 _cpack_nuget_variable_fallback_and_wrap_into_element(language LANGUAGE) 292 _cpack_nuget_variable_fallback_and_wrap_into_element(tags TAGS LIST_GLUE " ") 293 # "repository" holds repository metadata consisting of four optional 294 # attributes: "type", "url", "branch", and "commit". While all fields are 295 # considered optional, they are not independent. Currently unsupported. 296 297 # Handle dependencies 298 _cpack_nuget_variable_fallback(_deps DEPENDENCIES) 299 set(_collected_deps) 300 foreach(_dep IN LISTS _deps) 301 _cpack_nuget_debug(" checking dependency `${_dep}`") 302 303 _cpack_nuget_variable_fallback(_ver DEPENDENCIES_${_dep}_VERSION) 304 305 if(NOT _ver) 306 string(TOUPPER "${_dep}" _dep_upper) 307 _cpack_nuget_variable_fallback(_ver DEPENDENCIES_${_dep_upper}_VERSION) 308 endif() 309 310 if(_ver) 311 _cpack_nuget_debug(" got `${_dep}` dependency version ${_ver}") 312 string(CONCAT _collected_deps "${_collected_deps}" " <dependency id=\"${_dep}\" version=\"${_ver}\" />\n") 313 endif() 314 endforeach() 315 316 # Render deps into the variable 317 if(_collected_deps) 318 string(CONCAT _CPACK_NUGET_DEPENDENCIES_TAG "<dependencies>\n" "${_collected_deps}" " </dependencies>") 319 endif() 320 321 # Render the spec file 322 # NOTE The spec filename doesn't matter. Being included into a package, 323 # NuGet will name it properly. 324 _cpack_nuget_debug("Rendering `${CPACK_TEMPORARY_DIRECTORY}/CPack.NuGet.nuspec` file...") 325 configure_file( 326 "${CMAKE_ROOT}/Modules/Internal/CPack/CPack.NuGet.nuspec.in" 327 "${CPACK_TEMPORARY_DIRECTORY}/CPack.NuGet.nuspec" 328 @ONLY 329 ) 330endfunction() 331 332function(_cpack_nuget_make_files_tag) 333 set(_files) 334 foreach(_comp IN LISTS ARGN) 335 string(APPEND _files " <file src=\"${_comp}/**\" target=\".\" />\n") 336 endforeach() 337 set(_CPACK_NUGET_FILES_TAG "<files>\n${_files} </files>" PARENT_SCOPE) 338endfunction() 339 340find_program(NUGET_EXECUTABLE nuget) 341_cpack_nuget_debug_var(NUGET_EXECUTABLE) 342if(NOT NUGET_EXECUTABLE) 343 message(FATAL_ERROR "NuGet executable not found") 344endif() 345 346# Add details for debug run 347if(CPACK_NUGET_PACKAGE_DEBUG) 348 list(APPEND CPACK_NUGET_PACK_ADDITIONAL_OPTIONS "-Verbosity" "detailed") 349endif() 350 351# Case one: ordinal all-in-one package 352if(CPACK_NUGET_ORDINAL_MONOLITIC) 353 # This variable `CPACK_NUGET_ALL_IN_ONE` set by C++ code: 354 # Meaning to pack all installed files into a single package 355 _cpack_nuget_debug("---[Making an ordinal monolitic package]---") 356 _cpack_nuget_render_spec() 357 execute_process( 358 COMMAND "${NUGET_EXECUTABLE}" pack ${CPACK_NUGET_PACK_ADDITIONAL_OPTIONS} 359 WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" 360 RESULT_VARIABLE _nuget_result 361 ) 362 if(NOT _nuget_result EQUAL 0) 363 message(FATAL_ERROR "Nuget pack failed") 364 endif() 365 366elseif(CPACK_NUGET_ALL_IN_ONE) 367 # This variable `CPACK_NUGET_ALL_IN_ONE` set by C++ code: 368 # Meaning to pack all installed components into a single package 369 _cpack_nuget_debug("---[Making a monolitic package from installed components]---") 370 371 # Prepare the `files` element which include files from several components 372 _cpack_nuget_make_files_tag(${CPACK_NUGET_COMPONENTS}) 373 _cpack_nuget_render_spec() 374 execute_process( 375 COMMAND "${NUGET_EXECUTABLE}" pack ${CPACK_NUGET_PACK_ADDITIONAL_OPTIONS} 376 WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" 377 RESULT_VARIABLE _nuget_result 378 ) 379 if(NOT _nuget_result EQUAL 0) 380 message(FATAL_ERROR "Nuget pack failed") 381 endif() 382 383else() 384 # Is there any grouped component? 385 if(CPACK_NUGET_GROUPS) 386 _cpack_nuget_debug("---[Making grouped component(s) package(s)]---") 387 foreach(_group IN LISTS CPACK_NUGET_GROUPS) 388 _cpack_nuget_debug("Starting to make the package for group `${_group}`") 389 string(MAKE_C_IDENTIFIER "${_group}" _group_up) 390 string(TOUPPER "${_group_up}" _group_up) 391 392 # Render a spec file which includes all components in the current group 393 unset(_CPACK_NUGET_FILES_TAG) 394 _cpack_nuget_make_files_tag(${CPACK_NUGET_${_group_up}_GROUP_COMPONENTS}) 395 # Temporary set `CPACK_NUGET_PACKAGE_COMPONENT` to the group name 396 # to properly collect various per group settings 397 set(CPACK_NUGET_PACKAGE_COMPONENT ${_group}) 398 _cpack_nuget_render_spec() 399 unset(CPACK_NUGET_PACKAGE_COMPONENT) 400 execute_process( 401 COMMAND "${NUGET_EXECUTABLE}" pack ${CPACK_NUGET_PACK_ADDITIONAL_OPTIONS} 402 WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" 403 RESULT_VARIABLE _nuget_result 404 ) 405 if(NOT _nuget_result EQUAL 0) 406 message(FATAL_ERROR "Nuget pack failed") 407 endif() 408 endforeach() 409 endif() 410 # Is there any single component package needed? 411 if(CPACK_NUGET_COMPONENTS) 412 _cpack_nuget_debug("---[Making single-component(s) package(s)]---") 413 foreach(_comp IN LISTS CPACK_NUGET_COMPONENTS) 414 _cpack_nuget_debug("Starting to make the package for component `${_comp}`") 415 # Render a spec file which includes only given component 416 unset(_CPACK_NUGET_FILES_TAG) 417 _cpack_nuget_make_files_tag(${_comp}) 418 # Temporary set `CPACK_NUGET_PACKAGE_COMPONENT` to the current 419 # component name to properly collect various per group settings 420 set(CPACK_NUGET_PACKAGE_COMPONENT ${_comp}) 421 _cpack_nuget_render_spec() 422 unset(CPACK_NUGET_PACKAGE_COMPONENT) 423 execute_process( 424 COMMAND "${NUGET_EXECUTABLE}" pack ${CPACK_NUGET_PACK_ADDITIONAL_OPTIONS} 425 WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" 426 RESULT_VARIABLE _nuget_result 427 ) 428 if(NOT _nuget_result EQUAL 0) 429 message(FATAL_ERROR "Nuget pack failed") 430 endif() 431 endforeach() 432 endif() 433endif() 434 435file(GLOB_RECURSE GEN_CPACK_OUTPUT_FILES "${CPACK_TEMPORARY_DIRECTORY}/*.nupkg") 436if(NOT GEN_CPACK_OUTPUT_FILES) 437 message(FATAL_ERROR "NuGet package was not generated at `${CPACK_TEMPORARY_DIRECTORY}`!") 438endif() 439 440_cpack_nuget_debug("Generated files: ${GEN_CPACK_OUTPUT_FILES}") 441