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}