This is an automated email from the ASF dual-hosted git repository.

kasjer pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git

commit ebb4bb9702c65f2c0dc766d7e72b9adfd8d0f3f3
Author: Szymon Janc <[email protected]>
AuthorDate: Thu Oct 22 16:45:06 2026 +0200

    newt/toolchain: Add tests for dependency files generated by compiler
    
    Check with a fake compiler that the dependency file written during
    compilation is used to decide whether a later compile is required,
    that an object file without a dependency file is rebuilt, and that
    dependency options are left out of compile_commands.json.
    
    Assisted-by: Claude:claude-opus-5-5
---
 newt/toolchain/deps_test.go | 160 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 160 insertions(+)

diff --git a/newt/toolchain/deps_test.go b/newt/toolchain/deps_test.go
index eb0008c8..e72447d2 100644
--- a/newt/toolchain/deps_test.go
+++ b/newt/toolchain/deps_test.go
@@ -23,7 +23,9 @@ import (
        "fmt"
        "os"
        "path/filepath"
+       "runtime"
        "slices"
+       "strings"
        "sync"
        "testing"
        "time"
@@ -79,6 +81,164 @@ func TestCompilerConcurrentUpdates(t *testing.T) {
        }
 }
 
+// The dependency file must be generated by the compile itself and used to
+// decide whether a later compile is required.
+func TestCompileGeneratesDepsFile(t *testing.T) {
+       if runtime.GOOS == "windows" {
+               t.Skip("uses a shell script as a fake compiler")
+       }
+
+       dir := t.TempDir()
+       t.Chdir(dir)
+
+       // Fake compiler writing the object and dependency files like gcc -MMD.
+       cc := filepath.Join(dir, "fake-cc")
+       script := "#!/bin/sh\n" +
+               "while [ $# -gt 0 ]; do\n" +
+               "  case \"$1\" in\n" +
+               "    -MF) dep=\"$2\"; shift;;\n" +
+               "    -o) obj=\"$2\"; shift;;\n" +
+               "    -*) ;;\n" +
+               "    *) src=\"$1\";;\n" +
+               "  esac\n" +
+               "  shift\n" +
+               "done\n" +
+               "printf '%s: %s src/a.h\\n' \"$obj\" \"$src\" > \"$dep\"\n" +
+               ": > \"$obj\"\n"
+       if err := os.WriteFile(cc, []byte(script), 0755); err != nil {
+               t.Fatal(err)
+       }
+
+       c := newTestCompiler()
+       c.ccPath = cc
+       c.baseDir = dir
+       c.dstDir = "bin"
+       src := dir + "/src/a.c"
+       hdr := dir + "/src/a.h"
+
+       if err := os.MkdirAll(filepath.Dir(src), 0755); err != nil {
+               t.Fatal(err)
+       }
+       for _, f := range []string{src, hdr} {
+               if err := os.WriteFile(f, nil, 0644); err != nil {
+                       t.Fatal(err)
+               }
+       }
+       old := time.Now().Add(-time.Hour)
+       for _, f := range []string{src, hdr} {
+               if err := os.Chtimes(f, old, old); err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       required, err := c.depTracker.CompileRequired(src, COMPILER_TYPE_C)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !required {
+               t.Fatalf("compile not required for new object file")
+       }
+
+       if err := c.CompileFile(src, COMPILER_TYPE_C); err != nil {
+               t.Fatal(err)
+       }
+
+       deps, err := ParseDepsFile(c.dstFilePath(src) + ".d")
+       if err != nil {
+               t.Fatal(err)
+       }
+       exp := []string{"src/a.c", "src/a.h"}
+       if !slices.Equal(deps, exp) {
+               t.Errorf("unexpected dependencies: expected %q, got %q", exp, 
deps)
+       }
+
+       for _, cmd := range c.GetCompileCommands() {
+               if strings.Contains(cmd.Command, "-MMD") ||
+                       strings.Contains(cmd.Command, "-MF") {
+
+                       t.Errorf("dependency options in compile command: %s",
+                               cmd.Command)
+               }
+       }
+
+       required, err = c.depTracker.CompileRequired(src, COMPILER_TYPE_C)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if required {
+               t.Errorf("compile required for up to date object file")
+       }
+
+       now := time.Now().Add(time.Minute)
+       if err := os.Chtimes(hdr, now, now); err != nil {
+               t.Fatal(err)
+       }
+
+       required, err = c.depTracker.CompileRequired(src, COMPILER_TYPE_C)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !required {
+               t.Errorf("compile not required after header was modified")
+       }
+}
+
+// An object file without dependency file (e.g., built by an older newt) must
+// be rebuilt, as it is not known which headers it depends on.
+func TestCompileRequiredMissingDepsFile(t *testing.T) {
+       dir := t.TempDir()
+       c := newTestCompiler()
+       c.ccPath = "cc"
+       c.baseDir = dir
+       c.dstDir = dir + "/bin"
+       src := dir + "/src/a.c"
+       dst := c.dstFilePath(src)
+
+       if err := os.MkdirAll(filepath.Dir(src), 0755); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.WriteFile(src, nil, 0644); err != nil {
+               t.Fatal(err)
+       }
+
+       cmd, err := c.CompileFileCmd(src, COMPILER_TYPE_C)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := writeCommandFile(dst+".o", cmd); err != nil {
+               t.Fatal(err)
+       }
+       if err := os.WriteFile(dst+".o", nil, 0644); err != nil {
+               t.Fatal(err)
+       }
+
+       old := time.Now().Add(-time.Hour)
+       if err := os.Chtimes(src, old, old); err != nil {
+               t.Fatal(err)
+       }
+
+       required, err := c.depTracker.CompileRequired(src, COMPILER_TYPE_C)
+       if err != nil {
+               t.Fatalf("unexpected error: %s", err)
+       }
+       if !required {
+               t.Errorf("compile not required without dependency file")
+       }
+}
+
+func TestStripDepsFlags(t *testing.T) {
+       cmd := []string{"cc", "-Wall", "-MMD", "-MF", "bin/a.d", "-c", "-o",
+               "bin/a.o", "a.c"}
+       exp := []string{"cc", "-Wall", "-c", "-o", "bin/a.o", "a.c"}
+
+       if got := stripDepsFlags(cmd); !slices.Equal(got, exp) {
+               t.Errorf("expected %q, got %q", exp, got)
+       }
+}
+
 func TestParseDepsFileEscaping(t *testing.T) {
        dir := t.TempDir()
        obj := dir + "/my dir/my file.o"

Reply via email to