Run custom command before some project build in Superbuild

Hi,

The schema of my project is something like:

MyProject |
          FetchContent_Populate(MyDep) |
                                        ExternalProject_Add(ExternalDep_1)
                                        ExternalProject_Add(ExternalDep_2)
                                        ExternalProject_Add(...)

That means that my project MyProject fetches other project MyDep and that other project uses superbuild to to build all its dependencies via ExternalProject_Add.

I need to run some command (probably by using add_custom_command) when MyDep is ready to be built (all ExternalProject_Add are built). I guess this is similar to run the command while MyDep buildtime (but when all its deps are already built).

Is there a way to achieve that?

I think using ExternalProject_add_step would be the way to do this (with DEPENDEES build).

1 Like

Thank you for reply,

I’ve just read the documentation on ExternalProject_add_step and it seems that this command only adds the steps for targets that are created via ExternalProject_Add.

In my case my project depends on MyDep project that uses add_library() command to create target and to build its dependencies it uses ExternalProject_Add().

I need to run the command after all ExternalProject_Add() dependencies of MyDep are built but before MyDep starts building.

I’m sorry if I confused you

EDIT:
I just understood that probably add_custom_command second use signature may do what I need. Probably I should try that

add_custom_command(
  TARGET MySubDep PRE_BUILD
  COMMAND 
    python -m editcpp 
    --src-file=src.cxx
    --dest-file=out.cxx
    --oldfile-keep 
    -std=c++17
    \"$<$<BOOL:$<TARGET_PROPERTY:INCLUDE_DIRECTORIES>>:-I$<JOIN:$<TARGET_PROPERTY:INCLUDE_DIRECTORIES>, -I>>\"
  VERBATIM)

but I get error:

No TARGET 'MySubDep' has been created in this directory.

MySubDep is a dependency of MyDep that is created via add_library.
Is there a way to tell the cmake that somewhere in subfolder there is a target MySubDep and it should trust me? :slight_smile:

Yeah, I don’t think you can attach additional {PRE,POST}_BUILD commands to “remote” targets. You should be able to add your own target that depends on the dependencies and is then depended on by MySubDep using add_dependencies though.

1 Like

Thank you for the hints but it seems that this doesn’t work (Cannot add target-level dependencies to non-existent target "MySubDep".)…

# Somwhere within external project:
add_library(MySubDep somefile.cxx)
target_link_libraries(MySubDep lib_0 lib_1)

#---------------another CMakeLists.txt-------------------#
# My top level project:
# I add `FakeTarget` that depends on `lib_0` and `lib_1` and 
# at the same time `MySubDep` should depend on `FakeTarget`
add_custom_target(FakeTarget 
  COMMAND 
    editcpp 
    --src-file=src.cxx
    --dest-file=out.cxx
    --oldfile-keep 
    -std=c++17
    \"-I$<JOIN:$<TARGET_PROPERTY:MySubDep ,INCLUDE_DIRECTORIES>, -I>\"
    VERBATIM)
add_dependencies(FakeTarget lib_0 lib_1)
add_dependencies(MySubDep FakeTarget)

If I misunderstood you could you please explain a little more?

I think this looks fine except that why MySubDep is not around is odd. Does it pass an if (TARGET MySubDep) conditional?

1 Like

Thank you for response,

You were right, there is no if (TARGET MySubDep) but there is another condition that checks if superbuild is ON then it doesn’t add add_subdirectory() with the MySubDep target…
I need to discover how those libs are added in superbuild mode.

I will report back when I have progress

The system works in the following manner:

  1. MyProject uses FetchContent_Populate(MyDep) ;
  2. MyDep uses superbuild to build its dependencies as well as itself (i.e. uses ExternalProject_Add(MyDep) with -DUSE_SUPERBUILD:BOOL=OFF);
  3. MyDep creates MySubDep via add_library(MySubDep mysubdep.cpp);
  4. MySubDep has its dependencies that are added via target_link_libraries(MySubDep lib1 lib2 ...);

My task is to modify source code of mysubdep.cpp (I need to change the implementation of some functions there) before MySubDep is built. To do that (to parse .cpp files) I use my tool that uses clang and I need to get all dirs that are linked to MySubDep (that is why I use something like -I$<JOIN:$<TARGET_PROPERTY:MySubDep ,INCLUDE_DIRECTORIES>, -I>)

I hope now my task is clearer and I’m sorry that it took so long to explain.

For now I think the simplest solution would be modify the CMakeLists.txt where MySubDep target is created by appending custom command to it:

set(cmake_file "~/MyProject/MyDep/MySubDep/CMakeLists.txt")
file(READ ${cmake_file} MySubDepFile)

string(FIND ${MySubDepFile} 
  "EDITED BY ME"
  isMySubDepFileEdited)

if(isMySubDepFileEdited LESS 0)
  # write the command to a variable
  set(MY_COMMAND
  "
  # EDITED BY ME

  add_custom_command(
    TARGET MySubDep PRE_BUILD
    COMMAND 
      editcpp 
      --src-file=mysubdep.cpp
      --dest-file=~/MyProject/MyDep/MySubDep/mysubdep.cpp
      --oldfile-keep 
      -std=c++17
      \"-I$<JOIN:$<TARGET_PROPERTY:MySubDep,INCLUDE_DIRECTORIES>, -I>\"
    VERBATIM)
  "
  )

  # append my command to `CMakeLists.txt` where `MySubDep` is added as a target
  file(APPEND  
    ${cmake_file}
    ${MY_COMMAND}
  )
endif()

but I feel that this is not the best way to solve the problem…

FetchContent uses ExternalProject under the hood. You can use PATCH_COMMAND to patch the source.

1 Like

I think you gave me all the necessary info I need to know to solve my problem, I appreciate that.
Now I need some time to understand and to test that.
Thank you very much!

Is there a way to add to variable ${VAR} without substitution value of a VAR?
For example I need to prepare variable and write it as is to a file:

  set(MY_COMMAND
  "
  get_target_property(tgt_deps MySubDep LINK_LIBRARIES)

  add_custom_command(
    TARGET MySubDep PRE_BUILD
    COMMAND 
      editcpp 
      --src-file=mysubdep.cpp
      --dest-file=~/MyProject/MyDep/MySubDep/mysubdep.cpp
      --oldfile-keep 
      -std=c++17
      \"-I$<JOIN:$<TARGET_PROPERTY:MySubDep,INCLUDE_DIRECTORIES>, -I>\"
    DEPENDS \"${tgt_deps}\"
    VERBATIM)
  "
  )

file(APPEND ~/CMakeLists.txt ${MY_COMMAND})

I would like to see that tgt_depsis written in modified CMakeLists without substitution its value (like ${tgt_deps}).
I’ve seen that configure_file is able to do something similar with @ONLY parameter set, and also file(CONFIGURE ...) but that is not an exact solution to what I would do I think.

You can escale the $:

\${tgt_deps}
1 Like

Also consider using bracket syntax instead of double-quotes. Bracket syntax doesn’t require you to escape the contents and does not dereference variables. E.g.

set(MY_COMMAND [==[
  get_target_property(tgt_deps MySubDep LINK_LIBRARIES)

  add_custom_command(
    TARGET MySubDep PRE_BUILD
    COMMAND 
      editcpp 
      --src-file=mysubdep.cpp
      --dest-file=~/MyProject/MyDep/MySubDep/mysubdep.cpp
      --oldfile-keep 
      -std=c++17
      "-I$<JOIN:$<TARGET_PROPERTY:MySubDep,INCLUDE_DIRECTORIES>, -I>"
    DEPENDS "${tgt_deps}"
    VERBATIM)
]==])
1 Like

Thank you, I didn’t know that