feat(191): add EfcptForceRegenerate property for IDE-triggered regeneration#203
Merged
Conversation
…ration Adds a first-class EfcptForceRegenerate MSBuild property (default false) so IDE extensions (VS #182, VS Code #183) and CLI wrappers can trigger a full model regeneration through a stable public contract instead of deleting obj/-relative fingerprint/stamp files by hand. Setting it to true bypasses the fingerprint/incremental cache for one build; unset/false preserves today's incremental behavior exactly. - BuildTransitivePropsFactory: EfcptForceRegenerate property + typed struct. - BuildTransitiveTargetsFactory: EfcptGenerateModels Condition now also triggers on EfcptForceRegenerate=true; a new internal _EfcptForceRegenerateInvalidateStamp target (BeforeTargets= EfcptGenerateModels, gated on EfcptForceRegenerate=true) deletes the stamp file before EfcptGenerateModels's own Inputs/Outputs up-to-date check runs, so the Outputs are genuinely missing and MSBuild's incremental gate can't skip the target - Condition alone would not have been sufficient. - Regenerated buildTransitive/JD.Efcpt.Build.{props,targets} via JDMSBuildFluentGenerate and committed them. - Added targets-content assertions (SqlProjectTargetGenerationTests) and an executable mechanism test (ForceRegenerateTests) proving the stamp file is deleted only when EfcptForceRegenerate=true. - Fixed FINGERPRINTING.md: the documented fingerprint path (obj/Debug/net8.0/.efcpt/fingerprint.txt) contradicted the real props default (EfcptFingerprintFile = $(EfcptOutput)fingerprint.txt = obj/efcpt/fingerprint.txt, not per-configuration/TFM); corrected throughout, including the previously-wrong "parallel builds isolation" claim. - Added docs/user-guide/force-regenerate.md documenting the extension-facing contract, plus a toc.yml entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds ForceRegenerateOrderingTests: a synthetic-MSBuild integration test that proves the crux claim behind #191 (and the #182/#183 integrations) which the existing direct-invocation tests could not - that a BeforeTargets="Gen" hook runs BEFORE Gen's own Inputs/Outputs up-to-date evaluation, so deleting Gen's Outputs from that hook genuinely forces a RE-RUN of a target that would otherwise be skipped as up-to-date. The test writes a minimal standalone .proj reproducing the exact pattern (an Inputs/Outputs-gated target Gen + a Force-gated BeforeTargets _Invalidate that deletes the stamp) and drives it with real `dotnet msbuild -t:Gen`: (a) first build -> Gen runs, run count = 1 (b) unforced rebuild, inputs unchanged -> Gen SKIPPED up-to-date (count = 1) (c) -p:Force=true, inputs still unchanged -> Gen RE-RUNS (count = 2, stamp rewritten with a fresh value) Step (b)'s skip is asserted explicitly (count must stay 1), so the test is not tautological; a second sanity scenario independently confirms the incremental gate is real. No efcpt tool or database required. Also corrects an inaccurate line in force-regenerate.md: a forced regen does NOT always write a fresh fingerprint - ComputeFingerprint only writes the fingerprint file when the computed value changed (HasChanged); when unchanged the existing correct value is left in place, and the next non-forced build is still correctly incremental. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Contributor
Code Coverage |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #203 +/- ##
==========================================
+ Coverage 83.70% 92.19% +8.48%
==========================================
Files 92 92
Lines 4788 4791 +3
Branches 737 737
==========================================
+ Hits 4008 4417 +409
+ Misses 485 374 -111
+ Partials 295 0 -295
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 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.
Summary
Prerequisite ("PR-0") for the VS (#182) and VS Code (#183) extensions: adds a first-class
EfcptForceRegenerateMSBuild property (defaultfalse) so IDE/CLI integrations can trigger a full model regeneration through a stable public contract instead of pokingobj/-relative fingerprint/stamp files directly.EfcptForceRegenerate(defaultfalse) added toBuildTransitivePropsFactory.cs→ generatedJD.Efcpt.Build.props.EfcptGenerateModels'sConditionnow also runs whenEfcptForceRegenerate=true, in addition to the existing fingerprint-changed / stamp-missing checks.Conditionchange alone isn't sufficient —EfcptGenerateModelsalso hasInputs/Outputs(Outputs="$(EfcptStampFile)") incremental gating, which can still consider the target up to date and skip its tasks even when theConditionis true. A new internal target,_EfcptForceRegenerateInvalidateStamp, is hooked in withBeforeTargets="EfcptGenerateModels"and — only whenEfcptForceRegenerate=true— deletes the stamp file.BeforeTargets-hooked targets run afterDependsOnTargetsbut before the target's own up-to-date evaluation, so by the time MSBuild checksEfcptGenerateModels'sOutputs, the stamp file is genuinely missing and the target cannot be skipped. This is the cleanest MSBuild-idiomatic way to force a rebuild without touching theInputs/Outputsdeclarations themselves.buildTransitive/JD.Efcpt.Build.{props,targets}viaJDMSBuildFluentGenerateand committed the output.SqlProjectTargetGenerationTests(property default, extended condition, invalidation-target wiring) plus an executable mechanism test inForceRegenerateTeststhat runsdotnet msbuild -t:_EfcptForceRegenerateInvalidateStampdirectly and proves the stamp file is deleted only whenEfcptForceRegenerate=true(default behavior is untouched otherwise).FINGERPRINTING.mddocumented the fingerprint path asobj/Debug/net8.0/.efcpt/fingerprint.txt, which contradicts the real props default —EfcptFingerprintFile = $(EfcptOutput)fingerprint.txt, andEfcptOutputdefaults to$(BaseIntermediateOutputPath)efcpt\(obj/efcpt/), not per-configuration/TFM (BaseIntermediateOutputPath≠IntermediateOutputPath). Corrected throughout, including a previously-wrong "parallel builds have isolated fingerprints" claim — by default they actually share the same file.docs/user-guide/force-regenerate.mddocuments the extension-facing contract (EfcptForceRegenerate=truebypasses the fingerprint/incremental cache for one build), with atoc.ymlentry.Backward compatible: default behavior with
EfcptForceRegenerateunset is identical to today's incremental/fingerprint-gated generation (verified via tests + full solution build).Test plan
dotnet build JD.Efcpt.Build.sln -c Debug— 0 errors (TreatWarningsAsErrors=true)buildTransitive/*.{props,targets}regenerated and committed;git statusclean after builddotnet test tests/JD.Efcpt.Build.Tests --filter "FullyQualifiedName~Target |FullyQualifiedName~Generation|FullyQualifiedName~Fingerprint|FullyQualifiedName~Regenerat"→ 107 passed, 2 skipped (pre-existing, require live Snowflake), 0 failedForceRegenerateTests(2) + newSqlProjectTargetGenerationTestsfacts (3) all pass in isolationNot merging — needs two adversarial reviews first, per the coordinating task.
🤖 Generated with Claude Code