Anchor dev code and data directories on the running source tree - #1093
Open
johnml1135 wants to merge 1 commit into
Open
Anchor dev code and data directories on the running source tree#1093johnml1135 wants to merge 1 commit into
johnml1135 wants to merge 1 commit into
Conversation
FwDirectoryFinder found the dev DistFiles by assuming the running assembly sat exactly two levels below the tree root, and then let HKCU RootCodeDir/RootDataDir override whatever it found. So a build run from any other output folder missed DistFiles entirely, and every worktree read the DistFiles named by the shared registry value, which belongs to whichever tree last ran the build or the launch script. FindDevDistFiles now walks up from the running assembly to the directory holding both DistFiles and FieldWorks.sln, and that tree wins over the registry. An installed FieldWorks has no solution file beside it, so it keeps reading the registry as before. Set FW_USE_REGISTRY_DIRS to opt a dev build back into the registry. Tests cover the walk from Output/Debug, from an architecture subfolder, and from a project bin folder; the installed case; and the precedence of the source tree over a registry value naming another worktree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1093 +/- ##
=======================================
Coverage 38.31% 38.31%
=======================================
Files 1507 1507
Lines 350524 350538 +14
Branches 40288 40290 +2
=======================================
+ Hits 134302 134310 +8
- Misses 186990 186994 +4
- Partials 29232 29234 +2
🚀 New features to boost your workflow:
|
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.
A Debug build now reads the
DistFilesof the source tree it was built in. Before this, it read whichever tree the shared registry valueHKCU\SOFTWARE\SIL\FieldWorks\9\RootCodeDirhappened to name — on the machine that prompted this, a build in the main repo was loading parts, layouts, and configuration from an unrelated worktree under.tmp/worktrees/.The reviewer's question here is "what did this break for installed FieldWorks?", and the answer is nothing: the new probe only matches a directory that has both
DistFilesandFieldWorks.slnbeside it, which no install has. The question worth your time is whether making the source tree outrank the registry is the right default.Where to look
CodeDirectory/DataDirectorynow return before consulting the registry when the running assembly is inside a source tree — the deliberate behavior change;FW_USE_REGISTRY_DIRS=1restores the old precedence.FindDevDistFileswalks up instead of assuming<assembly>/../../DistFiles, so it also works fromOutput/Debug/x64and from a project's ownbinfolder. Three layouts pinned by tests.DistFilespresent, no solution file, resultnull.TidyRootDiris extracted fromGetDirectoryso both paths normalize trailing separators identically — the ~100Path.Combinecallers see no difference.ProjectsDirectoryis untouched.ProjectsDirstays a shared user preference across worktrees.Deliberately not here
Src/FwParatextLexiconPlugin/ParatextLexiconPluginDirectoryFinder.cskeeps its registry-only resolution; it runs inside Paratext, never from a source tree.RootCodeDiroverride stops applying.Build/mkall.targetsstill writesRootCodeDir/RootDataDir; a source-tree build now ignores them.Verification
.\build.ps1 -CommentHygiene -BuildTestssucceeded (0 warnings, 0 errors; comment-hygiene clean)..\test.ps1 -CommentHygiene -SkipNative -TestProject Src\Common\FwUtils\FwUtilsTests\FwUtilsTests.csproj— 407/407 passed, including 6 new cases. Not run: the full suite, native tests, installer validation. Not done: a manual two-worktree launch.Reading this a year from now — start here
This started as a question, not a bug report: "my local Debug build still looks at DistFiles — could it look at
Outputinstead?" The investigation said no to the literal request and yes to the problem behind it. Both halves are recorded below, because the rejected half is the one that will otherwise be re-proposed.There were no working documents to delete; the reasoning never existed anywhere but here.
Decisions, and why
The registry loses to the source tree, rather than the probe merely being fixed. Fixing the anchoring alone would have changed nothing on a real dev machine:
GetDevDistFilesPath()only ever feddefaultDir, andGetDirectoryreturns the registry value whenever it is non-empty. On a dev machine it is always non-empty —Build/mkall.targets(setKeysInHKCU) writes it, and so does the winapp skill's launch script. The value is a single machine-wide slot shared by every worktree, so it names whichever tree ran last. That is the actual defect; the fixed-depth probe is a second, independent one.FieldWorks.slnas the tree marker. The probe needs something that exists in a source tree and never beside an install. The installer harvestsDistFiles\**\*— the contents, into the install root — so an install has neither aDistFilesfolder nor a solution file at that level. Requiring both makes the installed path unreachable by construction rather than by convention.FW_USE_REGISTRY_DIRSas the escape hatch, read throughEnvironmentVariables.IsTrue. Reuses the repo's existing opt-in convention rather than adding a new registry value, which would have reintroduced the shared-slot problem it exists to escape.No memoization. Each
CodeDirectoryget now walks up doingDirectory.Exists+File.Existsper level. The old path opened and read a registry key on every get, so this is not a regression, and a static cache would freezeFW_USE_REGISTRY_DIRSfor any future test that sets it in-process. Revisit only with a measurement.Paths not taken
Pointing the code directory at
Output/<Configuration>— the literal request.Outputholds build artifacts only; the code/data payload exists solely inDistFiles. Counted in the tree at the time:Language Explorer10 entries inDistFilesvs absent fromOutput/Debug;Parts4 vs absent;Helps7 vs absent;Icu70present vs absent;Templates42 vs 1.FlexStylesPath,FlexFolder,TemplateDirectory, andEditorialChecksDirectorywould all have broken.An overlay that probes
Outputfirst, then falls back toDistFiles. This cannot be expressed through the current API:CodeDirectoryreturns one string that ~100 call sitesPath.Combineonto. An overlay needs aResolveCodeFile(relativePath)seam instead — a much larger change, for a duplication problem that does not currently exist (onlyTemplatesoverlaps at all, with one entry).Just fixing the registry and stopping there. That is what unblocked the reporter (
Resolve-FieldWorksDevRegistry.ps1 -Force, run before this branch existed), and it is what every worktree switch will need again tomorrow. It treats the symptom.Evidence
Precedence, before this change —
GetDirectory(RegistryKey, string, string)inSrc/Common/FwUtils/FwDirectoryFinder.cs:rootDiris read from the registry, anddefaultDiris used onlyif (string.IsNullOrEmpty(rootDir)).GetDevDistFilesPath()feddefaultDir. Hence: registry set → probe irrelevant.The shared slot —
Build/mkall.targetstargetsetKeysInHKCUwritesRootCodeDir,RootDataDir, andProjectsDirtoHKCU\SOFTWARE\SIL\FieldWorks\$(FWMAJOR)from$(dir-fwdistfiles). Nothing inSrc/writes those two values at runtime (searching forSetValue("RootCodeDir"outside tests returns no hits), so the value persists from whichever tree last built or launched.Existing tests keep passing for a non-trivial reason —
FwDirectoryFinderTestssets the registry toUtilsAssemblyDir/../../DistFiles, andInitializeFwRegistryHelperAttributedoes the same. Under the new precedence those values are ignored, but the walk-up returns the same path for a test run out ofOutput/Debug, so the assertions still hold.New coverage —
FindDevDistFiles_InsideSourceTree_FindsTreeDistFiles(Output/Debug,Output/Debug/x64,Src/Common/FwUtils/bin/Debug/net8.0),FindDevDistFiles_OutsideSourceTree_ReturnsNull, andCodeAndDataDirectory_PreferSourceTreeOverRegistry(RootCodeDir,RootDataDir), which points the registry at a fabricated other worktree and asserts both directories still resolve to this tree.Preflight review details
The preflight found no Critical issues and two Important open questions, neither of which was put to the author (the author pre-authorized commit, push, and PR in the same instruction that requested the fix, so no interview was held). They are open questions for the reviewer, not dismissed findings:
RootCodeDirat a non-tree location silently loses that override. Mitigated byFW_USE_REGISTRY_DIRS; not mitigated by any notification that the override stopped applying.FieldWorks.sln. If the solution is renamed or removed, every dev build silently falls back to the registry/Program Files path with no diagnostic. A second marker or a build-time assertion would harden this.Minor: no memoization (considered and rejected, see Decisions); the Paratext plugin's parallel finder now differs in policy and neither file mentions the other; the new precedence test restores a fixture-owned registry value and would throw if the fixture stopped setting it.
Build and test evidence is in the Verification section above. The first build attempt failed on an unrelated ILRepack file lock on
Output\Debug\SIL.LCModel.Core.dll.config; the rerun was clean.gitlint --commits HEAD~1..HEADis clean.This change is