0

I have a c++ cmake project. In this project I build (among other) one example, where I need to use another project, call it Foo. This Foo project does not offer a cmake build system. Instead, it has a pre-made Makefile.custom.in. In order to build an executable that uses Foo's features, one needs to copy this makefile in his project, and modify it (typically setting the SOURCES variable and a few compiler flags). Basically, this Makefile ends up having the sources for your executable and also all the source files for the Foo project. You will not end up using Foo as a library.

Now, this is a design I don't like, but for the sake of the question, let's say we stick with it.

To create my example inside my cmake build I added a custom target:

CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/Makefile.custom.in Makefile.custom)

ADD_CUSTOM_TARGET(my_target COMMAND $(MAKE) -f Makefile.custom
                  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})

This works. I can specify some variables to cmake, which get resolved in the call to CONFIGURE_FILE, and I end up with a working Makefile.custom. Then, invoking make my_target from the build directory, I can build the executable. I can even add it to the all target (to save me the effort of typing make my_target) with

SET_TARGET_PROPERTIES(my_target PROPERTIES EXCLUDE_FROM_ALL FALSE)

Sweet. However, cmake appears to assign a single job to the custom target, slowing down my compilation time (the Foo source folder contains a couple dozens cpp files). On top of that, the make clean target does not forward to the custom makefile. I end up having to add another target:

ADD_CUSTOM_TARGET(really-clean COMMAND "$(MAKE)" clean
                  COMMAND "$(MAKE)" -f Makefile.custom clean
                  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})

which, unlike my_target with all, I can't include in the clean target (can I?).

Now, I know that a cleaner solution would be to have the Foo project be built as an external project, and then link to it. However, I've been 'recommended' to use their Makefile.custom.in makefile, modifying the few lines I need (adding my sources, specifying compiler flags, and few other minor modifications). So, regardless of how neat and clean this design pattern is, my questions are:

  • is there a way to tell cmake that make should use more than 1 job when making the target my_target?
  • is there a cleaner way to include a pre-existing makefile in a cmake project? Note that I don't want (can't?) use Foo as a library (and link against it). I want (need?) to compile it together with my executable using a makefile not generated by cmake (well, cmake can help a bit, through CONFIGURE_FILE, by resolving some variables, but that's it).

Note: I am aware of ExternalProject (as suggested also in this answer), but I think it's not exactly what I need here (since it would build Foo and then use it as a library). Also, both my project and Foo are written exclusively in C++ (not sure this matter at all).

I hope the question makes sense (regardless of how ugly/annoying/unsatisfactory the resulting design would be).

Edit: I am using cmake version 3.5.2

Community
  • 1
  • 1
bartgol
  • 1,703
  • 2
  • 20
  • 30
  • If build time matters, consider switching to Ninja. That would help in your case, too. – usr1234567 Jan 23 '17 at 22:33
  • I thought about ninja. I did not use it because I may or may not include also some fortran code at some point, and as far as I understand the ninja generator of cmake does not support fortran yet (but I admit I did not check for updates in a while). – bartgol Jan 23 '17 at 22:35
  • which version of CMake are you using? – fedepad Jan 23 '17 at 23:31
  • Sorry, I forgot to mention that! It's version 3.5.2. – bartgol Jan 24 '17 at 00:03
  • Ugh, why a down vote on the question? I thought it was a legitimate one. If I forgot to add details, tags or was not compliant with SO rules, consider leaving a comment to help me improve next time. Thanks. – bartgol Jan 26 '17 at 15:23

1 Answers1

1

First, since you define your own target, you can assign more cores to the build process for the target my_target, directly inside your CMakeLists.txt.
You can include the Cmake module ProcessCount to determine the number of cores in your machine and then use this for a parallel build.

include(ProcessorCount)
ProcessorCount(N)
if(NOT N EQUAL 0)
  # given that cores != 0 you could modify
  # math(EXPR N "${N}+1")  # modify (increment/decrement) N at your will, in this case, just incrementing N by one
  set(JOBS_IN_PARALLEL -j${N})
endif(NOT N EQUAL 0)

and when you define your custom target have something like the following:

ADD_CUSTOM_TARGET(my_target 
                  COMMAND ${CMAKE_MAKE_PROGRAM} ${JOBS_IN_PARALLEL} -f Makefile.custom
                  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})

by the way, I don't think there's the need for you to include also CMAKE_BUILD_TOOL among the COMMANDs in your target.
I believe that instead of modifying the lines as above, you could call

make -j8 my_target  

and it might start 8 jobs (just an example) without modifying the CMakeLists.txt, but I cannot guarantee this works having defined the COMMAND the way you have, just try if that's enough.
For the second point, I cannot think right now of a "cleaner" way.

fedepad
  • 4,509
  • 1
  • 13
  • 27
  • Thanks! Do you mean I should write 'make' directly instead of using `${CMAKE_MAKE_PROGRAM}`? I thought using the cmake variable makes the build system a little more portable, no? – bartgol Jan 24 '17 at 15:27
  • It's two different things. `${CMAKE_MAKE_PROGRAM}` is used inside `CMakeLists.txt` and you should keep it that way. The command `make my_target` you use in the command line to trigger the the build of the custom target . – fedepad Jan 24 '17 at 15:40