I assumed that the following claim from the release notes would mean that changes to a header dependency would cause the compileMainExecutable task to execute:
Detects changes to compiler and linker settings, in addition to changes in source and header files.
After running gradle mainExecutable, it builds a working executable. Changing the contents of first.h and running the command again results in gradle saying that compileMainExecutable is UP-TO-DATE. Why?
Iâm actually surprised that this is working for you at all: youâre not telling Gradle where to find your header file, so itâs not being included in the inputs for compile. Presumably, GCC is smart enough to look in the same directory as the source file to locate your header, but Gradle knows nothing about it.
Try something like this:
cpp {
sourceSets {
main {
source {
srcDirs project.rootDir
include '*.cpp'
}
exportedHeaders {
srcDirs project.rootDir
include '*.h'
}
}
}
}
But header files are not (usually) inputs to a compiler invocation.
That behavior is not specific to GCC. The C++ standard says that it is implementation-defined, but practically any compiler tool worth itâs salt will always look in the same directory as the translation unit being compiled (cpp file).
Iâd argue that we shouldnât have to tell Gradle about it, at least not for the purposes of dependency calculation. I havenât tried your suggestion yet, but Iâd figure that it would cause a recompilation if any .h file in the directory were to change, without regard to whether or not it actually leads to a change in a translation unit. That is not C++ incremental compilation.
The alternative would be to explicitly list every header dependency that an individual .cpp file might need, and that would be exhausting. What Gradle should be doing is parsing the .cpp file for the list of header dependencies, and then make the determination if a compilation should happen (per translation unit) based on that information. I know that it a pain to implement correctly, but anything other than that is simply not incremental compilation for C++.
What is reasonable to declare in the Gradle script are the locations that should be passed to the compiler to instruct it where to look to resolve #include statements.
The incremental build support offered by the C++ plugins has been improved in this release, making incremental build very accurate:
Detects changes to compiler and linker settings, in addition to changes in source and header files. No longer recompiles source files when linker settings change. Detects changes to dependencies of a binary and recompiles or relinks as appropriate. Detects changes to the toolchain used to build a binary and recompiles and relinks. Removes stale object files when source files are removed or renamed. Removes stale output files when compiler and linker settings change. For example, removes stale debug files when debug is disabled.
Incremental build is not the same thing as incremental compilation in Gradle terminology.
Incremental build refers to the ability to determine which tasks in the Gradle build need to be executed. So in terms of C++, this would be executing (or not) the a particular compile task.
Incremental compilation is something else, and is potentially complimentary to incremental build. The C++ support does not support incremental compilation at this time.
Detects changes to compiler and linker settings, in addition to changes in source and header files.
Thatâs not a very useful form of incremental behavior for C++, and in fact, it would be undesirable. I would not want to rebuild a project just because a header file in one of my include search paths has changed.
Youâll also get a lot of C++ folks getting the same impression that I had if you use the term âincremental buildâ. It has been a well-established term for a long time. Maybe you should just call this âImproved Dependency Managementâ in the release notes instead?
Iâm confused. Your first concern was that Gradle didnât recompile when a header file changed: now youâre concerned that it will. The way it works is that Gradle will recompile the sources when an incoming header file has changed. So if your C++ source includes a header file, and that file has changed, then the source file will be recompiled. Is this not what should happen?
Itâs understood that the current implementation is not efficient, which is why pretty soon weâll implement incremental compile, which means that weâll only recompile the sources that actually need recompiling. So if âfoo.cppâ includes âbar.hâ and âbar.hâ changes, weâll recompile âfoo.cppâ.
The terminology âincremental-buildâ has for a long time been used in gradle to mean âonly re-run the build steps that are requiredâ. Iâm sure youâre correct in thinking that we need to get a bit more with the lingo of the C++ community, but the meaning of this term in gradle is pretty well established: âbuildâ is much more than âcompileâ.
There are two concerns. 1) Not rebuilding when an included header file has changed. 2) Rebuilding when a header file that is not included has changed. The implementation in 1.7rc1 allows you to do either of those things, but both are wrong. Perhaps the second concern is just suboptimal instead of wrong.
Iâm assuming that one could list the include dependencies explicitly instead of using fileset wildcards, but that would be a disaster for any real C++ project.
What youâve described in the second paragraph is the correct implementation.
I totally understand that it can be difficult to meld the domain language from two domains and that there will always be some amount of confusion. Would it be useful to have a glossary to clarify where there might be collisions between terms in the two domains? Or rather, would the usefulness of such a thing outweigh the cost of maintaining it?
Not rebuilding when an included header file has changed.
This only happens (I think) if you donât tell Gradle what the included header files are, via the âexportedHeadersâ block, and instead rely on GCC to pick up the headers from the source directory. Gradle relies on a strong model of what sources/headers are included in your project, and this model does not yet automatically include these header files.
Weâll probably help in 2 ways: i) make it easy to conventionally include all â*.hâ files in any source directory and ii) stop GCC from automatically using header files in the same directory if they arenât in the Gradle model (since it makes your compilation succeed without Gradle being aware of the required header files).
You need to tell Gradle about your project sources and header files: thatâs how Gradle works. The way you currently tell Gradle about these things is not always convenient: thatâs something weâll work on. Priority is to first make things possible, later to make them easier.
Rebuilding when a header file that is not included has changed
For now, if you tell us about a header file for a compilation, we assume it is included . Later weâll be smarter about this, perhaps by parsing the source files and determining the files that are actually included. If you need this feature now, you could implement this parsing yourself and generate the correct âexportedHeadersâ declaration based on the result.