1

I want to make a builder project that checks out sub-modules and builds them as a group, and I would like to build them in a single pass.

builder
  submod1
  submod2 #depends on submod1 
  submod3 #depends on submod2

For testing I downloaded ZeroMQ and cppzmq as submodules and built both with the cppzmq/demo to confirm they are linkable. I chose them because cppzmq checks for libzmq as a target but the demo only links with a find_package.

cmake_minimum_required(VERSION 3.10)
project(ZMQ_builder)
option(BUILD_TESTS "No tests" OFF)
option(CPPZMQ_BUILD_TESTS "No tests" OFF)
option(BUILD_STATIC "ninja can only build static or shared" OFF)
option(BUILD_SHARED "ninja can only build static or shared" ON)
add_subdirectory(libzmq)
set_property(TARGET libzmq PROPERTY CXX_STANDARD 17)
if (NOT TARGET libzmq AND NOT TARGET libzmq-static)
    message(WARNING "libzmq and libzmq-static don't exist after being created")
endif()
if(NOT TARGET ZeroMQ)
    message(status "ZeroMQ needs an alias") #prints this
    add_library(ZeroMQ ALIAS libzmq)
endif()
if(NOT TARGET ZeroMQ)
    message(WARNING "ZeroMQ Target Still doesn't exist")
endif()
if(NOT ZeroMQ_FOUND)
    message(WARNING "ZeroMQ marked as not found") #prints this warning
    find_package(ZeroMQ)
    #set(ZeroMQ_FOUND true)
endif()
add_subdirectory(cppzmq)
find_package(cppzmq) #dies here
add_subdirectory(cppzmq/demo)

which outputs

ZeroMQ marked as not found .../builder\CMakeLists.txt   28
CMake Warning at ...\builder\CMakeLists.txt:29 (find_package):
  By not providing "FindZeroMQ.cmake" in CMAKE_MODULE_PATH this project has
  asked CMake to find a package configuration file provided by "ZeroMQ", but
  CMake did not find one.

  Could not find a package configuration file provided by "ZeroMQ" with any
  of the following names:

    ZeroMQConfig.cmake
    zeromq-config.cmake

  Add the installation prefix of "ZeroMQ" to CMAKE_PREFIX_PATH or set
  "ZeroMQ_DIR" to a directory containing one of the above files.  If "ZeroMQ"
  provides a separate development package or SDK, be sure it has been
  installed.    ...\builder\CMakeLists.txt  29  
ZeroMQ was NOT found!   .../builder/build/cppzmq/cppzmqConfig.cmake 53  

So the find call ignored the ZeroMQ alias Target (add_library(ZeroMQ ALIAS libzmq)) and also missed ".../builder/build/libzmq/ZeroMQConfig.cmake" despite finding "../builder/build/cppzmq/cppzmqConfig.cmake"

I don't understand why find_package is ignoring the ZeroMQ target that's already in the build? and also why the build can find cppzmqConfig.cmake but not ZeroMQConfig.cmake despite both existing?

Sir Demios
  • 83
  • 11
  • 1
    "I don't understand why find_package is ignoring the ZeroMQ target that's already in the build?" - Because corresponding ["find" module](https://github.com/zeromq/cppzmq/blob/master/libzmq-pkg-config/FindZeroMQ.cmake) doesn't check ZeroMQ target. The module expects that ZeroMQ library is **already installed**, and `add_subdirectory(libzmq)` doesn't fulfill that expectation. Normally, `find_package` doesn't work with the library added via `add_subdirectory`. You may try to emulate installed library, but that emulation depends on how find module actually searches the library. – Tsyvarev Apr 07 '22 at 20:07
  • @Tsyvarev but there is a ZeroMQ target, I added it as an alias. I would expect find_package to see it even if its malformed, but that shouldn't return as a "missing config" error. Even without defining the target its failing inside a generated config file that is in the build folder, so it seems its looking in the build folder and missing one config but not the other. also added the exact error messages – Sir Demios Apr 07 '22 at 21:25
  • 1
    "I would expect find_package to see it (ZeroMQ target)" - Your expectations are false. `find_package` looks either for "find" script or for "config" script. Exactly this is written in the [documentation](https://cmake.org/cmake/help/latest/command/find_package.html) and exactly this the warning tells you. "Even without defining the target its failing inside a generated config file that is in the build folder" - `find_package` doesn't look into the build directory by default. This behavior is intended: that config file should be **installed** along with the project, otherwise it won't work. – Tsyvarev Apr 07 '22 at 22:39
  • 1
    As I previously commented, `find_package` works only when the searched package is **already installed**. Since this is not your case (the call to `add_subdirectory(libzmq)` just prepares ZeroMQ project for build), then you have no reason to use `find_package`. Instead, use **targets** created inside `add_subdirectory` call. E.g. you link with ZeroMQ library via its target (`libzmq` or `libzmq-static`). The same is about cppzmq: `find_package` won't work for it because the package is not installed yet. – Tsyvarev Apr 07 '22 at 22:44
  • @Tsyvarev yeah i had read that find_package searches targets but digging further on that it searches externally included targets. so i guess i can see why creating a target to find doesn't work but it had seemed like one of the tenets of "modern cmake" was that internal and external importing should be identical. So I could add a target guard before the find call, but I was trying to make it so I could import unmodified code and link build them. I guess i could make a Find<>.cmake file to my modules that adds the target guard for each of the subprojects? But is that bad practice? – Sir Demios Apr 08 '22 at 17:59
  • 1
    I find useful ability for `FindXXX.cmake` modules to work not only with installed package, but with the package added via `add_subdirectory` (or with FetchContent) too. So if you could write such module (even for your own usage), then you are welcomed to do so. – Tsyvarev Apr 08 '22 at 18:24

1 Answers1

0

find_package() doesn't actually look at the global targets list, it has its own targets list that only prevents other find calls from refetching

instead of creating an alias target call find_package_handle_standard_args in a Find<>.cmake module for each class

so for this case a Findcppzmq.cmake

include(FindPackageHandleStandardArgs)
find_package(ZeroMQ REQUIRED)
if(TARGET cppzmq)
    find_package_handle_standard_args(cppzmq
    REQUIRED_VARS cppzmq_BINARY_DIR)
endif()

and a FindZeroMQ.cmake

include(FindPackageHandleStandardArgs)
if(TARGET ZeroMQ)
    find_package_handle_standard_args(ZeroMQ
    REQUIRED_VARS ZeroMQ_BINARY_DIR)
endif()

alternatively if all the repos in the builder project are seperate you can instead use ExternalProject_Add so that projects will be generated in order at build time. Making an effective package.

cmake_minimum_required(VERSION 3.16)
project(ExternalBuilder)
include(ExternalProject)
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_LIST_DIR}/install)
set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX})
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
ExternalProject_Add(
    ZeroMQ
    GIT_REPOSITORY https://github.com/zeromq/libzmq.git
    GIT_TAG v4.3.2
    CMAKE_ARGS -DCMAKE_MODULE_PATH:PATH=${CMAKE_INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} -DBUILD_TESTS=OFF -DBUILD_STATIC=OFF
)
ExternalProject_Add(
    cppzmq
    DEPENDS ZeroMQ
    GIT_REPOSITORY https://github.com/zeromq/cppzmq.git
    GIT_TAG v4.7.1
    CMAKE_ARGS -DCMAKE_MODULE_PATH:PATH=${CMAKE_INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} -DCPPZMQ_BUILD_TESTS=OFF
)
ExternalProject_Add(
    zmqProject
    DEPENDS ZeroMQ
    ...
)
Sir Demios
  • 83
  • 11