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

    Projects

    Status

    New

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions