Skip to content

Use fork-sync and make a local copy of fmt#15919

Open
vmoroz wants to merge 6 commits intomicrosoft:mainfrom
vmoroz:PR/fork-sync-fmt
Open

Use fork-sync and make a local copy of fmt#15919
vmoroz wants to merge 6 commits intomicrosoft:mainfrom
vmoroz:PR/fork-sync-fmt

Conversation

@vmoroz
Copy link
Copy Markdown
Member

@vmoroz vmoroz commented Apr 2, 2026

Description

Type of Change

  • New feature (non-breaking change which adds functionality)

Why

Third-party dependencies in RNW (fmt, Folly, ReactCommon) use a bespoke
MSBuild download-unzip-overlay pipeline that has no conflict detection, no
3-way merge, and duplicates the same concepts per library. This PR begins
adopting @rnx-kit/fork-sync — the same tool used in hermes-windows — to get
Git 3-way merges, AI-assisted conflict resolution, and a unified
manifest-driven workflow.

fmt is the first dependency to migrate because it is the simplest case: a
small library with no active patches.

What

Phase 0 — fork-sync infrastructure:

  • Created @rnw-scripts/fork-sync package — thin wrapper that invokes
    @rnx-kit/fork-sync with -C pointing to the repo root.
  • Added sync-manifest.json at the repo root listing fmt as the first
    managed dependency.
  • Added /.sync/ to .gitignore (fork-sync's working directory).
  • Added fork-sync and fork-sync:status scripts to root package.json.

Phase 1 — migrate fmt to fork-sync:

  • Committed fmt 11.0.2 source into vnext/external/fmt/ with
    sync-config.json, .syncignore, and cgmanifest.json.
  • Added vnext/external/.clang-format (DisableFormat: true) to prevent
    formatting of vendored source files.
  • Added vnext/external to .unbroken_exclusions to skip link checking
    in vendored docs.
  • Each external dependency gets its own .vcxitems file next to its
    source (e.g., vnext/external/fmt/fmt.vcxitems). A top-level
    vnext/external/External.vcxitems aggregates all per-dependency items
    and applies shared compiler settings via a single
    <ClCompile Update="$(ExternalDir)**"> block. Shared.vcxitems
    imports External.vcxitems with one line — keeping vendored code
    cleanly separated from RNW's own source.
  • Defined $(ExternalDir) in Directory.Build.props pointing to
    vnext\external\.
  • Updated fmt include paths in React.Cpp.props, Folly.vcxproj, and
    Microsoft.ReactNative.vcxproj from $(FmtDir)include to
    $(ExternalDir)fmt\include.
  • Removed FmtVersion, FmtCommitHash, and FmtDir properties from
    Directory.Build.props.
  • Updated Layout-Desktop-Headers.ps1 to use committed fmt source
    instead of downloading.
  • Deleted vnext/fmt/ directory (vcxproj, filters, pch files,
    clang-format, packages.lock.json).
  • Removed fmt project entry and build configurations from 12 .sln files
    and 2 .slnf files.
  • Removed ProjectReference to fmt from Folly.vcxproj.

Screenshots

N/A

Testing

  • Built Microsoft.ReactNative.vcxproj (Debug|x64) — fmt compiles
    correctly via the vcxitems chain.
  • Built Folly.vcxproj (Debug|x64) — finds fmt headers via updated
    include path.
  • Built React.Windows.Desktop.DLL.vcxproj (Debug|x64) — Desktop target
    also works.
  • Verified yarn format no longer reformats vendored fmt source (thanks
    to vnext/external/.clang-format).

Changelog

Should this change be included in the release notes: no

@vmoroz vmoroz requested review from a team as code owners April 2, 2026 19:20
@Saadnajmi
Copy link
Copy Markdown
Contributor

Can we not pull fmt as a package and patch it if need be? Do we need a source copy?

@vmoroz
Copy link
Copy Markdown
Member Author

vmoroz commented Apr 2, 2026

Can we not pull fmt as a package and patch it if need be? Do we need a source copy?

All major OSS projects typically have a local copy of 3rd party code. It makes it easy to support them.
For us having the sources in our repo makes it easy to investigate issues in the crash dumps and do any quick changes.
The new fork-sync tool makes it trivial to support such local forks.

@Saadnajmi
Copy link
Copy Markdown
Contributor

Saadnajmi commented Apr 2, 2026

Can we not pull fmt as a package and patch it if need be? Do we need a source copy?

All major OSS projects typically have a local copy of 3rd party code. It makes it easy to support them.
For us having the sources in our repo makes it easy to investigate issues in the crash dumps and do any quick changes.
The new fork-sync tool makes it trivial to support such local forks.

That's not true? Most OSS projects use a package or dep manager to download them?

@vmoroz
Copy link
Copy Markdown
Member Author

vmoroz commented Apr 3, 2026

That's not true? Most OSS projects use a package or dep manager to download them?

I am referring to Node.js and V8 OSS projects. A number of other projects do the same, e.g. Hermes.
These changes were already discussed with other RNW team members.
Let's continue this discussion offline.

@Saadnajmi
Copy link
Copy Markdown
Contributor

That's not true? Most OSS projects use a package or dep manager to download them?

I am referring to Node.js and V8 OSS projects. A number of other projects do the same, e.g. Hermes. These changes were already discussed with other RNW team members. Let's continue this discussion offline.

I'll trust your judgement. It's not my prefernce but I am sure you have weighed the tradeoffs for RNW.

Copy link
Copy Markdown
Contributor

@JunielKatarn JunielKatarn left a comment

Choose a reason for hiding this comment

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

Moved fmt ClCompile/ClInclude entries into Shared.vcxitems — fmt is
now compiled inline instead of as a separate static library.

I would argue against this.
Shared should be scoped only to RNW sources ultimately built into the Desktop and modern DLLs, not third-party code.

Got two questions:

  1. Why not keep the static library?
  2. If the static library is a problem, why not create a separate VCXITEMS project (similar to shared) dedicated to fmt (i.e. fmt.vcxitems)?

@microsoft-github-policy-service microsoft-github-policy-service bot added the Needs: Author Feedback The issue/PR needs activity from its author (label drives bot activity) label Apr 3, 2026
@vmoroz vmoroz requested a review from a team as a code owner April 6, 2026 18:03
@microsoft-github-policy-service microsoft-github-policy-service bot removed the Needs: Author Feedback The issue/PR needs activity from its author (label drives bot activity) label Apr 6, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

Performance Test Results

Branch: PR/fork-sync-fmt
Commit: 9d38c736
Time: 2026-04-06T19:40:31.716Z
Tests: 160/161 passed

❌ Regressions Detected

ScrollView

Scenario Baseline Current Change Status
ScrollView children-500 19.67ms 46.93ms +131.6%

ScrollView children-500: Duration increased by 131.6% / +25.00ms (threshold: 10% & 10ms)

TouchableHighlight

Scenario Baseline Current Change Status
TouchableHighlight multiple-touchables-50 13.00ms 19.80ms +52.0%

TouchableHighlight multiple-touchables-50: Duration increased by 52.0% / +6.50ms (threshold: 15% & 5ms)

✅ Passed

138 scenario(s) across 26 suite(s) — no regressions

SectionList

Scenario Mean Median StdDev Renders vs Baseline
SectionList mount 5.80ms 5.50ms ±1.62ms 1 +10.0%
SectionList unmount 0.30ms 0.00ms ±0.48ms 0 +0.0%
SectionList rerender 13.60ms 13.00ms ±2.80ms 2 +23.8%
SectionList with-3-sections-15-items 6.40ms 5.50ms ±1.84ms 1 +0.0%
SectionList with-5-sections-50-items 6.40ms 6.00ms ±1.51ms 1 +0.0%
SectionList with-10-sections-200-items 5.60ms 5.50ms ±1.07ms 1 +0.0%
SectionList with-20-sections-200-items 5.30ms 5.00ms ±2.00ms 1 +0.0%
SectionList with-section-separator 1.90ms 2.00ms ±0.74ms 1 +0.0%
SectionList with-item-separator 3.40ms 3.00ms ±1.78ms 1 +50.0%
SectionList with-header-footer 2.20ms 2.00ms ±0.63ms 1 +0.0%
SectionList with-section-footer 2.30ms 3.00ms ±0.95ms 1 +50.0%
SectionList with-sticky-section-headers 2.40ms 2.00ms ±2.41ms 1 +0.0%
SectionList with-empty-list 0.50ms 0.50ms ±0.53ms 1 -50.0%
SectionList with-50-sections-1000-items 1.50ms 1.50ms ±0.53ms 1 -25.0%

FlatList

Scenario Mean Median StdDev Renders vs Baseline
FlatList mount 5.20ms 5.00ms ±1.40ms 1 +25.0%
FlatList unmount 0.10ms 0.00ms ±0.32ms 0 +0.0%
FlatList rerender 11.50ms 11.00ms ±1.58ms 2 +22.2%
FlatList with-10-items 6.00ms 6.00ms ±1.05ms 1 +50.0%
FlatList with-100-items 6.50ms 6.00ms ±1.78ms 1 +20.0%
FlatList with-500-items 5.60ms 5.00ms ±1.58ms 1 +25.0%
FlatList with-1000-items 5.20ms 5.00ms ±1.62ms 1 +25.0%
FlatList horizontal 4.50ms 4.50ms ±1.58ms 1 -10.0%
FlatList with-separator 1.70ms 2.00ms ±0.67ms 1 +0.0%
FlatList with-header-footer 1.80ms 2.00ms ±0.42ms 1 +0.0%
FlatList with-empty-list 0.40ms 0.00ms ±0.52ms 1 -100.0%
FlatList with-get-item-layout 1.80ms 2.00ms ±0.42ms 1 +100.0%
FlatList inverted 1.90ms 1.00ms ±1.85ms 1 -33.3%
FlatList with-num-columns 3.70ms 3.50ms ±1.16ms 1 +16.7%

TouchableOpacity

Scenario Mean Median StdDev Renders vs Baseline
TouchableOpacity mount 0.80ms 1.00ms ±0.42ms 1 +0.0%
TouchableOpacity unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
TouchableOpacity rerender 1.20ms 1.00ms ±0.63ms 2 +0.0%
TouchableOpacity custom-active-opacity 0.70ms 1.00ms ±0.48ms 1 +0.0%
TouchableOpacity disabled 1.00ms 1.00ms ±1.49ms 1 +0.0%
TouchableOpacity with-all-handlers 0.70ms 1.00ms ±0.48ms 1 +0.0%
TouchableOpacity with-hit-slop 1.00ms 1.00ms ±0.00ms 1 +0.0%
TouchableOpacity with-delay 0.70ms 1.00ms ±0.48ms 1 +0.0%
TouchableOpacity nested 1.60ms 2.00ms ±0.52ms 1 +100.0%
TouchableOpacity multiple-10 7.80ms 8.00ms ±2.14ms 1 +33.3%
TouchableOpacity multiple-50 30.60ms 31.00ms ±3.54ms 1 +6.9%
TouchableOpacity multiple-100 48.80ms 50.00ms ±12.94ms 1 +0.0%

Pressable

Scenario Mean Median StdDev Renders vs Baseline
Pressable mount 0.20ms 0.00ms ±0.42ms 1 +0.0%
Pressable unmount 0.10ms 0.00ms ±0.32ms 0 +0.0%
Pressable rerender 0.60ms 1.00ms ±0.52ms 2 +100.0%
Pressable with-all-handlers 0.90ms 0.00ms ±1.85ms 1 +0.0%
Pressable with-style-function 0.40ms 0.00ms ±0.52ms 1 +0.0%
Pressable disabled 0.40ms 0.00ms ±0.52ms 1 +0.0%
Pressable with-hit-slop 0.30ms 0.00ms ±0.48ms 1 +0.0%
Pressable nested 0.80ms 1.00ms ±0.42ms 1 +0.0%
Pressable multiple-10 3.93ms 4.00ms ±0.96ms 1 +33.3%
Pressable multiple-50 16.87ms 16.00ms ±2.39ms 1 +14.3%
Pressable multiple-100 19.60ms 16.00ms ±11.95ms 1 +33.3%

Modal

Scenario Mean Median StdDev Renders vs Baseline
Modal mount 0.40ms 0.00ms ±0.52ms 1 +0.0%
Modal unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
Modal rerender 0.40ms 0.00ms ±0.52ms 2 +0.0%
Modal slide-animation 0.20ms 0.00ms ±0.42ms 1 +0.0%
Modal fade-animation 0.20ms 0.00ms ±0.42ms 1 +0.0%
Modal transparent 0.40ms 0.00ms ±0.52ms 1 +0.0%
Modal with-callbacks 0.00ms 0.00ms ±0.00ms 1 +0.0%
Modal rich-content 1.60ms 2.00ms ±0.52ms 1 +0.0%
Modal with-accessibility 0.30ms 0.00ms ±0.48ms 1 +0.0%

Image

Scenario Mean Median StdDev Renders vs Baseline
Image mount 0.10ms 0.00ms ±0.32ms 1 +0.0%
Image unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
Image rerender 0.30ms 0.00ms ±0.48ms 2 +0.0%
Image with-resize-mode 0.20ms 0.00ms ±0.42ms 1 +0.0%
Image with-border-radius 0.20ms 0.00ms ±0.42ms 1 +0.0%
Image with-tint-color 0.10ms 0.00ms ±0.32ms 1 +0.0%
Image with-blur-radius 0.20ms 0.00ms ±0.42ms 1 +0.0%
Image with-accessibility 0.10ms 0.00ms ±0.32ms 1 +0.0%
Image multiple-10 0.80ms 1.00ms ±0.41ms 1 +0.0%
Image multiple-50 5.07ms 4.00ms ±2.58ms 1 +33.3%
Image multiple-100 10.47ms 10.00ms ±3.02ms 1 +25.0%

ActivityIndicator

Scenario Mean Median StdDev Renders vs Baseline
ActivityIndicator mount 0.10ms 0.00ms ±0.32ms 1 +0.0%
ActivityIndicator unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
ActivityIndicator rerender 0.70ms 0.00ms ±1.25ms 2 +0.0%
ActivityIndicator size-large 0.30ms 0.00ms ±0.48ms 1 +0.0%
ActivityIndicator size-small 0.10ms 0.00ms ±0.32ms 1 +0.0%
ActivityIndicator with-color 0.20ms 0.00ms ±0.42ms 1 +0.0%
ActivityIndicator not-animating 0.20ms 0.00ms ±0.42ms 1 +0.0%
ActivityIndicator with-accessibility 0.10ms 0.00ms ±0.32ms 1 +0.0%
ActivityIndicator multiple-10 0.93ms 1.00ms ±0.46ms 1 +0.0%
ActivityIndicator multiple-50 4.00ms 4.00ms ±0.53ms 1 +0.0%
ActivityIndicator multiple-100 9.53ms 8.00ms ±2.80ms 1 +14.3%

Switch

Scenario Mean Median StdDev Renders vs Baseline
Switch mount 0.30ms 0.00ms ±0.48ms 1 +0.0%
Switch unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
Switch rerender 0.30ms 0.00ms ±0.48ms 2 -100.0%
Switch value-true 0.20ms 0.00ms ±0.42ms 1 +0.0%
Switch disabled 0.30ms 0.00ms ±0.48ms 1 +0.0%
Switch custom-colors 0.20ms 0.00ms ±0.42ms 1 +0.0%
Switch on-value-change 0.10ms 0.00ms ±0.32ms 1 +0.0%
Switch with-accessibility 0.30ms 0.00ms ±0.48ms 1 +0.0%
Switch multiple-10 2.47ms 2.00ms ±1.41ms 1 +0.0%
Switch multiple-50 10.73ms 10.00ms ±3.06ms 1 +11.1%
Switch multiple-100 20.93ms 20.00ms ±4.10ms 1 +25.0%

Button

Scenario Mean Median StdDev Renders vs Baseline
Button mount 0.60ms 1.00ms ±0.52ms 1 +0.0%
Button unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
Button rerender 0.70ms 1.00ms ±0.48ms 2 +0.0%
Button disabled 1.10ms 1.00ms ±1.45ms 1 +0.0%
Button with-color 0.50ms 0.50ms ±0.53ms 1 +0.0%
Button with-accessibility 0.60ms 1.00ms ±0.52ms 1 +0.0%
Button multiple-10 7.73ms 8.00ms ±1.33ms 1 +33.3%
Button multiple-50 26.20ms 27.00ms ±8.42ms 1 +0.0%
Button multiple-100 22.40ms 20.00ms ±10.93ms 1 +5.3%

TextInput

Scenario Mean Median StdDev Renders vs Baseline
TextInput mount 0.20ms 0.00ms ±0.42ms 1 +0.0%
TextInput unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
TextInput rerender 0.50ms 0.50ms ±0.53ms 2 +Infinity%
TextInput multiline 0.10ms 0.00ms ±0.32ms 1 +0.0%
TextInput with-value 0.20ms 0.00ms ±0.42ms 1 +0.0%
TextInput styled 0.20ms 0.00ms ±0.42ms 1 +0.0%
TextInput multiple-100 8.73ms 8.00ms ±2.37ms 1 +14.3%

View

Scenario Mean Median StdDev Renders vs Baseline
View mount 0.20ms 0.00ms ±0.42ms 1 +0.0%
View unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
View rerender 0.20ms 0.00ms ±0.42ms 2 +0.0%
View nested-50 3.73ms 3.00ms ±1.39ms 1 +0.0%
View nested-100 9.00ms 8.00ms ±2.17ms 1 +14.3%
View shadow 0.20ms 0.00ms ±0.42ms 1 +0.0%
View border-radius 0.20ms 0.00ms ±0.42ms 1 +0.0%
View nested-500 18.80ms 10.00ms ±14.44ms 1 +0.0%

Text

Scenario Mean Median StdDev Renders vs Baseline
Text mount 0.30ms 0.00ms ±0.48ms 1 +0.0%
Text unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
Text rerender 0.20ms 0.00ms ±0.42ms 2 +0.0%
Text long-1000 0.20ms 0.00ms ±0.42ms 1 +0.0%
Text nested 0.30ms 0.00ms ±0.48ms 1 +0.0%
Text styled 0.20ms 0.00ms ±0.42ms 1 +0.0%
Text multiple-100 10.00ms 9.00ms ±2.80ms 1 +28.6%

SectionList.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
SectionList native mount 8.24ms 8.09ms ±1.34ms 1 +24.3%

FlatList.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
FlatList native mount 7.64ms 8.16ms ±1.15ms 1 -11.6%

TouchableHighlight.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
TouchableHighlight native mount 2.04ms 1.97ms ±0.33ms 1 -5.6%

TouchableOpacity.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
TouchableOpacity native mount 2.50ms 2.43ms ±0.47ms 1 -22.7%

Pressable.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Pressable native mount 1.97ms 1.86ms ±0.26ms 1 -25.8%

ScrollView.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
ScrollView native mount 4.53ms 4.26ms ±0.73ms 1 +5.2%

ActivityIndicator.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
ActivityIndicator native mount 1.80ms 1.65ms ±0.45ms 1 -33.4%

TextInput.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
TextInput native mount 2.56ms 2.47ms ±0.31ms 1 -39.6%

Switch.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Switch native mount 1.55ms 1.51ms ±0.20ms 1 -12.8%

Button.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Button native mount 2.55ms 2.22ms ±0.56ms 1 -14.6%

Modal.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Modal native mount 1.33ms 1.28ms ±0.17ms 1 +5.3%

Image.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Image native mount 2.17ms 2.08ms ±0.29ms 1 -8.1%

View.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
View native mount 1.50ms 1.41ms ±0.38ms 1 -1.6%

Text.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Text native mount 1.61ms 1.59ms ±0.20ms 1 -8.7%

@vmoroz vmoroz requested a review from JunielKatarn April 6, 2026 20:49
@vmoroz
Copy link
Copy Markdown
Member Author

vmoroz commented Apr 6, 2026

Moved fmt ClCompile/ClInclude entries into Shared.vcxitems — fmt is
now compiled inline instead of as a separate static library.

I would argue against this. Shared should be scoped only to RNW sources ultimately built into the Desktop and modern DLLs, not third-party code.

Got two questions:

  1. Why not keep the static library?
  2. If the static library is a problem, why not create a separate VCXITEMS project (similar to shared) dedicated to fmt (i.e. fmt.vcxitems)?

The static libraries create unnecessary artifacts and may affect the speed and quality of compilations.
I like the idea about the vcxitems per external project and the latest commit implemented the chenge.

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.

3 participants