diff --git a/cth_target_utilities.cmake b/cth_target_utilities.cmake index c42c5f4..41d2056 100644 --- a/cth_target_utilities.cmake +++ b/cth_target_utilities.cmake @@ -875,4 +875,66 @@ function(cth_target_set_constexpr_steps target number) $<$:-fconstexpr-ops-limit=${number}> $<$:/constexpr:steps${number}> ) -endfunction(cth_target_set_constexpr_steps target number) \ No newline at end of file +endfunction(cth_target_set_constexpr_steps target number) +#[[.rst: +.. command:: cth_target_alias_any + + .. code-block:: cmake + + cth_target_alias_any( ) + + Creates ```` as an ALIAS of the first target in ```` that exists. + + Packages do not always export the same target name in every configuration. vcpkg's libhv for + example exports ``hv`` when built as a shared library and ``hv_static`` when built as a static + one. Linking the alias keeps that choice out of every ``target_link_libraries`` call. + + :param alias: Name of the alias target to create + :type alias: string + :param candidates: Target names to choose from, in order of preference + :type candidates: list of strings + + :pre: alias is not a target yet + :pre: at least one of the candidates is a target + :post: alias is an ALIAS of the first existing candidate + + .. code-block:: cmake + + find_package(libhv CONFIG REQUIRED) + cth_target_alias_any(libhv::hv hv hv_static) + + target_link_libraries(my_target PRIVATE libhv::hv) + + .. note:: + Candidates are checked in the given order, the first existing target wins. If that candidate + is an ALIAS itself, the new alias refers to the target behind it. + + .. note:: + ``find_package`` creates non-global imported targets. An alias of such a target is only + visible in the directory that created it and its subdirectories, same as the imported target. + + .. warning:: + Installed export sets record the aliased target, not the alias. Consumers of an installed + package need the same target that was picked at configure time. + +#]] +function(cth_target_alias_any ALIAS_NAME) + cth_assert_not_target("${ALIAS_NAME}" REASON "cth_target_alias_any: alias '${ALIAS_NAME}' already exists") + cth_assert_not_empty("${ARGN}" REASON "cth_target_alias_any: no candidates given for '${ALIAS_NAME}'") + + foreach(CANDIDATE IN LISTS ARGN) + if(NOT TARGET ${CANDIDATE}) + continue() + endif() + + get_target_property(ALIASED_TARGET ${CANDIDATE} ALIASED_TARGET) + if(ALIASED_TARGET) + set(CANDIDATE ${ALIASED_TARGET}) + endif() + + add_library(${ALIAS_NAME} ALIAS ${CANDIDATE}) + return() + endforeach() + + message(FATAL_ERROR "ERROR cth_target_alias_any: none of the candidates is a target [args: ${ARGV}]") +endfunction() diff --git a/readme.md b/readme.md index a13d2a9..bf1fc77 100644 --- a/readme.md +++ b/readme.md @@ -37,6 +37,7 @@ To help you set up targets and dependencies quicker: - `cth_glob_cpp` — recursive glob for common C++ source/header/file-set extensions and append results to a variable. - `cth_glob_cppm` — recursive glob for C++ module interface files (.cppm). - `cth_add_resources` — add a POST_BUILD step to copy resource directories next to a target's binary. + - `cth_target_alias_any` — alias the first existing target of several candidates (e.g. `hv` / `hv_static` depending on the linkage). - `cth_target_add_modules` — add C++ module files to a target with PUBLIC/PRIVATE visibility. - `cth_target_enable_sanitizers` — enable Address/Undefined sanitizers for specified targets/configurations. - `cth_target_enable_build_cache` — enable per-target build-cache integration ([installation](#optional))