force one target to be compiled after a shared library is built

Hi,

I’ve got an unusual scenario where I’m trying to build and load a shared library as a plugin to clang, and I’m struggling to figure out how to express this with CMake.


The project has two parts:

  1. the sources that are used to build a shared library, some_library.so. It must be a shared library.
  2. the sources that use some_library.sonot by linking against it, but loading it into clang via the flag -fpass-plugin=/path/to/some_library.so

Part 1 works without issue: add_library(some_library SHARED src.cpp).

The problem shows up with part 2. When I write

target_compile_options(part_2 "-fpass-plugin=$<TARGET_FILE:some_library>")

as far as CMake is concerned, part_2 doesn’t really depend on some_library, since that dependence happens implicitly through the custom flag. This means part_2 gets compiled before some_library.so is actually built, so the compilation fails:

clang: can't load "/path/to/some_library.so", file doesn't exist

I’ve tried to explicitly specify this dependence by

add_dependencies(part_2 some_library)

but that also doesn’t ensure that part_2 is built after some_library (presumably because some_library is a shared library, so CMake thinks part_2 and some_library can still be built in parallel).

I’ve also tried making an intermediate file (that is not a shared library) to depend on

add_custom_command(
    TARGET some_library
    POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E echo "built!" > ${CMAKE_BINARY_DIR}/some_library.built
    BYPRODUCTS ${CMAKE_BINARY_DIR}/some_library.built
)
add_dependencies(part_2 ${CMAKE_BINARY_DIR}/some_library.built)

but this also doesn’t work. part_2 is still being compiled in parallel with some_library.


Is there some way to ensure that part_2 will be compiled only once some_library.so has finished building?

What CMake generator are you using?

What kind of target is part_2? If it is a static library, the dependency rules may be getting relaxed such that compiling its sources doesn’t have to wait for the linking of some_library to complete first.

Take a look at the OPTIMIZE_DEPENDENCIES target property and perhaps the OBJECT_DEPENDS source file property. The former may be related to why things build earlier than you want. The latter might give you a way to implement the constraint you are trying to enforce.

This won’t work. The add_dependencies() command expects CMake targets, and ${CMAKE_BINARY_DIR}/some_library.built is a file, not a target.

Hi Craig, thanks for the feedback-- also your CMake book is great!


What CMake generator are you using?

I want it to work with Ninja and Unix Makefiles.

What kind of target is part_2 ?

It’s an executable, but I would like to find a solution that also works for static libraries.

Take a look at the OPTIMIZE_DEPENDENCIES target property and perhaps the OBJECT_DEPENDS source file property.

I don’t really understand OPTIMIZE_DEPENDENCIES, and I can’t seem to find any examples of it in use. Could you recommend a way that I might try to use it to solve this issue?

OBJECT_DEPENDS is indeed helpful, if I put

set_source_files_properties(part_2 OBJECT_DEPENDS ${CMAKE_BINARY_DIR}/some_library.built)

Then part_2 does wait for some_library to finish building :tada:. However, I don’t want the user to have to manage the build weirdness of some_library manually, so I’ve been trying to hide it behind an interface target, something like:

# manage the weird build options here
add_library(some_library SHARED ...)
add_library(some_library_interface_target INTERFACE)
target_compile_options(some_library_interface_target "-fpass-plugin=$<TARGET_FILE:some_library>")

... 

# so the user just has to write
target_link_libraries(part_2 PUBLIC some_library_interface_target)

but when I try to set the OBJECT_DEPENDS property on the interface target with either of

set_target_properties(some_library_interface_target PROPERTIES OBJECT_DEPENDS ${CMAKE_BINARY_DIR}/some_library.built)
# or
set_source_files_properties(some_library_interface_target OBJECT_DEPENDS ${CMAKE_BINARY_DIR}/some_library.built)
# or
set_target_properties(some_library_interface_target PROPERTIES OBJECT_DEPENDS $<TARGET_FILE:some_library>)
# or
set_source_files_properties(some_library_interface_target OBJECT_DEPENDS $<TARGET_FILE:some_library>)

It doesn’t seem to work any more (i.e. part_2 is being compiled before some_library finishes). I was hoping that doing

set_source_files_properties(some_library_interface_target OBJECT_DEPENDS ...)

would broadcast that set_source_files_properties over all the sources linking against some_library_interface_target.

I only mentioned OPTIMIZE_DEPENDENCIES in case your build was already setting it to true. In your case, you explicitly don’t want that optimization, so you want that to be turned off.

OBJECT_DEPENDS is a source file property. Setting OBJECT_DEPENDS on a target will have no effect. You would need to set it on every source file whose compilation needs the some_library plugin. I don’t see any advantage to inserting another target between the source files and some_library, you still have to set the OBJECT_DEPENDS property on all the source files either way.

That line looks malformed to me. Is your source file really called part_2? That looks more like your target name. It might be working by accident if your source file is named something like part_2.cpp. You need to list out all the actual source files there, you can’t put a target name.

I only mentioned OPTIMIZE_DEPENDENCIES in case your build was already setting it to true. In your case, you explicitly don’t want that optimization, so you want that to be turned off.

I haven’t explicitly set it, and if I do

get_target_property(output ... OPTIMIZE_DEPENDENCIES)
message("${output}")

on part_2 or some_library, they both report “NOTFOUND”.


That line looks malformed to me.

Sorry for the confusion, you’re right-- I made a clerical error when copying/anonymizing the actual project code for this discussion. It should have read

set_source_files_properties(part_2.cpp OBJECT_DEPENDS ${CMAKE_BINARY_DIR}/some_library.built)

Also, on a related note

set_target_properties(part_2 PROPERTIES OBJECT_DEPENDS ${CMAKE_BINARY_DIR}/some_library.built)

doesn’t make part_2 wait for some_library either.

But I’m still looking for a way to manage this from the interface target, if possible.

There’s no way to do that today. That would be a new feature request.

Sorry, I forgot about this discussion.

The approach that seems to be working is to use add_custom_target to define a dummy target that depends on some_library and then make the interface target depend on the dummy target:

add_custom_target(dummy "" DEPENDS some_library)
add_dependencies(some_library_interface_target dummy)

It’s an inelegant solution (and hard to discover), so maybe a CMake feature request is justified, but I’m satisfied with this.