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
10 changes: 10 additions & 0 deletions azure-pipelines-PR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -901,3 +901,13 @@ stages:
commit: b321cb41a0bea0dab6b4509f895e6cd4d024e9e5
buildScript: dotnet build Nu.sln --configuration Release
displayName: Nu_Build
# Design-time provider packaging oracle. Pin d8aba70 (pre-workaround base of FSharp.Data.GraphQL#583):
# it uses the bare IsFSharpDesignTimeProvider gesture, so the client pack drops the provider without this fix.
# Linux-only: the $PWD local feed and the grep content assertion need bash. (nupkg entry names are stored
# uncompressed in the zip, so grep -aF on the package finds the provider path without extra tools.)
- repo: fsprojects/FSharp.Data.GraphQL
commit: d8aba70757792f2d76a66cb8171086ca4c7c1e8e
buildScript: dotnet pack src/FSharp.Data.GraphQL.Shared/FSharp.Data.GraphQL.Shared.fsproj -c Release -p:TreatWarningsAsErrors=false -o "$PWD/__gqlfeed__" ;; dotnet pack src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj -c Release -p:IsNuGet=true -p:TreatWarningsAsErrors=false --source https://api.nuget.org/v3/index.json --source "$PWD/__gqlfeed__" -o "$PWD/__gqlfeed__" && grep -qaF typeproviders/fsharp41/netstandard2.0/FSharp.Data.GraphQL.Client.DesignTime.dll "$PWD/__gqlfeed__"/FSharp.Data.GraphQL.Client.*.nupkg
displayName: FSharpDataGraphQL_DesignTimeProvider_Packaging
useVmImage: $(LinuxMachineQueueName)
usePool: $(DncEngPublicBuildPool)
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Fixed

* Restore packaging of an F# design-time type provider that is activated via a `ProjectReference` carrying `IsFSharpDesignTimeProvider="true"`. The provider assembly is again included under `fsharp41` when packing (including `pack --no-build`); `PackageFSharpDesignTimeTools` now resolves the provider via `GetTargetPath`, which works in `dotnet pack`'s `BuildProjectReferences=false` content build without forcing an early `ResolveReferences`. ([Issue #18924](https://github.com/dotnet/fsharp/issues/18924), [PR #19979](https://github.com/dotnet/fsharp/pull/19979))
* Provided types used from multiple files no longer produce spurious FS0001 type mismatches under parallel compilation; provided-type entities are now interned so every file linking a given provided type shares one entity. ([PR #19969](https://github.com/dotnet/fsharp/pull/19969))
* TypeProviders-SDK providers now load under an unoptimized compiler; the `systemRuntimeContainsType` closure field the SDK reflects on (`tcImports`) is captured stably regardless of optimization settings. ([PR #19969](https://github.com/dotnet/fsharp/pull/19969))
* Fixed: Inheriting from an undefined type now reports `FS0039` exactly once instead of three times. Phase 1F and Phase 2A of inherit-clause type-checking now skip re-resolving a syntactic clause whose Phase 1D resolution already failed with `UndefinedName`, eliminating both the duplicate diagnostic and the redundant work. ([Issue #16432](https://github.com/dotnet/fsharp/issues/16432), [PR #19862](https://github.com/dotnet/fsharp/pull/19862))
Expand Down
91 changes: 65 additions & 26 deletions src/FSharp.Build/Microsoft.FSharp.NetSdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,11 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
<PackProjectInputFile>$(MSBuildProjectFullPath)</PackProjectInputFile>
</PropertyGroup>

<!-- Gate inclusion of PackageFSharpDesignTimeTools target when design-time provider is present -->
<PropertyGroup Condition=" '$(IsFSharpDesignTimeProvider)' == 'true' ">
<!-- Register unconditionally at eval time (NuGet fixes the TargetsForTfmSpecificContentInPackage schedule before any target runs); the target's own Condition gates execution. -->
<PropertyGroup>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackageFSharpDesignTimeTools</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>

<!-- Check for design-time provider references and add target if needed -->
<!-- This target attempts to update TargetsForTfmSpecificContentInPackage based on reference metadata -->
<Target Name="_CheckForDesignTimeProviderReferences" BeforeTargets="GenerateNuspec" Condition=" '$(IsFSharpDesignTimeProvider)' != 'true' ">
<ItemGroup>
<_FSharpDesignTimeProviderProject Include="@(ProjectReference)" Condition="'%(ProjectReference.IsFSharpDesignTimeProvider)' == 'true'" />
<_FSharpDesignTimeProviderPackage Include="@(PackageReference)" Condition="'%(PackageReference.IsFSharpDesignTimeProvider)' == 'true'" />
</ItemGroup>
<PropertyGroup Condition=" '@(_FSharpDesignTimeProviderProject)' != '' or '@(_FSharpDesignTimeProviderPackage)' != '' ">
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackageFSharpDesignTimeTools</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
</Target>

<PropertyGroup Condition=" '$(DisableImplicitLibraryPacksFolder)' != 'true' ">
<RestoreAdditionalProjectSources Condition="Exists('$(_FSharpCoreLibraryPacksFolder)')">$(RestoreAdditionalProjectSources);$(_FSharpCoreLibraryPacksFolder)</RestoreAdditionalProjectSources>
</PropertyGroup>
Expand All @@ -78,7 +66,9 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
</ItemGroup>
</Target>

<Target Name="PackageFSharpDesignTimeTools" AfterTargets="ResolveReferences">
<!-- Pack-only (run via TargetsForTfmSpecificContentInPackage, no AfterTargets); execution-time Condition skips non-providers. -->
<Target Name="PackageFSharpDesignTimeTools"
Condition="'$(IsFSharpDesignTimeProvider)' == 'true' or '@(ProjectReference->AnyHaveMetadataValue('IsFSharpDesignTimeProvider', 'true'))' == 'true'">
<PropertyGroup>
<FSharpDesignTimeProtocol Condition = " '$(FSharpDesignTimeProtocol)' == '' ">fsharp41</FSharpDesignTimeProtocol>
<FSharpToolsDirectory Condition = " '$(FSharpToolsDirectory)' == '' ">tools</FSharpToolsDirectory>
Expand All @@ -87,22 +77,71 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
<Error Text="'$(FSharpToolsDirectory)' is an invalid value for 'FSharpToolsDirectory' valid values are 'typeproviders' and 'tools'." Condition="'$(FSharpToolsDirectory)' != 'typeproviders' and '$(FSharpToolsDirectory)' != 'tools'" />
<Error Text="The 'FSharpDesignTimeProtocol' property can be only 'fsharp41'" Condition="'$(FSharpDesignTimeProtocol)' != 'fsharp41'" />

<!-- 1. Query provider TFMs without building; RemoveProperties=TargetFramework queries the outer cross-targeting project, not one inner build. -->
<MSBuild Projects="@(ProjectReference->WithMetadataValue('IsFSharpDesignTimeProvider', 'true'))"
Targets="GetTargetFrameworks"
RemoveProperties="TargetFramework">
<Output TaskParameter="TargetOutputs" ItemName="_FSharpDesignTimeProviderTFMs" />
</MSBuild>

<!-- 2. Compute the nearest compatible TFM for each DT provider based on the consumer's TargetFramework.
GetReferenceNearestTargetFrameworkTask is provided by NuGet's build targets (imported during pack), the
same task the SDK uses for ProjectReference nearest-TFM assignment, so no UsingTask is needed here. -->
<GetReferenceNearestTargetFrameworkTask AnnotatedProjectReferences="@(_FSharpDesignTimeProviderTFMs)"
CurrentProjectTargetFramework="$(TargetFramework)"
CurrentProjectName="$(MSBuildProjectName)"
FallbackTargetFrameworks="$(AssetTargetFallback)"
Condition="'@(_FSharpDesignTimeProviderTFMs)' != ''">
<Output ItemName="_FSharpDesignTimeProviderNearestTFM" TaskParameter="AssignedProjects" />
</GetReferenceNearestTargetFrameworkTask>

<!-- 3. GetTargetPath on the exact matched inner build, safely picking the proper path for single-match, multi-match, or cross-tfm. -->
<MSBuild Projects="@(_FSharpDesignTimeProviderNearestTFM)"
Targets="GetTargetPath"
Properties="TargetFramework=%(_FSharpDesignTimeProviderNearestTFM.NearestTargetFramework)"
Condition="'@(_FSharpDesignTimeProviderNearestTFM)' != ''">
<Output TaskParameter="TargetOutputs" ItemName="_FSharpDesignTimeProviderPath" />
</MSBuild>

<!-- Never silently drop a provider: if the gesture is present but resolved no output, fail loud (an
incompatible provider target framework is already a NuGet restore error, NU1201, before we get here). -->
<Error Condition="'@(ProjectReference->AnyHaveMetadataValue('IsFSharpDesignTimeProvider', 'true'))' == 'true' and '@(_FSharpDesignTimeProviderPath)' == ''"
Text="A design-time provider ProjectReference did not resolve a build output for '$(TargetFramework)'. Ensure the provider targets a compatible framework and is built before packing." />

<!-- GetTargetPath returns the path the provider WOULD produce, not proof it exists; assert (per provider) that it
was actually built, so packing before building the provider fails loud instead of packaging nothing. -->
<Error Condition="'@(_FSharpDesignTimeProviderPath)' != '' and !Exists('%(_FSharpDesignTimeProviderPath.FullPath)')"
Text="Design-time provider output '%(_FSharpDesignTimeProviderPath.FullPath)' was not built before packing." />

<!-- Each resolved output carries the SDK-computed nearest target framework (GetReferenceNearestTargetFrameworkTask
placed it on the reference item, and the MSBuild task flows it onto GetTargetPath's outputs). Fail loud rather
than pack under a blank folder if it is ever missing. -->
<Error Condition="'@(_FSharpDesignTimeProviderPath)' != '' and '%(_FSharpDesignTimeProviderPath.NearestTargetFramework)' == ''"
Text="Could not determine the nearest target framework for design-time provider output '%(_FSharpDesignTimeProviderPath.FullPath)'." />

<ItemGroup>
<_ResolvedOutputFiles
Include="%(_ResolvedProjectReferencePaths.RootDir)%(_ResolvedProjectReferencePaths.Directory)/**/*"
Exclude="%(_ResolvedProjectReferencePaths.RootDir)%(_ResolvedProjectReferencePaths.Directory)/**/FSharp.Core.dll;%(_ResolvedProjectReferencePaths.RootDir)%(_ResolvedProjectReferencePaths.Directory)/**/System.ValueTuple.dll"
Condition="'%(_ResolvedProjectReferencePaths.IsFSharpDesignTimeProvider)' == 'true'">
<NearestTargetFramework>%(_ResolvedProjectReferencePaths.NearestTargetFramework)</NearestTargetFramework>
<!-- ProjectReference case: the whole design-time build output (provider dll + its bundled dependencies),
subfolder structure preserved (satellites under <culture>/, native under runtimes/<rid>/...). The package
folder is the PROVIDER's resolved TFM — the moniker the compiler's design-time probe looks under
(src/Compiler/Facilities/CompilerLocation.fs), not the consumer's. The guards above keep the path
non-empty and the framework known, so this recursive glob can never widen to a drive-root scan. -->
<_ResolvedOutputFiles Condition="'@(_FSharpDesignTimeProviderPath)' != ''"
Include="%(_FSharpDesignTimeProviderPath.RootDir)%(_FSharpDesignTimeProviderPath.Directory)**\*"
Comment thread
T-Gro marked this conversation as resolved.
Exclude="%(_FSharpDesignTimeProviderPath.RootDir)%(_FSharpDesignTimeProviderPath.Directory)**\*.pdb;%(_FSharpDesignTimeProviderPath.RootDir)%(_FSharpDesignTimeProviderPath.Directory)**\*.deps.json;%(_FSharpDesignTimeProviderPath.RootDir)%(_FSharpDesignTimeProviderPath.Directory)ref\**;%(_FSharpDesignTimeProviderPath.RootDir)%(_FSharpDesignTimeProviderPath.Directory)analyzers\**">
<DesignTimeFramework>%(_FSharpDesignTimeProviderPath.NearestTargetFramework)</DesignTimeFramework>
</_ResolvedOutputFiles>

<_ResolvedOutputFiles
Include="@(BuiltProjectOutputGroupKeyOutput)"
Condition=" '$(IsFSharpDesignTimeProvider)' == 'true' and '%(BuiltProjectOutputGroupKeyOutput->Filename)%(BuiltProjectOutputGroupKeyOutput->Extension)' != 'FSharp.Core.dll' and '%(BuiltProjectOutputGroupKeyOutput->Filename)%(BuiltProjectOutputGroupKeyOutput->Extension)' != 'System.ValueTuple.dll' ">
<NearestTargetFramework>$(TargetFramework)</NearestTargetFramework>
<!-- Direct case (<IsFSharpDesignTimeProvider>true</...>): this project's own output, under its own framework. -->
<_ResolvedOutputFiles Condition="'$(IsFSharpDesignTimeProvider)' == 'true'" Include="@(BuiltProjectOutputGroupKeyOutput)">
<DesignTimeFramework>$(TargetFramework)</DesignTimeFramework>
</_ResolvedOutputFiles>

<TfmSpecificPackageFile Include="@(_ResolvedOutputFiles)">
<PackagePath>$(FSharpToolsDirectory)/$(FSharpDesignTimeProtocol)/%(_ResolvedOutputFiles.NearestTargetFramework)/%(_ResolvedOutputFiles.FileName)%(_ResolvedOutputFiles.Extension)</PackagePath>
<!-- The consumer always supplies its own FSharp.Core / System.ValueTuple (and FSharp.Core's culture
satellites), so never pack those. Preserve the file's relative subpath (%(RecursiveDir)) so a provider's
own satellites/native keep their layout; it is empty for the top-level dll and for the direct case. -->
<TfmSpecificPackageFile Include="@(_ResolvedOutputFiles)"
Condition="'%(_ResolvedOutputFiles.Filename)' != 'FSharp.Core' and '%(_ResolvedOutputFiles.Filename)' != 'System.ValueTuple' and '%(_ResolvedOutputFiles.Filename)' != 'FSharp.Core.resources'">
<PackagePath>$(FSharpToolsDirectory)/$(FSharpDesignTimeProtocol)/%(_ResolvedOutputFiles.DesignTimeFramework)/%(_ResolvedOutputFiles.RecursiveDir)%(_ResolvedOutputFiles.FileName)%(_ResolvedOutputFiles.Extension)</PackagePath>
</TfmSpecificPackageFile>
</ItemGroup>
</Target>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project>

<!-- Re-import the parent props chain, then pin a deterministic package version so the test assertions
(which look for e.g. Host.1.0.0.nupkg) match the produced package regardless of the product version. -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<PropertyGroup>
<Version>1.0.0</Version>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ echo [Test 2] PASSED: Provider test passed
echo.
echo === Test 3: Host with ProjectReference to Provider ===
echo [Test 3] Packing Host with ProjectReference to Provider...
echo [Test 3] Note: This tests experimental execution-time reference checking
echo [Test 3] Command: dotnet pack Host\Host.fsproj -o %~dp0artifacts -c %configuration% -v minimal -bl:%~dp0artifacts\host.binlog -p:FSharpTestCompilerVersion=coreclr
dotnet pack Host\Host.fsproj -o %~dp0artifacts -c %configuration% -v minimal -bl:%~dp0artifacts\host.binlog -p:FSharpTestCompilerVersion=coreclr
if ERRORLEVEL 1 (
Expand All @@ -94,9 +93,17 @@ if ERRORLEVEL 1 (
goto :failure
)

rem Note: This test may not work as expected due to MSBuild evaluation phase limitations
rem The current implementation only checks IsFSharpDesignTimeProvider property directly
echo [Test 3] PASSED: Host test completed (implementation limitation noted - may not check references correctly)
rem The provider is activated only through ProjectReference IsFSharpDesignTimeProvider metadata; it must still be
rem packaged under tools/fsharp41/ in the consumer's package. This is the case regressed by #18929 (the provider
rem was silently dropped from the package).
echo [Test 3] Checking that the referenced provider is packaged under tools/fsharp41...
powershell -command "& { Add-Type -AssemblyName System.IO.Compression.FileSystem; $names = ([System.IO.Compression.ZipFile]::OpenRead('%~dp0artifacts\Host.1.0.0.nupkg')).Entries.FullName; if ($names | Where-Object { $_ -like 'tools/fsharp41/*Provider.dll' }) { exit 0 } else { Write-Host ('Contents: ' + ($names -join ', ')); exit 1 } }"
if ERRORLEVEL 1 (
echo [Test 3] FAILED: Host.1.0.0.nupkg does not contain the design-time provider under tools/fsharp41/
echo [Test 3] The provider referenced via the ProjectReference gesture was dropped from the package.
goto :failure
)
echo [Test 3] PASSED: Host (ProjectReference gesture) test passed

echo.
echo === Test 4: Pack with --no-build (No Provider) ===
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,24 @@ echo "[Test 2] PASSED: Provider test passed"
echo
echo "=== Test 3: Host with ProjectReference to Provider ==="
echo "[Test 3] Packing Host with ProjectReference to Provider..."
echo "[Test 3] Note: This tests experimental execution-time reference checking"
echo "[Test 3] Command: dotnet pack Host/Host.fsproj -o artifacts -c $configuration -v minimal -bl:artifacts/host.binlog -p:FSharpTestCompilerVersion=coreclr"
if ! dotnet pack Host/Host.fsproj -o artifacts -c $configuration -v minimal -bl:artifacts/host.binlog -p:FSharpTestCompilerVersion=coreclr; then
echo "[Test 3] FAILED: Pack command returned error code $?"
echo "[Test 3] Check artifacts/host.binlog for details"
exit 1
fi

# Note: This test may not work as expected due to MSBuild evaluation phase limitations
# The current implementation only checks IsFSharpDesignTimeProvider property directly
echo "[Test 3] PASSED: Host test completed (implementation limitation noted - may not check references correctly)"
# The provider is activated only through ProjectReference IsFSharpDesignTimeProvider metadata; it must still be
# packaged under tools/fsharp41/ in the consumer's package. This is the case regressed by #18929 (the provider
# was silently dropped from the package).
echo "[Test 3] Checking that the referenced provider is packaged under tools/fsharp41..."
if ! unzip -l artifacts/Host.1.0.0.nupkg | grep -q "tools/fsharp41/.*Provider.dll"; then
echo "[Test 3] FAILED: Host.1.0.0.nupkg does not contain the design-time provider under tools/fsharp41/"
echo "[Test 3] The provider referenced via the ProjectReference gesture was dropped from the package."
unzip -l artifacts/Host.1.0.0.nupkg | grep -i "fsharp41" || true
exit 1
fi
echo "[Test 3] PASSED: Host (ProjectReference gesture) test passed"

echo
echo "=== Test 4: Pack with --no-build (No Provider) ==="
Expand Down
Loading