cmake and parallel building with "make -jN"

13,502

Solution 1

looks like this is not a cmake issue but make only.

Solution 2

In the generated Makefile, when calling into a sub-make it needs to either use $(MAKE) (not just 'make') or else precede the line with a +. That is, a rule should look like this:

mysubdir:
    $(MAKE) -C mysubdir

or like this:

mysubdir:
    +make -C mysubdir

If you don't do it one of those two ways, make will give you that warning.

I don't know anything about cmake, so maybe it's generating Makefiles that aren't correct. Or maybe you did something incorrectly on your end.

Solution 3

In my case (with CMake 3.5.2) the trivial cd build && cmake .. && make -j5 works just fine.

But, I do get the jobserver unavailable error when building custom targets (as dependencies of other targets) via the cmake --build . --target foo idiom.

Like this:

add_custom_target(buildroot
   COMMAND ${CMAKE_COMMAND} --build . --target install
   COMMENT "Populating buildroot..."
)
add_dependencies(deb buildroot)
add_dependencies(rpm buildroot) #... etc

— so that the user can make deb and it Just Works. CMake will regenerate makefiles if needed, run the compilation, install everything exactly as with make install, and then run my custom scripts to package up the populated buildroot into whatever shape or form I need.

Sure enough, I'd like to make -j15 deb — but that fails.


Now, as explained on the mailing list by CMake devs, the root cause lies, surprisingly (or not), within GNU Make; there is a workaround.

The root cause is that make will not pass its jobserver environment to child processes it thinks aren't make.

To illustrate, here's a process tree (ps -A f) branch: … \_ bash \_ make -j15 deb \_ make -f CMakeFiles/Makefile2 deb \_ make -f CMakeFiles/buildroot.dir/build.make CMakeFiles/buildroot.dir/build \_ /usr/bin/cmake --build . --target install ⦿ \_ /usr/bin/gmake install …

At ⦿ point, make drops jobserver environment, ultimately causing single-threaded compilation.


The workaround which worked great for me, as given away in the linked email, is to prefix all custom commands with +env. Like this:

add_custom_target(buildroot
   #-- this ↓↓↓ here -- https://stackoverflow.com/a/41268443/531179
   COMMAND +env ${CMAKE_COMMAND} --build . --target install
   COMMENT "Populating buildroot..."
)
add_dependencies(deb buildroot)
add_dependencies(rpm buildroot) #... etc

In the end, this appears in the rule for buildroot in the appropriate makefile (CMake generates a bunch of them), and causes GNU Make to behave properly and respect -j.

Hope this helps.

Share:
13,502
Roman Dmitrienko
Author by

Roman Dmitrienko

Updated on July 04, 2022

Comments

  • Roman Dmitrienko
    Roman Dmitrienko almost 2 years

    I'm trying to setup a parallel CMake-based build for my source tree, but when I issue

    $ cmake .
    $ make -j2
    

    I get:

    jobserver unavailable: using -j1.  Add '+' to parent make rule
    

    as a warning. Does anyone have an idea if it is possible to fix it somehow?

  • Roman Dmitrienko
    Roman Dmitrienko almost 14 years
    No, I don't. I guess the solution to my problem lies somewhere deep in CMake, i.e. I need to convince it to pass the right parameters to the make.
  • dimba
    dimba almost 14 years
    you can run "VERBOSE=1 make' to see what make exactly executing
  • Roman Dmitrienko
    Roman Dmitrienko over 13 years
    It turned out to be the case, so I'm accepting this answer ;)
  • Stuart Berg
    Stuart Berg over 11 years
    ....but....cmake is the tool that generated your Makefile. So, if CMake is producing Makefiles that can't be parallelized, then fixing the issue requires fixing your CMakeLists, no?
  • Walter
    Walter almost 9 years
    I get said error even though I use $(MAKE) -C mysubdir. What's wrong here?
  • ulidtko
    ulidtko over 7 years
    This is correct, but not enlightening in any way or helpful. ▼
  • taranaki
    taranaki over 6 years
    This works great except on Windows, where the syntax confuses make and causes the command to fail with the error "'+env' is not recognized as an internal or external command."
  • ulidtko
    ulidtko over 6 years
    I'd say... too many softwares work great "except on Windows".
  • Hans
    Hans over 5 years
    doing just + (without calling env) seems to work as well for my use case (e.g. COMMAND +${CMAKE_COMMAND} --build .. blah blah), which avoids needing to use env.
  • Carlo Wood
    Carlo Wood over 4 years
    What is needed is to have that '+' in front of the generated Makefile rule. On windows the generator used isn't the Makefiles generator, so the + doesn't end up in a Makefile and gets interpreted the way we want. Moreover in some case cmake doesn't put the string that follows COMMAND literally in the Makefile as a rule, but instead does for example: cd working/directory && +env ... which then obviously fails as well. What is REALLY needed is a way to tell cmake to add this '+' at the beginning of the makefile rule, iff the generator is a Makefiles generator with job-server support.
  • einpoklum
    einpoklum about 4 years
    @apenwarr, can you make this into a separate question-and-answer pair? Your answer is not getting the attention it needs IMHO because the OP was concerned about CMake.