Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 30, 2025

Emits FSSDK0001 warning when a project references (directly or transitively) an FSharp.Core package with a higher major version than the SDK's compiler supports.

Implementation

Props file (Microsoft.FSharp.Core.NetSdk.props):

  • Renamed _FSCorePackageVersionSetFSCorePackageVersionSet (now public for test hooks)
  • Added FSharpCoreMaximumMajorVersion property computed from SDK's FSCorePackageVersion
  • Strips prerelease suffix via .Split('-')[0] before version parsing

Targets file (Microsoft.FSharp.NetSdk.targets):

  • Added _CheckForUnsupportedFSharpCoreVersion target (runs after ResolvePackageAssets)
  • Compares resolved FSharp.Core major version against SDK maximum
  • Emits warning: "The referenced FSharp.Core X.Y.Z has a higher major version (X) than this SDK supports (N)..."
  • Opt-out via <ValidateFSharpCoreVersion>false</ValidateFSharpCoreVersion>

Test Coverage

Four MSBuild test projects validate:

  • Higher major version triggers warning (SDK 8.x + Core 9.x → warn)
  • Same major version passes (SDK 9.x + Core 9.y → OK)
  • Lower major version passes (SDK 9.x + Core 8.x → OK)
  • Prerelease versions parse correctly (SDK 9.0-preview + Core 10.0-beta → warn)

Example

<!-- User pins higher version -->
<PackageReference Include="FSharp.Core" Version="11.0.0" />

Build output with SDK 10.x:

warning FSSDK0001: The referenced FSharp.Core 11.0.0 has a higher major version (11) 
than this SDK supports (10). This may cause compilation errors or runtime failures.
Original prompt

FSharp.Core Version Validation

Goal

Warn (FSSDK0001) when a project references FSharp.Core with a higher major version than the SDK's compiler supports (directly or via transitive dependency).

Scenarios

Scenario Expected Result
Default FSharp.Core (SDK-provided) ✅ OK
User pins FSharp.Core major version == SDK major version ✅ OK
User pins FSharp.Core major version < SDK major version ✅ OK
User pins FSharp.Core major version > SDK major version ⚠️ Warning FSSDK0001
Transitive dependency brings in FSharp.Core > SDK version ⚠️ Warning FSSDK0001

Implementation

1. Modify Props File

File: src/FSharp.Build/Microsoft.FSharp.Core.NetSdk.props

Current content (lines 23-27):

<PropertyGroup Condition="'$(_FSCorePackageVersionSet)' != 'true'">
  <_FSCorePackageVersionSet>true</_FSCorePackageVersionSet>
  <FSCorePackageVersion>{{FSCorePackageVersionValue}}</FSCorePackageVersion>
  <_FSharpCoreLibraryPacksFolder Condition="'$(_FSharpCoreLibraryPacksFolder)' == ''">$([MSBuild]::EnsureTrailingSlash('$(MSBuildThisFileDirectory)'))library-packs</_FSharpCoreLibraryPacksFolder>
</PropertyGroup>

Replace with:

<PropertyGroup Condition="'$(FSCorePackageVersionSet)' != 'true'">
  <FSCorePackageVersionSet>true</FSCorePackageVersionSet>
  <FSCorePackageVersion>{{FSCorePackageVersionValue}}</FSCorePackageVersion>
  <_FSharpCoreLibraryPacksFolder Condition="'$(_FSharpCoreLibraryPacksFolder)' == ''">$([MSBuild]::EnsureTrailingSlash('$(MSBuildThisFileDirectory)'))library-packs</_FSharpCoreLibraryPacksFolder>
</PropertyGroup>

<PropertyGroup Condition="'$(FSCorePackageVersionSet)' == 'true' and '$(FSCorePackageVersion)' != '' and !$(FSCorePackageVersion.Contains('{'))">
  <FSharpCoreMaximumMajorVersion Condition="'$(FSharpCoreMaximumMajorVersion)' == ''">$([System.Version]::Parse('$(FSCorePackageVersion.Split('-')[0])').Major)</FSharpCoreMaximumMajorVersion>
</PropertyGroup>

Changes:

  • Rename _FSCorePackageVersionSetFSCorePackageVersionSet (public, for testability)
  • Add FSharpCoreMaximumMajorVersion computation
  • .Split('-')[0] strips prerelease suffix (required because System.Version.Parse doesn't handle semver)

2. Add Validation Target

File: src/FSharp.Build/Microsoft.FSharp.NetSdk.targets

Add before closing </Project> tag (after line 221):

<!--
  Warn if resolved FSharp.Core major version exceeds SDK's compiler version.
-->
<Target Name="_CheckForUnsupportedFSharpCoreVersion" 
        AfterTargets="ResolvePackageAssets"
        Condition="'$(ValidateFSharpCoreVersion)' != 'false' and '$(FSharpCoreMaximumMajorVersion)' != ''">

  <ItemGroup>
    <_ResolvedFSharpCore Include="@(ReferencePath)" 
                          Condition="'%(ReferencePath.NuGetPackageId)' == 'FSharp.Core'" />
  </ItemGroup>

  <PropertyGroup Condition="'@(_ResolvedFSharpCore)' != ''">
    <_ResolvedFSharpCoreVersion>%(_ResolvedFSharpCore.NuGetPackageVersion)</_ResolvedFSharpCoreVersion>
    <_ResolvedFSharpCoreMajor>$([System.Version]::Parse('$(_ResolvedFSharpCoreVersion.Split('-')[0])').Major)</_ResolvedFSharpCoreMajor>
    <_FSharpCoreVersionTooHigh Condition="$(_ResolvedFSharpCoreMajor) &gt; $(FSharpCoreMaximumMajorVersion)">true</_FSharpCoreVersionTooHigh>
  </PropertyGroup>

  <Warning 
    Condition="'$(_FSharpCoreVersionTooHigh)' == 'true'"
    Code="FSSDK0001"
    Text="The referenced FSharp.Core $(_ResolvedFSharpCoreVersion) has a higher major version ($(_ResolvedFSharpCoreMajor)) than this SDK supports ($(FSharpCoreMaximumMajorVersion)). This may cause compilation errors or runtime failures." />

</Target>

Key points:

  • AfterTargets="ResolvePackageAssets" — runs after NuGet resolves all packages (including transitives)
  • %(ReferencePath.NuGetPackageVersion) — gets the resolved package version
  • User can disable with <ValidateFSharpCoreVersion>false</ValidateFSharpCoreVersion>

Testing

Test Location

Directory: tests/fsharp/SDKTests/tests/

Test runner: tests/fsharp/SDKTests/AllSdkTargetsTests.proj

Existing patterns to follow:

  • tests/fsharp/SDKTests/tests/PackageTest.props
  • tests/fsharp/SDKTests/tests/PackageTest.targets
  • tests/fsharp/SDKTests/tests/SetImplicitReferenceTest.proj

New Test Files to Create

tests/fsharp/SDKTests/tests/FSharpCoreVersionTest.props

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <Import Project="$(FSharpRepositoryPath)\eng\Versions.props" />

  <PropertyGroup>
    <TargetFramework Condition="'$(TargetFramework)' == ''">net8.0</TargetFramework>
    <_TargetFrameworkVersionWithoutV>8.0</_TargetFrameworkVersionWithoutV>
    <TargetFrameworkIdentifier>.NETCoreApp</TargetFrameworkIdentifier>
    <Configuration Condition="'$(Configuration)' == ''">Release</Configuration>
    <TargetsDirectory>$(FSharpRepositoryPath)...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.

Copilot AI changed the title [WIP] Add warning for FSharp.Core version validation Warn when FSharp.Core major version exceeds SDK compiler version Dec 30, 2025
Copilot AI requested a review from T-Gro December 30, 2025 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

2 participants