diff --git a/FSharp.slnx b/FSharp.slnx
index 50819fbfb6b..3d0ba13488e 100644
--- a/FSharp.slnx
+++ b/FSharp.slnx
@@ -61,6 +61,9 @@
+
+
+
diff --git a/UseLocalCompiler.Directory.Build.props b/UseLocalCompiler.Directory.Build.props
index 8bd796bfe90..b0e5f0db2f4 100644
--- a/UseLocalCompiler.Directory.Build.props
+++ b/UseLocalCompiler.Directory.Build.props
@@ -1,45 +1,32 @@
-
-
-
-
False
-
Release
+ $(MSBuildThisFileDirectory)
+ $([System.IO.Path]::Combine('$(LocalFSharpCompilerPath)', 'artifacts', 'bin', 'UseLocalCompiler.FSharp.Build.Tasks', '$(LocalFSharpCompilerConfiguration)', 'netstandard2.0', 'UseLocalCompiler.FSharp.Build.Tasks.dll'))
+
- $(MSBuildThisFileDirectory)
-
- true
- true
- $([System.IO.Path]::GetDirectoryName($(DOTNET_HOST_PATH)))
- $([System.IO.Path]::GetFileName($(DOTNET_HOST_PATH)))
-
- $(LocalFSharpCompilerPath)/artifacts/bin/fsc/$(LocalFSharpCompilerConfiguration)/$(FSharpNetCoreProductTargetFramework)/fsc.dll
- $(LocalFSharpCompilerPath)/artifacts/bin/fsc/$(LocalFSharpCompilerConfiguration)/$(FSharpNetCoreProductTargetFramework)/fsc.dll
+
- False
- True
-
+
+
+ <_LocalFSharpCoreTargetFramework>netstandard2.1
+ <_TargetFrameworkVersionWithoutV>$(TargetFrameworkVersion)
+ <_TargetFrameworkVersionWithoutV Condition="'$(_TargetFrameworkVersionWithoutV)' != '' and $([System.String]::Copy('$(_TargetFrameworkVersionWithoutV)').StartsWith('v'))">$([System.String]::Copy('$(_TargetFrameworkVersionWithoutV)').Substring(1))
+ <_LocalFSharpCoreTargetFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">netstandard2.0
+ <_LocalFSharpCoreTargetFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard' and '$(_TargetFrameworkVersionWithoutV)' != '' and $([System.Version]::Parse('$(_TargetFrameworkVersionWithoutV)')) < $([System.Version]::Parse('2.1'))">netstandard2.0
+ <_LocalFSharpCoreTargetFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' and '$(_TargetFrameworkVersionWithoutV)' != '' and $([System.Version]::Parse('$(_TargetFrameworkVersionWithoutV)')) < $([System.Version]::Parse('3.0'))">netstandard2.0
+ $([System.IO.Path]::Combine('$(LocalFSharpCompilerPath)', 'artifacts', 'bin', 'FSharp.Core', '$(LocalFSharpCompilerConfiguration)', '$(_LocalFSharpCoreTargetFramework)', 'FSharp.Core.dll'))
+
-
-
- $(LocalFSharpCompilerPath)/artifacts/bin/fsc/$(LocalFSharpCompilerConfiguration)/$(FSharpNetCoreProductTargetFramework)
- $(LocalFSharpBuildBinPath)/FSharp.Build.dll
- $(LocalFSharpBuildBinPath)/Microsoft.FSharp.Targets
- $(LocalFSharpBuildBinPath)/Microsoft.FSharp.NetSdk.props
- $(LocalFSharpBuildBinPath)/Microsoft.FSharp.NetSdk.targets
- $(LocalFSharpBuildBinPath)/Microsoft.FSharp.Overrides.NetSdk.targets
-
+
-
-
-
+
+
+
+ $(LocalFSharpCoreAssemblyPath)
+ true
+
+
+
diff --git a/VisualFSharp.slnx b/VisualFSharp.slnx
index 9f6eed02f50..518543604c9 100644
--- a/VisualFSharp.slnx
+++ b/VisualFSharp.slnx
@@ -80,6 +80,9 @@
+
+
+
diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml
index 8647164d91a..f6b6915ee59 100644
--- a/azure-pipelines-PR.yml
+++ b/azure-pipelines-PR.yml
@@ -656,18 +656,26 @@ stages:
publishLocation: pipeline
condition: succeeded()
+ - task: PublishPipelineArtifact@1
+ displayName: Publish UseLocalCompiler task artifacts for Regression Tests
+ inputs:
+ targetPath: '$(Build.SourcesDirectory)/artifacts/bin/UseLocalCompiler.FSharp.Build.Tasks'
+ artifactName: 'UseLocalCompilerTaskArtifacts'
+ publishLocation: pipeline
+ condition: succeeded()
+
- pwsh: |
- # Stage UseLocalCompiler props and TargetFrameworks.props together
+ # Stage the local test props file and TargetFrameworks.props together
$stagingDir = "$(Build.SourcesDirectory)/UseLocalCompilerPropsStaging"
New-Item -ItemType Directory -Force -Path $stagingDir | Out-Null
Copy-Item "$(Build.SourcesDirectory)/UseLocalCompiler.Directory.Build.props" -Destination $stagingDir
Copy-Item "$(Build.SourcesDirectory)/eng/TargetFrameworks.props" -Destination $stagingDir
Write-Host "Staged files for UseLocalCompilerProps artifact:"
Get-ChildItem $stagingDir -Name
- displayName: Stage UseLocalCompiler props files
+ displayName: Stage local test props files
- task: PublishPipelineArtifact@1
- displayName: Publish UseLocalCompiler props file for Regression Tests
+ displayName: Publish local test props file for Regression Tests
inputs:
targetPath: '$(Build.SourcesDirectory)/UseLocalCompilerPropsStaging'
artifactName: 'UseLocalCompilerProps'
@@ -892,6 +900,14 @@ stages:
commit: e81e00a464b9d35d272f61708a1a0bfbf487b6d5
buildScript: dotnet build Nu.sln --configuration Release
displayName: Nu_Build
+ - repo: Lanayx/Oxpecker
+ commit: cb7e4b83e3f2aba7ded46b17a36d1aea8c13c292
+ buildScript: dotnet build .\Oxpecker.slnx -bl
+ displayName: Oxpecker_Build
+ - repo: Lanayx/Oxpecker
+ commit: cb7e4b83e3f2aba7ded46b17a36d1aea8c13c292
+ buildScript: dotnet build .\Oxpecker.Solid.slnx -bl
+ displayName: Oxpecker_Solid_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
diff --git a/eng/scripts/PrepareRepoForRegressionTesting.fsx b/eng/scripts/PrepareRepoForRegressionTesting.fsx
index e1df77bcb45..f718d0cfef6 100644
--- a/eng/scripts/PrepareRepoForRegressionTesting.fsx
+++ b/eng/scripts/PrepareRepoForRegressionTesting.fsx
@@ -1,5 +1,5 @@
-/// Script to inject UseLocalCompiler.Directory.Build.props import into a third-party repository's Directory.Build.props
-/// Usage: dotnet fsi PrepareRepoForRegressionTesting.fsx
+/// Script to inject a local F# test props import into a third-party repository's Directory.Build.props
+/// Usage: dotnet fsi PrepareRepoForRegressionTesting.fsx
open System
open System.IO
@@ -14,21 +14,23 @@ let useLocalCompilerPropsPath =
if scriptArgs.Length > 0 then
scriptArgs.[0]
else
- failwith "Usage: dotnet fsi PrepareRepoForRegressionTesting.fsx "
+ failwith "Usage: dotnet fsi PrepareRepoForRegressionTesting.fsx "
printfn "PrepareRepoForRegressionTesting.fsx"
printfn "==================================="
printfn "UseLocalCompiler props path: %s" useLocalCompilerPropsPath
if not (File.Exists(useLocalCompilerPropsPath)) then
- failwithf "UseLocalCompiler.Directory.Build.props not found at: %s" useLocalCompilerPropsPath
+ failwithf "Local test props file not found at: %s" useLocalCompilerPropsPath
-printfn "✓ UseLocalCompiler.Directory.Build.props found"
+printfn "✓ Local test props file found"
let absolutePropsPath =
Path.GetFullPath(useLocalCompilerPropsPath).Replace("\\", "/")
printfn "Absolute path: %s" absolutePropsPath
+let propsFileName = Path.GetFileName(absolutePropsPath)
+
if File.Exists(propsFilePath) then
printfn "Directory.Build.props exists, modifying it..."
@@ -40,7 +42,7 @@ if File.Exists(propsFilePath) then
if isNull projectElement then
failwith "Could not find Project element in Directory.Build.props"
- let xpath = "//Import[contains(@Project, 'UseLocalCompiler.Directory.Build.props')]"
+ let xpath = sprintf "//Import[contains(@Project, '%s')]" propsFileName
let existingImport = doc.SelectSingleNode(xpath)
if isNull existingImport then
diff --git a/eng/templates/regression-test-jobs.yml b/eng/templates/regression-test-jobs.yml
index 16da81059c2..bf68e91b1f0 100644
--- a/eng/templates/regression-test-jobs.yml
+++ b/eng/templates/regression-test-jobs.yml
@@ -47,7 +47,13 @@ jobs:
downloadPath: '$(Pipeline.Workspace)/FSharpCompiler/artifacts/bin/FSharp.Core'
- task: DownloadPipelineArtifact@2
- displayName: Download UseLocalCompiler props
+ displayName: Download UseLocalCompiler task artifacts
+ inputs:
+ artifactName: 'UseLocalCompilerTaskArtifacts'
+ downloadPath: '$(Pipeline.Workspace)/FSharpCompiler/artifacts/bin/UseLocalCompiler.FSharp.Build.Tasks'
+
+ - task: DownloadPipelineArtifact@2
+ displayName: Download local test props
inputs:
artifactName: 'UseLocalCompilerProps'
downloadPath: '$(Pipeline.Workspace)/Props'
diff --git a/tests/UseLocalCompiler.FSharp.Build.Tasks/Fsc.fs b/tests/UseLocalCompiler.FSharp.Build.Tasks/Fsc.fs
new file mode 100644
index 00000000000..a92051cfc86
--- /dev/null
+++ b/tests/UseLocalCompiler.FSharp.Build.Tasks/Fsc.fs
@@ -0,0 +1,118 @@
+namespace UseLocalCompiler.FSharp.Build.Tasks
+
+open System
+open System.IO
+open Microsoft.Build.Framework
+open Microsoft.Build.Utilities
+
+type public Fsc() =
+ inherit FSharp.Build.Fsc()
+
+ static member private TryGetBuildContext() =
+ try
+ let assemblyDir = DirectoryInfo(Path.GetDirectoryName(typeof.Assembly.Location))
+
+ if
+ not (isNull assemblyDir.Parent)
+ && not (isNull assemblyDir.Parent.Parent)
+ && not (isNull assemblyDir.Parent.Parent.Parent)
+ && not (isNull assemblyDir.Parent.Parent.Parent.Parent)
+ && not (isNull assemblyDir.Parent.Parent.Parent.Parent.Parent)
+ && StringComparer.OrdinalIgnoreCase.Equals(assemblyDir.Parent.Parent.Parent.Name, "bin")
+ && StringComparer.OrdinalIgnoreCase.Equals(assemblyDir.Parent.Parent.Parent.Parent.Name, "artifacts")
+ then
+ Some(assemblyDir.Parent.Parent.Parent.Parent.Parent.FullName, assemblyDir.Parent.Name)
+ else
+ None
+ with _ ->
+ None
+
+ static member private TryGetLocalDotnetFscCompilerPath(repoRoot: string, configuration: string) =
+ let fscOutputRoot = Path.Combine(repoRoot, "artifacts", "bin", "fsc", configuration)
+
+ if Directory.Exists fscOutputRoot then
+ fscOutputRoot
+ |> Directory.GetDirectories
+ |> Array.choose (fun dir ->
+ let candidate = Path.Combine(dir, "fsc.dll")
+
+ if File.Exists candidate then
+ Some candidate
+ else
+ None)
+ |> function
+ | [| candidate |] -> Some candidate
+ | _ -> None
+ else
+ None
+
+ static member private IsFSharpCoreReference(item: ITaskItem) =
+ let itemName = Path.GetFileNameWithoutExtension(item.ItemSpec)
+ StringComparer.OrdinalIgnoreCase.Equals(itemName, "FSharp.Core")
+
+ static member private TryGetReferenceTargetFramework(item: ITaskItem) =
+ try
+ Path.GetDirectoryName(item.ItemSpec)
+ |> Path.GetFileName
+ |> function
+ | null
+ | "" -> None
+ | tfm -> Some tfm
+ with _ ->
+ None
+
+ static member private TryGetLocalFSharpCoreReference(responseFileCommands: string) =
+ let referenceLines =
+ responseFileCommands.Split([| '\r'; '\n' |], StringSplitOptions.RemoveEmptyEntries)
+ |> Array.filter (fun line -> line.StartsWith("-r:", StringComparison.Ordinal))
+
+ let fsharpCoreTargetFrameworks =
+ referenceLines
+ |> Array.map (fun line -> line.Substring(3).Trim([| '"' |]))
+ |> Array.filter (fun path -> Fsc.IsFSharpCoreReference(TaskItem(path) :> ITaskItem))
+ |> Array.choose (fun path -> Fsc.TryGetReferenceTargetFramework(TaskItem(path) :> ITaskItem))
+ |> Array.distinct
+
+ match Fsc.TryGetBuildContext(), fsharpCoreTargetFrameworks with
+ | Some(repoRoot, configuration), [| fsharpCoreTargetFramework |] ->
+ let localFSharpCorePath =
+ Path.Combine(repoRoot, "artifacts", "bin", "FSharp.Core", configuration, fsharpCoreTargetFramework, "FSharp.Core.dll")
+
+ if File.Exists localFSharpCorePath then
+ Some localFSharpCorePath
+ else
+ None
+ | _ -> None
+
+ override _.GenerateCommandLineCommands() =
+ match Fsc.TryGetBuildContext() with
+ | Some(repoRoot, configuration) ->
+ match Fsc.TryGetLocalDotnetFscCompilerPath(repoRoot, configuration) with
+ | Some localDotnetFscCompilerPath -> localDotnetFscCompilerPath
+ | None -> base.GenerateCommandLineCommands()
+ | None -> base.GenerateCommandLineCommands()
+
+ override _.GenerateResponseFileCommands() =
+ let responseFileCommands = base.GenerateResponseFileCommands()
+
+ match Fsc.TryGetLocalFSharpCoreReference(responseFileCommands) with
+ | None -> responseFileCommands
+ | Some localFSharpCorePath ->
+ let mutable emittedLocalFSharpCoreReference = false
+
+ responseFileCommands.Split([| '\r'; '\n' |], StringSplitOptions.RemoveEmptyEntries)
+ |> Array.choose (fun line ->
+ if line.StartsWith("-r:", StringComparison.Ordinal) then
+ let referencePath = line.Substring(3).Trim([| '"' |])
+
+ if Fsc.IsFSharpCoreReference(TaskItem(referencePath) :> ITaskItem) then
+ if emittedLocalFSharpCoreReference then
+ None
+ else
+ emittedLocalFSharpCoreReference <- true
+ Some("-r:" + localFSharpCorePath)
+ else
+ Some line
+ else
+ Some line)
+ |> String.concat Environment.NewLine
diff --git a/tests/UseLocalCompiler.FSharp.Build.Tasks/UseLocalCompiler.FSharp.Build.Tasks.fsproj b/tests/UseLocalCompiler.FSharp.Build.Tasks/UseLocalCompiler.FSharp.Build.Tasks.fsproj
new file mode 100644
index 00000000000..4254656aaa3
--- /dev/null
+++ b/tests/UseLocalCompiler.FSharp.Build.Tasks/UseLocalCompiler.FSharp.Build.Tasks.fsproj
@@ -0,0 +1,27 @@
+
+
+
+
+
+ netstandard2.0
+ Library
+ UseLocalCompiler.FSharp.Build.Tasks
+ true
+ true
+
+
+
+ $(ArtifactsDir)/bin/$(MSBuildProjectName)/$(Configuration)/
+ $(ArtifactsDir)obj/$(MSBuildProjectName)/$(Configuration)/
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs b/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs
index 43743484e96..7b330eddc6f 100644
--- a/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs
+++ b/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs
@@ -60,6 +60,7 @@ private static string CreateStandaloneProjectFile()
return $@"
+ True
Debug
{RepoRoot}