Skip to content

Proposal: Centralize Product TFM into a Single Source of Truth #19204

@T-Gro

Description

@T-Gro

Summary

Currently, the product TFM (e.g., net10.0, 10.0,net10,10) is hardcoded in multiple places across the repository:

  • PowerShell scripts (Build.ps1, build-utils.ps1, test-determinism.ps1)
  • Bash scripts (build.sh)
  • MSBuild props (Directory.Build.props, UseLocalCompiler.Directory.Build.props)
  • Pipeline YAML (regression-test-jobs.yml)
  • Test code (TestFramework.fs, single-test.fs, etc.)
  • Product code (FxResolver.fs, CompilerLocation.fs)

This makes TFM updates error-prone and requires touching many files.

Proposed Architecture: Single Source of Truth

┌─────────────────────────────────────────────────────────────────────────────┐
│                         eng/productTfm.txt                                  │
│                              "net10.0"                                      │
│                        (SINGLE SOURCE OF TRUTH)                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
        ┌───────────────────────────┼───────────────────────────┐
        ▼                           ▼                           ▼
┌───────────────────┐    ┌─────────────────────┐    ┌─────────────────────┐
│     SCRIPTS       │    │      MSBUILD        │    │    COMPILED CODE    │
└───────────────────┘    └─────────────────────┘    └─────────────────────┘
        │                           │                           │
        ▼                           ▼                           ▼
┌───────────────────┐    ┌─────────────────────┐    ┌─────────────────────┐
│ PowerShell:       │    │ Directory.Build.props│   │ buildproperties.fs  │
│ Get-Content       │    │ reads txt file →    │    │ (generated)         │
│ eng/productTfm.txt│    │ sets:               │    │                     │
│                   │    │ • $(FSharp...TFM)   │    │ fsProductTfm =      │
│ Bash:             │    │ • $(FSharp...Major) │    │   "net10.0"         │
│ cat               │    │                     │    │ fsProductTfmMajor = │
│ eng/productTfm.txt│    │         ▼           │    │   10                │
│                   │    │ All .fsproj, .props │    │                     │
│ Used by:          │    │ .targets reference  │    │         ▼           │
│ • Build.ps1       │    │ $(FSharp...TFM)     │    │ ┌─────────────────┐ │
│ • build.sh        │    │                     │    │ │  PRODUCT CODE   │ │
│ • build-utils.ps1 │    │ Used by:            │    │ │ FxResolver.fs   │ │
│ • test-determ.ps1 │    │ • TargetFramework   │    │ │ CompilerLoc.fs  │ │
│ • regression.yml  │    │ • Output paths      │    │ │ uses FSharp.    │ │
│                   │    │ • UseLocalCompiler  │    │ │ BuildProperties │ │
│                   │    │ • checkpackages     │    │ └─────────────────┘ │
│                   │    │                     │    │                     │
│                   │    │                     │    │ ┌─────────────────┐ │
│                   │    │                     │    │ │   TEST CODE     │ │
│                   │    │                     │    │ │ TestFramework.fs│ │
│                   │    │                     │    │ │ reads txt file  │ │
│                   │    │                     │    │ │ at runtime via  │ │
│                   │    │                     │    │ │ repoRoot path   │ │
│                   │    │                     │    │ └─────────────────┘ │
└───────────────────┘    └─────────────────────┘    └─────────────────────┘

Implementation Details

1. Create the source file

Create eng/productTfm.txt containing just:

net10.0

2. Update Directory.Build.props

<PropertyGroup>
  <!-- Read TFM from single source of truth -->
  <FSharpNetCoreProductDefaultTargetFramework>$([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)eng/productTfm.txt').Trim())</FSharpNetCoreProductDefaultTargetFramework>
  
  <!-- Derive major version: "net10.0" → "10" -->
  <FSharpNetCoreProductMajorVersion>$(FSharpNetCoreProductDefaultTargetFramework.Replace('net','').Replace('.0',''))</FSharpNetCoreProductMajorVersion>
</PropertyGroup>

3. Update FSharpBuild.Directory.Build.targets

Add to _GenerateBuildPropertiesFile target:

<_BuildPropertyLines Include="let fsProductTfm = &quot;$(FSharpNetCoreProductDefaultTargetFramework)&quot;" />
<_BuildPropertyLines Include="let fsProductTfmMajorVersion = $(FSharpNetCoreProductMajorVersion)" />

4. Update Scripts

PowerShell (Build.ps1, build-utils.ps1, test-determinism.ps1):

$fsharpNetCoreProductTfm = (Get-Content "$PSScriptRoot/productTfm.txt").Trim()

Bash (build.sh):

tfm=$(cat "$scriptroot/productTfm.txt" | tr -d '[:space:]')

5. Update Product Code

CompilerLocation.fs - Use generated constant to build the TFM probe list:

let toolingCompatibleVersions =
    if typeof<obj>.Assembly.GetName().Name = "System.Private.CoreLib" then
        [|
            for v in FSharp.BuildProperties.fsProductTfmMajorVersion .. -1 .. 5 do
                $"net{v}.0"
            "netcoreapp3.1"; "netcoreapp3.0"; "netstandard2.1"
            "netcoreapp2.2"; "netcoreapp2.1"; "netcoreapp2.0"; "netstandard2.0"
        |]
    // ... rest unchanged

FxResolver.fs - Use generated constant for fallback:

| _ -> if isRunningOnCoreClr then FSharp.BuildProperties.fsProductTfm else "net472"

6. Update Test Code

TestFramework.fs - Add a shared value:

/// Product TFM read from single source of truth
let productTfm = 
    File.ReadAllText(repoRoot ++ "eng" ++ "productTfm.txt").Trim()

Then replace all hardcoded "net10.0" references in test files with TestFramework.productTfm.

Files to Update

Category Files
New file eng/productTfm.txt
MSBuild Directory.Build.props, FSharpBuild.Directory.Build.targets
Scripts eng/Build.ps1, eng/build.sh, eng/build-utils.ps1, eng/test-determinism.ps1
Product src/Compiler/Driver/FxResolver.fs, src/Compiler/Facilities/CompilerLocation.fs
Tests tests/FSharp.Test.Utilities/TestFramework.fs + all files referencing hardcoded TFM

Benefit

To change the product TFM, edit one file: eng/productTfm.txt

Change net10.0 to net11.0. Done.

Metadata

Metadata

Assignees

No one assigned
    No fields configured for Feature.

    Projects

    Status

    New

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions