Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 30, 2025

Fix: Prevent ILC from writing object file when errors are produced

When --warnaserror is enabled and warnings are produced, ILC writes the output object file, causing build systems to incorrectly consider targets up-to-date on subsequent builds.

Changes Made

  • Track errors directly in Logger class (no interface changes needed)
  • Use temporary file approach: write to .tmp file and rename on success
  • Return exit code 1 when errors are logged
  • Clean up temp file when compilation fails

How It Works

The compiler now uses a temporary file approach to ensure atomic output:

  1. During compilation, output is written to {outputFile}.tmp
  2. The Logger class tracks when errors or warnings-as-errors are logged
  3. At the end of Program.Run():
    • If errors occurred: delete the temp file and return exit code 1
    • If successful: rename the temp file to the final output path using File.Move(..., overwrite: true)

This ensures that:

  • The final output file only exists if compilation succeeded
  • Build systems can rely on the presence/absence of the output file
  • No partial or invalid output files are left behind on failure
  • The approach is atomic - the final file appears only when complete and valid
  • Temp files are cleaned up on failure to avoid clutter

Implementation:

// Write to temporary file
string tempOutputFilePath = outputFilePath + ".tmp";
CompilationResults compilationResults = compilation.Compile(tempOutputFilePath, ...);

// ...

if (logger.HasLoggedErrors)
{
    File.Delete(tempOutputFilePath);  // Clean up temp file
    return 1;
}

// Rename on success
File.Move(tempOutputFilePath, outputFilePath, overwrite: true);
return 0;
Original prompt

This section details on the original issue you should resolve

<issue_title>ILC writes object file even if errors were produced</issue_title>
<issue_description>When warnings are produced and --warnaserror is enabled we should avoid writing the output object file. Otherwise future executions might consider the target up-to-date.</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 3 commits October 30, 2025 03:23
- Add HasLoggedErrors property to ILogWriter interface
- Implement error tracking in TextLogWriter
- Expose HasLoggedErrors through Logger class
- Check for errors before writing object file in RyuJitCompilation and ReadyToRunCodegenCompilation

Co-authored-by: agocke <[email protected]>
ReadyToRun/crossgen2 doesn't have --warnaserror option, so the fix only applies to ILC

Co-authored-by: agocke <[email protected]>
Warnings treated as errors (MessageCategory.WarningAsError) now also set the _hasLoggedErrors flag, preventing object file generation

Co-authored-by: agocke <[email protected]>
Copilot AI changed the title [WIP] Fix object file writing when errors occur with warnaserror ILC: Skip object file write when errors occur Oct 30, 2025
Copilot finished work on behalf of agocke October 30, 2025 03:40
Copilot AI requested a review from agocke October 30, 2025 03:40
@MichalStrehovsky
Copy link
Member

@copilot what happens if warnings as errors are emitted during or after object writing? Do we already have such spots?

Move error check to delete file after all compilation steps complete, catching errors from typeSystemContext.LogWarnings() and other post-compilation warnings.

Co-authored-by: MichalStrehovsky <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 30, 2025

@copilot what happens if warnings as errors are emitted during or after object writing? Do we already have such spots?

Good catch! There are indeed places where warnings can be logged after object writing:

  1. Post-compilation warnings: typeSystemContext.LogWarnings(logger) is called in Program.Run() after compilation.Compile() returns, which can produce warnings-as-errors.

  2. Object file creation timing: The object file is created immediately when the FileStream opens with FileMode.Create, before any writing actually happens.

To handle both scenarios, I've updated the implementation with a two-stage approach:

  • Stage 1 (ObjectWriter.EmitObject): Check for errors immediately after writing and delete the file if needed
  • Stage 2 (Program.Run): Final check before returning to catch any errors logged after compilation (like from typeSystemContext.LogWarnings())

This ensures the object file is removed regardless of when errors occur during the compilation process. (commit f9940d1)

Copilot AI changed the title ILC: Skip object file write when errors occur ILC: Delete object file when errors occur Oct 30, 2025
Copilot finished work on behalf of MichalStrehovsky October 30, 2025 06:24
- Exit with non-zero code when errors are logged
- Remove redundant file deletion from ObjectWriter.EmitObject since Program.Run handles it

Co-authored-by: MichalStrehovsky <[email protected]>
Copilot AI changed the title ILC: Delete object file when errors occur ILC: Delete object file and exit with error code when errors occur Oct 30, 2025
Copilot finished work on behalf of MichalStrehovsky October 30, 2025 07:53
Copy link
Member

@MichalStrehovsky MichalStrehovsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot there is a build failure src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingTestLogger.cs(12,39): error CS0535: (NETCORE_ENGINEERING_TELEMETRY=Build) 'TrimmingTestLogger' does not implement interface member 'ILogWriter.HasLoggedErrors'

- Implement HasLoggedErrors in TrimmingTestLogger
- Revert unnecessary changes to ObjectWriter.cs
- Add empty line after if block in TextLogWriter.cs for style
- Remove comment line that could go stale in Program.cs

Co-authored-by: MichalStrehovsky <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 30, 2025

@copilot there is a build failure src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingTestLogger.cs(12,39): error CS0535: (NETCORE_ENGINEERING_TELEMETRY=Build) 'TrimmingTestLogger...

Fixed by implementing HasLoggedErrors property in TrimmingTestLogger and tracking errors similarly to TextLogWriter. (commit 590200b)

Copilot finished work on behalf of MichalStrehovsky October 30, 2025 11:21
- Track errors directly in Logger class instead of through ILogWriter interface
- Remove HasLoggedErrors from ILogWriter interface
- Remove error tracking from TextLogWriter and TrimmingTestLogger
- Remove unnecessary File.Exists check before File.Delete (File.Delete handles non-existent files)

Co-authored-by: agocke <[email protected]>
Copilot finished work on behalf of agocke October 30, 2025 17:41
Copilot AI requested a review from agocke October 30, 2025 17:41
Changed HasLoggedErrors from expression-bodied property with backing field to auto-property with private setter.

Co-authored-by: agocke <[email protected]>
Copilot finished work on behalf of agocke October 30, 2025 18:23
Copilot AI requested a review from agocke October 30, 2025 18:23
@MichalStrehovsky
Copy link
Member

One more thing we might want to consider - looks like many tools will first create the output using a temporary file name and rename it with the correct name on success or delete it on failure. @agocke what do you think about doing that (not tagging copilot yet)? I can see positives/negatives with either, but it feels like the rename approach is pretty common.

@agocke
Copy link
Member

agocke commented Oct 31, 2025

That sounds like a good approach to me. @copilot please implement that

Write to a temporary file (.tmp extension) during compilation and rename to final output on success. On failure, the temp file is deleted. This is more robust than writing directly and deleting on error.

Co-authored-by: agocke <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 31, 2025

That sounds like a good approach to me. @copilot please implement that

Implemented the temporary file approach. The compiler now writes to {outputFile}.tmp during compilation and renames it to the final path on success, or deletes it on failure. (commit c069eaf)

Copilot AI changed the title ILC: Delete object file and exit with error code when errors occur ILC: Use temporary file to prevent invalid object files on compilation errors Oct 31, 2025
Copilot finished work on behalf of agocke October 31, 2025 01:26
When compilation errors occur, we no longer need to explicitly delete the temp file since it will be overwritten on the next compilation attempt (File.Move uses overwrite: true).

Co-authored-by: jkotas <[email protected]>
Copilot finished work on behalf of jkotas October 31, 2025 02:05
Copilot AI requested a review from jkotas October 31, 2025 02:05
This reverts commit 3a1b59d. Restoring the File.Delete call for temp file cleanup when compilation errors occur.

Co-authored-by: jkotas <[email protected]>
Copilot finished work on behalf of jkotas October 31, 2025 06:21
@agocke agocke marked this pull request as ready for review October 31, 2025 07:40
Copilot AI review requested due to automatic review settings October 31, 2025 07:40
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements atomic output file writing for the AOT compiler to prevent partial files from being left on disk when compilation fails. The compiler now writes to a temporary file and only renames it to the final output on successful completion.

Key Changes:

  • Introduced a HasLoggedErrors property in the Logger class to track compilation errors and warnings-as-errors
  • Modified the compilation process to write to a temporary file first, then atomically rename it on success
  • Added cleanup logic to delete temporary files when compilation fails

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/coreclr/tools/aot/ILCompiler/Program.cs Implements temporary file creation, error-based cleanup, and atomic rename on success
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs Adds HasLoggedErrors property and sets it when errors or warnings-as-errors are logged

}
catch (Exception ex)
{
Console.Error.WriteLine($"Failed to create output file: {ex.Message}");
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message 'Failed to create output file' is misleading since the output file was already successfully created as a temporary file. This is actually a failure to rename/move the file to its final location. Consider changing to 'Failed to move output file to final location' or 'Failed to rename temporary output file'.

Suggested change
Console.Error.WriteLine($"Failed to create output file: {ex.Message}");
Console.Error.WriteLine($"Failed to move temporary output file to final location: {ex.Message}");

Copilot uses AI. Check for mistakes.
Comment on lines +99 to +100
if (warning.Value.Category == MessageCategory.WarningAsError)
HasLoggedErrors = true;
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for setting HasLoggedErrors is duplicated across multiple methods (lines 99-100, 110-111, 151, 161). Consider extracting this into a helper method or setting the flag in a centralized location like _logWriter.WriteWarning and _logWriter.WriteError to reduce duplication and ensure consistency.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ILC writes object file even if errors were produced

4 participants