feat(materials): add MSBUILD_LOG material type for compiler build logs

Merged
JA
10h ago
+3035-20

Summary

Adds an MSBUILD_LOG material type, so that a policy can check the compiler warnings of a build. The material reads:

  • MSBuild binary logs (-bl), format 18 or later (MSBuild 17.8 and later). Older formats are rejected with an "unsupported MSBuild binary log version" error.
  • MSBuild text logs:
    • file loggers (-fl), console captures and dotnet build output;
    • several builds in one file;
    • translated MSBuild output (de, fr, ja, ko, ru, zh-CN, zh-TW);
    • CI line prefixes: ISO 8601 timestamps (GitHub Actions) and [hh:mm:ss] (AppVeyor).
  • Visual C++ BuildLog.htm (vcbuild, UTF-16 or ASCII).
  • A zip or tar archive of any of these, for example one binary log per solution. One nested archive level is read, and *.ProjectImports.zip is skipped.

The file is uploaded as is. MSBUILD_LOG is not auto-detected: a contract or --kind must name it.

What a policy gets

{
  "tool": {"name": "msbuild", "version": "17.14.40"},
  "logs": [
    {
      "source": "release/build.log", "solution": "C:\\src\\app.sln", "format": "msbuild-text",
      "tool": {"name": "msbuild", "version": "17.14.40"}, "result": "succeeded",
      "summary": {"warnings": 2, "errors": 0, "by_code": {"C4996": 1, "CA5351": 1}},
      "parsed": {"warnings": 4, "errors": 0}, "reported": {"warnings": 4, "errors": 0}
    }
  ],
  "skipped": ["release/readme.txt"],
  "summary": {"warnings": 2, "errors": 0, "by_code": {"C4996": 1, "CA5351": 1}},
  "diagnostics": [
    {
      "severity": "warning", "code": "C4996", "message": "'strcpy': This function or variable may be unsafe. ...",
      "file": "C:\\src\\a.cpp", "line": 5, "column": 3, "project": "C:\\src\\a.vcxproj",
      "occurrences": 3, "logs": [0]
    }
  ]
}
  • parsed counts every warning and error read, repeats included, while summary counts distinct diagnostics. reported is what the log says about itself (the summary of a text log, the results line of a BuildLog.htm; a binary log has none), so a policy can detect warnings that could not be read.
  • result is succeeded, failed, or empty when the log does not show how the build ended (for example, a log that was cut short).
  • Identical diagnostics are folded into one entry: same severity, code, message, file, project, line and column, where file and project are compared without regard to case. occurrences counts them, and logs holds the indexes of the logs they were seen in, so each diagnostic can be traced back to the build that reported it.
  • skipped lists the archive entries that are not build logs.

Limits

  • Archives use the archiveio limits: 1024 entries and 4 GiB of content. A nested archive is read up to 512 MiB.
  • A binary log is streamed: only string, warning, error, project-started and build-finished records are loaded (64 MiB each at most). All other records are skipped without being loaded.
  • Input that is not a build log fails with ErrInvalidMaterialType.

Testing

  • Unit tests for each format, archives, folding, logs with several builds, translated and CI-prefixed logs, and malformed or truncated input.
  • sample.binlog and lib.binlog are real binary logs, built with the .NET 9 SDK from a small synthetic solution. The text and HTML fixtures are synthetic.
  • Checked locally against 64 public build logs from open-source projects (not committed). Each log was either read, with the warning counts it states, or rejected with an error.
  • End to end with a local control plane: a contract with an MSBUILD_LOG material, a policy evaluated over it, and the attestation pushed.