build parallelism?

Consider the example:

CASE1)
add_library(liba ...) #nothing exotic, no generated, etc
add_library(libb ...) # only includes headers from liba, but the functions have to be there linktime
target_link_library(libb liba) # 
add_executable(main ...) # only includes headers from libb
target_link_libraries(main libb)

Case2)
add_library(liba ...) #nothing exotic, no generated, etc
add_library(libb ...) # only includes headers from liba, but the functions have to be there linktime
target_include_directories(libb ${liba_include_dirs})
add_executable(main ...) 
target_link_libraries(main liba libb) 

Case1, Case2 both build executable main without issue

The Case1 build is entirely sequential,

The Case2 build parallel builds liba, libb,

There is a obvious problem with case 2, but its very hard to argue with “but case2 builds two times faster”, and its about 40 times faster in the real case,

How can I get the parallelism of case 2 while keeping the propagation of properties, etc. I’ve seen somewhere that ninja might get around this, but a sufficient portion of the coders refuse to know what that is, so it has to be a pure cmake solution, we use cmake 3.175 minimum, but I think I can bump that to 3,18 at least if needed. Ideas?

If you can stretch up to CMake 3.19, the OPTIMIZE_DEPENDENCIES target property might be what you’re looking for. If your real project satisfies the criteria described in the docs for that property, then even for case 1, liba and libb should compile in parallel. The only caveat I’m aware of is this issue which affects Ninja only (not dangerous, just ends up potentially rebuilding things when it doesn’t need to).

This is an artifact of CMake’s guarantees. Basically, if target B uses target A, A is historically waited on to link before building anything in B because if A has custom commands to, say, generate headers, this guarantees it happens first. In Issue 15555, Ninja generators learned to break this dependency when it can. It’s not perfect, but there are other issues linked to there that document future progress on that front.

Yeah i know its an error in cmake,
But it was fixed in 3.19 as per craigs answer. There is zero reason why it would not be default, but thats just a one liner that will be in every cmake script i write till it becomes default.

There are probably projects out there that rely on CMake’s stronger guarantees to be correct in a parallel build. Changing the default means breaking all of those projects. There’s also no good way for CMake to know that there are undeclared dependencies in the build graph to warn about such things.

Hmm, fair point, but had it been the default from the start it would have been easier to let the exceptionally rare case be something you opt into imo.