#StackBounty: #cmake #ninja Using an ExternalProject download step with Ninja

Bounty: 200

This seems to be a common problem without a clear answer.

The situation is: we have a 3rd party dependency that we want to install at build time when building a target that depends on it. That’s roughly:

ExternalProject_Add(target-ep
    DOWNLOAD_COMMAND <whatever>
    BUILD_COMMAND ""
    INSTALL_COMMAND ""
    CONFIGURE_COMMAND "")

add_library(target-imp STATIC IMPORTED)
set_target_properties(target-imp PROPERTIES
    INTERFACE_INCLUDE_DIRECTORIES /path/to/install/include
    IMPORTED_LOCATION /path/to/install/lib/libwhatever.a)

add_library(target INTERFACE)
target_link_libraries(target INTERFACE target-imp)
add_dependencies(target target-ep)

(It takes three to tango here because of cmake issue 15052)

When using Unix Makefiles as the generator, this works great. Only installs dependencies on demand, all the builds work correctly.

However, on Ninja, this fails immediately with something like:

ninja: error: '/path/to/install/lib/libwhatever.a', needed by 'something', missing and no known rule to make it

This is because Ninja scans dependencies differently from Make (see ninja issue 760). So what we have to do is actually tell Ninja that this external dependency exists. We can do that:

ExternalProject_Add(target-ep
    DOWNLOAD_COMMAND <whatever>
    BUILD_BYPRODUCTS /path/to/install/lib/libwhatever.a
    BUILD_COMMAND ""
    INSTALL_COMMAND ""
    CONFIGURE_COMMAND "")

Which unfortunately also fails with:

No build step for 'target-ep'ninja: error: mkdir(/path/to/install): Permission denied

This is because my download step has permissions to write to that path, but whatever mkdir command is being run by the underlying add_custom_command() from with ExternalProject_Add() does not.

So:

  1. Is this possible at all with Ninja and CMake? (Version is not an issue, I can use the latest CMake if that solves the problem)
  2. If there is some way to workaround with explicitly listing BUILD_BYPRODUCTS, is there a way to simply communicate that the entire directory that will get installed is a byproduct? That is, /path/to/install/* is a byproduct?


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.