Skip to content

Don't rewrite Project.toml on resolve/instantiate#4713

Open
PatrickHaecker wants to merge 2 commits into
JuliaLang:masterfrom
PatrickHaecker:dont-write-project-on-resolve-instantiate
Open

Don't rewrite Project.toml on resolve/instantiate#4713
PatrickHaecker wants to merge 2 commits into
JuliaLang:masterfrom
PatrickHaecker:dont-write-project-on-resolve-instantiate

Conversation

@PatrickHaecker

Copy link
Copy Markdown
Contributor

Project.toml is a file edited by the user and Manifest.toml is the derived output, but resolve/instantiate shared the write_env path with add/rm and could rewrite Project.toml as a side effect of producing the manifest. With a top-level workspace [sources] entry this normalized the source path and dropped comments and formatting.

Producing the manifest should never edit the project, and the project serializer is lossy, so the rewrite is destructive rather than just noisy. This bug kind of kills the reproducibility story: When you check out a repo with a Manifest.toml and a Project.toml and you instantiate it, you really don't want changes to your source tree!

Make resolve default to skip_writing_project=true, and have instantiate pass it in both of its up fallbacks (missing manifest and update_on_mismatch); the normal instantiate path never wrote the project. Add a workspace regression test and a CHANGELOG entry.

Claude Opus 4.8 supported in creating this PR

@PatrickHaecker

Copy link
Copy Markdown
Contributor Author

@KristofferC: Could you please review this?
This is some change in behavior, but I think it is exactly in line with your view of Project.toml being the source and Manifest.toml being the derived file.
This is also necessary to fix JuliaC to work correctly with workspaces.

@PatrickHaecker

Copy link
Copy Markdown
Contributor Author

This should probably be backported to 1.13.

PatrickHaecker pushed a commit to PatrickHaecker/JuliaC.jl that referenced this pull request Jul 7, 2026
`compile_products` previously copied the entire project tree into a
temporary directory so `Pkg.instantiate` could write a manifest even when
the source was read-only. That copy is harmful because it kills reproducibility:

- An app which is part of a workspace loses its Manifest by the copy.
- Therefore, the `instantiate` will be a `resolve` (already bad because
any version changes can happen within the compatible set). But as
the source entries from the top-level project are missing, too, it will
in general also resolve to totally different branches from different
URLs (very bad!).

This PR :
- Resolve the manifest in place. If no manifest applies and the directory is
  read-only, error out instead of silently resolving into a throwaway,
  non-reproducible manifest.
- Inject HostCPUFeatures' `freeze_cpu_target` preference through a throwaway
  load-path overlay environment rather than writing a `LocalPreferences.toml`
  into the project. A leading empty JULIA_LOAD_PATH entry expands to Julia's
  default load path, so JuliaC no longer hardcodes it.
- Drop the now-unused `TOML` and `Preferences` imports.
- Add WorkspaceProject test fixtures and cover the in-place workspace-member
  build.

  This PR is ideally combined with [Pkg.jl#4713](JuliaLang/Pkg.jl#4713)

  Claude Opus 4.8 helped in creating this PR.
@topolarity

Copy link
Copy Markdown
Member

This change seems reasonable to me, but I am confused: Is there any reason to set skip_writing_project = true?

Does resolve ever meaningfully modify the Project.toml part of the environment, or is it just re-written to "canonicalize" the file (deps ordering, etc.)?

@KristofferC

KristofferC commented Jul 7, 2026

Copy link
Copy Markdown
Member

That kwarg seems to have originated from #2541. Not sure what it does.CI failures might explain something of it?

@PatrickHaecker

PatrickHaecker commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

You want to set skip_writing_project = true to get a reproducible build in a workspace and to keep Pkg's (virtual) hands off of your potentially manually edited Project.toml .

skip_writing_project = false is good if you want alphabetically sorted entries, normalized paths, consistent formatting and removal of any comments (but only happens if your Project.toml would actually change).
I think as long as we make sure that Pkg does the "canonicalize" when the user clearly wants to modify Project.toml (Pkg.add and the like), I think there is a story to make the skip_writing_project = true behavior the default, remove the keyword argument and potentially offer a separate "canonicalize" functionality which reliably does the job when you call it (and does not depend on whether there were modifying side effects or not).

Update: I am not sure whether skip_writing_project is part of public API. Documentation does not mention it, but it is listed as a method of the public function Pkg.resolve in Julia 1.12:

julia> Pkg.resolve |> methods
# 2 methods for generic function "resolve" from Pkg.API:
 [1] resolve(; io, kwargs...)
     @ ~/.julia/juliaup/julia-1.12.6+0.x64.linux.gnu/share/julia/stdlib/v1.12/Pkg/src/API.jl:454
 [2] resolve(ctx::Pkg.Types.Context; skip_writing_project, kwargs...)
     @ ~/.julia/juliaup/julia-1.12.6+0.x64.linux.gnu/share/julia/stdlib/v1.12/Pkg/src/API.jl:455

@PatrickHaecker

Copy link
Copy Markdown
Contributor Author

@KristofferC, @topolarity: So what do you propose?

  • merge as is and potentially remove skip_writing_project in a follow-up PR?
  • remove skip_writing_project now, because you decide that this is not public API, and merge afterwards?

Having this PR merged would help correctly supporting workspaces in JuliaC.

@topolarity

Copy link
Copy Markdown
Member

I think I agree with the argument and I'm generally in support - What's the cause of the CI failures though?

@DilumAluthge
DilumAluthge force-pushed the dont-write-project-on-resolve-instantiate branch from ba70b61 to 4d9e3e4 Compare July 14, 2026 03:08
@PatrickHaecker
PatrickHaecker force-pushed the dont-write-project-on-resolve-instantiate branch from 4d9e3e4 to a10f6ae Compare July 14, 2026 03:14
@DilumAluthge

Copy link
Copy Markdown
Member

@PatrickHaecker Can you update the master branch in your fork? It looks like it's behind a few commits.

Patrick Häcker added 2 commits July 14, 2026 05:22
Project.toml is a file edited by the user and Manifest.toml is the derived output, but
resolve/instantiate shared the write_env path with add/rm and could rewrite
Project.toml as a side effect of producing the manifest. With a top-level
workspace [sources] entry this normalized the source path and dropped comments
and formatting.

Producing the manifest should never edit the project, and the project serializer
is lossy, so the rewrite is destructive rather than just noisy. This bug kind of kills
the reproducibility story: When you check out a repo with a Manifest.toml and
a Project.toml and you `instantiate` it, you really don't want changes to your
source tree!

Make resolve default to skip_writing_project=true, and have instantiate pass it
in both of its `up` fallbacks (missing manifest and update_on_mismatch); the
normal instantiate path never wrote the project. Add a workspace regression test
and a CHANGELOG entry.

Claude Opus 4.8 supported in creating this PR
@PatrickHaecker
PatrickHaecker force-pushed the dont-write-project-on-resolve-instantiate branch from a10f6ae to ea89c18 Compare July 14, 2026 03:22
@PatrickHaecker

Copy link
Copy Markdown
Contributor Author

@PatrickHaecker Can you update the master branch in your fork? It looks like it's behind a few commits.

Thanks, @DilumAluthge. Seems to have been a VS Code Git problem: Fetching from upstream did not show any changes, but pulling updated now anyway)

@DilumAluthge

Copy link
Copy Markdown
Member

Thanks!

@PatrickHaecker

Copy link
Copy Markdown
Contributor Author

I think I agree with the argument and I'm generally in support - What's the cause of the CI failures though?

Thanks for the hint. I was to much in "contribute to a package" mode and ignored issues, because they were only in nightly, which (in retrospect) obviously are the only useful tests for Pkg.

There is one test which previously tested the implicit rewrite of Project.toml. Due to the "do not rewrite per default policy" of this PR, I changed this to now explicitly request to rewrite the file.

The other test should be fixed by rebasing on recent master. So testing should (hopefully) be green.

@PatrickHaecker

Copy link
Copy Markdown
Contributor Author

The windows test error is a disk space problem on the CI runner. So tests look good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

4 participants