| # Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| # file LICENSE.rst or https://cmake.org/licensing for details. |
| |
| # Resolve the Maven dependencies listed in CPACK_APK_MAVEN_DEPENDENCIES the |
| # way Gradle resolves the runtime classpath of an Android application, and |
| # download them. Included by CPackAPK.cmake. |
| # |
| # Gradle module metadata (`.module`) is used when published: the runtime |
| # variant for Android is selected and `available-at` redirections, which |
| # Kotlin Multiplatform libraries use, are followed. Otherwise the POM is |
| # used. The highest version requested wins, in the order of Gradle, only |
| # the modules at their selected versions requesting versions, and |
| # dependency constraints, including those of platforms, can raise versions |
| # or give them to dependencies that have none. Exclusions apply below a |
| # module when they apply on every path to it. |
| |
| if(NOT DEFINED CPACK_APK_MAVEN_REPOSITORIES) |
| set(CPACK_APK_MAVEN_REPOSITORIES |
| https://maven.google.com |
| https://repo.maven.apache.org/maven2 |
| ) |
| endif() |
| set(_maven_repositories) |
| # Relative paths are relative to the directory cpack runs in, like those of |
| # the other variables. |
| file(REAL_PATH "." _cpack_directory) |
| foreach(_repository IN LISTS CPACK_APK_MAVEN_REPOSITORIES) |
| # A local directory is a repository as well. It need not exist. |
| if(NOT _repository MATCHES "^[A-Za-z][A-Za-z0-9+.-]*://") |
| cmake_path(ABSOLUTE_PATH _repository |
| BASE_DIRECTORY "${_cpack_directory}" NORMALIZE) |
| if(NOT _repository MATCHES "^/") |
| string(PREPEND _repository "/") |
| endif() |
| string(PREPEND _repository "file://") |
| endif() |
| string(REGEX REPLACE "/+$" "" _repository "${_repository}") |
| list(APPEND _maven_repositories "${_repository}") |
| endforeach() |
| |
| if(NOT CPACK_APK_MAVEN_CACHE_DIRECTORY) |
| # Next to the top level directory, which is removed after packaging. |
| set(CPACK_APK_MAVEN_CACHE_DIRECTORY |
| "${CPACK_PACKAGE_DIRECTORY}/_CPack_Packages/maven") |
| endif() |
| |
| # Download `url` to `file` unless it exists. Sets `result` to TRUE on |
| # success. An optional `<algorithm>=<hash>` argument is verified. |
| function(_cpack_apk_maven_download url file result) |
| if(EXISTS "${file}") |
| set(${result} TRUE PARENT_SCOPE) |
| return() |
| endif() |
| file(DOWNLOAD "${url}" "${file}.part" STATUS _status) |
| list(GET _status 0 _code) |
| if(NOT _code EQUAL 0) |
| file(REMOVE "${file}.part") |
| set(${result} FALSE PARENT_SCOPE) |
| return() |
| endif() |
| if(ARGV3 MATCHES "^([A-Z0-9]+)=(.+)$") |
| set(_expected "${CMAKE_MATCH_2}") |
| file(${CMAKE_MATCH_1} "${file}.part" _actual) |
| if(NOT _actual STREQUAL _expected) |
| file(REMOVE "${file}.part") |
| message(FATAL_ERROR |
| "CPack APK: The file downloaded from\n" |
| " ${url}\n" |
| "does not have the ${CMAKE_MATCH_1} hash the repository publishes.\n" |
| " Expected: ${_expected}\n" |
| " Actual: ${_actual}") |
| endif() |
| endif() |
| file(RENAME "${file}.part" "${file}") |
| # Repositories are tried in turn, so only the successful download is |
| # reported. |
| message(STATUS "CPack APK: Downloaded ${url}") |
| set(${result} TRUE PARENT_SCOPE) |
| endfunction() |
| |
| # Download `name` of `group:artifact:version` from the first repository |
| # that has it, into the cache. Sets `result` to the local path, or to an |
| # empty string when no repository has it. |
| function(_cpack_apk_maven_fetch group artifact version name result) |
| string(REPLACE "." "/" _group_path "${group}") |
| set(_relative "${_group_path}/${artifact}/${version}/${name}") |
| set(_file "${CPACK_APK_MAVEN_CACHE_DIRECTORY}/${_relative}") |
| foreach(_repository IN LISTS _maven_repositories) |
| _cpack_apk_maven_download("${_repository}/${_relative}" "${_file}" _ok |
| ${ARGN}) |
| if(_ok) |
| set(${result} "${_file}" PARENT_SCOPE) |
| return() |
| endif() |
| endforeach() |
| set(${result} "" PARENT_SCOPE) |
| endfunction() |
| |
| # Split `group:artifact:version` into the variables named by the other |
| # arguments. |
| function(_cpack_apk_maven_split coordinates group_var artifact_var version_var) |
| string(REPLACE ":" ";" _parts "${coordinates}") |
| list(LENGTH _parts _length) |
| if(NOT _length EQUAL 3) |
| message(FATAL_ERROR |
| "CPack APK: '${coordinates}' is not of the form " |
| "<group>:<artifact>:<version>.") |
| endif() |
| list(GET _parts 0 _group) |
| list(GET _parts 1 _artifact) |
| list(GET _parts 2 _version) |
| set(${group_var} "${_group}" PARENT_SCOPE) |
| set(${artifact_var} "${_artifact}" PARENT_SCOPE) |
| set(${version_var} "${_version}" PARENT_SCOPE) |
| endfunction() |
| |
| # Strip the brackets of a hard version requirement, `[1.2.3]` -> `1.2.3`. |
| function(_cpack_apk_maven_plain_version version context result) |
| string(REGEX REPLACE "^\\[([^,]*)\\]$" "\\1" _version "${version}") |
| if(_version MATCHES "[][(),]") |
| message(FATAL_ERROR |
| "CPack APK: ${context} requires the version range '${version}'. " |
| "Version ranges are not supported.") |
| endif() |
| set(${result} "${_version}" PARENT_SCOPE) |
| endfunction() |
| |
| # Split `version` into the parts Gradle compares: separated by `.`, `-`, |
| # `_` and `+`, and where digits and other characters meet. |
| function(_cpack_apk_maven_version_parts version result) |
| string(REGEX REPLACE "[-_+]" "." _version "${version}") |
| string(REGEX REPLACE "([0-9])([^0-9.])" "\\1.\\2" _version "${_version}") |
| string(REGEX REPLACE "([^0-9.])([0-9])" "\\1.\\2" _version "${_version}") |
| string(REGEX REPLACE "\\.\\.+" "." _version "${_version}") |
| string(REGEX REPLACE "^\\.|\\.$" "" _version "${_version}") |
| string(REPLACE "." ";" _version "${_version}") |
| set(${result} "${_version}" PARENT_SCOPE) |
| endfunction() |
| |
| # Rank of the version part `part`: `dev` is below the other qualifiers, |
| # `rc`, `snapshot`, `final`, `ga`, `release` and `sp` are above them, in |
| # this order, and numbers are above all qualifiers. |
| function(_cpack_apk_maven_version_rank part result) |
| if(part MATCHES "^[0-9]+$") |
| set(${result} 8 PARENT_SCOPE) |
| return() |
| endif() |
| string(TOLOWER "${part}" _part) |
| list(FIND _cpack_apk_maven_qualifiers "${_part}" _rank) |
| if(_rank EQUAL -1) |
| set(_rank 1) |
| endif() |
| set(${result} ${_rank} PARENT_SCOPE) |
| endfunction() |
| set(_cpack_apk_maven_qualifiers dev _other_ rc snapshot final ga release sp) |
| |
| # Set `result` to 1, 0 or -1 as the version `a` is higher than, equal to or |
| # lower than `b`, the way Gradle orders versions. `1.0-alpha` is lower |
| # than `1.0`, which is lower than `1.0.1`. |
| function(_cpack_apk_maven_version_compare a b result) |
| _cpack_apk_maven_version_parts("${a}" _a) |
| _cpack_apk_maven_version_parts("${b}" _b) |
| list(LENGTH _a _a_length) |
| list(LENGTH _b _b_length) |
| set(_length ${_a_length}) |
| if(_b_length LESS _length) |
| set(_length ${_b_length}) |
| endif() |
| set(_i 0) |
| while(_i LESS _length) |
| list(GET _a ${_i} _a_part) |
| list(GET _b ${_i} _b_part) |
| _cpack_apk_maven_version_rank("${_a_part}" _a_rank) |
| _cpack_apk_maven_version_rank("${_b_part}" _b_rank) |
| if(_a_rank GREATER _b_rank) |
| set(${result} 1 PARENT_SCOPE) |
| return() |
| elseif(_a_rank LESS _b_rank) |
| set(${result} -1 PARENT_SCOPE) |
| return() |
| elseif(_a_rank EQUAL 8) |
| if(_a_part GREATER _b_part) |
| set(${result} 1 PARENT_SCOPE) |
| return() |
| elseif(_a_part LESS _b_part) |
| set(${result} -1 PARENT_SCOPE) |
| return() |
| endif() |
| elseif(_a_rank EQUAL 1) |
| if(_a_part STRGREATER _b_part) |
| set(${result} 1 PARENT_SCOPE) |
| return() |
| elseif(_a_part STRLESS _b_part) |
| set(${result} -1 PARENT_SCOPE) |
| return() |
| endif() |
| endif() |
| math(EXPR _i "${_i} + 1") |
| endwhile() |
| # The longer version is higher if its next part is a number, and lower |
| # otherwise. |
| if(_a_length EQUAL _b_length) |
| set(${result} 0 PARENT_SCOPE) |
| return() |
| elseif(_a_length GREATER _b_length) |
| list(GET _a ${_length} _part) |
| set(_sign 1) |
| else() |
| list(GET _b ${_length} _part) |
| set(_sign -1) |
| endif() |
| if(NOT _part MATCHES "^[0-9]+$") |
| math(EXPR _sign "-${_sign}") |
| endif() |
| set(${result} ${_sign} PARENT_SCOPE) |
| endfunction() |
| |
| # Remove from `xml` the text from each `open` to the following `close`. |
| function(_cpack_apk_maven_xml_remove xml open close result) |
| string(LENGTH "${close}" _close_length) |
| while(TRUE) |
| string(FIND "${xml}" "${open}" _begin) |
| if(_begin EQUAL -1) |
| break() |
| endif() |
| string(SUBSTRING "${xml}" ${_begin} -1 _tail) |
| string(FIND "${_tail}" "${close}" _end) |
| if(_end EQUAL -1) |
| break() |
| endif() |
| math(EXPR _end "${_end} + ${_close_length}") |
| string(SUBSTRING "${xml}" 0 ${_begin} _head) |
| string(SUBSTRING "${_tail}" ${_end} -1 _tail) |
| set(xml "${_head}${_tail}") |
| endwhile() |
| set(${result} "${xml}" PARENT_SCOPE) |
| endfunction() |
| |
| # Set `result` to the content of the first element `tag` of `xml`, or to |
| # an empty string. |
| function(_cpack_apk_maven_xml_element xml tag result) |
| set(_content "") |
| string(FIND "${xml}" "<${tag}>" _begin) |
| if(_begin GREATER -1) |
| string(LENGTH "<${tag}>" _length) |
| math(EXPR _begin "${_begin} + ${_length}") |
| string(SUBSTRING "${xml}" ${_begin} -1 _content) |
| string(FIND "${_content}" "</${tag}>" _end) |
| if(_end GREATER -1) |
| string(SUBSTRING "${_content}" 0 ${_end} _content) |
| endif() |
| endif() |
| set(${result} "${_content}" PARENT_SCOPE) |
| endfunction() |
| |
| # Set `result` to TRUE if `group:artifact` matches one of the `excludes`, |
| # `group:artifact` patterns in which either part may be `*`. |
| function(_cpack_apk_maven_excluded group artifact excludes result) |
| foreach(_exclude IN LISTS excludes) |
| if(NOT _exclude MATCHES "^([^:]*):(.*)$") |
| continue() |
| endif() |
| set(_group "${CMAKE_MATCH_1}") |
| set(_artifact "${CMAKE_MATCH_2}") |
| if((_group STREQUAL "*" OR _group STREQUAL group) |
| AND (_artifact STREQUAL "*" OR _artifact STREQUAL artifact)) |
| set(${result} TRUE PARENT_SCOPE) |
| return() |
| endif() |
| endforeach() |
| set(${result} FALSE PARENT_SCOPE) |
| endfunction() |
| |
| # Index of the variant of the module metadata `json` that Gradle picks for |
| # the runtime classpath of an Android application, or an empty string. |
| # `kind` is `library`, or `platform` for a platform, which only has |
| # dependency constraints. |
| function(_cpack_apk_maven_variant json kind result) |
| set(_best "") |
| set(_best_score 0) |
| string(JSON _count LENGTH "${json}" variants) |
| if(_count EQUAL 0) |
| set(${result} "" PARENT_SCOPE) |
| return() |
| endif() |
| math(EXPR _last "${_count} - 1") |
| foreach(_i RANGE ${_last}) |
| foreach(_attribute IN ITEMS usage category platform environment build_type) |
| set(_${_attribute} "") |
| endforeach() |
| foreach(_pair IN ITEMS |
| usage=org.gradle.usage |
| category=org.gradle.category |
| platform=org.jetbrains.kotlin.platform.type |
| environment=org.gradle.jvm.environment |
| build_type=com.android.build.api.attributes.BuildTypeAttr) |
| string(REPLACE "=" ";" _pair "${_pair}") |
| list(GET _pair 0 _attribute) |
| list(GET _pair 1 _name) |
| string(JSON _value ERROR_VARIABLE _error |
| GET "${json}" variants ${_i} attributes ${_name}) |
| if(NOT _error) |
| set(_${_attribute} "${_value}") |
| endif() |
| endforeach() |
| if(NOT _usage STREQUAL "java-runtime") |
| continue() |
| endif() |
| if(kind STREQUAL "platform") |
| if(NOT _category MATCHES "^(enforced-)?platform$") |
| continue() |
| endif() |
| elseif(_category AND NOT _category STREQUAL "library") |
| continue() |
| endif() |
| set(_score 2) |
| if(_platform STREQUAL "androidJvm") |
| math(EXPR _score "${_score} + 8") |
| elseif(_platform STREQUAL "jvm") |
| math(EXPR _score "${_score} + 4") |
| elseif(_platform) |
| # Kotlin/Native, JavaScript, WebAssembly or common metadata. |
| continue() |
| endif() |
| if(_environment STREQUAL "android") |
| math(EXPR _score "${_score} + 2") |
| endif() |
| # Libraries publishing all their Android build types have a debug and a |
| # release variant; the application is packaged for release. |
| if(_build_type STREQUAL "" OR _build_type STREQUAL "release") |
| math(EXPR _score "${_score} + 1") |
| endif() |
| if(_score GREATER _best_score) |
| set(_best ${_i}) |
| set(_best_score ${_score}) |
| endif() |
| endforeach() |
| set(${result} "${_best}" PARENT_SCOPE) |
| endfunction() |
| |
| # Read the Gradle module metadata `json` of `coordinates`, as a `kind`. |
| function(_cpack_apk_maven_module json kind coordinates) |
| _cpack_apk_maven_variant("${json}" ${kind} _variant) |
| if(_variant STREQUAL "") |
| if(kind STREQUAL "platform") |
| set(_what "platform variant") |
| else() |
| set(_what "variant") |
| endif() |
| message(FATAL_ERROR |
| "CPack APK: The Maven module '${coordinates}' has no ${_what} for the " |
| "Java runtime.") |
| endif() |
| set(_deps) |
| set(_constraints) |
| set(_redirect) |
| set(_files) |
| string(JSON _available ERROR_VARIABLE _error |
| GET "${json}" variants ${_variant} available-at) |
| if(NOT _error) |
| string(JSON _g GET "${_available}" group) |
| string(JSON _m GET "${_available}" module) |
| string(JSON _v GET "${_available}" version) |
| set(_redirect "${_g}:${_m}:${_v}") |
| else() |
| foreach(_kind IN ITEMS dependencies dependencyConstraints) |
| string(JSON _count ERROR_VARIABLE _error |
| LENGTH "${json}" variants ${_variant} ${_kind}) |
| if(_error OR _count EQUAL 0) |
| continue() |
| endif() |
| math(EXPR _last "${_count} - 1") |
| foreach(_i RANGE ${_last}) |
| string(JSON _dep GET "${json}" variants ${_variant} ${_kind} ${_i}) |
| string(JSON _g GET "${_dep}" group) |
| string(JSON _m GET "${_dep}" module) |
| set(_v) |
| foreach(_key IN ITEMS strictly requires prefers) |
| string(JSON _v ERROR_VARIABLE _error GET "${_dep}" version ${_key}) |
| if(NOT _error) |
| break() |
| endif() |
| set(_v) |
| endforeach() |
| if(_v) |
| _cpack_apk_maven_plain_version("${_v}" |
| "The Maven module '${coordinates}'" _v) |
| endif() |
| if(_kind STREQUAL "dependencyConstraints") |
| # A constraint without a version, which only rejects versions, |
| # does not raise one. |
| if(_v) |
| list(APPEND _constraints "${_g}:${_m}:${_v}") |
| endif() |
| continue() |
| endif() |
| # A platform has no artifacts, but its constraints give versions, |
| # including to dependencies that have none. |
| set(_dep_kind library) |
| string(JSON _category ERROR_VARIABLE _error |
| GET "${_dep}" attributes org.gradle.category) |
| if(NOT _error AND _category MATCHES "platform") |
| set(_dep_kind platform) |
| endif() |
| set(_excludes) |
| string(JSON _excludes_count ERROR_VARIABLE _error |
| LENGTH "${_dep}" excludes) |
| if(NOT _error AND _excludes_count GREATER 0) |
| math(EXPR _excludes_last "${_excludes_count} - 1") |
| foreach(_j RANGE ${_excludes_last}) |
| foreach(_field IN ITEMS group module) |
| string(JSON _${_field} ERROR_VARIABLE _error |
| GET "${_dep}" excludes ${_j} ${_field}) |
| if(_error) |
| set(_${_field} "*") |
| endif() |
| endforeach() |
| list(APPEND _excludes "${_group}:${_module}") |
| endforeach() |
| endif() |
| list(JOIN _excludes "," _excludes) |
| list(APPEND _deps "${_dep_kind}|${_g}:${_m}:${_v}|${_excludes}") |
| endforeach() |
| endforeach() |
| string(JSON _count ERROR_VARIABLE _error |
| LENGTH "${json}" variants ${_variant} files) |
| if(NOT _error AND _count GREATER 0) |
| math(EXPR _last "${_count} - 1") |
| foreach(_i RANGE ${_last}) |
| string(JSON _url GET "${json}" variants ${_variant} files ${_i} url) |
| string(JSON _sha256 GET "${json}" variants ${_variant} files ${_i} sha256) |
| string(TOLOWER "${_sha256}" _sha256) |
| list(APPEND _files "${_url}|SHA256=${_sha256}") |
| endforeach() |
| endif() |
| endif() |
| set(_maven_deps "${_deps}" PARENT_SCOPE) |
| set(_maven_constraints "${_constraints}" PARENT_SCOPE) |
| set(_maven_redirect "${_redirect}" PARENT_SCOPE) |
| set(_maven_files "${_files}" PARENT_SCOPE) |
| endfunction() |
| |
| # The `<dependency>` elements of `xml`, as |
| # `group|artifact|version|scope|optional|excludes`, where `excludes` are |
| # the `group:artifact` of its exclusions, joined by `,`. The `[` and `]` |
| # of version ranges would keep lists from being split, so they are given |
| # as `<lb>` and `<rb>`, which the elements cannot hold. |
| function(_cpack_apk_maven_pom_dependencies xml result) |
| set(_space "[ \t\r\n]*") |
| set(_deps) |
| string(LENGTH "<dependency>" _open_length) |
| string(LENGTH "<exclusion>" _exclusion_open_length) |
| while(TRUE) |
| string(FIND "${xml}" "<dependency>" _begin) |
| if(_begin EQUAL -1) |
| break() |
| endif() |
| math(EXPR _begin "${_begin} + ${_open_length}") |
| string(SUBSTRING "${xml}" ${_begin} -1 xml) |
| string(FIND "${xml}" "</dependency>" _end) |
| string(SUBSTRING "${xml}" 0 ${_end} _chunk) |
| # The transitive dependencies to leave out. |
| _cpack_apk_maven_xml_element("${_chunk}" exclusions _exclusions) |
| _cpack_apk_maven_xml_remove("${_chunk}" "<exclusions>" "</exclusions>" |
| _chunk) |
| set(_excludes) |
| while(TRUE) |
| string(FIND "${_exclusions}" "<exclusion>" _begin) |
| if(_begin EQUAL -1) |
| break() |
| endif() |
| math(EXPR _begin "${_begin} + ${_exclusion_open_length}") |
| string(SUBSTRING "${_exclusions}" ${_begin} -1 _exclusions) |
| string(FIND "${_exclusions}" "</exclusion>" _end) |
| string(SUBSTRING "${_exclusions}" 0 ${_end} _exclusion) |
| foreach(_tag IN ITEMS groupId artifactId) |
| set(_${_tag} "*") |
| if(_exclusion MATCHES "<${_tag}>${_space}([^< \t\r\n]+)${_space}</${_tag}>") |
| set(_${_tag} "${CMAKE_MATCH_1}") |
| endif() |
| endforeach() |
| string(APPEND _excludes ",${_groupId}:${_artifactId}") |
| endwhile() |
| string(REGEX REPLACE "^," "" _excludes "${_excludes}") |
| foreach(_tag IN ITEMS groupId artifactId version scope optional) |
| set(_${_tag}) |
| if(_chunk MATCHES "<${_tag}>${_space}([^< \t\r\n]*)${_space}</${_tag}>") |
| set(_${_tag} "${CMAKE_MATCH_1}") |
| endif() |
| endforeach() |
| set(_entry |
| "${_groupId}|${_artifactId}|${_version}|${_scope}|${_optional}|${_excludes}") |
| string(REPLACE "[" "<lb>" _entry "${_entry}") |
| string(REPLACE "]" "<rb>" _entry "${_entry}") |
| list(APPEND _deps "${_entry}") |
| endwhile() |
| set(${result} "${_deps}" PARENT_SCOPE) |
| endfunction() |
| |
| # Set `result` to the version `value` given by |
| # `_cpack_apk_maven_pom_dependencies`, with its `[` and `]` restored. |
| function(_cpack_apk_maven_pom_dependency_version value result) |
| string(REPLACE "<lb>" "[" value "${value}") |
| string(REPLACE "<rb>" "]" value "${value}") |
| set(${result} "${value}" PARENT_SCOPE) |
| endfunction() |
| |
| # Set `result` to `value`, the `what` of `dependency` in the POM of |
| # `group:artifact:version`, whose `<properties>` element holds `properties`, |
| # with the properties it refers to replaced. |
| function(_cpack_apk_maven_pom_property value properties group artifact version |
| dependency what result) |
| set(_space "[ \t\r\n]*") |
| # Properties of this POM; those of parent POMs are not known. |
| while(value MATCHES "\\$\\{([^}]*)\\}") |
| set(_property "${CMAKE_MATCH_1}") |
| if(_property MATCHES "^((project|pom)\\.)?groupId$") |
| set(_value "${group}") |
| elseif(_property MATCHES "^((project|pom)\\.)?artifactId$") |
| set(_value "${artifact}") |
| elseif(_property MATCHES "^((project|pom)\\.)?version$") |
| set(_value "${version}") |
| elseif(properties MATCHES "<${_property}>${_space}([^<]*[^< \t\r\n])${_space}</${_property}>") |
| set(_value "${CMAKE_MATCH_1}") |
| else() |
| message(FATAL_ERROR |
| "CPack APK: The ${what} of '${dependency}' in the POM of " |
| "'${group}:${artifact}:${version}' refers to the unknown property " |
| "'${_property}'. Parent POMs are not supported.") |
| endif() |
| string(REPLACE "\${${_property}}" "${_value}" value "${value}") |
| endwhile() |
| set(${result} "${value}" PARENT_SCOPE) |
| endfunction() |
| |
| # Set `result` to the version `value` of `dependency` in the POM of |
| # `group:artifact:version`, whose `<properties>` element holds |
| # `properties`. |
| function(_cpack_apk_maven_pom_version value properties group artifact version |
| dependency result) |
| _cpack_apk_maven_pom_property("${value}" "${properties}" |
| ${group} ${artifact} ${version} "${dependency}" version value) |
| if(NOT value STREQUAL "") |
| _cpack_apk_maven_plain_version("${value}" |
| "The POM of '${group}:${artifact}:${version}'" value) |
| endif() |
| set(${result} "${value}" PARENT_SCOPE) |
| endfunction() |
| |
| # Replace the properties that the group and artifact held by the variables |
| # named `group_var` and `artifact_var` refer to, in the POM of |
| # `group:artifact:version`, whose `<properties>` element holds `properties`. |
| # Modules often name the group of their siblings `${project.groupId}`. |
| function(_cpack_apk_maven_pom_coordinates group_var artifact_var properties |
| group artifact version) |
| set(_dependency "${${group_var}}:${${artifact_var}}") |
| _cpack_apk_maven_pom_property("${${group_var}}" "${properties}" |
| ${group} ${artifact} ${version} "${_dependency}" group _group) |
| _cpack_apk_maven_pom_property("${${artifact_var}}" "${properties}" |
| ${group} ${artifact} ${version} "${_dependency}" artifact _artifact) |
| set(${group_var} "${_group}" PARENT_SCOPE) |
| set(${artifact_var} "${_artifact}" PARENT_SCOPE) |
| endfunction() |
| |
| # Read the POM `pom` of `group:artifact:version`, as a `kind`. A platform |
| # has the versions the POM manages as dependency constraints, and the |
| # platforms it imports as dependencies. |
| function(_cpack_apk_maven_pom pom kind group artifact version) |
| file(READ "${pom}" _content) |
| string(REPLACE ";" "," _content "${_content}") |
| # Only the elements of the project itself count, not those in comments, |
| # profiles or the build, such as the dependencies of plugins. |
| _cpack_apk_maven_xml_remove("${_content}" "<!--" "-->" _content) |
| foreach(_tag IN ITEMS profiles build reporting) |
| _cpack_apk_maven_xml_remove("${_content}" "<${_tag}>" "</${_tag}>" |
| _content) |
| endforeach() |
| _cpack_apk_maven_xml_element("${_content}" properties _properties) |
| _cpack_apk_maven_xml_element("${_content}" dependencyManagement _managed) |
| _cpack_apk_maven_xml_remove("${_content}" |
| "<dependencyManagement>" "</dependencyManagement>" _content) |
| |
| set(_deps) |
| set(_constraints) |
| _cpack_apk_maven_pom_dependencies("${_managed}" _managed) |
| foreach(_entry IN LISTS _managed) |
| string(REPLACE "|" ";" _fields "${_entry}") |
| list(GET _fields 0 _g) |
| list(GET _fields 1 _a) |
| list(GET _fields 2 _v) |
| list(GET _fields 3 _scope) |
| _cpack_apk_maven_pom_dependency_version("${_v}" _v) |
| _cpack_apk_maven_pom_coordinates(_g _a "${_properties}" |
| ${group} ${artifact} ${version}) |
| if(kind STREQUAL "platform") |
| _cpack_apk_maven_pom_version("${_v}" "${_properties}" |
| ${group} ${artifact} ${version} "${_g}:${_a}" _v) |
| if(_v STREQUAL "") |
| continue() |
| endif() |
| if(_scope STREQUAL "import") |
| list(APPEND _deps "platform|${_g}:${_a}:${_v}|") |
| else() |
| list(APPEND _constraints "${_g}:${_a}:${_v}") |
| endif() |
| elseif(NOT _scope STREQUAL "import") |
| # The versions of the dependencies that do not give one. |
| string(MAKE_C_IDENTIFIER "${_g}:${_a}" _id) |
| set(_managed_${_id} "${_v}") |
| endif() |
| endforeach() |
| |
| set(_files) |
| if(kind STREQUAL "library") |
| _cpack_apk_maven_pom_dependencies("${_content}" _dependencies) |
| foreach(_entry IN LISTS _dependencies) |
| string(REPLACE "|" ";" _fields "${_entry}") |
| list(GET _fields 0 _g) |
| list(GET _fields 1 _a) |
| list(GET _fields 2 _v) |
| list(GET _fields 3 _scope) |
| list(GET _fields 4 _optional) |
| list(GET _fields 5 _excludes) |
| _cpack_apk_maven_pom_dependency_version("${_v}" _v) |
| _cpack_apk_maven_pom_coordinates(_g _a "${_properties}" |
| ${group} ${artifact} ${version}) |
| string(REPLACE "," ";" _excludes "${_excludes}") |
| set(_resolved_excludes) |
| foreach(_exclude IN LISTS _excludes) |
| string(REPLACE ":" ";" _exclude "${_exclude}") |
| list(GET _exclude 0 _eg) |
| list(GET _exclude 1 _ea) |
| _cpack_apk_maven_pom_coordinates(_eg _ea "${_properties}" |
| ${group} ${artifact} ${version}) |
| list(APPEND _resolved_excludes "${_eg}:${_ea}") |
| endforeach() |
| list(JOIN _resolved_excludes "," _excludes) |
| if(_scope AND NOT _scope MATCHES "^(compile|runtime)$") |
| continue() |
| endif() |
| if(_optional STREQUAL "true") |
| continue() |
| endif() |
| if(_v STREQUAL "") |
| string(MAKE_C_IDENTIFIER "${_g}:${_a}" _id) |
| set(_v "${_managed_${_id}}") |
| endif() |
| _cpack_apk_maven_pom_version("${_v}" "${_properties}" |
| ${group} ${artifact} ${version} "${_g}:${_a}" _v) |
| if(_v STREQUAL "") |
| message(FATAL_ERROR |
| "CPack APK: The POM of '${group}:${artifact}:${version}' does not " |
| "give the version of '${_g}:${_a}'. Parent POMs and imported " |
| "BOMs are not supported.") |
| endif() |
| list(APPEND _deps "library|${_g}:${_a}:${_v}|${_excludes}") |
| endforeach() |
| |
| set(_space "[ \t\r\n]*") |
| set(_packaging jar) |
| if(_content MATCHES "<packaging>${_space}([^< \t\r\n]*)") |
| set(_packaging "${CMAKE_MATCH_1}") |
| endif() |
| if(_packaging STREQUAL "bundle") |
| set(_packaging jar) |
| endif() |
| if(NOT _packaging STREQUAL "pom") |
| set(_name "${artifact}-${version}.${_packaging}") |
| _cpack_apk_maven_fetch(${group} ${artifact} ${version} "${_name}.sha1" |
| _sha1_file) |
| if(_sha1_file) |
| file(STRINGS "${_sha1_file}" _sha1 LIMIT_COUNT 1 REGEX "^[0-9a-fA-F]+") |
| string(REGEX MATCH "^[0-9a-fA-F]+" _sha1 "${_sha1}") |
| string(TOLOWER "${_sha1}" _sha1) |
| list(APPEND _files "${_name}|SHA1=${_sha1}") |
| else() |
| list(APPEND _files "${_name}|") |
| endif() |
| endif() |
| endif() |
| set(_maven_deps "${_deps}" PARENT_SCOPE) |
| set(_maven_constraints "${_constraints}" PARENT_SCOPE) |
| set(_maven_redirect "" PARENT_SCOPE) |
| set(_maven_files "${_files}" PARENT_SCOPE) |
| endfunction() |
| |
| # Read the metadata of `group:artifact:version` as a `kind`, `library` or |
| # `platform`, and set in the caller: |
| # _maven_deps its dependencies, `kind|group:artifact:version|excludes`, |
| # where the version may be empty and `excludes` are |
| # `group:artifact` patterns joined by `,` |
| # _maven_constraints its dependency constraints, `group:artifact:version` |
| # _maven_redirect the `group:artifact:version` it moved to, if any |
| # _maven_files its files, `name|algorithm=hash` |
| function(_cpack_apk_maven_metadata kind group artifact version) |
| set(_coordinates "${group}:${artifact}:${version}") |
| _cpack_apk_maven_fetch(${group} ${artifact} ${version} |
| "${artifact}-${version}.module" _module) |
| if(_module) |
| file(READ "${_module}" _json) |
| _cpack_apk_maven_module("${_json}" ${kind} "${_coordinates}") |
| else() |
| _cpack_apk_maven_fetch(${group} ${artifact} ${version} |
| "${artifact}-${version}.pom" _pom) |
| if(NOT _pom) |
| list(JOIN _maven_repositories ", " _repositories) |
| message(FATAL_ERROR |
| "CPack APK: The Maven artifact '${_coordinates}' was not found in " |
| "any of: ${_repositories}") |
| endif() |
| _cpack_apk_maven_pom("${_pom}" ${kind} ${group} ${artifact} ${version}) |
| endif() |
| foreach(_field IN ITEMS deps constraints redirect files) |
| set(_maven_${_field} "${_maven_${_field}}" PARENT_SCOPE) |
| endforeach() |
| endfunction() |
| |
| # Set `result` to TRUE if the version `a` is higher than `b`, or `b` is |
| # empty. |
| function(_cpack_apk_maven_version_higher a b result) |
| set(_higher TRUE) |
| if(NOT b STREQUAL "") |
| _cpack_apk_maven_version_compare("${a}" "${b}" _order) |
| if(NOT _order EQUAL 1) |
| set(_higher FALSE) |
| endif() |
| endif() |
| set(${result} ${_higher} PARENT_SCOPE) |
| endfunction() |
| |
| # Resolve CPACK_APK_MAVEN_DEPENDENCIES and set `result` to the downloaded |
| # archives. |
| function(_cpack_apk_maven_resolve result) |
| list(JOIN CPACK_APK_MAVEN_DEPENDENCIES ", " _dependencies) |
| message(STATUS "CPack APK: Resolving Maven dependencies: ${_dependencies}") |
| # The graph is walked again whenever the version of a module, or the |
| # exclusions that apply below it, change after it was visited, until |
| # they are stable. Exclusions only shrink. Each walk starts from the |
| # versions the modules were requested at, and constrained to, in the |
| # previous one, so that, as with Gradle, the versions a module no longer |
| # selected requests do not count. Should those versions cycle, they are |
| # only raised from then on. |
| set(_ids) |
| set(_states) |
| set(_monotonic FALSE) |
| set(_walk TRUE) |
| while(_walk) |
| set(_walk FALSE) |
| set(_visited) |
| set(_visited_modules) |
| set(_selected_files) |
| set(_pending) |
| foreach(_id IN LISTS _ids) |
| set(_selected_${_id} "${_floor_${_id}}") |
| set(_constraint_${_id} "${_floor_constraint_${_id}}") |
| set(_requested_${_id}) |
| set(_constrained_${_id}) |
| endforeach() |
| # Entries are `kind|group:artifact:version|excludes`. |
| set(_queue) |
| foreach(_dependency IN LISTS CPACK_APK_MAVEN_DEPENDENCIES) |
| list(APPEND _queue "library|${_dependency}|") |
| endforeach() |
| while(_queue) |
| while(_queue) |
| list(POP_FRONT _queue _entry) |
| string(REPLACE "|" ";" _fields "${_entry}") |
| list(GET _fields 0 _kind) |
| list(GET _fields 1 _coordinates) |
| list(GET _fields 2 _incoming) |
| string(REPLACE "," ";" _incoming "${_incoming}") |
| _cpack_apk_maven_split("${_coordinates}" _g _a _v) |
| string(MAKE_C_IDENTIFIER "${_g}:${_a}" _id) |
| list(APPEND _ids "${_id}") |
| if(NOT _v STREQUAL "") |
| _cpack_apk_maven_version_higher("${_v}" "${_requested_${_id}}" |
| _higher) |
| if(_higher) |
| set(_requested_${_id} "${_v}") |
| endif() |
| endif() |
| foreach(_candidate IN ITEMS "${_v}" "${_constraint_${_id}}") |
| if(_candidate STREQUAL "") |
| continue() |
| endif() |
| _cpack_apk_maven_version_higher("${_candidate}" |
| "${_selected_${_id}}" _higher) |
| if(_higher) |
| set(_selected_${_id} "${_candidate}") |
| if("${_g}:${_a}" IN_LIST _visited_modules) |
| set(_walk TRUE) |
| endif() |
| endif() |
| endforeach() |
| |
| # Exclusions apply below a module if they apply on every path to it. |
| string(MAKE_C_IDENTIFIER "${_kind}:${_g}:${_a}" _node) |
| if(NOT DEFINED _excludes_${_node}) |
| set(_excludes_${_node} "${_incoming}") |
| else() |
| set(_common) |
| foreach(_exclude IN LISTS _excludes_${_node}) |
| if(_exclude IN_LIST _incoming) |
| list(APPEND _common "${_exclude}") |
| endif() |
| endforeach() |
| if(NOT "${_common}" STREQUAL "${_excludes_${_node}}") |
| set(_excludes_${_node} "${_common}") |
| if("${_kind}|${_g}:${_a}" IN_LIST _visited) |
| set(_walk TRUE) |
| endif() |
| endif() |
| endif() |
| |
| if("${_kind}|${_g}:${_a}" IN_LIST _visited) |
| continue() |
| endif() |
| if("${_selected_${_id}}" STREQUAL "") |
| # Required without a version, which a platform or a dependency |
| # constraint may give later. |
| list(APPEND _pending "${_entry}") |
| continue() |
| endif() |
| list(APPEND _visited "${_kind}|${_g}:${_a}") |
| list(APPEND _visited_modules "${_g}:${_a}") |
| set(_v "${_selected_${_id}}") |
| |
| string(MAKE_C_IDENTIFIER "${_kind}:${_g}:${_a}:${_v}" _key) |
| if(NOT DEFINED _metadata_${_key}_files) |
| _cpack_apk_maven_metadata(${_kind} ${_g} ${_a} ${_v}) |
| foreach(_field IN ITEMS deps constraints redirect files) |
| set(_metadata_${_key}_${_field} "${_maven_${_field}}") |
| endforeach() |
| endif() |
| |
| foreach(_constraint IN LISTS _metadata_${_key}_constraints) |
| _cpack_apk_maven_split("${_constraint}" _cg _ca _cv) |
| string(MAKE_C_IDENTIFIER "${_cg}:${_ca}" _cid) |
| list(APPEND _ids "${_cid}") |
| _cpack_apk_maven_version_higher("${_cv}" "${_constrained_${_cid}}" |
| _higher) |
| if(_higher) |
| set(_constrained_${_cid} "${_cv}") |
| endif() |
| _cpack_apk_maven_version_higher("${_cv}" "${_constraint_${_cid}}" |
| _higher) |
| if(_higher) |
| set(_constraint_${_cid} "${_cv}") |
| if("${_cg}:${_ca}" IN_LIST _visited_modules) |
| _cpack_apk_maven_version_higher("${_cv}" "${_selected_${_cid}}" |
| _higher) |
| if(_higher) |
| set(_walk TRUE) |
| endif() |
| endif() |
| endif() |
| endforeach() |
| |
| set(_excludes "${_excludes_${_node}}") |
| foreach(_dep IN LISTS _metadata_${_key}_deps) |
| string(REPLACE "|" ";" _fields "${_dep}") |
| list(GET _fields 0 _dep_kind) |
| list(GET _fields 1 _dep_coordinates) |
| list(GET _fields 2 _dep_excludes) |
| _cpack_apk_maven_split("${_dep_coordinates}" _dg _da _dv) |
| _cpack_apk_maven_excluded(${_dg} ${_da} "${_excludes}" _excluded) |
| if(_excluded) |
| continue() |
| endif() |
| string(REPLACE "," ";" _dep_excludes "${_dep_excludes}") |
| set(_below ${_excludes} ${_dep_excludes}) |
| list(REMOVE_DUPLICATES _below) |
| list(JOIN _below "," _below) |
| list(APPEND _queue "${_dep_kind}|${_dep_coordinates}|${_below}") |
| endforeach() |
| if(_metadata_${_key}_redirect) |
| list(JOIN _excludes "," _below) |
| list(APPEND _queue |
| "${_kind}|${_metadata_${_key}_redirect}|${_below}") |
| endif() |
| foreach(_file IN LISTS _metadata_${_key}_files) |
| list(APPEND _selected_files "${_g}:${_a}:${_v}|${_file}") |
| endforeach() |
| endwhile() |
| |
| # Modules required without a version may have been given one since. |
| set(_waiting) |
| foreach(_entry IN LISTS _pending) |
| string(REPLACE "|" ";" _fields "${_entry}") |
| list(GET _fields 1 _coordinates) |
| _cpack_apk_maven_split("${_coordinates}" _g _a _v) |
| string(MAKE_C_IDENTIFIER "${_g}:${_a}" _id) |
| if(NOT "${_selected_${_id}}${_constraint_${_id}}" STREQUAL "") |
| list(APPEND _queue "${_entry}") |
| else() |
| list(APPEND _waiting "${_entry}") |
| endif() |
| endforeach() |
| set(_pending "${_waiting}") |
| endwhile() |
| |
| # The versions the next walk starts from. |
| list(REMOVE_DUPLICATES _ids) |
| set(_changed FALSE) |
| set(_state) |
| foreach(_id IN LISTS _ids) |
| foreach(_floor_kind IN ITEMS requested constrained) |
| if(_floor_kind STREQUAL "requested") |
| set(_floor_var _floor_${_id}) |
| else() |
| set(_floor_var _floor_constraint_${_id}) |
| endif() |
| set(_floor "${_${_floor_kind}_${_id}}") |
| if(_monotonic) |
| _cpack_apk_maven_version_higher("${${_floor_var}}" "${_floor}" |
| _higher) |
| if(_higher) |
| set(_floor "${${_floor_var}}") |
| endif() |
| endif() |
| if(NOT "${_floor}" STREQUAL "${${_floor_var}}") |
| set(${_floor_var} "${_floor}") |
| set(_changed TRUE) |
| endif() |
| list(APPEND _state "${_floor}") |
| endforeach() |
| endforeach() |
| if(_changed) |
| set(_walk TRUE) |
| if(NOT _monotonic) |
| string(MD5 _state "${_ids};${_state}") |
| if(_state IN_LIST _states) |
| set(_monotonic TRUE) |
| else() |
| list(APPEND _states "${_state}") |
| endif() |
| endif() |
| endif() |
| |
| if(_pending AND NOT _walk) |
| set(_modules) |
| foreach(_entry IN LISTS _pending) |
| string(REPLACE "|" ";" _fields "${_entry}") |
| list(GET _fields 1 _coordinates) |
| _cpack_apk_maven_split("${_coordinates}" _g _a _v) |
| list(APPEND _modules "${_g}:${_a}") |
| endforeach() |
| list(REMOVE_DUPLICATES _modules) |
| list(JOIN _modules "\n " _modules) |
| message(FATAL_ERROR |
| "CPack APK: The Maven artifacts\n ${_modules}\nare required " |
| "without a version, and no platform or dependency constraint gives " |
| "one.") |
| endif() |
| endwhile() |
| |
| set(_archives) |
| foreach(_entry IN LISTS _selected_files) |
| string(REPLACE "|" ";" _fields "${_entry}") |
| list(GET _fields 0 _coordinates) |
| list(GET _fields 1 _name) |
| list(GET _fields 2 _hash) |
| _cpack_apk_maven_split("${_coordinates}" _g _a _v) |
| _cpack_apk_maven_fetch(${_g} ${_a} ${_v} "${_name}" _path ${_hash}) |
| if(NOT _path) |
| message(FATAL_ERROR |
| "CPack APK: '${_name}' of the Maven artifact '${_coordinates}' could " |
| "not be downloaded.") |
| endif() |
| # Only code archives are consumed; a module may publish other files. |
| if(_path MATCHES "\\.(aar|jar)$") |
| list(APPEND _archives "${_path}") |
| _cpack_apk_debug("Maven dependency: ${_path}") |
| endif() |
| endforeach() |
| set(${result} "${_archives}" PARENT_SCOPE) |
| endfunction() |