Skip to content

Commit

Permalink
Merge pull request #69 from lanl/mauneyc/fix-poc-already-present-error
Browse files Browse the repository at this point in the history
Fix "already present target" error when using submodules
  • Loading branch information
Yurlungur authored Jun 25, 2023
2 parents 285d8f1 + c764628 commit 1c229ff
Showing 1 changed file with 148 additions and 150 deletions.
298 changes: 148 additions & 150 deletions cmake/content.cmake
Original file line number Diff line number Diff line change
@@ -1,96 +1,95 @@
include(FetchContent)

###############################################################
#
# ##############################################################################
#
# For dependency management, use the `FetchContent` pattern
#
# NOTE: We seek to replicate the implementation of `FetchContent`
###############################################################

# Constructs `FetchContent_Declare` call, and records information
# for population and linking
#
# ::Overview
# With `[email protected]` and higher, the functionality of `FetchContent_` has
# been updated to provide the capability to try a `find_package` at the
# content population stage. This allows for the flexibility provided by
# `FetchContent_` to easily coexist with the dependency configuration
# provided by `find_package`. Some benefits include:
# - automated fetching of content if it is unavailable
# - content population at configure. This allows for a package import
# to utilize `find_package()` or `add_subdirectory()` operations
# - local clones can be given priority with `FETCHCONTENT_SOURCE_DIR_<uppercaseName>`,
# which lets developers use dependencies explicitly in-tree
# The case where i.) the content or dependency is unavailable or ii.) it cannot
# be fetched (due, for example, to a failure of a download step) is an
# unavoidable error.
# ##############################################################################

# Constructs `FetchContent_Declare` call, and records information for population
# and linking
#
# As this cmake version is relatively fresh, we also want to provide a bridge
# to downstream code that still is pinned to an earlier releases of `cmake`.
# To that end, these macros provide for replicating the main features introduced
# to `FetchContent_` in `[email protected]`. Future releases of `spiner` will require
# a minimum of `[email protected]`.
# ::Overview With `[email protected]` and higher, the functionality of `FetchContent_`
# has been updated to provide the capability to try a `find_package` at the
# content population stage. This allows for the flexibility provided by
# `FetchContent_` to easily coexist with the dependency configuration provided
# by `find_package`. Some benefits include: - automated fetching of content if
# it is unavailable - content population at configure. This allows for a package
# import to utilize `find_package()` or `add_subdirectory()` operations - local
# clones can be given priority with `FETCHCONTENT_SOURCE_DIR_<uppercaseName>`,
# which lets developers use dependencies explicitly in-tree The case where i.)
# the content or dependency is unavailable or ii.) it cannot be fetched (due,
# for example, to a failure of a download step) is an unavoidable error.
#
# :: Arguments
# pkg_name - the name of the package or content. usually the argument to `find_package(<pkg_name>)`
# options:
# - NO_FETCH - disables all download steps. If `find_package(<pkg_name>)` fails, produce an error
# single_value:
# - GIT_REPO - the URL of the git repository to clone, if necessary
# - GIT_TAG - the tag or commit to use
# - NAMESPACE - prefix book-keeping variables with this
# multi_value:
# - COMPONENTS - specify required components of <pkg_name>. same as used in to `find_package`
# - EXPECTED_TARGETS - a list of targets that expected at population.
# if these targets are not available after the population stage, an error is produced.
# if not specified, will default to `<pkg_name>::<pkg_name>`
# - ENABLE_OPTS - a list of cache vars used to configure content that is imported using `add_subdirectory()`
# As this cmake version is relatively fresh, we also want to provide a bridge to
# downstream code that still is pinned to an earlier releases of `cmake`. To
# that end, these macros provide for replicating the main features introduced to
# `FetchContent_` in `[email protected]`. Future releases of `spiner` will require a
# minimum of `[email protected]`.
#
# :: Arguments pkg_name - the name of the package or content. usually the
# argument to `find_package(<pkg_name>)` options: - NO_FETCH - disables all
# download steps. If `find_package(<pkg_name>)` fails, produce an error
# single_value: - GIT_REPO - the URL of the git repository to clone, if
# necessary - GIT_TAG - the tag or commit to use - NAMESPACE - prefix
# book-keeping variables with this multi_value: - COMPONENTS - specify required
# components of <pkg_name>. same as used in to `find_package` - EXPECTED_TARGETS
# - a list of targets that expected at population. if these targets are not
# available after the population stage, an error is produced. if not specified,
# will default to `<pkg_name>::<pkg_name>` - ENABLE_OPTS - a list of cache vars
# used to configure content that is imported using `add_subdirectory()`
#
macro(spiner_content_declare pkg_name)
set(options
NO_FETCH
)
set(one_value_args
GIT_REPO
GIT_TAG
NAMESPACE
)
set(multi_value_args
COMPONENTS
EXPECTED_TARGETS
ENABLE_OPTS
)

cmake_parse_arguments(fp "${options}" "${one_value_args}" "${multi_value_args}" "${ARGN}")
set(options NO_FETCH)
set(one_value_args GIT_REPO GIT_TAG NAMESPACE)
set(multi_value_args COMPONENTS EXPECTED_TARGETS ENABLE_OPTS)

cmake_parse_arguments(fp "${options}" "${one_value_args}"
"${multi_value_args}" "${ARGN}")

string(TOUPPER ${pkg_name} pkg_CAP)
string(REPLACE "-" "_" pkg_CAP "${pkg_CAP}")

message(STATUS
"[${pkg_name}] FetchContent_Declare wrapper"
)
# because the signature is different between versions,
# we build the cmake call beforehand
# loop through targets, if already present then bail
foreach(expected_target ${fp_EXPECTED_TARGETS};${pkg_name})
if(TARGET ${expected_target})
message(
STATUS
"target \"${expected_target}\" already exists, tagging ${pkg_name} as already present"
)
set(${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_ALREADY_PRESENT TRUE)
break()
endif()
endforeach()

message(STATUS "[${pkg_name}] FetchContent_Declare wrapper")
# because the signature is different between versions, we build the cmake call
# beforehand
set(_fetch_content_cmd "FetchContent_Declare(${pkg_name}")

if(fp_NO_FETCH)
message(VERBOSE
message(
VERBOSE
" :: \"${pkg_name}\" is specified not fetchable, will rely on `find_package` for population"
)
string(APPEND _fetch_content_cmd " DOWNLOAD_COMMAND \":\"")
string(APPEND _fetch_content_cmd " DOWNLOAD_COMMAND \":\"")
set(${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_NOFETCH TRUE)
else()
message(VERBOSE
message(
VERBOSE
" :: \"${pkg_name}\" is fetchable, will fall-back to git clone [${fp_GIT_REPO}] if other population methods fail"
)
string(APPEND _fetch_content_cmd " GIT_REPOSITORY ${fp_GIT_REPO} GIT_TAG ${fp_GIT_TAG}")
string(APPEND _fetch_content_cmd
" GIT_REPOSITORY ${fp_GIT_REPO} GIT_TAG ${fp_GIT_TAG}")
endif()

# bifurcation on cmake version
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
# versions >= 3.24 will do an implicit `find_package`, so pass on
# requirements to declaration
string(APPEND _fetch_content_cmd " FIND_PACKAGE_ARGS COMPONENTS ${fp_COMPONENTS}")
string(APPEND _fetch_content_cmd
" FIND_PACKAGE_ARGS COMPONENTS ${fp_COMPONENTS}")
endif()

# close the command
Expand All @@ -101,7 +100,7 @@ macro(spiner_content_declare pkg_name)
cmake_language(EVAL CODE "${_fetch_content_cmd}")
# be safe and destroy the string
unset(_fetch_content_cmd)

# return some info

list(APPEND ${fp_NAMESPACE}_DECLARED_EXTERNAL_CONTENT ${pkg_name})
Expand All @@ -110,102 +109,103 @@ macro(spiner_content_declare pkg_name)
set(${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_PRIORS ${fp_PRIORS})

if(fp_EXPECTED_TARGETS)
set(${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_TARGETS ${fp_EXPECTED_TARGETS})
set(${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_TARGETS
${fp_EXPECTED_TARGETS})
else()
set(${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_TARGETS "${pkg_name}::${pkg_name}")
set(${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_TARGETS
"${pkg_name}::${pkg_name}")
endif()

endmacro()

#
# :: Overview
# This macro wraps a call to `FetchContent_MakeAvailable`.
# If we are using a `cmake` prior to 3.24, explicitly do a
# `find_package()` with the declared options
# :: Arguments
# single_value:
# - NAMESPACE - the namespace used in the corrisponding `spiner_content_declare` call
# :: Overview This macro wraps a call to `FetchContent_MakeAvailable`. If we are
# using a `cmake` prior to 3.24, explicitly do a `find_package()` with the
# declared options :: Arguments single_value: - NAMESPACE - the namespace used
# in the corrisponding `spiner_content_declare` call
#
macro(spiner_content_populate)
set(options)
set(one_value_args
NAMESPACE
)
set(multi_value_args
)

cmake_parse_arguments(fp "${options}" "${one_value_args}" "${multi_value_args}" "${ARGN}")

message(STATUS
"[${fp_NAMESPACE}] Populating declared content"
)
# fill lists to populate
# if [email protected]+, these are just the lists prepared in spiner_content_declare
# otherwise, manually check `find_package` and remove content if found
set(one_value_args NAMESPACE)
set(multi_value_args)

cmake_parse_arguments(fp "${options}" "${one_value_args}"
"${multi_value_args}" "${ARGN}")

message(STATUS "[${fp_NAMESPACE}] Populating declared content")
# fill lists to populate if [email protected]+, these are just the lists prepared in
# spiner_content_declare otherwise, manually check `find_package` and remove
# content if found
foreach(pkg_name ${${fp_NAMESPACE}_DECLARED_EXTERNAL_CONTENT})
string(TOUPPER ${pkg_name} pkg_CAP)
string(REPLACE "-" "_" pkg_CAP "${pkg_CAP}")
# bifurcation on cmake version
if (NOT CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
find_package(${pkg_name}
COMPONENTS ${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_COMPONETS}
QUIET
)
if(${pkg_name}_FOUND)
message(VERBOSE
"${pkg_name} located with `find_package`"
"${pkg_name}_DIR: ${${pkg_name}_DIR}"
)

if(NOT ${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_ALREADY_PRESENT)
# bifurcation on cmake version
if(NOT CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
find_package(
${pkg_name}
COMPONENTS ${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_COMPONETS}
QUIET)
if(${pkg_name}_FOUND)
message(VERBOSE "${pkg_name} located with `find_package`"
"${pkg_name}_DIR: ${${pkg_name}_DIR}")
else()
# if no fetching and not found, produce an error conditionally include
# a custom error msg
if(${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_NOFETCH)
message(
FATAL_ERROR
"${pkg_name} is requested, but it was not located and is not declared as fetchable.\n"
"if ${pkg_name} is installed, set \"-D${pkg_name}_ROOT=<install-dir>\""
)
endif() # NOFETCH
message(
VERBOSE
"${pkg_name} NOT located with `find_package`, appending to populate list."
" Will attempt to clone repository when content is populated.")
list(APPEND _fetchList ${pkg_name})
list(APPEND _fetchOpts
${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_ENABLEOPTS})
list(APPEND _fetchTars
${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_TARGETS})

endif() # FOUND
else()
# if no fetching and not found, produce an error
# conditionally include a custom error msg
if(${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_NOFETCH)
message(FATAL_ERROR
"${pkg_name} is requested, but it was not located and is not declared as fetchable.\n"
"if ${pkg_name} is installed, set \"-D${pkg_name}_ROOT=<install-dir>\""
)
endif() # NOFETCH
message(VERBOSE
"${pkg_name} NOT located with `find_package`, appending to populate list."
" Will attempt to clone repository when content is populated."
)
list(APPEND _fetchList ${pkg_name})
list(APPEND _fetchOpts ${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_ENABLEOPTS})
list(APPEND _fetchTars ${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_TARGETS})

endif() # FOUND
else()
list(APPEND _fetchList ${pkg_name})
list(APPEND _fetchOpts ${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_ENABLEOPTS})
list(APPEND _fetchTars ${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_TARGETS})
endif() #CMAKE_VERSION
# collect all targets, reguardless of populated
list(APPEND _expectedTars ${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_TARGETS})
list(APPEND _fetchOpts
${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_ENABLEOPTS})
list(APPEND _fetchTars
${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_TARGETS})
endif() # CMAKE_VERSION
# collect all targets, reguardless of populated
endif() # _ALREADY_PRESENT
list(APPEND _expectedTars
${${fp_NAMESPACE}_DECLARED_EXTERNAL_${pkg_CAP}_TARGETS})
endforeach()

# for content to be populated, set some cache options given in spiner_content_declare
# for content to be populated, set some cache options given in
# spiner_content_declare
foreach(ext_opt ${_fetchOpts})
message(DEBUG "setting \"${ext_opt}\"=ON")
set(${ext_opt} ON CACHE INTERNAL "")
set(${ext_opt}
ON
CACHE INTERNAL "")
endforeach()

message(VERBOSE "\n"
" :: Populating dependency targets ${_fetchTars}\n"
" :: Calling `FetchContent_MakeAvailable` with ${_fetchList}\n"
)
message(STATUS
"FetchContent_MakeAvailable prepared, "
"this may take a few moments if a download is required...\n"
)
# populate
message(VERBOSE "\n" " :: Populating dependency targets ${_fetchTars}\n"
" :: Calling `FetchContent_MakeAvailable` with ${_fetchList}\n")
message(STATUS "FetchContent_MakeAvailable prepared, "
"this may take a few moments if a download is required...\n")
# populate
FetchContent_MakeAvailable(${_fetchList})

# check that declared targets exist
foreach(expected_target ${_expectedTars})
if(NOT TARGET ${expected_target})
message(FATAL_ERROR
message(
FATAL_ERROR
"target \"${expected_target}\" was expected, but does not exist after population!"
) # NOT TARGET
) # NOT TARGET
endif()
endforeach()

Expand All @@ -220,15 +220,13 @@ macro(spiner_content_populate)
unset(${fp_NAMESPACE}_DECLARED_EXTERNAL_CONTENT)
endmacro()


# © 2021. Triad National Security, LLC. All rights reserved. This
# program was produced under U.S. Government contract 89233218CNA000001
# for Los Alamos National Laboratory (LANL), which is operated by Triad
# National Security, LLC for the U.S. Department of Energy/National
# Nuclear Security Administration. All rights in the program are
# reserved by Triad National Security, LLC, and the U.S. Department of
# Energy/National Nuclear Security Administration. The Government is
# granted for itself and others acting on its behalf a nonexclusive,
# paid-up, irrevocable worldwide license in this material to reproduce,
# prepare derivative works, distribute copies to the public, perform
# © 2021. Triad National Security, LLC. All rights reserved. This program was
# produced under U.S. Government contract 89233218CNA000001 for Los Alamos
# National Laboratory (LANL), which is operated by Triad National Security, LLC
# for the U.S. Department of Energy/National Nuclear Security Administration.
# All rights in the program are reserved by Triad National Security, LLC, and
# the U.S. Department of Energy/National Nuclear Security Administration. The
# Government is granted for itself and others acting on its behalf a
# nonexclusive, paid-up, irrevocable worldwide license in this material to
# reproduce, prepare derivative works, distribute copies to the public, perform
# publicly and display publicly, and to permit others to do so.

0 comments on commit 1c229ff

Please sign in to comment.