Skip to content

Commit

Permalink
Adds find_dependency (#148)
Browse files Browse the repository at this point in the history
* backup [skip ci]

* fixes failing unit test/refactors CMaizeProject

* Update tests/cmaize/user_api/dependencies/test_find_dependency.cmake

Co-authored-by: Zachery Crandall <[email protected]>

---------

Co-authored-by: Zachery Crandall <[email protected]>
  • Loading branch information
ryanmrichard and zachcran authored Dec 22, 2023
1 parent f339094 commit 5d0bacd
Show file tree
Hide file tree
Showing 15 changed files with 832 additions and 254 deletions.
194 changes: 3 additions & 191 deletions cmake/cmaize/project/cmaize_project.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ include(cmakepp_lang/cmakepp_lang)

include(cmaize/targets/cmaize_target)
include(cmaize/project/package_specification)
include(cmaize/project/impl_/impl_)
include(cmaize/utilities/utilities)

#[[[
Expand Down Expand Up @@ -310,93 +311,12 @@ cpp_class(CMaizeProject)
cpp_member(add_target CMaizeProject str CMaizeTarget args)
function("${add_target}" self _at_target_name _at_target)

CMaizeProject(_add_target
_add_target(
"${self}" "${_at_target}" NAME "${_at_target_name}" ${ARGN}
)

endfunction()

#[[[
# Add a target to the project. Duplicate objects will not be added.
#
# This internal implementation exists so a required keyword argument is
# not part of the public interface, as well as to handle both ``desc`` and
# ``target`` types. Both types are effectively strings representing target
# names in this algorithm and can be treated equivalently, but cannot be
# typecast to each other by CMakePPLang. The CMakePPLang type checking
# is bypassed through the aforementioned required keyword argument for
# the target name, essentially combining the two types.
#
# :param __at_target: CMaizeTarget object to be added.
# :type __at_target: CMaizeTarget
# :param NAME: Required keyword argument. See description below.
# :type NAME: desc or target
# :param **kwargs: Additional keyword arguments may be necessary.
#
# :Keyword Arguments:
# * **INSTALLED** (*bool*) --
# Flag to indicate that the target being added is already installed
# on the system.
# * **NAME** (*desc* or *target*) --
# Identifying name for the target. This can match name of either the
# CMake target or CMaizeTarget object, but is required to do match
# them. This keyword argument is **required**.
#]]
cpp_member(_add_target CMaizeProject CMaizeTarget args)
function("${_add_target}" self __at_target)

set(__at_options INSTALLED OVERWRITE)
set(__at_one_value_args NAME)
cmake_parse_arguments(__at "${__at_options}" "${__at_one_value_args}" "" ${ARGN})

# Ensure that NAME was provided
cpp_contains(__at_name_exists __at_NAME "${__at_KEYWORDS_MISSING_VALUES}")
if(__at_name_exists)
cpp_raise(KeywordMissing "Missing required keyword argument: NAME")
endif()

# Default to the build target list
set(__at_tgt_attr "build_targets")
if(__at_INSTALLED)
set(__at_tgt_attr "installed_targets")
endif()

# Check if a CMaizeTarget with the same name exists already as either
# a build or installed target
CMaizeProject(check_target "${self}" __at_found "${__at_NAME}" ALL)

# Determine what to do if a target in the project al
if(__at_found)
# Exit early if a target with the same name already exists
if(NOT __at_OVERWRITE)
cpp_return("")
endif()

CMaizeProject(check_target "${self}" __at_build "${__at_NAME}")
CMaizeProject(check_target "${self}" __at_install "${__at_NAME}" INSTALLED)

if(__at_build)
CMaizeProject(GET "${self}" __at_tgt_map "build_targets")

# We cannot easily remove a key from a map, so the best option
# is to just clear the value
cpp_map(SET "${__at_tgt_map}" "${__at_NAME}" "")
endif()

if (__at_install)
CMaizeProject(GET "${self}" __at_tgt_map "installed_targets")

cpp_map(SET "${__at_tgt_map}" "${__at_NAME}" "")
endif()
endif()

# Add the target to the map
CMaizeProject(GET "${self}" __at_tgt_map "${__at_tgt_attr}")

cpp_map(SET "${__at_tgt_map}" "${__at_NAME}" "${__at_target}")

endfunction()

#[[[
# Checks if a package manager with the same type is already added to
# this project.
Expand Down Expand Up @@ -450,121 +370,13 @@ cpp_class(CMaizeProject)
cpp_member(check_target CMaizeProject desc str args)
function("${check_target}" self _ct_found _ct_target_name)

CMaizeProject(_check_target
_check_target(
"${self}" "${_ct_found}" NAME "${_ct_target_name}" ${ARGN}
)
cpp_return("${_ct_found}")

endfunction()

#[[[
# Checks if a target with the same name is already added to this project.
# This defaults to build targets.
#
# This internal implementation exists so a required keyword argument is
# not part of the public interface, as well as to handle both ``desc`` and
# ``target`` types. Both types are effectively strings representing target
# names in this algorithm and can be treated equivalently, but cannot be
# typecast to each other by CMakePPLang. The CMakePPLang type checking
# is bypassed through the aforementioned required keyword argument for
# the target name, essentially combining the two types.
#
# :param __ct_found: Return variable for whether the target was found.
# :type __ct_found: bool*
# :param NAME: Required keyword argument. See description below.
# :type NAME: desc or target
# :param **kwargs: Additional keyword arguments may be necessary.
#
# :Keyword Arguments:
# * **INSTALLED** (*bool*) --
# Flag to indicate that the installed targets should be checked.
# * **ALL** (*bool*) --
# Flag to indicate that both the build and installed targets should
# be checked.
# * **NAME** (*desc* or *target*) --
# Identifying name for a target contained in the current Cmaize
# project. This keyword argument is **required**.
#
# :returns: CMaizeTarget found (TRUE) or not (FALSE).
# :rtype: bool
#]]
cpp_member(_check_target CMaizeProject desc args)
function("${_check_target}" self __ct_found)

set(__ct_options INSTALLED ALL)
set(__ct_one_value_args NAME)
cmake_parse_arguments(
__ct "${__ct_options}" "${__ct_one_value_args}" "" ${ARGN}
)

# Ensure that NAME was provided
cpp_contains(
__ct_name_exists __ct_NAME "${__ct_KEYWORDS_MISSING_VALUES}"
)
if(__ct_name_exists)
cpp_raise(KeywordMissing "Missing required keyword argument: NAME")
endif()

# Default to the build target list
set(__ct_tgt_attr "build_targets")
if(__ct_ALL)
# Search both lists
list(APPEND __ct_tgt_attr "installed_targets")
elseif(__ct_INSTALLED)
set(__ct_tgt_attr "installed_targets")
endif()

foreach(__ct_tgt_attr_i ${__ct_tgt_attr})
# Get the collection of targets
CMaizeProject(GET "${self}" __ct_tgt_map "${__ct_tgt_attr_i}")

# We check if the key exists here, but that is not sufficient
# to know if the value is valid if the key exists. It is possible
# from CMaizeProject(add_target for the key to exist but the value
# to be blank since we cannot easily remove keys from a map
cpp_map(KEYS "${__ct_tgt_map}" __ct_keys)
cpp_contains(__ct_key_found_i "${__ct_NAME}" "${__ct_keys}")

# Initially, set whether the key was found and whether the target
# was found to the same value, but this may change in the check
# below
set(__ct_found_i "${__ct_key_found_i}")

# Check if a target with the same name already exists
# NOTE: Doesn't work for some reason
# cpp_map(HAS_KEY "${__ct_tgt_map}" __ct_found_i "${__ct_NAME}")

# Additional check if the target key exists in the map to ensure
# that the target actually exists
if(__ct_key_found_i)
cpp_map(GET "${__ct_tgt_map}" __ct_key_value_i "${__ct_NAME}")

cpp_type_of(__ct_key_value_i_type "${__ct_key_value_i}")

# Check if the value is implicitly convertible to a CMaizeTarget
cpp_implicitly_convertible(__ct_is_target_type "${__ct_key_value_i_type}" CMaizeTarget)

# If it is convertible to a CMaizeTarget, then the target exists
set(__ct_found_i "${__ct_is_target_type}")

# Unless it is an empty string
if("${__ct_key_value_i}" STREQUAL "")
set(__ct_found_i FALSE)
endif()
endif()

# Return early if target is found
if(__ct_found_i)
set("${__ct_found}" "${__ct_found_i}")
cpp_return("${__ct_found}")
endif()
endforeach()

set("${__ct_found}" FALSE)
cpp_return("${__ct_found}")

endfunction()

#[[[
# Get a package manager of the requested type stored in the project.
#
Expand Down
99 changes: 99 additions & 0 deletions cmake/cmaize/project/impl_/add_target.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright 2023 CMakePP
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

include_guard()

#[[[
# Add a target to the project. Duplicate objects will not be added.
#
# This internal implementation exists so a required keyword argument is
# not part of the public interface, as well as to handle both ``desc`` and
# ``target`` types. Both types are effectively strings representing target
# names in this algorithm and can be treated equivalently, but cannot be
# typecast to each other by CMakePPLang. The CMakePPLang type checking
# is bypassed through the aforementioned required keyword argument for
# the target name, essentially combining the two types.
#
# :param __at_target: CMaizeTarget object to be added.
# :type __at_target: CMaizeTarget
# :param NAME: Required keyword argument. See description below.
# :type NAME: desc or target
# :param **kwargs: Additional keyword arguments may be necessary.
#
# :Keyword Arguments:
# * **INSTALLED** (*bool*) --
# Flag to indicate that the target being added is already installed
# on the system.
# * **NAME** (*desc* or *target*) --
# Identifying name for the target. This can match name of either the
# CMake target or CMaizeTarget object, but is required to do match
# them. This keyword argument is **required**.
#]]
function(_add_target self __at_target)

set(__at_options INSTALLED OVERWRITE)
set(__at_one_value_args NAME)
cmake_parse_arguments(
__at "${__at_options}" "${__at_one_value_args}" "" ${ARGN}
)

# Ensure that NAME was provided
cpp_contains(__at_name_exists __at_NAME "${__at_KEYWORDS_MISSING_VALUES}")
if(__at_name_exists)
cpp_raise(KeywordMissing "Missing required keyword argument: NAME")
endif()

# Default to the build target list
set(__at_tgt_attr "build_targets")
if(__at_INSTALLED)
set(__at_tgt_attr "installed_targets")
endif()

# Check if a CMaizeTarget with the same name exists already as either
# a build or installed target
CMaizeProject(check_target "${self}" __at_found "${__at_NAME}" ALL)

# Determine what to do if a target in the project al
if(__at_found)
# Exit early if a target with the same name already exists
if(NOT __at_OVERWRITE)
cpp_return("")
endif()

CMaizeProject(check_target "${self}" __at_build "${__at_NAME}")
CMaizeProject(
check_target "${self}" __at_install "${__at_NAME}" INSTALLED
)

if(__at_build)
CMaizeProject(GET "${self}" __at_tgt_map "build_targets")

# We cannot easily remove a key from a map, so the best option
# is to just clear the value
cpp_map(SET "${__at_tgt_map}" "${__at_NAME}" "")
endif()

if (__at_install)
CMaizeProject(GET "${self}" __at_tgt_map "installed_targets")

cpp_map(SET "${__at_tgt_map}" "${__at_NAME}" "")
endif()
endif()

# Add the target to the map
CMaizeProject(GET "${self}" __at_tgt_map "${__at_tgt_attr}")

cpp_map(SET "${__at_tgt_map}" "${__at_NAME}" "${__at_target}")

endfunction()
Loading

0 comments on commit 5d0bacd

Please sign in to comment.