Resolve output paths against the client's cwd, not the daemon's - #34
Open
dkulp wants to merge 1 commit into
Open
Resolve output paths against the client's cwd, not the daemon's#34dkulp wants to merge 1 commit into
dkulp wants to merge 1 commit into
Conversation
nocc-daemon is a long-lived, machine-wide process on a fixed socket. It is
started by whichever `nocc` invocation first finds no daemon running, inherits
that process's working directory, and then serves every later invocation on the
machine -- from any directory -- until it idles out.
Relative paths in a request were resolved against that inherited cwd rather than
the cwd the client sends alongside the command line:
* the .o itself (files-receiving.go wrote invocation.objOutFile verbatim),
* the .d file name,
* the header paths inside the .d, which were relativized with os.Getwd(),
* the .nocc-pch path, plus OrigHFile/OrigPchFile, which the server maps into
its per-pch root dir by concatenating them onto rootDir and so need to be
absolute the way the dep includes in that struct already are.
Build one project, then build a second from a different directory while the
daemon is still alive, and the second build's objects land in the first one's
tree. When the relative path happens to exist there this is silent: the compile
exits 0, prints nothing, and make simply does not find its output. When it does
not exist, the daemon logs "compiling locally: open <path>: no such file or
directory" and falls back to the local compiler -- correct output, but with no
distribution at all. A project whose Makefile writes objects into a
subdirectory (-o src/foo.o) hits this whenever the daemon happens to be rooted
somewhere without a matching subdirectory.
Store the request cwd on Invocation and add GetObjOutFileAbs(), mirroring the
existing GetCppInFileAbs(). -I/-iquote/-isystem/-include and -MF were already
pre-resolved this way at parse time; -o was the one that was not.
The default -MT target and the input file recorded in the .d are deliberately
left as spelled on the command line -- that is what the compiler writes, and
what make matches its rule targets against.
No effect on the tests/ suite: EmulateDaemonInsideThisProcessForDev fills the
request cwd from the test process's own os.Getwd(), so there the two were
already the same directory.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a correctness bug in nocc-daemon where relative output/dependency/PCH-related paths were effectively resolved against the daemon’s inherited working directory (from whichever client started it) instead of the requesting client’s working directory, causing outputs to be written to the wrong tree (sometimes silently).
Changes:
- Persist the requesting client’s cwd on
Invocationand introduceGetObjOutFileAbs()to resolve-oagainst that cwd. - Switch object receive/write, depfile naming + header-path relativization, and own-PCH path fields to use the request cwd rather than the daemon process cwd.
- Add unit tests ensuring
-oand depfile naming resolve against the request cwd while keeping depfile target strings as-specified.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| internal/client/pch-generation.go | Uses absolute object/PCH paths derived from the client request cwd for own-PCH generation. |
| internal/client/invocation.go | Stores request cwd on Invocation, hardens pathAbs, and adds GetObjOutFileAbs(). |
| internal/client/invocation_test.go | Adds unit tests covering object output path resolution and depfile naming behavior. |
| internal/client/files-receiving.go | Writes received .o to the absolute output path resolved from the request cwd. |
| internal/client/dep-cmd-flags.go | Generates depfile name and depfile header paths relative to the client request cwd (not daemon cwd). |
| internal/client/daemon.go | Updates logging to report the absolute output path for PCH fallback behavior. |
| internal/client/compile-remotely.go | Updates logging to report the absolute output path for saved .o. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
nocc-daemonis a long-lived, machine-wide process on a fixed socket. It is started by whichevernoccinvocation first finds no daemon running, inherits that process's working directory, and then serves every later invocation on the machine — from any directory — until it idles out.Relative paths in a request were resolved against that inherited cwd rather than the cwd the client sends alongside the command line:
.oitself —files-receiving.gowroteinvocation.objOutFileverbatim.dfile name.d, which were relativized withos.Getwd().nocc-pchpath, plusOrigHFile/OrigPchFile, which the server maps into its per-pch root dir by concatenating them ontorootDirand so need to be absolute the way the dep includes in that struct already areReproducing
Build one project, then build a second from a different directory while the daemon is still alive:
fb.olands ina/sub/.b/sub/is empty. Exit code 0, nothing logged.When the relative path does not happen to exist under the daemon's cwd, it is at least loud — the daemon logs
and falls back to the local compiler. Correct output, but with no distribution at all. A project whose Makefile writes objects into a subdirectory (
-o src/foo.o) hits this whenever the daemon happens to be rooted somewhere without a matching subdirectory.The fix
Store the request cwd on
Invocationand addGetObjOutFileAbs(), mirroring the existingGetCppInFileAbs().-I/-iquote/-isystem/-includeand-MFwere already pre-resolved this way at parse time;-owas the one that was not.The default
-MTtarget and the input file recorded in the.dare deliberately left as spelled on the command line — that is what the compiler writes, and what make matches its rule targets against.Test
Three unit tests in
internal/client/invocation_test.gocovering resolution against the request cwd, the as-specified string surviving for the depfile target, two cwds not colliding on one output path, and the depfile name.No effect on the
tests/suite:EmulateDaemonInsideThisProcessForDevfills the request cwd from the test process's ownos.Getwd(), so there the two were already the same directory. Same results as master on my machine.