From 4c5cbadf4cb8849ae3238dd5d9c2cef74bff7a7e Mon Sep 17 00:00:00 2001 From: Pieter Pas Date: Sun, 17 Dec 2023 20:47:51 +0100 Subject: [PATCH] [CMake] add dl-problem.cmake --- examples/problems/CMakeLists.txt | 30 +---- .../problems/sparse-logistic-regression.cpp | 2 +- src/CMakeLists.txt | 1 + src/cmake/DlConfig.cmake.in | 1 + src/cmake/Install.cmake | 8 ++ src/cmake/dl-problem.cmake | 114 ++++++++++++++++++ 6 files changed, 126 insertions(+), 30 deletions(-) create mode 100644 src/cmake/dl-problem.cmake diff --git a/examples/problems/CMakeLists.txt b/examples/problems/CMakeLists.txt index 31e94a9c7f..fce5c31a3f 100644 --- a/examples/problems/CMakeLists.txt +++ b/examples/problems/CMakeLists.txt @@ -1,31 +1,3 @@ -function(configure_problem_visibility target) - set_target_properties(${target} PROPERTIES CXX_VISIBILITY_PRESET "hidden" - C_VISIBILITY_PRESET "hidden" - VISIBILITY_INLINES_HIDDEN true) - if (CMAKE_SYSTEM_NAME MATCHES "Linux") - set(VERSION_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/${target}-export.lds") - file(WRITE ${VERSION_SCRIPT} - "{ global: register_alpaqa_problem; local: *; };") - target_link_options(${target} PRIVATE - "LINKER:--version-script=${VERSION_SCRIPT}") - endif() -endfunction() - -function(add_problem_module name) - add_library(${name} MODULE "${name}.cpp") - target_link_libraries(${name} PRIVATE alpaqa::dl-api alpaqa::alpaqa) - configure_problem_visibility(${name}) - set_target_properties(${name} PROPERTIES - PREFIX "" RELEASE_POSTFIX "" DEBUG_POSTFIX "" RELWITHDEBINFO_POSTFIX "" - MINSIZEREL_POSTFIX "") - include(GenerateExportHeader) - generate_export_header(${name} - EXPORT_FILE_NAME export/${name}-export.h) - target_include_directories(${name} PRIVATE - $) - target_compile_features(${name} PRIVATE cxx_std_20) -endfunction() - if (TARGET alpaqa::dl-api) - add_problem_module("sparse-logistic-regression") + alpaqa_add_dl_problem_module("sparse-logistic-regression" LINK_ALPAQA) endif() diff --git a/examples/problems/sparse-logistic-regression.cpp b/examples/problems/sparse-logistic-regression.cpp index 5a45ced004..9e000a02e1 100644 --- a/examples/problems/sparse-logistic-regression.cpp +++ b/examples/problems/sparse-logistic-regression.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 81ef07bfd3..f11c2890eb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -190,6 +190,7 @@ if (MSVC) target_compile_options(dl-api INTERFACE "/utf-8") endif() add_library(alpaqa::dl-api ALIAS dl-api) +include(cmake/dl-problem.cmake) list(APPEND ALPAQA_INSTALL_TARGETS dl-api) if (ALPAQA_WITH_DL) diff --git a/src/cmake/DlConfig.cmake.in b/src/cmake/DlConfig.cmake.in index 993be7b268..cd97588e8a 100644 --- a/src/cmake/DlConfig.cmake.in +++ b/src/cmake/DlConfig.cmake.in @@ -1,5 +1,6 @@ @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/alpaqaDlTargets.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/dl-problem.cmake") check_required_components(alpaqaDl) \ No newline at end of file diff --git a/src/cmake/Install.cmake b/src/cmake/Install.cmake index 1e6a224851..6841be8aba 100644 --- a/src/cmake/Install.cmake +++ b/src/cmake/Install.cmake @@ -61,6 +61,13 @@ macro(alpaqa_install_headers DIR COMP) FILES_MATCHING REGEX "/.*\\.(h|[hti]pp)$") endmacro() +macro(alpaqa_install_cmake FILES COMP) + # Install a CMake script + install(FILES ${FILES} + DESTINATION "${ALPAQA_INSTALL_CMAKEDIR}" + COMPONENT ${COMP}) +endmacro() + set(ALPAQA_INSTALLED_TARGETS_MSG "\nSummary of alpaqa components and targets to install:\n\n") # Install the alpaqa core libraries @@ -103,6 +110,7 @@ install(TARGETS dl-api EXPORT alpaqaDlTargets) alpaqa_install_config(Dl dl_dev) alpaqa_install_headers("${PROJECT_SOURCE_DIR}/src/interop/dl-api/include/" dl_dev) +alpaqa_install_cmake("${CMAKE_CURRENT_SOURCE_DIR}/cmake/dl-problem.cmake" dl_dev) string(APPEND ALPAQA_INSTALLED_TARGETS_MSG " * Dl: dl-api\n") # Install everything else diff --git a/src/cmake/dl-problem.cmake b/src/cmake/dl-problem.cmake new file mode 100644 index 0000000000..1fefef49ab --- /dev/null +++ b/src/cmake/dl-problem.cmake @@ -0,0 +1,114 @@ +function(alpaqa_configure_dl_problem_visibility target) + cmake_parse_arguments(ALPAQA_CONFIG_VIS "" "FUNCTION_NAME" "" ${ARGN}) + if (NOT DEFINED ALPAQA_CONFIG_VIS_FUNCTION_NAME) + set(ALPAQA_CONFIG_VIS_FUNCTION_NAME "register_alpaqa_problem") + endif() + set_target_properties(${target} PROPERTIES CXX_VISIBILITY_PRESET "hidden" + C_VISIBILITY_PRESET "hidden" + VISIBILITY_INLINES_HIDDEN true) + if (CMAKE_SYSTEM_NAME MATCHES "Linux") + set(VERSION_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/${target}-export.lds") + file(WRITE ${VERSION_SCRIPT} + "{ global: ${ALPAQA_CONFIG_VIS_FUNCTION_NAME}; local: *; };") + target_link_options(${target} PRIVATE + "LINKER:--version-script=${VERSION_SCRIPT}" + "LINKER:--exclude-libs,ALL") + endif() +endfunction() + +#[=======================================================================[.rst: + +.. cmake:command:: + alpaqa_add_dl_problem_module + + Create a target for a problem that can be dymanically loaded by the alpaqa + solvers. + + .. code-block:: cmake + + alpaqa_add_dl_problem_module( [LINK_ALPAQA] [VISIBILITY_DEFAULT] [NO_CXX_20] + [FUNCTION_NAME ] [FILES [ ...]]) + + This function creates a CMake module library target with the given name + ````, configures the correct visibility settings, generates an + export header, and links to the correct alpaqa targets. + + If the ``FILES`` option is given, the provided files are added to the + target. Otherwise, the source file ``${target}.cpp`` in the current folder + is used. + + In addition to linking to the ``alpaqa::dl-api`` headers, the main + ``alpaqa::alpaqa`` library is linked as well if the ``LINK_ALPAQA`` + option is provided. This is useful if you need any of the utility functions + provided by alpaqa. + + If the ``VISIBILITY_DEFAULT`` option is given the visibility settings are + not altered. + + By default, C++20 support is enabled, unless the ``NO_CXX_20`` is given. + + ``FUNCTION_NAME`` can be used to set the name of the registration function + to expose. It should match the function defined in the module (which + should have C linkage, using ``extern "C"``). + + .. note:: + + This function requires the alpaqa ``Dl`` component to be loaded: + + .. code-block:: cmake + + find_package(alpaqa 1.0.0 REQUIRED COMPONENTS Dl) + + If ``LINK_ALPAQA`` is specified, both the ``Core`` and ``Dl`` components + are required. + +#]=======================================================================] +function(alpaqa_add_dl_problem_module target) + cmake_parse_arguments(ALPAQA_PROBLEM_MODULE + "LINK_ALPAQA;VISIBILITY_DEFAULT;NO_CXX_20" + "FUNCTION_NAME" + "FILES" ${ARGN}) + # The resulting binary is meant to be loaded dynamically, so we use the + # MODULE type (not SHARED). + if (DEFINED ALPAQA_PROBLEM_MODULE_FILES) + add_library(${target} MODULE ${ALPAQA_PROBLEM_MODULE_FILES}) + else() + add_library(${target} MODULE "${target}.cpp") + endif() + # We only depend on the dl-api header, but the user might also want to link + # to the main alpaqa library for some convenience functions. + target_link_libraries(${target} PRIVATE alpaqa::dl-api) + if (ALPAQA_PROBLEM_MODULE_LINK_ALPAQA) + if (NOT TARGET alpaqa::alpaqa) + message(FATAL_ERROR "The LINK_ALPAQA option requires the alpaqa Core component to be loaded. " + "For example, use find_package(alpaqa ${ALPAQA_VERSION} REQUIRED COMPONENTS Core Dl).\n") + endif() + target_link_libraries(${target} PRIVATE alpaqa::alpaqa) + endif() + # Enable C++20 for optional features of the DL api + if (NOT ALPAQA_PROBLEM_MODULE_NO_CXX_20) + target_compile_features(${target} PRIVATE cxx_std_20) + endif() + # By default, we want to hide all symbols except the main entry point, to + # avoid conflicts and to keep the interface clean + if (NOT ALPAQA_PROBLEM_MODULE_VISIBILITY_DEFAULT) + if (DEFINED ALPAQA_PROBLEM_MODULE_FUNCTION_NAME) + alpaqa_configure_dl_problem_visibility(${target} + FUNCTION_NAME ${ALPAQA_PROBLEM_MODULE_FUNCTION_NAME}) + else() + alpaqa_configure_dl_problem_visibility(${target}) + endif() + endif() + # We don't want any prefixes or postfixes to modify the file name + set_target_properties(${target} PROPERTIES + PREFIX "" RELEASE_POSTFIX "" DEBUG_POSTFIX "" RELWITHDEBINFO_POSTFIX "" + MINSIZEREL_POSTFIX "") + # Finally, we generate a header with macros to export the entry point + # symbols (e.g. using [[gnu::visibility("default")]] for GCC and Clang, or + # __declspec(dllexport) for MSVC) + include(GenerateExportHeader) + generate_export_header(${target} + EXPORT_FILE_NAME export-${target}/${target}/export.h) + target_include_directories(${target} PRIVATE + $) +endfunction()