Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

* `paket pack --symbols` no longer fails on a project with Compile entries without an `Include` or `Update` attribute, such as `<Compile Remove="..." />` or a Compile item definition in an `ItemDefinitionGroup`, and leaves out of the symbols package the files that a Remove names without wildcards - https://github.com/fsprojects/Paket/issues/4222
* `paket update --no-install` no longer fails when a group has `generate_load_scripts: true` and the packages of the new paket.lock aren't all restored, as on a fresh checkout or for a package that the update adds: it leaves the load scripts to the next restore, which downloads the packages that they load - https://github.com/fsprojects/Paket/issues/2785
* `paket install` no longer warns about missing "redirects: force" for FSharp.Core in SDK projects that take their target framework from Directory.Build.props, by inheriting it or through a property such as `$(DefaultTargetFramework)`. Paket, which doesn't read Directory.Build.props, also stops taking such a property for net10 (.NET Framework 1.0): where it needs the framework of the project, as for `framework: auto-detect` or `paket pack`, it falls back to net40, like for a project without framework - https://github.com/fsprojects/Paket/issues/4485

#### 12.0.0-alpha005 - 2026-11-07

Expand Down
17 changes: 16 additions & 1 deletion integrationtests/Paket.IntegrationTests/BindingRedirect.fs
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,19 @@ let ``#1783 generates binding redirect when assembly with different version of m

let config = File.ReadAllText(configPath) |> normalizeLineEndings

config |> shouldContainText ``Newtonsoft.Json``
config |> shouldContainText ``Newtonsoft.Json``

[<Test>]
let ``#4485 does not warn about FSharp.Core redirects when the framework comes from Directory.Build.props``() =
let scenario = "i004485-fsharp-core-warning-inherited-framework"
let cleanup, output = paket "install" scenario
use __ = cleanup
let warnsAbout (project: string) =
output.Split('\n')
|> Array.exists (fun line -> line.Contains "without specifying \"redirects: force\"" && line.Contains project)

// the project that declares net48 itself is warned, so that the checks below can't pass
// because the text of the warning changed
Assert.IsTrue(warnsAbout "Framework.fsproj", output)
Assert.IsFalse(warnsAbout "Inherited.fsproj", output)
Assert.IsFalse(warnsAbout "Property.fsproj", output)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<DefaultTargetFramework>net10.0</DefaultTargetFramework>
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Lib.fs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module Lib
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FSharp.Core
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<Compile Include="Lib.fs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module Lib
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FSharp.Core
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source https://api.nuget.org/v3/index.json
storage: none

nuget FSharp.Core 9.0.303
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module Lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Lib.fs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FSharp.Core
4 changes: 3 additions & 1 deletion src/Paket.Core/Installation/InstallProcess.fs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ let private applyBindingRedirects isFirstGroup createNewBindingFiles cleanBindin
/// DotNetFramework V5+ but is not .NET Framework) - .NET Core/.NET 5+ and
/// netstandard-only projects use assembly unification via deps.json instead, so the
/// warning would be noise there.
/// The profiles are the ones that the project file declares: without any, as when the
/// framework comes from Directory.Build.props, Paket can't tell and doesn't warn.
let shouldWarnAboutFSharpCoreRedirects (targetProfiles: TargetProfile list) (createBindingRedirects: BindingRedirectsSettings option) =
let targetsDotNetFramework =
targetProfiles
Expand Down Expand Up @@ -519,7 +521,7 @@ let InstallIntoProjects(options : InstallerOptions, forceTouch, dependenciesFile
traceWarnfn "F# project %s does not reference FSharp.Core." project.FileName
| Some kv ->
let _, settings = kv.Value
if shouldWarnAboutFSharpCoreRedirects (project.GetTargetProfiles()) settings.CreateBindingRedirects then
if shouldWarnAboutFSharpCoreRedirects (project.GetDeclaredTargetProfiles()) settings.CreateBindingRedirects then
traceWarnfn "F# project %s references FSharp.Core without specifying \"redirects: force\" in paket.dependencies. This can lead to \"Could not load file or assembly 'FSharp.Core'\" errors at runtime. See https://fsprojects.github.io/Paket/dependencies-file.html#Controlling-assembly-binding-redirects for details." project.FileName

// if any errors have been found during the installation process thus far, fail and print all errors collected
Expand Down
23 changes: 19 additions & 4 deletions src/Paket.Core/PaketConfigFiles/ProjectFile.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1099,7 +1099,10 @@ module ProjectFile =
| true, version -> adjustIfWeHaveSDK version
| _ -> adjustIfWeHaveSDK 4.0

let getTargetProfiles (project:ProjectFile) =
/// The target profiles that the project file declares itself. Paket doesn't read the files that
/// it imports, so this is empty when the framework comes from one of them, as from
/// Directory.Build.props, or when the file declares no framework that Paket recognizes.
let getDeclaredTargetProfiles (project:ProjectFile) =
let fallback () =
let prefix() =
match getTargetFrameworkIdentifier project with
Expand All @@ -1114,9 +1117,12 @@ module ProjectFile =

| Some x -> [prefix() + (x.Replace("v",""))]

match frameworks |> List.choose (fun s -> FrameworkDetection.internalExtract s |> Option.map TargetProfile.SinglePlatform) |> List.filter (fun x -> match x with TargetProfile.SinglePlatform(Unsupported _) -> false | _ -> true) with
| [] -> [TargetProfile.SinglePlatform (DotNetFramework FrameworkVersion.V4)]
| xs -> xs
frameworks
// a property defined outside of the project file, as in $(DefaultTargetFramework), expands
// to an empty string, which internalExtract would take for .NET Framework 1.0
|> List.filter (String.IsNullOrWhiteSpace >> not)
|> List.choose (fun s -> FrameworkDetection.internalExtract s |> Option.map TargetProfile.SinglePlatform)
|> List.filter (fun x -> match x with TargetProfile.SinglePlatform(Unsupported _) -> false | _ -> true)


match getTargetFrameworkProfile project with
Expand All @@ -1136,6 +1142,13 @@ module ProjectFile =
fallback()
| _ -> fallback()

/// The target profiles of the project: the ones that the project file declares, or net40 when
/// it declares none (see getDeclaredTargetProfiles).
let getTargetProfiles (project:ProjectFile) =
match getDeclaredTargetProfiles project with
| [] -> [TargetProfile.SinglePlatform (DotNetFramework FrameworkVersion.V4)]
| profiles -> profiles

let updateReferences
(completeModel: Map<GroupName*PackageName,_*InstallModel>)
(directPackages : Map<GroupName*PackageName,_*InstallSettings>)
Expand Down Expand Up @@ -1813,6 +1826,8 @@ type ProjectFile with

member this.GetTargetProfiles() = ProjectFile.getTargetProfiles this

member this.GetDeclaredTargetProfiles() = ProjectFile.getDeclaredTargetProfiles this

member this.AddImportForPaketTargets relativeTargetsPath = ProjectFile.addImportForPaketTargets relativeTargetsPath this

member this.RemoveImportForPaketTargets() = ProjectFile.removeImportForPaketTargets this
Expand Down
30 changes: 30 additions & 0 deletions tests/Paket.Tests/ProjectFile/TargetFrameworkSpecs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,33 @@ let ``should detect the correct framework on test projects`` projectFile expecte
p.GetTargetProfiles() |> shouldEqual [expectedProfile]
p.GetTargetProfiles().Head.ToString() |> shouldEqual expectedProfileString


let private sdkProject properties =
let content =
sprintf """<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>%s</PropertyGroup>
</Project>""" properties
ProjectFile.LoadFromString("Project.fsproj", content)

[<Test>]
let ``declares no target profile when the framework comes from Directory.Build.props`` () =
let p = sdkProject ""
p.GetDeclaredTargetProfiles() |> shouldEqual []
p.GetTargetProfiles() |> shouldEqual [TargetProfile.SinglePlatform(DotNetFramework FrameworkVersion.V4)]

[<Test>]
let ``declares no target profile when the framework is a property defined outside of the project file`` () =
let p = sdkProject "<TargetFramework>$(DefaultTargetFramework)</TargetFramework>"
p.GetDeclaredTargetProfiles() |> shouldEqual []
p.GetTargetProfiles() |> shouldEqual [TargetProfile.SinglePlatform(DotNetFramework FrameworkVersion.V4)]

[<Test>]
let ``ignores an empty entry of TargetFrameworks`` () =
// the space after the last separator survives the split, and becomes "" once trimmed
let p = sdkProject "<TargetFrameworks>net8.0; </TargetFrameworks>"
p.GetDeclaredTargetProfiles() |> shouldEqual [TargetProfile.SinglePlatform(DotNetFramework FrameworkVersion.V8)]

[<Test>]
let ``declares the target profile of the framework in the project file`` () =
let p = sdkProject "<TargetFramework>net48</TargetFramework>"
p.GetDeclaredTargetProfiles() |> shouldEqual [TargetProfile.SinglePlatform(DotNetFramework FrameworkVersion.V4_8)]