diff --git a/docs/maintainers/portfile-functions.md b/docs/maintainers/portfile-functions.md index 45aa4589bc4..6527d8e0bbb 100644 --- a/docs/maintainers/portfile-functions.md +++ b/docs/maintainers/portfile-functions.md @@ -44,6 +44,7 @@ - [vcpkg\_from\_sourceforge](vcpkg_from_sourceforge.md) - [vcpkg\_get\_program\_files\_platform\_bitness](vcpkg_get_program_files_platform_bitness.md) - [vcpkg\_get\_windows\_sdk](vcpkg_get_windows_sdk.md) +- [vcpkg\_host\_path\_list](vcpkg_host_path_list.md) - [vcpkg\_install\_cmake](vcpkg_install_cmake.md) (deprecated, use [vcpkg\_cmake\_install](ports/vcpkg-cmake/vcpkg_cmake_install.md)) - [vcpkg\_install\_gn](vcpkg_install_gn.md) - [vcpkg\_install\_make](vcpkg_install_make.md) diff --git a/docs/maintainers/vcpkg_host_path_list.md b/docs/maintainers/vcpkg_host_path_list.md new file mode 100644 index 00000000000..8517e7f56e1 --- /dev/null +++ b/docs/maintainers/vcpkg_host_path_list.md @@ -0,0 +1,26 @@ +# vcpkg_host_path_list + +The latest version of this document lives in the [vcpkg repo](https://github.com/Microsoft/vcpkg/blob/master/docs/maintainers/vcpkg_host_path_list.md). + +Modify a host path list variable (PATH, INCLUDE, LIBPATH, etc.) + +```cmake +vcpkg_host_path_list(PREPEND [...]) +vcpkg_host_path_list(APPEND [...]) +``` + +`` may be either a regular variable name, or `ENV{variable-name}`, +in which case `vcpkg_host_path_list` will modify the environment. + +`vcpkg_host_path_list` adds all of the paths passed to it to ``; +`PREPEND` puts them before the existing list, so that they are searched first; +`APPEND` places them after the existing list, +so they would be searched after the paths which are already in the variable. + +For both `APPEND` and `PREPEND`, +the paths are added (and thus searched) in the order received. + +If no paths are passed, then nothing will be done. + +## Source +[scripts/cmake/vcpkg\_host\_path\_list.cmake](https://github.com/Microsoft/vcpkg/blob/master/scripts/cmake/vcpkg_host_path_list.cmake) diff --git a/scripts/cmake/vcpkg_add_to_path.cmake b/scripts/cmake/vcpkg_add_to_path.cmake index fe780c72a8d..b17aca36803 100644 --- a/scripts/cmake/vcpkg_add_to_path.cmake +++ b/scripts/cmake/vcpkg_add_to_path.cmake @@ -24,14 +24,11 @@ If no paths are passed, then nothing will be done. #]===] function(vcpkg_add_to_path) cmake_parse_arguments(PARSE_ARGV 0 "arg" "PREPEND" "" "") - if(NOT DEFINED arg_UNPARSED_ARGUMENTS) - return() + if(arg_PREPEND) + set(operation PREPEND) + else() + set(operation APPEND) endif() - list(JOIN arg_UNPARSED_ARGUMENTS "${VCPKG_HOST_PATH_SEPARATOR}" add_to_path) - if(arg_PREPEND) - set(ENV{PATH} "${add_to_path}${VCPKG_HOST_PATH_SEPARATOR}$ENV{PATH}") - else() - set(ENV{PATH} "$ENV{PATH}${VCPKG_HOST_PATH_SEPARATOR}${add_to_path}") - endif() + vcpkg_host_path_list("${operation}" ENV{PATH} ${arg_UNPARSED_ARGUMENTS}) endfunction() diff --git a/scripts/cmake/vcpkg_fixup_pkgconfig.cmake b/scripts/cmake/vcpkg_fixup_pkgconfig.cmake index afa7e554812..9cb35b7b248 100644 --- a/scripts/cmake/vcpkg_fixup_pkgconfig.cmake +++ b/scripts/cmake/vcpkg_fixup_pkgconfig.cmake @@ -45,112 +45,121 @@ Still work in progress. If there are more cases which can be handled here feel f * [brotli](https://github.com/Microsoft/vcpkg/blob/master/ports/brotli/portfile.cmake) #]===] -function(vcpkg_fixup_pkgconfig_check_files pkg_cfg_cmd _file _config) - set(PATH_SUFFIX_DEBUG /debug) - set(PATH_SUFFIX_RELEASE) - set(PKGCONFIG_INSTALLED_DIR "${CURRENT_INSTALLED_DIR}${PATH_SUFFIX_${_config}}/lib/pkgconfig") - set(PKGCONFIG_INSTALLED_SHARE_DIR "${CURRENT_INSTALLED_DIR}/share/pkgconfig") - set(PKGCONFIG_PACKAGES_DIR "${CURRENT_PACKAGES_DIR}${PATH_SUFFIX_${_config}}/lib/pkgconfig") - set(PKGCONFIG_PACKAGES_SHARE_DIR "${CURRENT_PACKAGES_DIR}/share/pkgconfig") +function(z_vcpkg_fixup_pkgconfig_check_files file config) + set(path_suffix_DEBUG /debug) + set(path_suffix_RELEASE "") if(DEFINED ENV{PKG_CONFIG_PATH}) - set(BACKUP_ENV_PKG_CONFIG_PATH "$ENV{PKG_CONFIG_PATH}") + set(backup_env_pkg_config_path "$ENV{PKG_CONFIG_PATH}") else() - unset(BACKUP_ENV_PKG_CONFIG_PATH) - endif() - if(DEFINED ENV{PKG_CONFIG_PATH} AND NOT ENV{PKG_CONFIG_PATH} STREQUAL "") - set(ENV{PKG_CONFIG_PATH} "${PKGCONFIG_INSTALLED_DIR}${VCPKG_HOST_PATH_SEPARATOR}${PKGCONFIG_INSTALLED_SHARE_DIR}${VCPKG_HOST_PATH_SEPARATOR}${PKGCONFIG_PACKAGES_DIR}${VCPKG_HOST_PATH_SEPARATOR}${PKGCONFIG_PACKAGES_SHARE_DIR}${VCPKG_HOST_PATH_SEPARATOR}$ENV{PKG_CONFIG_PATH}") - else() - set(ENV{PKG_CONFIG_PATH} "${PKGCONFIG_INSTALLED_DIR}${VCPKG_HOST_PATH_SEPARATOR}${PKGCONFIG_INSTALLED_SHARE_DIR}${VCPKG_HOST_PATH_SEPARATOR}${PKGCONFIG_PACKAGES_DIR}${VCPKG_HOST_PATH_SEPARATOR}${PKGCONFIG_PACKAGES_SHARE_DIR}") + unset(backup_env_pkg_config_path) endif() + vcpkg_host_path_list(PREPEND ENV{PKG_CONFIG_PATH} + "${CURRENT_PACKAGES_DIR}${path_suffix_${config}}/lib/pkgconfig" + "${CURRENT_PACKAGES_DIR}/share/pkgconfig" + "${CURRENT_INSTALLED_DIR}${path_suffix_${config}}/lib/pkgconfig" + "${CURRENT_INSTALLED_DIR}/share/pkgconfig" + ) + # First make sure everything is ok with the package and its deps - get_filename_component(_package_name "${_file}" NAME_WLE) - debug_message("Checking package (${_config}): ${_package_name}") - execute_process(COMMAND "${pkg_cfg_cmd}" --print-errors --exists ${_package_name} - WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}" - RESULT_VARIABLE _pkg_error_var - OUTPUT_VARIABLE _pkg_output - ERROR_VARIABLE _pkg_error_out - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_STRIP_TRAILING_WHITESPACE - ) - if(NOT _pkg_error_var EQUAL 0) - message(STATUS "pkg_cfg_cmd call with:${pkg_cfg_cmd} --exists ${_package_name} failed") - message(STATUS "ENV{PKG_CONFIG_PATH}:$ENV{PKG_CONFIG_PATH}") - message(STATUS "pkg-config call failed with error code:${_pkg_error_var}") - message(STATUS "pkg-config output:${_pkg_output}") - message(FATAL_ERROR "pkg-config error output:${_pkg_error_out}") + cmake_path(GET file STEM LAST_ONLY package_name) + debug_message("Checking package (${config}): ${package_name}") + execute_process( + COMMAND "${PKGCONFIG}" --print-errors --exists "${package_name}" + WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}" + RESULT_VARIABLE error_var + OUTPUT_VARIABLE output + ERROR_VARIABLE output + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + ) + if(NOT "${error_var}" EQUAL "0") + message(FATAL_ERROR "${PKGCONFIG} --exists ${package_name} failed with error code: ${error_var} + ENV{PKG_CONFIG_PATH}: \"$ENV{PKG_CONFIG_PATH}\" + output: ${output}" + ) else() - debug_message("pkg-config returned:${_pkg_error_var}") - debug_message("pkg-config output:${_pkg_output}") - debug_message("pkg-config error output:${_pkg_error_out}") + debug_message("pkg-config --exists ${package_name} output: ${output}") endif() - if(DEFINED BACKUP_ENV_PKG_CONFIG_PATH) - set(ENV{PKG_CONFIG_PATH} "${BACKUP_ENV_PKG_CONFIG_PATH}") + if(DEFINED backup_env_pkg_config_path) + set(ENV{PKG_CONFIG_PATH} "${backup_env_pkg_config_path}") else() unset(ENV{PKG_CONFIG_PATH}) endif() endfunction() function(vcpkg_fixup_pkgconfig) - # parse parameters such that semicolons in options arguments to COMMAND don't get erased - cmake_parse_arguments(PARSE_ARGV 0 _vfpkg "SKIP_CHECK" "" "RELEASE_FILES;DEBUG_FILES;SYSTEM_LIBRARIES;SYSTEM_PACKAGES;IGNORE_FLAGS") + cmake_parse_arguments(PARSE_ARGV 0 arg + "SKIP_CHECK" + "" + "RELEASE_FILES;DEBUG_FILES;SYSTEM_LIBRARIES;SYSTEM_PACKAGES;IGNORE_FLAGS" + ) - if(_vfpkg_UNPARSED_ARGUMENTS) - message(FATAL_ERROR "vcpkg_fixup_pkgconfig() was passed extra arguments: ${_vfct_UNPARSED_ARGUMENTS}") + if(DEFINED arg_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} was passed extra arguments: ${arg_UNPARSED_ARGUMENTS}") endif() - if((DEFINED _vfpkg_RELEASE_FILES AND NOT DEFINED _vfpkg_DEBUG_FILES) OR (NOT DEFINED _vfpkg_RELEASE_FILES AND DEFINED _vfpkg_DEBUG_FILES)) - message(FATAL_ERROR "vcpkg_fixup_pkgconfig() requires both or neither of DEBUG_FILES and RELEASE_FILES") + if(DEFINED arg_RELEASE_FILES AND NOT DEFINED arg_DEBUG_FILES) + message(FATAL_ERROR "DEBUG_FILES must be specified if RELEASE_FILES was specified.") + endif() + if(NOT DEFINED arg_RELEASE_FILES AND DEFINED arg_DEBUG_FILES) + message(FATAL_ERROR "RELEASE_FILES must be specified if DEBUG_FILES was specified.") endif() - if(NOT DEFINED _vfpkg_RELEASE_FILES) - file(GLOB_RECURSE _vfpkg_RELEASE_FILES "${CURRENT_PACKAGES_DIR}/**/*.pc") - file(GLOB_RECURSE _vfpkg_DEBUG_FILES "${CURRENT_PACKAGES_DIR}/debug/**/*.pc") - if(_vfpkg_DEBUG_FILES) - list(REMOVE_ITEM _vfpkg_RELEASE_FILES ${_vfpkg_DEBUG_FILES}) - endif() + if(NOT DEFINED arg_RELEASE_FILES) + file(GLOB_RECURSE arg_RELEASE_FILES "${CURRENT_PACKAGES_DIR}/**/*.pc") + file(GLOB_RECURSE arg_DEBUG_FILES "${CURRENT_PACKAGES_DIR}/debug/**/*.pc") + foreach(debug_file IN LISTS arg_DEBUG_FILES) + vcpkg_list(REMOVE_ITEM arg_RELEASE_FILES "${debug_file}") + endforeach() endif() - #Absolute Unix like paths - string(REGEX REPLACE "([a-zA-Z]):/" "/\\1/" _VCPKG_PACKAGES_DIR "${CURRENT_PACKAGES_DIR}") - string(REGEX REPLACE "([a-zA-Z]):/" "/\\1/" _VCPKG_INSTALLED_DIR "${CURRENT_INSTALLED_DIR}") + string(REGEX REPLACE "^([a-zA-Z]):/" [[/\1/]] unix_packages_dir "${CURRENT_PACKAGES_DIR}") + string(REGEX REPLACE "^([a-zA-Z]):/" [[/\1/]] unix_installed_dir "${CURRENT_INSTALLED_DIR}") - foreach(CONFIG RELEASE DEBUG) - debug_message("${CONFIG} Files: ${_vfpkg_${CONFIG}_FILES}") - if(VCPKG_BUILD_TYPE STREQUAL "debug" AND CONFIG STREQUAL "RELEASE") + foreach(config IN ITEMS RELEASE DEBUG) + debug_message("${config} Files: ${arg_${config}_FILES}") + if("${VCPKG_BUILD_TYPE}" STREQUAL "debug" AND "${config}" STREQUAL "RELEASE") continue() endif() - if(VCPKG_BUILD_TYPE STREQUAL "release" AND CONFIG STREQUAL "DEBUG") + if("${VCPKG_BUILD_TYPE}" STREQUAL "release" AND "${config}" STREQUAL "DEBUG") continue() endif() - foreach(_file ${_vfpkg_${CONFIG}_FILES}) - message(STATUS "Fixing pkgconfig file: ${_file}") - get_filename_component(PKG_LIB_SEARCH_PATH "${_file}" DIRECTORY) - if(CONFIG STREQUAL "DEBUG") - file(RELATIVE_PATH RELATIVE_PC_PATH "${PKG_LIB_SEARCH_PATH}" "${CURRENT_PACKAGES_DIR}/debug/") + foreach(file IN LISTS "arg_${config}_FILES") + message(STATUS "Fixing pkgconfig file: ${file}") + cmake_path(GET file PARENT_PATH pkg_lib_search_path) + if("${config}" STREQUAL "DEBUG") + set(relative_pc_path "${CURRENT_PACKAGES_DIR}/debug") + cmake_path(RELATIVE_PATH relative_pc_path BASE_DIRECTORY "${pkg_lib_search_path}") else() - file(RELATIVE_PATH RELATIVE_PC_PATH "${PKG_LIB_SEARCH_PATH}" "${CURRENT_PACKAGES_DIR}") + set(relative_pc_path "${CURRENT_PACKAGES_DIR}") + cmake_path(RELATIVE_PATH relative_pc_path BASE_DIRECTORY "${pkg_lib_search_path}") endif() - # strip trailing slash - string(REGEX REPLACE "/$" "" RELATIVE_PC_PATH "${RELATIVE_PC_PATH}") #Correct *.pc file - file(READ "${_file}" _contents) - string(REPLACE "${CURRENT_PACKAGES_DIR}" "\${prefix}" _contents "${_contents}") - string(REPLACE "${CURRENT_INSTALLED_DIR}" "\${prefix}" _contents "${_contents}") - string(REPLACE "${_VCPKG_PACKAGES_DIR}" "\${prefix}" _contents "${_contents}") - string(REPLACE "${_VCPKG_INSTALLED_DIR}" "\${prefix}" _contents "${_contents}") - string(REGEX REPLACE "(^|\n)prefix[\t ]*=[^\n]*" "" _contents "${_contents}") - if(CONFIG STREQUAL "DEBUG") - string(REPLACE "}/debug" "}" _contents "${_contents}") - # Prefix points at the debug subfolder - string(REPLACE "\${prefix}/include" "\${prefix}/../include" _contents "${_contents}") - string(REPLACE "\${prefix}/share" "\${prefix}/../share" _contents "${_contents}") + file(READ "${file}" contents) + + # this normalizes all files to end with a newline, and use LF instead of CRLF; + # this allows us to use regex matches easier to modify these files. + if(NOT "${contents}" MATCHES "\n$") + string(APPEND contents "\n") endif() - string(REGEX REPLACE " -L(\\\${[^}]*}[^ \n\t]*)" " -L\"\\1\"" _contents "${_contents}") - string(REGEX REPLACE " -I(\\\${[^}]*}[^ \n\t]*)" " -I\"\\1\"" _contents "${_contents}") - string(REGEX REPLACE " -l(\\\${[^}]*}[^ \n\t]*)" " -l\"\\1\"" _contents "${_contents}") + string(REPLACE "\r\n" "\n" contents "${contents}") + + string(REPLACE "${CURRENT_PACKAGES_DIR}" [[${prefix}]] contents "${contents}") + string(REPLACE "${CURRENT_INSTALLED_DIR}" [[${prefix}]] contents "${contents}") + string(REPLACE "${unix_packages_dir}" [[${prefix}]] contents "${contents}") + string(REPLACE "${unix_installed_dir}" [[${prefix}]] contents "${contents}") + + string(REGEX REPLACE "(^|\n)prefix[\t ]*=[^\n]*" "" contents "${contents}") + if("${config}" STREQUAL "DEBUG") + # prefix points at the debug subfolder + string(REPLACE [[${prefix}/debug]] [[${prefix}]] contents "${contents}") + string(REPLACE [[${prefix}/include]] [[${prefix}/../include]] contents "${contents}") + string(REPLACE [[${prefix}/share]] [[${prefix}/../share]] contents "${contents}") + endif() + # quote -L, -I, and -l paths starting with `${blah}` + string(REGEX REPLACE " -([LIl])(\\\${[^}]*}[^ \n\t]*)" [[ -\1"\2"]] contents "${contents}") # This section fuses XYZ.private and XYZ according to VCPKG_LIBRARY_LINKAGE # # Pkgconfig searches Requires.private transitively for Cflags in the dynamic case, @@ -158,41 +167,44 @@ function(vcpkg_fixup_pkgconfig) # # Once this transformation is complete, users of vcpkg should never need to pass # --static. - if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") - # Libs comes before Libs.private - string(REGEX REPLACE "(^|\n)(Libs: *[^\n]*)(.*)\nLibs.private:( *[^\n]*)" "\\1\\2\\4\\3" _contents "${_contents}") - # Libs.private comes before Libs - string(REGEX REPLACE "(^|\n)Libs.private:( *[^\n]*)(.*\nLibs: *[^\n]*)" "\\3\\2" _contents "${_contents}") - # Only Libs.private - string(REGEX REPLACE "(^|\n)Libs.private: *" "\\1Libs: " _contents "${_contents}") - # Cflags comes before Cflags.private - string(REGEX REPLACE "(^|\n)(Cflags: *[^\n]*)(.*)\nCflags.private:( *[^\n]*)" "\\1\\2\\4\\3" _contents "${_contents}") - # Cflags.private comes before Cflags - string(REGEX REPLACE "(^|\n)Cflags.private:( *[^\n]*)(.*\nCflags: *[^\n]*)" "\\3\\2" _contents "${_contents}") - # Only Cflags.private - string(REGEX REPLACE "(^|\n)Cflags.private: *" "\\1Cflags: " _contents "${_contents}") - # Requires comes before Requires.private - string(REGEX REPLACE "(^|\n)(Requires: *[^\n]*)(.*)\nRequires.private:( *[^\n]*)" "\\1\\2\\4\\3" _contents "${_contents}") - # Requires.private comes before Requires - string(REGEX REPLACE "(^|\n)Requires.private:( *[^\n]*)(.*\nRequires: *[^\n]*)" "\\3\\2" _contents "${_contents}") - # Only Requires.private - string(REGEX REPLACE "(^|\n)Requires.private: *" "\\1Requires: " _contents "${_contents}") + if("${VCPKG_LIBRARY_LINKAGE}" STREQUAL "static") + # how this works: + # we want to transform: + # Libs: $1 + # Libs.private: $2 + # into + # Libs: $1 $2 + # and the same thing for Requires and Requires.private + + foreach(item IN ITEMS "Libs" "Requires" "Cflags") + set(line "") + if("${contents}" MATCHES "(^|\n)${item}: *([^\n]*)") + string(APPEND line " ${CMAKE_MATCH_2}") + endif() + if("${contents}" MATCHES "(^|\n)${item}\\.private: *([^\n]*)") + string(APPEND line " ${CMAKE_MATCH_2}") + endif() + + string(REGEX REPLACE "(^|\n)${item}(\\.private)?:[^\n]*\n" [[\1]] contents "${contents}") + if(NOT "${line}" STREQUAL "") + string(APPEND contents "${item}:${line}\n") + endif() + endforeach() endif() - file(WRITE "${_file}" "prefix=\${pcfiledir}/${RELATIVE_PC_PATH}\n${_contents}") - unset(PKG_LIB_SEARCH_PATH) + file(WRITE "${file}" "prefix=\${pcfiledir}/${relative_pc_path}\n${contents}") endforeach() - if(NOT _vfpkg_SKIP_CHECK) # The check can only run after all files have been corrected! + if(NOT arg_SKIP_CHECK) # The check can only run after all files have been corrected! vcpkg_find_acquire_program(PKGCONFIG) debug_message("Using pkg-config from: ${PKGCONFIG}") - foreach(_file ${_vfpkg_${CONFIG}_FILES}) - vcpkg_fixup_pkgconfig_check_files("${PKGCONFIG}" "${_file}" "${CONFIG}") + foreach(file IN LISTS "arg_${config}_FILES") + z_vcpkg_fixup_pkgconfig_check_files("${file}" "${config}") endforeach() endif() endforeach() debug_message("Fixing pkgconfig --- finished") - set(VCPKG_FIXUP_PKGCONFIG_CALLED TRUE CACHE INTERNAL "See below" FORCE) + set(Z_VCPKG_FIXUP_PKGCONFIG_CALLED TRUE CACHE INTERNAL "See below" FORCE) # Variable to check if this function has been called! # Theoreotically vcpkg could look for *.pc files and automatically call this function # or check if this function has been called if *.pc files are detected. diff --git a/scripts/cmake/vcpkg_host_path_list.cmake b/scripts/cmake/vcpkg_host_path_list.cmake new file mode 100644 index 00000000000..d849cd42d35 --- /dev/null +++ b/scripts/cmake/vcpkg_host_path_list.cmake @@ -0,0 +1,81 @@ +#[===[ +# vcpkg_host_path_list + +Modify a host path list variable (PATH, INCLUDE, LIBPATH, etc.) + +```cmake +vcpkg_host_path_list(PREPEND [...]) +vcpkg_host_path_list(APPEND [...]) +``` + +`` may be either a regular variable name, or `ENV{variable-name}`, +in which case `vcpkg_host_path_list` will modify the environment. + +`vcpkg_host_path_list` adds all of the paths passed to it to ``; +`PREPEND` puts them before the existing list, so that they are searched first; +`APPEND` places them after the existing list, +so they would be searched after the paths which are already in the variable. + +For both `APPEND` and `PREPEND`, +the paths are added (and thus searched) in the order received. + +If no paths are passed, then nothing will be done. +#]===] +function(vcpkg_host_path_list) + if("${ARGC}" LESS "2") + message(FATAL_ERROR "vcpkg_host_path_list requires at least two arguments.") + endif() + + if("${ARGV1}" MATCHES "^ARGV([0-9]*)$|^ARG[CN]$|^CMAKE_CURRENT_FUNCTION|^CMAKE_MATCH_") + message(FATAL_ERROR "vcpkg_host_path_list does not support the list_var being ${ARGV1}. + Please use a different variable name.") + endif() + + if("${ARGV1}" MATCHES [[^ENV\{(.*)\}$]]) + set(list "$ENV{${CMAKE_MATCH_1}}") + set(env_var ON) + elseif("${ARGV1}" MATCHES [[^([A-Z]+)\{.*\}$]]) + message(FATAL_ERROR "vcpkg_host_path_list does not support ${CMAKE_MATCH_1} variables; + only ENV{} and regular variables are supported.") + else() + set(list "${${ARGV1}}") + set(env_var OFF) + endif() + set(operation "${ARGV0}") + set(list_var "${ARGV1}") + + if("${operation}" MATCHES "^(APPEND|PREPEND)$") + cmake_parse_arguments(PARSE_ARGV 2 arg "" "" "") + if(NOT DEFINED arg_UNPARSED_ARGUMENTS) + return() + endif() + + if("${VCPKG_HOST_PATH_SEPARATOR}" STREQUAL ";") + set(to_add "${arg_UNPARSED_ARGUMENTS}") + string(FIND "${arg_UNPARSED_ARGUMENTS}" [[\;]] index_of_host_path_separator) + else() + vcpkg_list(JOIN arg_UNPARSED_ARGUMENTS "${VCPKG_HOST_PATH_SEPARATOR}" to_add) + string(FIND "${arg_UNPARSED_ARGUMENTS}" "${VCPKG_HOST_PATH_SEPARATOR}" index_of_host_path_separator) + endif() + + if(NOT "${index_of_host_path_separator}" EQUAL "-1") + message(FATAL_ERROR "Host path separator (${VCPKG_HOST_PATH_SEPARATOR}) in path; this is unsupported.") + endif() + + if("${list}" STREQUAL "") + set(list "${to_add}") + elseif(arg_PREPEND) + set(list "${to_add}${VCPKG_HOST_PATH_SEPARATOR}${list}") + else() + set(list "${list}${VCPKG_HOST_PATH_SEPARATOR}${to_add}") + endif() + else() + message(FATAL_ERROR "Operation ${operation} not recognized.") + endif() + + if(env_var) + set("${list_var}" "${list}") + else() + set("${list_var}" "${list}" PARENT_SCOPE) + endif() +endfunction() diff --git a/scripts/ports.cmake b/scripts/ports.cmake index e33fa58657e..914c47cae9c 100644 --- a/scripts/ports.cmake +++ b/scripts/ports.cmake @@ -42,6 +42,7 @@ include("${SCRIPTS}/cmake/vcpkg_from_gitlab.cmake") include("${SCRIPTS}/cmake/vcpkg_from_sourceforge.cmake") include("${SCRIPTS}/cmake/vcpkg_get_program_files_platform_bitness.cmake") include("${SCRIPTS}/cmake/vcpkg_get_windows_sdk.cmake") +include("${SCRIPTS}/cmake/vcpkg_host_path_list.cmake") include("${SCRIPTS}/cmake/vcpkg_install_cmake.cmake") include("${SCRIPTS}/cmake/vcpkg_install_gn.cmake") include("${SCRIPTS}/cmake/vcpkg_install_make.cmake") diff --git a/scripts/test_ports/unit-test-cmake/portfile.cmake b/scripts/test_ports/unit-test-cmake/portfile.cmake index a567c11dec0..497cb18a383 100644 --- a/scripts/test_ports/unit-test-cmake/portfile.cmake +++ b/scripts/test_ports/unit-test-cmake/portfile.cmake @@ -23,6 +23,56 @@ endmacro() set(Z_VCPKG_UNIT_TEST_HAS_ERROR OFF CACHE BOOL "" FORCE) unset_fatal_error() +# in order to allow namespacing +function(unit_test_match namespace value regex) + if("${value}" MATCHES "${regex}") + set("${namespace}_MATCHED" ON PARENT_SCOPE) + if("${CMAKE_MATCH_COUNT}" EQUAL "0") + return() + endif() + + foreach(match RANGE 1 "${CMAKE_MATCH_COUNT}") + set("${namespace}_CMAKE_MATCH_${match}" "${CMAKE_MATCH_${match}}" PARENT_SCOPE) + endforeach() + else() + set("${namespace}_MATCHED" OFF PARENT_SCOPE) + endif() +endfunction() + +function(unit_test_check_variable_unset utcvu_test utcvu_variable) + cmake_language(EVAL CODE "${utcvu_test}") + if(Z_VCPKG_UNIT_TEST_HAS_FATAL_ERROR) + unset_fatal_error() + set_has_error() + message(STATUS "${utcvu_test} had an unexpected FATAL_ERROR; + expected: \"${utcvu_value}\"") + message(STATUS "FATAL_ERROR: ${Z_VCPKG_UNIT_TEST_FATAL_ERROR}") + return() + endif() + + unit_test_match(utcvu "${utcvu_variable}" [[^(ENV|CACHE)\{(.*)\}$]]) + if(utcvu_MATCHED) + message(STATUS "utcvu_variable: ${utcvu_CMAKE_MATCH_2}") + if("${utcvu_CMAKE_MATCH_1}" STREQUAL "ENV") + set(utcvu_actual_value "$ENV{${utcvu_CMAKE_MATCH_2}}") + elseif("${utcvu_CMAKE_MATCH_1}" STREQUAL "CACHE") + set(utcvu_actual_value "$CACHE{${utcvu_CMAKE_MATCH_2}}") + else() + _message(FATAL_ERROR "unexpected value for CMAKE_MATCH_1: ${utcvu_CMAKE_MATCH_1}") + endif() + else() + set(utcvu_actual_value "${${utcvu_variable}}") + endif() + + if(DEFINED "${utcvu_variable}") + message(STATUS "${utcvu_test} set ${utcvu_variable}; + expected: \"${utcvu_variable}\" unset + actual : \"${utcvu_actual_value}\"") + set_has_error() + return() + endif() +endfunction() + function(unit_test_check_variable_equal utcve_test utcve_variable utcve_value) cmake_language(EVAL CODE "${utcve_test}") if(Z_VCPKG_UNIT_TEST_HAS_FATAL_ERROR) @@ -40,10 +90,25 @@ function(unit_test_check_variable_equal utcve_test utcve_variable utcve_value) set_has_error() return() endif() - if(NOT "${${utcve_variable}}" STREQUAL "${utcve_value}") + + unit_test_match(utcve "${utcve_variable}" [[^(ENV|CACHE)\{(.*)\}$]]) + if(utcve_MATCHED) + message(STATUS "utcve_variable: ${utcve_CMAKE_MATCH_2}") + if("${utcve_CMAKE_MATCH_1}" STREQUAL "ENV") + set(utcve_actual_value "$ENV{${utcve_CMAKE_MATCH_2}}") + elseif("${utcve_CMAKE_MATCH_1}" STREQUAL "CACHE") + set(utcve_actual_value "$CACHE{${utcve_CMAKE_MATCH_2}}") + else() + _message(FATAL_ERROR "unexpected value for CMAKE_MATCH_1: ${utcve_CMAKE_MATCH_1}") + endif() + else() + set(utcve_actual_value "${${utcve_variable}}") + endif() + + if(NOT "${utcve_actual_value}" STREQUAL "${utcve_value}") message(STATUS "${utcve_test} resulted in the wrong value for ${utcve_variable}; expected: \"${utcve_value}\" - actual : \"${${utcve_variable}}\"") + actual : \"${utcve_actual_value}\"") set_has_error() return() endif() @@ -74,6 +139,9 @@ endif() if("list" IN_LIST FEATURES) include("${CMAKE_CURRENT_LIST_DIR}/test-vcpkg_list.cmake") endif() +if("add-to-path" IN_LIST FEATURES) + include("${CMAKE_CURRENT_LIST_DIR}/test-vcpkg_host_path_list.cmake") +endif() if("function-arguments" IN_LIST FEATURES) include("${CMAKE_CURRENT_LIST_DIR}/test-z_vcpkg_function_arguments.cmake") endif() diff --git a/scripts/test_ports/unit-test-cmake/test-vcpkg_host_path_list.cmake b/scripts/test_ports/unit-test-cmake/test-vcpkg_host_path_list.cmake new file mode 100644 index 00000000000..a6322d37556 --- /dev/null +++ b/scripts/test_ports/unit-test-cmake/test-vcpkg_host_path_list.cmake @@ -0,0 +1,263 @@ +# CACHE{var} is a fatal error +unit_test_ensure_fatal_error([[vcpkg_host_path_list(APPEND CACHE{var})]]) +unit_test_ensure_fatal_error([[vcpkg_host_path_list(PREPEND CACHE{var})]]) +unit_test_ensure_fatal_error([[vcpkg_host_path_list(APPEND CACHE{var} c d)]]) +unit_test_ensure_fatal_error([[vcpkg_host_path_list(PREPEND CACHE{var} c d)]]) + +# regular variable, HOST_PATH_SEPARATOR = ';' +set(VCPKG_HOST_PATH_SEPARATOR ";") + +unit_test_ensure_fatal_error([[vcpkg_host_path_list(APPEND var "a;b")]]) +unit_test_ensure_fatal_error([[vcpkg_host_path_list(PREPEND var "a;b")]]) + +set(var "a;b") +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND var d e)]] + var "a;b;d;e" +) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND var)]] + var "a;b" +) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND var d e)]] + var "d;e;a;b" +) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND var)]] + var "a;b" +) + +set(var "") +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND var d e)]] + var "d;e" +) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND var)]] + var "" +) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND var d e)]] + var "d;e" +) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND var)]] + var "" +) + +unset(var) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND var d e)]] + var "d;e" +) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND var)]] + var "" +) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND var d e)]] + var "d;e" +) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND var)]] + var "" +) + +# regular variable, HOST_PATH_SEPARATOR = ':' +set(VCPKG_HOST_PATH_SEPARATOR ":") + +unit_test_ensure_fatal_error([[vcpkg_host_path_list(APPEND var "a:b")]]) +unit_test_ensure_fatal_error([[vcpkg_host_path_list(PREPEND var "a:b")]]) + +set(ENV{var} "a:b") +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{var} d e)]] + ENV{var} "a:b:d:e" +) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{var})]] + ENV{var} "a:b" +) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var} d e)]] + ENV{var} "d:e:a:b" +) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var})]] + ENV{var} "a:b" +) + +set(ENV{var} "") +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{var} d e)]] + ENV{var} "d:e" +) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{var})]] + ENV{var} "" +) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var} d e)]] + ENV{var} "d:e" +) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var})]] + ENV{var} "" +) + +unset(ENV{var}) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{var} d e)]] + ENV{var} "d:e" +) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{var})]] + ENV{var} "" +) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var} d e)]] + ENV{var} "d:e" +) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var})]] + ENV{var} "" +) + +# environment ENV{var}iable, HOST_PATH_SEPARATOR = ';' +set(VCPKG_HOST_PATH_SEPARATOR ";") + +unit_test_ensure_fatal_error([[vcpkg_host_path_list(APPEND ENV{ENV{var}} "a;b")]]) +unit_test_ensure_fatal_error([[vcpkg_host_path_list(PREPEND ENV{ENV{var}} "a;b")]]) + +set(ENV{ENV{var}} "a;b") +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{ENV{var}} d e)]] + ENV{ENV{var}} "a;b;d;e" +) +set(ENV{ENV{var}} "a;b") +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{ENV{var}})]] + ENV{ENV{var}} "a;b" +) +set(ENV{ENV{var}} "a;b") +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{ENV{var}} d e)]] + ENV{ENV{var}} "d;e;a;b" +) +set(ENV{ENV{var}} "a;b") +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{ENV{var}})]] + ENV{ENV{var}} "a;b" +) + +set(ENV{ENV{var}} "") +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{ENV{var}} d e)]] + ENV{ENV{var}} "d;e" +) +set(ENV{ENV{var}} "") +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{ENV{var}})]] + ENV{ENV{var}} "" +) +set(ENV{ENV{var}} "") +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{ENV{var}} d e)]] + ENV{ENV{var}} "d;e" +) +set(ENV{ENV{var}} "") +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var})]] + ENV{ENV{var}} "" +) + +unset(ENV{ENV{var}}) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{ENV{var}} d e)]] + ENV{ENV{var}} "d;e" +) +unset(ENV{ENV{var}}) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(APPEND ENV{ENV{var}})]] + ENV{ENV{var}} "" +) +unset(ENV{ENV{var}}) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{ENV{var}} d e)]] + ENV{ENV{var}} "d;e" +) +unset(ENV{ENV{var}}) +unit_test_check_ENV{var}iable_equal( + [[vcpkg_host_path_list(PREPEND ENV{ENV{var}})]] + ENV{ENV{var}} "" +) + +# regular ENV{var}iable, HOST_PATH_SEPARATOR = ':' +set(VCPKG_HOST_PATH_SEPARATOR ":") + +unit_test_ensure_fatal_error([[vcpkg_host_path_list(APPEND ENV{var} "a:b")]]) +unit_test_ensure_fatal_error([[vcpkg_host_path_list(PREPEND ENV{var} "a:b")]]) + +set(ENV{var} "a:b") +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND ENV{var} d e)]] + ENV{var} "a:b:d:e" +) +set(ENV{var} "a:b") +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND ENV{var})]] + ENV{var} "a:b" +) +set(ENV{var} "a:b") +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var} d e)]] + ENV{var} "d:e:a:b" +) +set(ENV{var} "a:b") +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var})]] + ENV{var} "a:b" +) + +set(ENV{var} "") +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND ENV{var} d e)]] + ENV{var} "d:e" +) +set(ENV{var} "") +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND ENV{var})]] + ENV{var} "" +) +set(ENV{var} "") +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var} d e)]] + ENV{var} "d:e" +) +set(ENV{var} "") +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var})]] + ENV{var} "" +) + +unset(ENV{var}) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND ENV{var} d e)]] + ENV{var} "d:e" +) +unset(ENV{var}) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(APPEND ENV{var})]] + ENV{var} "" +) +unset(ENV{var}) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var} d e)]] + ENV{var} "d:e" +) +unset(ENV{var}) +unit_test_check_variable_equal( + [[vcpkg_host_path_list(PREPEND ENV{var})]] + ENV{var} "" +) diff --git a/scripts/test_ports/unit-test-cmake/vcpkg.json b/scripts/test_ports/unit-test-cmake/vcpkg.json index c20cf2c6cb8..366d1d7a93f 100644 --- a/scripts/test_ports/unit-test-cmake/vcpkg.json +++ b/scripts/test_ports/unit-test-cmake/vcpkg.json @@ -9,6 +9,9 @@ "minimum-required" ], "features": { + "host-path-list": { + "description": "Test the vcpkg_host_path_list function" + }, "function-arguments": { "description": "Test the z_vcpkg_function_arguments function" },