diff --git a/src/Paket.Core/PaketConfigFiles/ProjectFile.fs b/src/Paket.Core/PaketConfigFiles/ProjectFile.fs index c7fac7430c..eeff28f21f 100644 --- a/src/Paket.Core/PaketConfigFiles/ProjectFile.fs +++ b/src/Paket.Core/PaketConfigFiles/ProjectFile.fs @@ -1069,6 +1069,20 @@ module ProjectFile = |> Array.map (fun x -> x.Trim()) |> Array.toList + // Same as getTargetFramework/getTargetFrameworksParsed above but evaluates conditional + // PropertyGroups (e.g. `Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"`) + // using the given Configuration/Platform, instead of an empty property map. + let getTargetFrameworkWithDefaults defaultProperties (project:ProjectFile) = + getPropertyWithDefaults "TargetFramework" defaultProperties project + + let getTargetFrameworksParsedWithDefaults defaultProperties (project:ProjectFile) = + getPropertyWithDefaults "TargetFrameworks" defaultProperties project + |> Option.map (fun x -> x.Split([|';'|],StringSplitOptions.RemoveEmptyEntries)) + |> Option.toArray + |> Array.concat + |> Array.map (fun x -> x.Trim()) + |> Array.toList + let getToolsVersion (project:ProjectFile) = let adjustIfWeHaveSDK v = try @@ -1622,22 +1636,28 @@ module ProjectFile = sprintf "%s.%s" assemblyName ending let getOutputDirectory buildConfiguration buildPlatform (targetProfile : TargetProfile option) (project:ProjectFile) = + // Some projects put TargetFramework(s) inside a conditional PropertyGroup keyed on + // Configuration/Platform (e.g. `Condition="'$(Configuration)'=='Release'"`), so we must + // evaluate the property map with the requested Configuration/Platform, not an empty one, + // or the frameworks won't be found at all (see issue #3799). + let defaultProperties = + Map.ofList [("Configuration", buildConfiguration); ("Platform", buildPlatform)] let targetFramework = match targetProfile with | Some targetProfile -> let targetProfile = targetProfile.ToString() - match getTargetFramework project with + match getTargetFrameworkWithDefaults defaultProperties project with | Some x -> if x = targetProfile then x else "" | None -> - let parsedTargetFrameworks = getTargetFrameworksParsed project + let parsedTargetFrameworks = getTargetFrameworksParsedWithDefaults defaultProperties project match List.tryFind ((=) targetProfile) parsedTargetFrameworks with | Some x -> x | None -> "" | None -> - match getTargetFramework project with + match getTargetFrameworkWithDefaults defaultProperties project with | Some x -> x | None -> - match getTargetFrameworksParsed project with + match getTargetFrameworksParsedWithDefaults defaultProperties project with | fwk :: _ -> fwk | [] -> "" diff --git a/tests/Paket.Tests/Paket.Tests.fsproj b/tests/Paket.Tests/Paket.Tests.fsproj index 0ff0e05548..2beb860531 100644 --- a/tests/Paket.Tests/Paket.Tests.fsproj +++ b/tests/Paket.Tests/Paket.Tests.fsproj @@ -130,6 +130,8 @@ + + diff --git a/tests/Paket.Tests/ProjectFile/OutputSpecs.fs b/tests/Paket.Tests/ProjectFile/OutputSpecs.fs index 88b95a32ed..7ab8f22034 100644 --- a/tests/Paket.Tests/ProjectFile/OutputSpecs.fs +++ b/tests/Paket.Tests/ProjectFile/OutputSpecs.fs @@ -103,6 +103,48 @@ let ``should detect output path for netsdk with outputPath and appendTargetFrame let expected = (System.IO.Path.Combine(@"bin", configuration,"netstandard1.4_bin") |> normalizePath) outPath.ToLowerInvariant() |> shouldEqual (expected.ToLowerInvariant()) +[] +let ``should detect output path for netsdk with conditional target frameworks csproj file`` + ([] configuration) = + ensureDir () + let projectFile = ProjectFile.TryLoad("./ProjectFile/TestData/MicrosoftNetSdkWithConditionalTargetFrameworks.csprojtest").Value + // Should not throw "Unable to find output path node" (see issue #3799) + let outPath = projectFile.GetOutputDirectory configuration "" None + outPath |> shouldNotEqual "" + +[] +let ``should detect output path for netsdk with only conditional target frameworks (no unconditional fallback) csproj file`` + ([] configuration) = + ensureDir () + // Reproduces the exact shape from issue #3799: no unconditional TargetFramework/TargetFrameworks + // element exists at all, only Configuration-conditioned PropertyGroups. + let projectFile = ProjectFile.TryLoad("./ProjectFile/TestData/MicrosoftNetSdkWithConditionalTargetFrameworksOnly.csprojtest").Value + let outPath = projectFile.GetOutputDirectory configuration "" None + outPath |> shouldNotEqual "" + +[] +let ``should pick the conditional target frameworks matching the requested configuration for netsdk csproj file`` + ([] configuration) = + ensureDir () + let projectFile = ProjectFile.TryLoad("./ProjectFile/TestData/MicrosoftNetSdkWithConditionalTargetFrameworksOnly.csprojtest").Value + let outPath = projectFile.GetOutputDirectory configuration "" None + // Debug config only lists netstandard2.0;net45, Release additionally lists net47; + // the first framework in each list is netstandard2.0, and it should be selected as the default. + let expected = (System.IO.Path.Combine(@"bin", configuration, "netstandard2.0") |> normalizePath) + outPath.ToLowerInvariant() |> shouldEqual (expected.ToLowerInvariant()) + +[] +let ``should detect output path for a specific target profile in a conditional target frameworks csproj file`` + ([] configuration) = + ensureDir () + let projectFile = ProjectFile.TryLoad("./ProjectFile/TestData/MicrosoftNetSdkWithConditionalTargetFrameworksOnly.csprojtest").Value + let targetProfile = + FrameworkDetection.internalExtract "net45" + |> Option.map TargetProfile.SinglePlatform + let outPath = projectFile.GetOutputDirectory configuration "" targetProfile + let expected = (System.IO.Path.Combine(@"bin", configuration, "net45") |> normalizePath) + outPath.ToLowerInvariant() |> shouldEqual (expected.ToLowerInvariant()) + [] let ``should detect framework profile for ProjectWithConditions file`` () = ensureDir () diff --git a/tests/Paket.Tests/ProjectFile/TestData/MicrosoftNetSdkWithConditionalTargetFrameworks.csprojtest b/tests/Paket.Tests/ProjectFile/TestData/MicrosoftNetSdkWithConditionalTargetFrameworks.csprojtest new file mode 100644 index 0000000000..23350487da --- /dev/null +++ b/tests/Paket.Tests/ProjectFile/TestData/MicrosoftNetSdkWithConditionalTargetFrameworks.csprojtest @@ -0,0 +1,15 @@ + + + + TestPaket + netstandard2.0 + + + + netstandard2.0;net45 + + + + netstandard2.0;net45;net47 + + diff --git a/tests/Paket.Tests/ProjectFile/TestData/MicrosoftNetSdkWithConditionalTargetFrameworksOnly.csprojtest b/tests/Paket.Tests/ProjectFile/TestData/MicrosoftNetSdkWithConditionalTargetFrameworksOnly.csprojtest new file mode 100644 index 0000000000..7b87c3d03c --- /dev/null +++ b/tests/Paket.Tests/ProjectFile/TestData/MicrosoftNetSdkWithConditionalTargetFrameworksOnly.csprojtest @@ -0,0 +1,10 @@ + + + + netstandard2.0;net45 + + + + netstandard2.0;net45;net47 + +