Skip to content

Commit f13ade8

Browse files
Let include detection generate .d files too
Previously, the including detection process caching relied on the .d files generated by compilation to know what .h files a given source files depends on. This works correctly, but if a project does not compile completely, not all .d files are generated, so not all cached include detection results can be used. In practice, this means that if a there is a compilation error in the first file that is compiled, include detection will run again for all files on the next run. If you have a few errors to solve and a big project, this gets annoying quickly. To fix this, the include detection process should generate .d files itself. At first glance it appears that there is a problematic case where the list of included header files changes, the include detection overwrites the .d file and then compilation only sees the new list (which was generated later than the .o file was generated). However, since this implies that changes are made to an #include directive in the source file itself or one of the files that are still included, this should be detected normally. There is still a corner case when a file is changed during the build, but that was already the case. Since include detections uses `-o /dev/null`, the compiler generates a slightly different .d file. During compilation, a file `foo.cpp.d` is generated in the output directory starting with: /path/to/foo.cpp.o: \ But when just passing `-MMD` to the preproc recipe, it generates a `foo.d` file in the source directory starting with: foo.o: \ To make these equal, `-MF` must be passed during include detection to set the .d filename, and `-MT` must be passed to set the .o filename inside the .d file. To enable this feature, platform.txt should be modified by adding ` -MMD -MF {dep_file} -MT {object_file}` to `preproc.macros.flags` (or `recipe.preproc.macros`). Without any changes to platform.txt, behaviour is unchanged. To allow this, this adds `{dep_file}` and `{object_file}` variables to the build properties for the preproc macros recipe. For consistency, `{dep_file}` is also added during normal compilation, though it is not currently used.
1 parent d4e9078 commit f13ade8

File tree

5 files changed

+23
-9
lines changed

5 files changed

+23
-9
lines changed

src/arduino.cc/builder/builder_utils/utils.go

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ func compileFileWithRecipe(sourcePath string, source string, buildPath string, b
138138
return "", i18n.WrapError(err)
139139
}
140140
properties[constants.BUILD_PROPERTIES_OBJECT_FILE] = filepath.Join(buildPath, relativeSource+".o")
141+
properties[constants.BUILD_PROPERTIES_DEP_FILE] = filepath.Join(buildPath, relativeSource+".d")
141142

142143
err = utils.EnsureFolderExists(filepath.Dir(properties[constants.BUILD_PROPERTIES_OBJECT_FILE]))
143144
if err != nil {

src/arduino.cc/builder/constants/constants.go

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ const BUILD_PROPERTIES_EXTRA_TIME_UTC = "extra.time.utc"
5757
const BUILD_PROPERTIES_EXTRA_TIME_ZONE = "extra.time.zone"
5858
const BUILD_PROPERTIES_INCLUDES = "includes"
5959
const BUILD_PROPERTIES_OBJECT_FILE = "object_file"
60+
const BUILD_PROPERTIES_DEP_FILE = "dep_file"
6061
const BUILD_PROPERTIES_OBJECT_FILES = "object_files"
6162
const BUILD_PROPERTIES_PATTERN = "pattern"
6263
const BUILD_PROPERTIES_PID = "pid"

src/arduino.cc/builder/container_add_prototypes.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ type ContainerAddPrototypes struct{}
4141

4242
func (s *ContainerAddPrototypes) Run(ctx *types.Context) error {
4343
sourceFile := filepath.Join(ctx.SketchBuildPath, filepath.Base(ctx.Sketch.MainFile.Name)+".cpp")
44+
depFile := sourceFile + ".d"
45+
objFile := sourceFile + ".o"
4446
commands := []types.Command{
45-
&GCCPreprocRunner{SourceFilePath: sourceFile, TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E, Includes: ctx.IncludeFolders},
47+
&GCCPreprocRunner{SourceFilePath: sourceFile, ObjFilePath: objFile, DepFilePath: depFile, TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E, Includes: ctx.IncludeFolders},
4648
&ReadFileAndStoreInContext{Target: &ctx.SourceGccMinusE},
4749
&FilterSketchSource{Source: &ctx.SourceGccMinusE},
4850
&CTagsTargetFileSaver{Source: &ctx.SourceGccMinusE, TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E},

src/arduino.cc/builder/container_find_includes.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,8 @@ func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFile t
295295

296296
// TODO: This should perhaps also compare against the
297297
// include.cache file timestamp. Now, it only checks if the file
298-
// changed after the object file was generated, but if it
299-
// changed between generating the cache and the object file,
298+
// changed after the dependency file was generated, but if it
299+
// changed between generating the cache and the dependency file,
300300
// this could show the file as unchanged when it really is
301301
// changed. Changing files during a build isn't really
302302
// supported, but any problems from it should at least be
@@ -305,7 +305,7 @@ func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFile t
305305
// TODO: This reads the dependency file, but the actual building
306306
// does it again. Should the result be somehow cached? Perhaps
307307
// remove the object file if it is found to be stale?
308-
unchanged, err := builder_utils.BuildResultIsUpToDate(sourcePath, objPath, objPath, depPath, ctx.DebugLevel, ctx.GetLogger())
308+
unchanged, err := builder_utils.BuildResultIsUpToDate(sourcePath, depPath, objPath, depPath, ctx.DebugLevel, ctx.GetLogger())
309309
if err != nil {
310310
return i18n.WrapError(err)
311311
}
@@ -326,7 +326,7 @@ func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFile t
326326
}
327327
} else {
328328
commands := []types.Command{
329-
&GCCPreprocRunnerForDiscoveringIncludes{SourceFilePath: sourcePath, TargetFilePath: targetFilePath, Includes: includes},
329+
&GCCPreprocRunnerForDiscoveringIncludes{SourceFilePath: sourcePath, ObjFilePath: objPath, DepFilePath: depPath, TargetFilePath: targetFilePath, Includes: includes},
330330
&IncludesFinderWithRegExp{Source: &ctx.SourceGccMinusE},
331331
}
332332
for _, command := range commands {
@@ -347,7 +347,7 @@ func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFile t
347347
library := ResolveLibrary(ctx, include)
348348
if library == nil {
349349
// Library could not be resolved, show error
350-
err := runCommand(ctx, &GCCPreprocRunner{SourceFilePath: sourcePath, TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E, Includes: includes})
350+
err := runCommand(ctx, &GCCPreprocRunner{SourceFilePath: sourcePath, ObjFilePath: objPath, DepFilePath: depPath, TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E, Includes: includes})
351351
return i18n.WrapError(err)
352352
}
353353

src/arduino.cc/builder/gcc_preproc_runner.go

+13-3
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,14 @@ import (
4343

4444
type GCCPreprocRunner struct {
4545
SourceFilePath string
46+
ObjFilePath string
47+
DepFilePath string
4648
TargetFileName string
4749
Includes []string
4850
}
4951

5052
func (s *GCCPreprocRunner) Run(ctx *types.Context) error {
51-
properties, targetFilePath, err := prepareGCCPreprocRecipeProperties(ctx, s.SourceFilePath, s.TargetFileName, s.Includes)
53+
properties, targetFilePath, err := prepareGCCPreprocRecipeProperties(ctx, s.SourceFilePath, s.ObjFilePath, s.DepFilePath, s.TargetFileName, s.Includes)
5254
if err != nil {
5355
return i18n.WrapError(err)
5456
}
@@ -72,12 +74,14 @@ func (s *GCCPreprocRunner) Run(ctx *types.Context) error {
7274

7375
type GCCPreprocRunnerForDiscoveringIncludes struct {
7476
SourceFilePath string
77+
ObjFilePath string
78+
DepFilePath string
7579
TargetFilePath string
7680
Includes []string
7781
}
7882

7983
func (s *GCCPreprocRunnerForDiscoveringIncludes) Run(ctx *types.Context) error {
80-
properties, _, err := prepareGCCPreprocRecipeProperties(ctx, s.SourceFilePath, s.TargetFilePath, s.Includes)
84+
properties, _, err := prepareGCCPreprocRecipeProperties(ctx, s.SourceFilePath, s.ObjFilePath, s.DepFilePath, s.TargetFilePath, s.Includes)
8185
if err != nil {
8286
return i18n.WrapError(err)
8387
}
@@ -100,7 +104,7 @@ func (s *GCCPreprocRunnerForDiscoveringIncludes) Run(ctx *types.Context) error {
100104
return nil
101105
}
102106

103-
func prepareGCCPreprocRecipeProperties(ctx *types.Context, sourceFilePath string, targetFilePath string, includes []string) (properties.Map, string, error) {
107+
func prepareGCCPreprocRecipeProperties(ctx *types.Context, sourceFilePath string, objFilePath string, depFilePath string, targetFilePath string, includes []string) (properties.Map, string, error) {
104108
if targetFilePath != utils.NULLFile() {
105109
preprocPath := ctx.PreprocPath
106110
err := utils.EnsureFolderExists(preprocPath)
@@ -113,6 +117,12 @@ func prepareGCCPreprocRecipeProperties(ctx *types.Context, sourceFilePath string
113117
properties := ctx.BuildProperties.Clone()
114118
properties[constants.BUILD_PROPERTIES_SOURCE_FILE] = sourceFilePath
115119
properties[constants.BUILD_PROPERTIES_PREPROCESSED_FILE_PATH] = targetFilePath
120+
properties[constants.BUILD_PROPERTIES_DEP_FILE] = depFilePath
121+
properties[constants.BUILD_PROPERTIES_OBJECT_FILE] = objFilePath
122+
err := utils.EnsureFolderExists(filepath.Dir(depFilePath))
123+
if err != nil {
124+
return nil, "", i18n.WrapError(err)
125+
}
116126

117127
includes = utils.Map(includes, utils.WrapWithHyphenI)
118128
properties[constants.BUILD_PROPERTIES_INCLUDES] = strings.Join(includes, constants.SPACE)

0 commit comments

Comments
 (0)