diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1967fdc502..3497fe6125 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,7 +113,7 @@ jobs: - name: Install dependencies (Linux) if: runner.os == 'Linux' timeout-minutes: 5 - run: sudo apt-get update && sudo apt-get install --yes ccache ${{ matrix.platform.apt }} + run: sudo apt-get update && sudo apt-get install --yes ccache pkg-config meson ninja-build ${{ matrix.platform.apt }} - name: Install dependencies (Windows) if: runner.os == 'Windows' run: choco install ccache --no-progress --yes @@ -147,6 +147,8 @@ jobs: - run: ccache --zero-stats - run: cmake --version + - run: meson --version + if: runner.os != 'Windows' - name: Configure (static) if: matrix.platform.type == 'static' run: > diff --git a/Brewfile b/Brewfile index 65799b9e8d..5945ce08ff 100644 --- a/Brewfile +++ b/Brewfile @@ -1,3 +1,6 @@ brew "ccache" brew "cmake" brew "doxygen" +brew "meson" +brew "ninja" +brew "pkgconf" diff --git a/CMakeLists.txt b/CMakeLists.txt index af73da73a5..a512c08e58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -267,6 +267,12 @@ if(SOURCEMETA_CORE_DIFF) add_subdirectory(src/core/diff) endif() +if(SOURCEMETA_CORE_INSTALL) + sourcemeta_pkgconfig_install(NAME sourcemeta_core + DESCRIPTION "${PROJECT_DESCRIPTION}" + COMPONENT sourcemeta_core_dev) +endif() + if(SOURCEMETA_CORE_DOCS) sourcemeta_target_doxygen(CONFIG "${PROJECT_SOURCE_DIR}/doxygen/Doxyfile.in" OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/website") diff --git a/cmake/FindCMarkGFM.cmake b/cmake/FindCMarkGFM.cmake index 6d50b32b36..e8a290cb3b 100644 --- a/cmake/FindCMarkGFM.cmake +++ b/cmake/FindCMarkGFM.cmake @@ -112,6 +112,7 @@ if(NOT CMarkGFM_FOUND) EXPORT_FILE_NAME "${CMARK_GFM_BINARY_DIR}/include/cmark-gfm_export.h") add_library(CMarkGFM::cmark_gfm ALIAS cmark_gfm) + sourcemeta_pkgconfig_declare(TARGET CMarkGFM::cmark_gfm LIBS "-lcmark_gfm") set_target_properties(cmark_gfm PROPERTIES diff --git a/cmake/FindLibDeflate.cmake b/cmake/FindLibDeflate.cmake index 1b830bbbb8..94fa5ccf11 100644 --- a/cmake/FindLibDeflate.cmake +++ b/cmake/FindLibDeflate.cmake @@ -136,6 +136,7 @@ if(NOT LibDeflate_FOUND) EXPORT_NAME LibDeflate) add_library(LibDeflate::LibDeflate ALIAS libdeflate) + sourcemeta_pkgconfig_declare(TARGET LibDeflate::LibDeflate LIBS "-ldeflate") if(SOURCEMETA_CORE_INSTALL) include(GNUInstallDirs) diff --git a/cmake/FindMimalloc.cmake b/cmake/FindMimalloc.cmake index 9ac939c40e..626f99576e 100644 --- a/cmake/FindMimalloc.cmake +++ b/cmake/FindMimalloc.cmake @@ -127,6 +127,22 @@ if(NOT Mimalloc_FOUND) add_library(Mimalloc::Mimalloc ALIAS mimalloc_interface) + # The same whole archive requirement as above, spelled for the consumers that + # read what we package rather than what CMake exports + if(BUILD_SHARED_LIBS) + sourcemeta_pkgconfig_declare(TARGET Mimalloc::Mimalloc LIBS "-lmimalloc") + elseif(APPLE) + sourcemeta_pkgconfig_declare(TARGET Mimalloc::Mimalloc + LIBS "-Wl,-force_load,\${libdir}/libmimalloc.a") + elseif(SOURCEMETA_COMPILER_MSVC) + sourcemeta_pkgconfig_declare(TARGET Mimalloc::Mimalloc + LIBS "/WHOLEARCHIVE:mimalloc.lib") + else() + sourcemeta_pkgconfig_declare(TARGET Mimalloc::Mimalloc + LIBS "-Wl,--whole-archive \${libdir}/libmimalloc.a" + "-Wl,--no-whole-archive") + endif() + if(SOURCEMETA_CORE_INSTALL) install(TARGETS mimalloc mimalloc_interface EXPORT mimalloc diff --git a/cmake/FindPCRE2.cmake b/cmake/FindPCRE2.cmake index 21756978b6..52256fefce 100644 --- a/cmake/FindPCRE2.cmake +++ b/cmake/FindPCRE2.cmake @@ -178,6 +178,7 @@ if(NOT PCRE2_FOUND) target_link_libraries(pcre2 PRIVATE sljit) add_library(PCRE2::pcre2 ALIAS pcre2) + sourcemeta_pkgconfig_declare(TARGET PCRE2::pcre2 LIBS "-lpcre2" "-lsljit") set_target_properties(pcre2 PROPERTIES diff --git a/cmake/Sourcemeta.cmake b/cmake/Sourcemeta.cmake index 70eaef5ab9..56da62fbec 100644 --- a/cmake/Sourcemeta.cmake +++ b/cmake/Sourcemeta.cmake @@ -8,6 +8,7 @@ include("${SOURCEMETA_UTILITIES_DIRECTORY}/compiler/options.cmake") include("${SOURCEMETA_UTILITIES_DIRECTORY}/options/enum.cmake") include("${SOURCEMETA_UTILITIES_DIRECTORY}/commands/copy-file.cmake") include("${SOURCEMETA_UTILITIES_DIRECTORY}/targets/library.cmake") +include("${SOURCEMETA_UTILITIES_DIRECTORY}/targets/pkgconfig.cmake") include("${SOURCEMETA_UTILITIES_DIRECTORY}/targets/executable.cmake") include("${SOURCEMETA_UTILITIES_DIRECTORY}/targets/clang-format.cmake") include("${SOURCEMETA_UTILITIES_DIRECTORY}/targets/shellcheck.cmake") diff --git a/cmake/common/targets/library.cmake b/cmake/common/targets/library.cmake index 8085dee0d7..8ca1231a4e 100644 --- a/cmake/common/targets/library.cmake +++ b/cmake/common/targets/library.cmake @@ -177,4 +177,6 @@ function(sourcemeta_library_install) DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${SOURCEMETA_LIBRARY_PROJECT}" NAMESPACE ${NAMESPACE_PREFIX} COMPONENT ${COMPONENT_NAME}_dev) + + sourcemeta_pkgconfig_register(${TARGET_NAME}) endfunction() diff --git a/cmake/common/targets/pkgconfig.cmake b/cmake/common/targets/pkgconfig.cmake new file mode 100644 index 0000000000..ea8d8d772b --- /dev/null +++ b/cmake/common/targets/pkgconfig.cmake @@ -0,0 +1,224 @@ +# Teach the packaging how to link against something that is not one of our own +# modules. A project states this next to wherever it brings the dependency in, +# so that adding one never means editing this file +function(sourcemeta_pkgconfig_declare) + cmake_parse_arguments(SOURCEMETA_PKGCONFIG "" "TARGET" "LIBS" ${ARGN}) + set_property(GLOBAL PROPERTY + "SOURCEMETA_PKGCONFIG_LIBS_${SOURCEMETA_PKGCONFIG_TARGET}" + "${SOURCEMETA_PKGCONFIG_LIBS}") +endfunction() + +# The order matters, as a module is declared after everything it depends on +function(sourcemeta_pkgconfig_register TARGET_NAME) + set_property(GLOBAL APPEND PROPERTY + SOURCEMETA_PKGCONFIG_TARGETS "${TARGET_NAME}") +endfunction() + +function(sourcemeta_pkgconfig_dependency LIBRARY MODULES_VARIABLE LIBS_VARIABLE) + set(MODULES ${${MODULES_VARIABLE}}) + set(LIBS ${${LIBS_VARIABLE}}) + + # CMake keeps the private dependencies of a static library in its exported + # link interface behind this expression, as they take part in linking but not + # in compiling against the library + string(REGEX REPLACE "^\\$$" "\\1" LIBRARY "${LIBRARY}") + + get_property(DECLARED GLOBAL PROPERTY + "SOURCEMETA_PKGCONFIG_LIBS_${LIBRARY}" SET) + + if(LIBRARY MATCHES "^sourcemeta::") + string(REPLACE "::" "_" MODULE "${LIBRARY}") + list(APPEND MODULES "${MODULE}") + elseif(DECLARED) + get_property(DECLARED_LIBS GLOBAL PROPERTY + "SOURCEMETA_PKGCONFIG_LIBS_${LIBRARY}") + list(APPEND LIBS ${DECLARED_LIBS}) + elseif(LIBRARY MATCHES "\\.framework$") + get_filename_component(FRAMEWORK "${LIBRARY}" NAME_WE) + list(APPEND LIBS "-framework ${FRAMEWORK}") + elseif(LIBRARY MATCHES "^-") + list(APPEND LIBS "${LIBRARY}") + elseif(NOT TARGET "${LIBRARY}" AND LIBRARY MATCHES "^[a-zA-Z0-9_]+$") + # A bare name that names no target is what CMake reads as a library the + # system already provides, which is linked the same way here + list(APPEND LIBS "-l${LIBRARY}") + else() + message(FATAL_ERROR "Cannot express the dependency on ${LIBRARY} as a " + "pkg-config flag. Declare it with sourcemeta_pkgconfig_declare, so that " + "consumers that do not read CMake keep linking against it") + endif() + + set("${MODULES_VARIABLE}" ${MODULES} PARENT_SCOPE) + set("${LIBS_VARIABLE}" ${LIBS} PARENT_SCOPE) +endfunction() + +function(sourcemeta_pkgconfig_direct + TARGET_NAME MODULES_VARIABLE OPTIONS_VARIABLE LIBS_VARIABLE) + set(MODULES ${${MODULES_VARIABLE}}) + set(OPTIONS ${${OPTIONS_VARIABLE}}) + set(LIBS ${${LIBS_VARIABLE}}) + + # A module that reaches for something the toolchain does not find on its own, + # such as a runtime that sits outside the default search paths, says so here + # rather than through a library it links against + get_target_property(LINK_OPTIONS ${TARGET_NAME} INTERFACE_LINK_OPTIONS) + if(LINK_OPTIONS) + foreach(OPTION IN LISTS LINK_OPTIONS) + if(OPTION MATCHES "^(SHELL|LINKER):") + message(FATAL_ERROR "The link option ${OPTION} of ${TARGET_NAME} is " + "spelled in a way that only CMake knows how to expand. Write it as " + "a single token instead, so that it survives into the packaging " + "that consumers which do not read CMake rely on") + endif() + list(APPEND OPTIONS "${OPTION}") + endforeach() + endif() + + set(ENTRIES) + get_target_property(TARGET_TYPE ${TARGET_NAME} TYPE) + get_target_property(PUBLIC_LIBRARIES + ${TARGET_NAME} INTERFACE_LINK_LIBRARIES) + if(PUBLIC_LIBRARIES) + list(APPEND ENTRIES ${PUBLIC_LIBRARIES}) + endif() + + # An interface library carries its entire dependency set in its link + # interface, and has no private one to read + if(NOT TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") + get_target_property(PRIVATE_LIBRARIES ${TARGET_NAME} LINK_LIBRARIES) + if(PRIVATE_LIBRARIES) + list(APPEND ENTRIES ${PRIVATE_LIBRARIES}) + endif() + endif() + + foreach(LIBRARY IN LISTS ENTRIES) + sourcemeta_pkgconfig_dependency("${LIBRARY}" MODULES LIBS) + endforeach() + + set("${MODULES_VARIABLE}" ${MODULES} PARENT_SCOPE) + set("${OPTIONS_VARIABLE}" ${OPTIONS} PARENT_SCOPE) + set("${LIBS_VARIABLE}" ${LIBS} PARENT_SCOPE) +endfunction() + +function(sourcemeta_pkgconfig_closure + ROOTS MODULES_VARIABLE OPTIONS_VARIABLE LIBS_VARIABLE) + set(PENDING ${ROOTS}) + set(VISITED) + set(OPTIONS) + set(LIBS) + + while(PENDING) + list(POP_FRONT PENDING CURRENT) + if(CURRENT IN_LIST VISITED) + continue() + endif() + list(APPEND VISITED "${CURRENT}") + set(DIRECT) + sourcemeta_pkgconfig_direct("${CURRENT}" DIRECT OPTIONS LIBS) + list(APPEND PENDING ${DIRECT}) + endwhile() + + # A module is declared after everything it depends on, as CMake could not + # resolve the dependency otherwise, so walking the declarations backwards + # gives a linker the order it needs + get_property(DECLARED GLOBAL PROPERTY SOURCEMETA_PKGCONFIG_TARGETS) + list(REVERSE DECLARED) + set(MODULES) + foreach(MODULE IN LISTS DECLARED) + if(MODULE IN_LIST VISITED) + list(APPEND MODULES "${MODULE}") + endif() + endforeach() + + list(LENGTH VISITED EXPECTED) + list(LENGTH MODULES ACTUAL) + if(NOT EXPECTED EQUAL ACTUAL) + message(FATAL_ERROR "Some of the modules that ${ROOTS} depends on are not " + "packaged, and consumers that do not read CMake would fail to link " + "against them: ${VISITED}") + endif() + + if(OPTIONS) + list(REMOVE_DUPLICATES OPTIONS) + endif() + if(LIBS) + list(REMOVE_DUPLICATES LIBS) + endif() + + set("${MODULES_VARIABLE}" ${MODULES} PARENT_SCOPE) + set("${OPTIONS_VARIABLE}" ${OPTIONS} PARENT_SCOPE) + set("${LIBS_VARIABLE}" ${LIBS} PARENT_SCOPE) +endfunction() + +function(sourcemeta_pkgconfig_write NAME OUTPUT DESCRIPTION) + get_property(TARGETS GLOBAL PROPERTY SOURCEMETA_PKGCONFIG_TARGETS) + sourcemeta_pkgconfig_closure("${TARGETS}" MODULES OPTIONS LIBS) + + # Ahead of the libraries, as some of them are the search paths that the + # libraries are then looked up in + set(FLAGS ${OPTIONS}) + list(APPEND FLAGS "-L\${libdir}") + foreach(MODULE IN LISTS MODULES) + get_target_property(TARGET_TYPE ${MODULE} TYPE) + if(NOT TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") + get_target_property(OUTPUT_NAME ${MODULE} OUTPUT_NAME) + if(NOT OUTPUT_NAME) + set(OUTPUT_NAME "${MODULE}") + endif() + list(APPEND FLAGS "-l${OUTPUT_NAME}") + endif() + endforeach() + list(APPEND FLAGS ${LIBS}) + list(JOIN FLAGS " " FLAGS) + + # Anchoring at the location of the file itself, rather than at the prefix + # that was configured, keeps the result correct for an installation that is + # staged, relocated, or packaged somewhere other than where it was built + if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + set(PREFIX "${CMAKE_INSTALL_PREFIX}") + else() + string(REGEX REPLACE "[^/]+" ".." PREFIX + "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + set(PREFIX "\${pcfiledir}/${PREFIX}") + endif() + + string(CONCAT CONTENT + "prefix=${PREFIX}\n" + "exec_prefix=\${prefix}\n" + "libdir=\${prefix}/${CMAKE_INSTALL_LIBDIR}\n" + "includedir=\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}\n" + "\n" + "Name: ${NAME}\n" + "Description: ${DESCRIPTION}\n" + "Version: ${PROJECT_VERSION}\n" + "Cflags: -I\${includedir}\n" + "Libs: ${FLAGS}\n") + + # Generated rather than written, as a link option is allowed to be a + # generator expression and only CMake itself knows what it stands for + file(GENERATE OUTPUT "${OUTPUT}" CONTENT "${CONTENT}") +endfunction() + +# The whole library behind a single name, which is all a consumer that does not +# read CMake needs to know about how we are put together +function(sourcemeta_pkgconfig_install) + cmake_parse_arguments(SOURCEMETA_PKGCONFIG "" + "NAME;DESCRIPTION;COMPONENT" "" ${ARGN}) + + set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${SOURCEMETA_PKGCONFIG_NAME}.pc") + + # A module states some of its dependencies after asking to be installed, so + # the link interface is only complete once the project has been walked. The + # arguments of a deferred call are evaluated when the call finally runs, by + # which point these variables are long gone, so bake them in here + cmake_language(EVAL CODE + "cmake_language(DEFER DIRECTORY \"${PROJECT_SOURCE_DIR}\" + CALL sourcemeta_pkgconfig_write + \"${SOURCEMETA_PKGCONFIG_NAME}\" \"${OUTPUT}\" + \"${SOURCEMETA_PKGCONFIG_DESCRIPTION}\")") + + include(GNUInstallDirs) + install(FILES "${OUTPUT}" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + COMPONENT ${SOURCEMETA_PKGCONFIG_COMPONENT}) +endfunction() diff --git a/src/core/crypto/CMakeLists.txt b/src/core/crypto/CMakeLists.txt index 03d0d32734..35fdd01e60 100644 --- a/src/core/crypto/CMakeLists.txt +++ b/src/core/crypto/CMakeLists.txt @@ -30,6 +30,7 @@ if(SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL) crypto_hkdf_openssl.cc crypto_openssl.h crypto_pkcs8.h) target_link_libraries(sourcemeta_core_crypto PRIVATE OpenSSL::Crypto) + sourcemeta_pkgconfig_declare(TARGET OpenSSL::Crypto LIBS "-lcrypto") elseif(APPLE) enable_language(OBJCXX) diff --git a/src/core/http/CMakeLists.txt b/src/core/http/CMakeLists.txt index ba553b9224..a1f610d911 100644 --- a/src/core/http/CMakeLists.txt +++ b/src/core/http/CMakeLists.txt @@ -37,6 +37,7 @@ if(SOURCEMETA_CORE_HTTP_USE_SYSTEM_CURL) target_compile_definitions(sourcemeta_core_http PRIVATE SOURCEMETA_CORE_HTTP_USE_SYSTEM_CURL) target_link_libraries(sourcemeta_core_http PRIVATE CURL::libcurl) + sourcemeta_pkgconfig_declare(TARGET CURL::libcurl LIBS "-lcurl") elseif(APPLE) # Resolve the framework to an absolute bundle path rather than passing a raw # linker flag, as the exported interface is also read by build systems that diff --git a/src/lang/parallel/CMakeLists.txt b/src/lang/parallel/CMakeLists.txt index ef981e99bc..e237b8af24 100644 --- a/src/lang/parallel/CMakeLists.txt +++ b/src/lang/parallel/CMakeLists.txt @@ -6,3 +6,8 @@ if(SOURCEMETA_CORE_INSTALL) endif() target_link_libraries(sourcemeta_core_parallel INTERFACE Threads::Threads) +if(CMAKE_USE_PTHREADS_INIT) + sourcemeta_pkgconfig_declare(TARGET Threads::Threads LIBS "-pthread") +else() + sourcemeta_pkgconfig_declare(TARGET Threads::Threads LIBS) +endif() diff --git a/src/lang/test/CMakeLists.txt b/src/lang/test/CMakeLists.txt index ecf71d6fc2..f41eb451c6 100644 --- a/src/lang/test/CMakeLists.txt +++ b/src/lang/test/CMakeLists.txt @@ -43,4 +43,5 @@ if(SOURCEMETA_CORE_INSTALL) DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/core" NAMESPACE sourcemeta:: COMPONENT sourcemeta_core_dev) + sourcemeta_pkgconfig_register(sourcemeta_core_test_main) endif() diff --git a/test/packaging/CMakeLists.txt b/test/packaging/CMakeLists.txt index 982ee84169..943da9d438 100644 --- a/test/packaging/CMakeLists.txt +++ b/test/packaging/CMakeLists.txt @@ -12,3 +12,33 @@ add_test(NAME ${PROJECT_NAME}.find_package_build COMMAND --config "${CMAKE_BUILD_TYPE}") set_tests_properties(${PROJECT_NAME}.find_package_build PROPERTIES DEPENDS ${PROJECT_NAME}.find_package_configure) + +# This consumption path speaks in `-l` flags and relies on pkg-config being +# around, neither of which is how a library is found on Windows +find_program(MESON_EXECUTABLE NAMES meson) +if(MESON_EXECUTABLE AND NOT WIN32) + # Unlike a plain setup, this works both on a fresh build directory and on one + # left behind by a previous run, which is what makes the test repeatable + add_test(NAME ${PROJECT_NAME}.meson_pkgconfig_setup COMMAND + "${MESON_EXECUTABLE}" setup --reconfigure + "${CMAKE_CURRENT_BINARY_DIR}/meson_pkgconfig" + "${CMAKE_CURRENT_SOURCE_DIR}/meson_pkgconfig" + "-Dpkg_config_path=${PROJECT_SOURCE_DIR}/build/dist/${CMAKE_INSTALL_LIBDIR}/pkgconfig") + add_test(NAME ${PROJECT_NAME}.meson_pkgconfig_compile COMMAND + "${MESON_EXECUTABLE}" compile -C "${CMAKE_CURRENT_BINARY_DIR}/meson_pkgconfig") + set_tests_properties(${PROJECT_NAME}.meson_pkgconfig_compile + PROPERTIES DEPENDS ${PROJECT_NAME}.meson_pkgconfig_setup) +endif() + +# The allocator is the one dependency whose absence a consumer cannot notice on +# its own, as a program links and runs perfectly well without it +find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config) +if(PKG_CONFIG_EXECUTABLE AND NOT WIN32 AND TARGET Mimalloc::Mimalloc) + add_test(NAME ${PROJECT_NAME}.pkg_config_allocator COMMAND + "${PKG_CONFIG_EXECUTABLE}" --libs sourcemeta_core) + set_tests_properties(${PROJECT_NAME}.pkg_config_allocator + PROPERTIES + PASS_REGULAR_EXPRESSION "mimalloc" + ENVIRONMENT + "PKG_CONFIG_PATH=${PROJECT_SOURCE_DIR}/build/dist/${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif() diff --git a/test/packaging/meson_pkgconfig/hello.cc b/test/packaging/meson_pkgconfig/hello.cc new file mode 100644 index 0000000000..9c236e83ac --- /dev/null +++ b/test/packaging/meson_pkgconfig/hello.cc @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // std::uint8_t +#include // EXIT_SUCCESS +#include // std::cout +#include // std::string + +auto main() -> int { + const sourcemeta::core::JSON document{"Hello World"}; + sourcemeta::core::stringify(document, std::cout); + std::cout << std::endl; + + // Every one of these reaches a dependency that the module itself does not + // name, which is what a consumer needs the packaging to resolve for it + std::cout << sourcemeta::core::matches_if_valid("^Hello", "Hello World") + << std::endl; + std::cout << sourcemeta::core::markdown_to_html("# Hello") << std::endl; + const std::string input{"Hello World"}; + std::cout << sourcemeta::core::gzip( + reinterpret_cast(input.data()), + input.size()) + .size() + << std::endl; + std::cout << sourcemeta::core::is_idn_email("hello@\xCE\xB4.example") + << std::endl; + std::cout << sourcemeta::core::uuidv4().size() << std::endl; + + // Reaches the Swift shim, which autolinks a runtime that lives outside the + // paths a linker searches on its own + const auto key{sourcemeta::core::make_eddsa_public_key( + sourcemeta::core::EdwardsCurve::Ed25519, std::string(32, '\x01'))}; + std::cout << (key.has_value() && + sourcemeta::core::eddsa_verify(key.value(), "message", + std::string(64, '\x00'))) + << std::endl; + return EXIT_SUCCESS; +} diff --git a/test/packaging/meson_pkgconfig/meson.build b/test/packaging/meson_pkgconfig/meson.build new file mode 100644 index 0000000000..ccbb5c1e6f --- /dev/null +++ b/test/packaging/meson_pkgconfig/meson.build @@ -0,0 +1,8 @@ +project('core_hello', 'cpp', + default_options : [ 'cpp_std=c++23' ]) + +# The whole library behind a single name, with no knowledge of how it is split +# into modules or of what any of them link against +core_dependency = dependency('sourcemeta_core', required : true) + +executable('core_hello', 'hello.cc', dependencies : [ core_dependency ])