'cmake --install'/'make install'/'ninja install' doesn't install dependencies

I’m integrating a Makefile’s functionality into CMake. The Makefile builds and installs a target. The install copies from a target’s install directory into the toolchain sysroot thus making it available for dependent targets. When building all targets, the install is performed for the dependency chain automatically.

I’m trying have this same functionality with CMake. Except ‘cmake --install’/‘make install’/‘ninja install’ performs a build of dependencies but doesn’t install them before the dependent target build starts. This causes a build failure because dependent files are not found.

I’m using ExternalProject to build open source packages. It performs all of the necessary steps to for ‘cmake --install’/‘make install’/‘ninja install’ to function.

Here is the install function for each open source package:

install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/install/
    DESTINATION ${CMAKE_SYSROOT_REL}
    USE_SOURCE_PERMISSIONS
    COMPONENT ${PROJECT_NAME}
)

For example, openssl depends upon zlib. The openssl/CMakeLists.txt has this line:

add_dependencies(${PROJECT_NAME} zlib)

This causes zlib to be built before openssl by ‘ninja install’. But zlib isn’t installed.

This forces me to perform builds and installs manually which isn’t following the functionality of the original Makefile:
cmake --build . --target zlib
cmake --install . --component zlib
cmake --build . --target openssl
cmake --install . --component openssl

Is there something I’m missing or is this a limitation with CMake?

You should work with targets, not with files or directories :open_mouth:

I don’t understand this response. I’m am working with targets not files or directories.
In this case, the problem is the ‘–install’ option doesn’t perform an install on dependencies.

you install a directory, not a target!

Ok I see what you were referring to.

As I mentioned, I’m using ExternalProject to build open source packages. When I try to call install with the following:

install(TARGETS ${PROJECT_NAME}
    DESTINATION ${CMAKE_SYSROOT_REL}
    COMPONENT ${PROJECT_NAME}
)

I get this error:

  install TARGETS given target "zlib" which is not an executable, library, or
  module.

I can’t help you if you show only snippets without context!

A minimum project to show you problems like this would really help:

cmake_minimum_required(VERSION 3.25...3.31)
project(OpenSSL_ExternalProject LANGUAGES C)

include(ExternalProject)

# Set OpenSSL version and source location
set(OPENSSL_VERSION "3.4.0") # Ändere die Version bei Bedarf
set(OPENSSL_URL "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz")

# Define installation directory
set(OPENSSL_INSTALL_DIR ${CMAKE_BINARY_DIR}/openssl-install)

# Add ExternalProject to download, configure, and build OpenSSL
if(OPENSSL_INSTALL_DIR)
  ExternalProject_Add(
    openssl
    URL ${OPENSSL_URL}
    URL_HASH SHA256=e15dda82fe2fe8139dc2ac21a36d4ca01d5313c75f99f46c4e8a27709b7294bf
    PREFIX ${CMAKE_BINARY_DIR}/openssl-src
    SOURCE_SUBDIR .
    CONFIGURE_COMMAND ${CMAKE_BINARY_DIR}/openssl-src/src/openssl/config --prefix=${OPENSSL_INSTALL_DIR}
                      --openssldir=${OPENSSL_INSTALL_DIR}/ssl
    BUILD_COMMAND make -C <BINARY_DIR> -j8
    INSTALL_COMMAND make -C <BINARY_DIR> -j8 install
    LOG_DOWNLOAD ON
    LOG_CONFIGURE ON
    LOG_BUILD ON
    LOG_INSTALL ON
  )

  # Optionally, add OpenSSL include and library directories for other targets
  set(OPENSSL_INCLUDE_DIR ${OPENSSL_INSTALL_DIR}/include)
  set(OPENSSL_LIB_DIR ${OPENSSL_INSTALL_DIR}/lib)

  # Display helpful messages
  message(STATUS "OpenSSL will be installed to: ${OPENSSL_INSTALL_DIR}")
  message(STATUS "Include directory: ${OPENSSL_INCLUDE_DIR}")
  message(STATUS "Library directory: ${OPENSSL_LIB_DIR}")

  add_executable(my_app main.c)
  target_include_directories(my_app PRIVATE ${OPENSSL_INCLUDE_DIR})
  target_link_directories(my_app PRIVATE ${OPENSSL_LIB_DIR})
  target_link_libraries(my_app PRIVATE ssl crypto)
  add_dependencies(my_app openssl_build)

else()
  ExternalProject_Add(
    openssl
    URL ${OPENSSL_URL}
    URL_HASH SHA256=e15dda82fe2fe8139dc2ac21a36d4ca01d5313c75f99f46c4e8a27709b7294bf
    PREFIX ${CMAKE_BINARY_DIR}/openssl-src
    SOURCE_SUBDIR .
    CONFIGURE_COMMAND ${CMAKE_BINARY_DIR}/openssl-src/src/openssl/config --prefix=/opt/local --openssldir=/opt/local/ssl
    BUILD_COMMAND make -C <BINARY_DIR> -j8
    INSTALL_COMMAND make -C <BINARY_DIR> -j8 install
    LOG_DOWNLOAD ON
    LOG_CONFIGURE ON
    LOG_BUILD ON
    LOG_INSTALL ON
  )
endif()

# Create a target that depends on the OpenSSL build
add_custom_target(openssl_build ALL DEPENDS openssl)

Building ExternalProjects in a specific order and manage dependencies between them is called a superbuild. Only ExternalProjects builds are done there.

You can only integrate ExternalProjects in a normal build if you do not rely on installed items at configure time. I guess that building openssl also configures it. You sometimes can work around this a bit by specifying the install target as build target in ExternalProject with a custom BUILD_COMMAND (or both commands if the install does not depend on build).

The other option is to tightly integrate the build using FetchContent which can bring its own set of problems.

It is in any case easier to have a tool that properly builds a sysroot that you can rely on instead of using cmake as a hammer to handle the screw.