Skip to content

Commit

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

* fixes failing unit test/refactors CMaizeProject

* backup [skip ci]

* adds_optional_dependency

* Apply suggestions from code review

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

* checks target type

---------

Co-authored-by: Zachery Crandall <[email protected]>
  • Loading branch information
ryanmrichard and zachcran authored Dec 22, 2023
1 parent 5d0bacd commit 2acab9a
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 2 deletions.
2 changes: 2 additions & 0 deletions cmake/cmaize/user_api/dependencies/dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@
include_guard()

include(cmaize/user_api/dependencies/find_dependency)
include(cmaize/user_api/dependencies/find_optional_dependency)
include(cmaize/user_api/dependencies/find_or_build_dependency)
64 changes: 64 additions & 0 deletions cmake/cmaize/user_api/dependencies/find_optional_dependency.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# 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()

include(cmaize/user_api/dependencies/impl_/check_optional_flag)
include(cmaize/user_api/dependencies/find_dependency)

#[[[
# Wraps the process of finding an optional dependency.
#
# Many build systems have optional dependencies. The inclusion of the dependency
# is usually controlled with a flag (e.g., a variable like ``ENABLE_XXX`` for
# an optional dependency ``XXX``). Because of CMake's verbose nature the logic
# for whether or not the dependency is enabled is usually needed in a few
# places. CMaize is capable of automatically propagating the logic for the
# user, as long as the user tells CMaize the flag that controls the inclusion of
# of the dependency.
#
# When the dependency is enabled this function essentially wraps
# ``cmaize_find_dependency`` and the user should consult the documentation for
# ``cmaize_find_dependency`` to understand the full set of options. When enabled
# CMaize will add a compile definition to the target with the same name as the
# flag (common practice for optional dependencies is to use such a definition
# for enabling/disabling sections of source code).
#
# When the dependency is disabled this function simply creates an interface
# target to serve as a placedholder for the dependency. The interface target
# has no state and linking to/installing the target does nothing.
#
# :param name: The name of the dependency.
# :type name: desc
# :param flag: The variable to use as a flag. Used to name the compile
# definition.
# :type flag: desc
# :param kwargs: Keyword arguments to forward to ``cmaize_find_dependency``.
# See the documentation for ``cmaize_find_dependency`` for the full list.
#]]
function(cmaize_find_optional_dependency _cfod_name _cfod_flag)

# N.B. ${_cfod_flag} is the variable serving as the flag and
# ${${_cfod_flag}} is its value

_check_optional_flag("${_cfod_flag}")

if("${${_cfod_flag}}")
cmaize_find_dependency("${_cfod_name}" ${ARGN})
target_compile_definitions("${_cfod_name}" INTERFACE "${_cfod_flag}")
elseif(NOT TARGET "${_cfod_name}")
add_library("${_cfod_name}" INTERFACE)
endif()

endfunction()
50 changes: 50 additions & 0 deletions cmake/cmaize/user_api/dependencies/impl_/check_optional_flag.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# 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()

include(cmakepp_lang/cmakepp_lang)

#[[[
# Code factorization for checking that a user passed the name of a flag.
#
# The various ``optional_dependency`` functions are tied to a configuration flag
# which determines whether the option is enabled or not. Users need to provide
# those functions with the name of the flag, NOT its value. This function wraps
# the error-checking logic for ensuring that users passed the value.
#
# :param flag: Name of the option variable.
# :type flag: desc
#
# :raises RUNTIME_ERROR: If ``flag`` is not the name of a variable. In
# particular we check for empty strings and truth-y/false-y values.
#
#]]
function(_check_optional_flag _cof_flag)

cpp_type_of(_cof_type "${_cof_flag}")

if("${_cof_flag}" STREQUAL "")
cpp_raise(
RUNTIME_ERROR
"Expected variable serving as the flag, received an empty string"
)
elseif("${_cof_type}" STREQUAL "bool")
cpp_raise(
RUNTIME_ERROR
"Expected variable serving as the flag, received boolean value."
)
endif()

endfunction()
1 change: 0 additions & 1 deletion cmake/cmaize/user_api/user_api.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,3 @@ include(cmaize/user_api/add_tests)
include(cmaize/user_api/cmaize_option)
include(cmaize/user_api/cmaize_project)
include(cmaize/user_api/dependencies/dependencies)
include(cmaize/user_api/find_or_build_dependency)
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# 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(cmake_test/cmake_test)
include(cmaize/user_api/dependencies/impl_/check_optional_flag)

ct_add_test(NAME "test_check_optional_flag")
function("${test_check_optional_flag}")

ct_add_section(NAME "empty_string" EXPECTFAIL)
function("${empty_string}")

_check_optional_flag("")

endfunction()

ct_add_section(NAME "value_is_true" EXPECTFAIL)
function("${value_is_true}")

_check_optional_flag(TRUE)

endfunction()

ct_add_section(NAME "value_is_on" EXPECTFAIL)
function("${value_is_on}")

_check_optional_flag(ON)

endfunction()

ct_add_section(NAME "value_is_false" EXPECTFAIL)
function("${value_is_false}")

_check_optional_flag(FALSE)

endfunction()

ct_add_section(NAME "value_is_off" EXPECTFAIL)
function("${value_is_off}")

_check_optional_flag(OFF)

endfunction()

ct_add_section(NAME "flag_value" EXPECTFAIL)
function("${flag_value}")

set(ENABLE_DEPENDENCY ON)
_check_optional_flag("${ENABLE_DEPENDENCY}")

endfunction()

ct_add_section(NAME "valid_flag")
function("${valid_flag}")
include(cmaize/user_api/dependencies/impl_/check_optional_flag)
_check_optional_flag(ENABLE_DEPENDENCY)

endfunction()

endfunction()
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# 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(cmake_test/cmake_test)
include(cmaize/user_api/cmaize_project)
include(cmaize/user_api/dependencies/find_optional_dependency)
include(cmaize/user_api/dependencies/impl_/get_package_manager)
include(cmaize/utilities/python)

ct_add_test(NAME "test_find_optional_dependency")
function("${test_find_optional_dependency}")

find_python(py_exe py_version)
create_virtual_env(
venv_dir
"${py_exe}"
"${CMAKE_BINARY_DIR}"
"${test_find_optional_dependency}"
)

# Make sure everything is using the venv Python
set(Python3_EXECUTABLE "${venv_dir}/bin/python3")

ct_add_section(NAME "is_disabled_empty")
function("${is_disabled_empty}")

ct_assert_target_does_not_exist(not_real_empty)

set(ENABLE_NOT_REAL "")
cmaize_find_optional_dependency(not_real_empty ENABLE_NOT_REAL)

ct_assert_target_exists(not_real_empty)
get_target_property(target_type not_real_empty TYPE)
ct_assert_equal(target_type "INTERFACE_LIBRARY")

endfunction()

ct_add_section(NAME "is_disabled_false")
function("${is_disabled_false}")

ct_assert_target_does_not_exist(not_real_false)

set(ENABLE_NOT_REAL FALSE)
cmaize_find_optional_dependency(not_real_false ENABLE_NOT_REAL)

ct_assert_target_exists(not_real_false)

# Verify we can call the function multiple times w/o error
cmaize_find_optional_dependency(not_real_false ENABLE_NOT_REAL)

endfunction()

ct_add_section(NAME "is_enabled")
function("${is_enabled}")

# Create a project
set(proj_name "test_find_optional_dependency_is_enabled")
project("${proj_name}")
cmaize_project("${proj_name}")
cpp_get_global(proj_obj CMAIZE_TOP_PROJECT)

# Create the specification for CMinx
PackageSpecification(CTOR cminx_corr)
PackageSpecification(SET "${cminx_corr}" name "cminx")

# Install CMinx
_fob_get_package_manager(pm "${proj_obj}" "pip")
PackageManager(install_package "${pm}" "${cminx_corr}")

set(ENABLE_CMINX TRUE)

ct_assert_target_does_not_exist(cminx)

cmaize_find_optional_dependency(cminx ENABLE_CMINX PACKAGE_MANAGER pip)

ct_assert_target_exists(cminx)

endfunction()

endfunction()
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

include(cmake_test/cmake_test)
include(cmaize/user_api/cmaize_project)
include(cmaize/user_api/find_or_build_dependency)
include(cmaize/user_api/dependencies/find_or_build_dependency)
include(cmaize/utilities/python)

#[[[
Expand Down

0 comments on commit 2acab9a

Please sign in to comment.