Is there any guarantee on the order of arguments to the compiler when compiling a file? There are many places where they can be set and while sometimes the documentation provides hints on order, I have an impression the exact precise order is not stated anywhere and I’m not sure if it can be relied on in the first place.
This in particular applies to order coming from properties on targets, including targets they depend on.
While it is often quite important. Project may require ensuring some include directory is placed before others so that it will “hijack” #include
s and continue with #include_next
. Similarly with compile options, where one may want the opposite - to ensure their option is last and overrides what was before.
I suspect the order is determinate, but not explicitly documented fully through the transitive relationships. For the INCLUDE_DIRECTORIES
target property, I would expect the order they appear on the command line will respect the order they are added to that property. I haven’t checked the code, but the transitive properties will probably be added after those, most likely in the order the dependencies are linked to that target. I don’t know if it is a depth-first or breadth-first algorithm though. You’d need to check the source code to answer that question.
Also note that header search paths are also subject to de-duplication. That shouldn’t affect the final behavior, but it will affect the paths you see on the compiler command line. Compile options are also subject to de-duplication.
Is lack of documentation here desired, to leave room for change and prevent users from depending on this? Or is it just that no one documented this? Are there any tests which protect the order?
Hm… Is the de-duplication safe? What about options that overwrite each other? Like, for example
... -march=ABC ... -march=DEF ... -march=ABC ...
... -DMY_SYMBOL ... -UMY_SYMBOL ... -DMY_SYMBOL ...
... -Wno-some-warning ... -Wsome-warning ... -Wno-some-warning ...
Removing duplicates at the end would change the result. Or are they removed at the front instead? But removing duplicates at the front would in turn not work with include directories (would change the result).
How this de-duplication works?
I would say no one even knows the complete, total, correct-for-every-scenario answer to the question.
The steps that compile and link line constructions pass through have been built up over decades by dozens of developers. It’s tricky, but possible, to develop a general description of the behavior at a high-level, especially for things like the actual compile options and include directory properties on targets, but several flags get constructed or manipulated in a generator-specific manner
Once you start considering things like forced includes, source file properties, precompiled headers, or C++20 modules, reasoning about the entire compile or link line becomes untenable. For some generators it’s not even entirely up to CMake where things end up, but rather the build system consuming CMake’s output.
The parts of the link line where ordering matters, like archives to support single-pass linkers, are tested extensively. Stuff that you can throw anywhere much less so.
So it’s undocumented because it’s undefined. It’s undefined because it’s complicated.
My recollection is that de-duplication keeps the first occurrence for the header search paths. I’m less certain about which occurrence it keeps for compiler and linker options, it’s been a while since I last looked at that closely. I want to say that it keeps the last occurrence for those, but I could be wrong about that. I see that the comments in my Professional CMake book about this could use revisiting, so I’ll add that to the list for the next update. In the meantime, you could probably do some experiments yourself locally to confirm which occurrence it keeps.
1 Like