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
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ $(POUND_R)

projectTemplate.Replace("$(POUND_R)", expandReferences)

let generateProjectBody =
let generateProjectFile =
"""
<Project Sdk='Microsoft.NET.Sdk'>

Expand Down Expand Up @@ -193,3 +193,13 @@ $(PACKAGEREFERENCES)
</Target>

</Project>"""

let generateProjectNugetConfigFile =
"""<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
$(NUGET_SOURCES)
</packageSources>
</configuration>
"""
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ open System.Diagnostics
open System.IO
open System.Reflection
open System.Security.Cryptography
open System.Text.RegularExpressions
open FSDependencyManager
open Internal.Utilities.FSharpEnvironment

Expand Down Expand Up @@ -285,7 +286,7 @@ module internal Utilities =

let generateSourcesFromNugetConfigs scriptDirectory workingDir timeout =
let dotnetHostPath = getDotnetHostPath ()
let args = "nuget list source --format short"
let args = "nuget list source --format detailed"

let success, stdOut, stdErr =
executeTool dotnetHostPath args scriptDirectory timeout
Expand All @@ -304,17 +305,36 @@ module internal Utilities =
ignore workingDir
ignore stdErr
#endif
seq {
if success then
for source in stdOut do
// String returned by `dotnet nuget list source --format short` is formatted similar to:
// E https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json
// Use enabled feeds only (see NuGet.Commands.ListSourceRunner.Run) and strip off the flags.
if source.Length > 0 && source.[0] = 'E' then
let pos = source.IndexOf(" ")

if pos >= 0 then
"i", source.Substring(pos).Trim()
}
if success then
// String returned by `dotnet nuget list source --format detailed` is formatted similar to:
// Registered Sources:
// 1. dotnet-eng [Enabled]
// https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json
// 2. dotnet-tools [Enabled]
// https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json
// 3. dotnet5 [Enabled]
// https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json
// Use enabled feeds only (see NuGet.Commands.ListSourceRunner.Run) and strip off the flags.
let pattern =
@"(\s*\d+\.+\s*)(?'name'\S*)(\s*)\[(?'enabled'Enabled|Disabled)\](\s*)$(\s*)(?'uri'\S*)"

let regex =
new Regex(pattern, RegexOptions.Multiline ||| RegexOptions.ExplicitCapture)

let sourcelist = String.concat Environment.NewLine stdOut

String.concat
Environment.NewLine
[|
for m in regex.Matches(sourcelist) do
let name = m.Groups["name"].Value
let enabled = m.Groups["enabled"].Value
let uri = m.Groups["uri"].Value

if enabled.Length > 0 && enabled[0] = 'E' then
$""" <add key="{name}" value="{uri}" />"""
|]
else
""

let computeSha256HashOfBytes (bytes: byte[]) : byte[] = SHA256.Create().ComputeHash(bytes)
32 changes: 22 additions & 10 deletions src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ type FSharpDependencyManager(outputDirectory: string option, useResultsCache: bo

let prepareDependencyResolutionFiles
(
scriptDirectory: string,
scriptExt: string,
directiveLines: (string * string) seq,
targetFrameworkMoniker: string,
Expand All @@ -368,28 +369,36 @@ type FSharpDependencyManager(outputDirectory: string option, useResultsCache: bo
|> List.map FSharpDependencyManager.formatPackageReference
|> Seq.concat

let generatedNugetSources =
generateSourcesFromNugetConfigs scriptDirectory projectDirectory.Value timeout

let packageReferenceText = String.Join(Environment.NewLine, packageReferenceLines)

let projectPath = Path.Combine(projectDirectory.Value, "Project.fsproj")
let nugetPath = Path.Combine(projectDirectory.Value, "NuGet.config")

let generateAndBuildProjectArtifacts =
let writeFile path body =
if not (generatedScripts.ContainsKey(body.GetHashCode().ToString())) then
emitFile path body

let generateProjBody =
generateProjectBody
let generateProjectFile =
generateProjectFile
.Replace("$(TARGETFRAMEWORK)", targetFrameworkMoniker)
.Replace("$(RUNTIMEIDENTIFIER)", runtimeIdentifier)
.Replace("$(PACKAGEREFERENCES)", packageReferenceText)
.Replace("$(SCRIPTEXTENSION)", scriptExt)

let generateProjectNugetConfigFile =
generateProjectNugetConfigFile.Replace("$(NUGET_SOURCES)", generatedNugetSources)

let timeout =
match package_timeout with
| Some _ -> package_timeout
| None -> Some timeout

writeFile projectPath generateProjBody
writeFile projectPath generateProjectFile
writeFile nugetPath generateProjectNugetConfigFile
buildProject projectPath binLogPath timeout

generateAndBuildProjectArtifacts
Expand Down Expand Up @@ -469,15 +478,10 @@ type FSharpDependencyManager(outputDirectory: string option, useResultsCache: bo
| _ -> "#r @\""

let generateAndBuildProjectArtifacts =
let configIncludes =
generateSourcesFromNugetConfigs scriptDirectory projectDirectory.Value timeout

let directiveLines = Seq.append packageManagerTextLines configIncludes

let resolutionHash =
FSharpDependencyManager.computeHashForResolutionInputs (
scriptExt,
directiveLines,
packageManagerTextLines,
targetFrameworkMoniker,
runtimeIdentifier
)
Expand All @@ -486,7 +490,15 @@ type FSharpDependencyManager(outputDirectory: string option, useResultsCache: bo
match tryGetResultsForResolutionHash resolutionHash projectDirectory with
| Some resolutionResult -> true, resolutionResult
| None ->
false, prepareDependencyResolutionFiles (scriptExt, directiveLines, targetFrameworkMoniker, runtimeIdentifier, timeout)
false,
prepareDependencyResolutionFiles (
scriptDirectory,
scriptExt,
packageManagerTextLines,
targetFrameworkMoniker,
runtimeIdentifier,
timeout
)

match resolutionResult.resolutionsFile with
| Some file ->
Expand Down
2 changes: 1 addition & 1 deletion tests/fsharp/core/printing/output.1000.stdout.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -2765,7 +2765,7 @@ val ShortName: string = "hi"
> val list2: int list = [1]

module FSI_0317.
C6f6ae524efb4d95b2b2eaa363022f9d4a28c777f788498ca81a55b9ec1aad1a
A8a951db8294f99e95ae1d276a7ddaefd93d1548e6bf749bdeae55d2649682b3

{"ImmutableField0":6}
type R1 =
Expand Down
2 changes: 1 addition & 1 deletion tests/fsharp/core/printing/output.200.stdout.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -2010,7 +2010,7 @@ val ShortName: string = "hi"
> val list2: int list = [1]

module FSI_0317.
C6f6ae524efb4d95b2b2eaa363022f9d4a28c777f788498ca81a55b9ec1aad1a
A8a951db8294f99e95ae1d276a7ddaefd93d1548e6bf749bdeae55d2649682b3

{"ImmutableField0":6}
type R1 =
Expand Down
2 changes: 1 addition & 1 deletion tests/fsharp/core/printing/output.multiemit.stdout.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -6312,7 +6312,7 @@ val ShortName: string = "hi"
> val list2: int list = [1]

module FSI_0316.
C6f6ae524efb4d95b2b2eaa363022f9d4a28c777f788498ca81a55b9ec1aad1a
A8a951db8294f99e95ae1d276a7ddaefd93d1548e6bf749bdeae55d2649682b3

{"ImmutableField0":6}
type R1 =
Expand Down
2 changes: 1 addition & 1 deletion tests/fsharp/core/printing/output.off.stdout.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -1779,7 +1779,7 @@ val ShortName: string = "hi"
> val list2: int list

module FSI_0317.
C6f6ae524efb4d95b2b2eaa363022f9d4a28c777f788498ca81a55b9ec1aad1a
A8a951db8294f99e95ae1d276a7ddaefd93d1548e6bf749bdeae55d2649682b3

{"ImmutableField0":6}
type R1 =
Expand Down
2 changes: 1 addition & 1 deletion tests/fsharp/core/printing/output.stdout.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -6312,7 +6312,7 @@ val ShortName: string = "hi"
> val list2: int list = [1]

module FSI_0316.
C6f6ae524efb4d95b2b2eaa363022f9d4a28c777f788498ca81a55b9ec1aad1a
A8a951db8294f99e95ae1d276a7ddaefd93d1548e6bf749bdeae55d2649682b3

{"ImmutableField0":6}
type R1 =
Expand Down
20 changes: 8 additions & 12 deletions tests/service/ProjectAnalysisTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5719,7 +5719,7 @@ let checkContentAsScript content =
// set).
// because of this we have to do it all manually
let scriptName = "test.fsx"
let tempDir = Path.GetTempPath()
let tempDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
let scriptFullPath = Path.Combine(tempDir, scriptName)
let sourceText = SourceText.ofString content
let projectOptions, _ = checker.GetProjectOptionsFromScript(scriptFullPath, sourceText, useSdkRefs = true, assumeDotNetFramework = false) |> Async.RunImmediate
Expand All @@ -5732,17 +5732,13 @@ let checkContentAsScript content =

[<Test>]
let ``References from #r nuget are included in script project options`` () =
let isMacos = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)
if isMacos then
Assert.Inconclusive("This test is failing on MacOS VMs now")
else
let checkResults = checkContentAsScript """
let checkResults = checkContentAsScript """
#i "nuget:https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json"
#r "nuget: Dapper"
"""
let assemblyNames =
checkResults.ProjectContext.GetReferencedAssemblies()
|> Seq.choose (fun f -> f.FileName |> Option.map Path.GetFileName)
|> Seq.distinct
printfn "%s" (assemblyNames |> String.concat "\n")
assemblyNames |> should contain "Dapper.dll"
let assemblyNames =
checkResults.ProjectContext.GetReferencedAssemblies()
|> Seq.choose (fun f -> f.FileName |> Option.map Path.GetFileName)
|> Seq.distinct
printfn "%s" (assemblyNames |> String.concat "\n")
assemblyNames |> should contain "Dapper.dll"
88 changes: 25 additions & 63 deletions tests/service/ScriptOptionsTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -23,85 +23,47 @@ open System
let pi = Math.PI
"""

[<TestCase(true, false, [| "--targetprofile:mscorlib" |])>]
[<TestCase(false, false, [| "--targetprofile:mscorlib" |])>]
[<TestCase(false, true, [| "--targetprofile:mscorlib" |])>]
[<TestCase(true, true, [| "--targetprofile:mscorlib" |])>]
[<TestCase(false, false, [| "--targetprofile:netstandard" |])>]
[<TestCase(false, true, [| "--targetprofile:netstandard" |])>]
[<TestCase(true, true, [| "--targetprofile:netstandard" |])>]
[<TestCase(false, false, [| "--targetprofile:netcore" |])>]
[<TestCase(false, true, [| "--targetprofile:netcore" |])>]
[<TestCase(true, true, [| "--targetprofile:netcore" |])>]
[<Test>]
let ``can generate options for different frameworks regardless of execution environment``(assumeNetFx, useSdk, flags) =
let ``can generate options for different frameworks regardless of execution environment - useSdkRefs = false``(assumeDotNetFramework, useSdkRefs, flags) =
let path = Path.GetTempPath()
let file = tryCreateTemporaryFileName () + ".fsx"
let tempFile = Path.Combine(path, file)
let _, errors =
checker.GetProjectOptionsFromScript(tempFile, SourceText.ofString scriptSource, assumeDotNetFramework = assumeNetFx, useSdkRefs = useSdk, otherFlags = flags)
checker.GetProjectOptionsFromScript(tempFile, SourceText.ofString scriptSource, assumeDotNetFramework = assumeDotNetFramework, useSdkRefs = useSdkRefs, otherFlags = flags)
|> Async.RunImmediate
match errors with
| [] -> ()
| errors -> failwithf "Error while parsing script with assumeDotNetFramework:%b, useSdkRefs:%b, and otherFlags:%A:\n%A" assumeNetFx useSdk flags errors
| errors -> failwithf "Error while parsing script with otherFlags:%A:\n%A" flags errors

#if NETFRAMEWORK
// See https://github.com/dotnet/fsharp/pull/13994#issuecomment-1259663865
//
// .NET Core-based tooling can't resolve nuget packages to .NET Framework references
[<TestCase(true, false, [| "--targetprofile:mscorlib" |])>]
#endif
[<TestCase(false, true, [| "--targetprofile:netcore" |])>]
// Bugbug: https://github.com/dotnet/fsharp/issues/14781
//[<TestCase([| "--targetprofile:mscorlib" |])>]
[<TestCase([| "--targetprofile:netcore" |])>]
[<TestCase([| "--targetprofile:netstandard" |])>]
[<Test>]
let ``can resolve nuget packages to right target framework for different frameworks regardless of execution environment``(assumeNetFx, useSdk, flags) =
let isMacos = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)
if isMacos then
Assert.Inconclusive("This test is failing on MacOS VMs now")
else
let path = Path.GetTempPath()
let file = tryCreateTemporaryFileName () + ".fsx"
let tempFile = Path.Combine(path, file)
let scriptSource = """
let ``can resolve nuget packages to right target framework for different frameworks regardless of execution environment``(flags) =
let path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
let file = tryCreateTemporaryFileNameInDirectory(path) + ".fsx"
let scriptFullPath = Path.Combine(path, file)
let scriptSource = """
#r "nuget: FSharp.Data, 3.3.3"
open System
let pi = Math.PI
"""
let options, errors =
checker.GetProjectOptionsFromScript(tempFile, SourceText.ofString scriptSource, assumeDotNetFramework = assumeNetFx, useSdkRefs = useSdk, otherFlags = flags)
|> Async.RunImmediate
match errors with
| [] -> ()
| errors -> failwithf "Error while parsing script with assumeDotNetFramework:%b, useSdkRefs:%b, and otherFlags:%A:\n%A" assumeNetFx useSdk flags errors
let expectedReferenceText = (if assumeNetFx then "net45" else "netstandard2.0")
let found = options.OtherOptions |> Array.exists (fun s -> s.Contains(expectedReferenceText) && s.Contains("FSharp.Data.dll"))
Assert.IsTrue(found)

// This test atempts to use a bad SDK number 666.666.666
//
// It's disabled because on CI server the SDK is still being resolved to 5.0.101 by `dotnet --version`.
//
// This must be because of some setting in the CI build scripts - e.g. an environment variable
// that allows SDK resolution to be overriden. I've tried to track this down by looking through
// https://learn.microsoft.com/dotnet/core/tools/dotnet resolution rules
// and the F# and CI settings but can't find the setting that is causing this.
//
// Because of this the test has been manually verified by running locally.
//[<Test>]
let ``sdk dir with dodgy global json gives warning``() =
let tempFile = tryCreateTemporaryFileName () + ".fsx"
let tempPath = Path.GetDirectoryName(tempFile)
let globalJsonPath = Path.Combine(tempPath, "global.json")
FileSystem.OpenFileForWriteShim(globalJsonPath).Write("""{ "sdk": { "version": "666.666.666" } }""")
let options, errors =
checker.GetProjectOptionsFromScript(tempFile, SourceText.ofString scriptSource, assumeDotNetFramework = false, useSdkRefs = true, otherFlags = [| |])
checker.GetProjectOptionsFromScript(file, SourceText.ofString scriptSource, assumeDotNetFramework = false, useSdkRefs = true, otherFlags = flags)
|> Async.RunImmediate
FileSystem.FileDeleteShim(globalJsonPath)
match errors with
| [] ->
printfn "Failure!"
printfn "tempPath = %A" tempPath
printfn "options = %A" options
let fxResolver = FSharp.Compiler.FxResolver(false, tempPath, rangeForErrors=range0, useSdkRefs=true, isInteractive=false, sdkDirOverride=None)
let result = fxResolver.TryGetDesiredDotNetSdkVersionForDirectory()
printfn "sdkVersion = %A" result

printfn "options = %A" options
failwith "Expected error while parsing script"
| errors ->
for error in errors do
// {C:\Users\Administrator\AppData\Local\Temp\tmp6F0F.tmp.fsx (0,1)-(0,1) The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' ensure the relevant .NET SDK is installed. The output from 'C:\Program Files\dotnet\dotnet.exe --version' in the script directory was: ' 2.1.300 [C:\Program Files\dotnet\sdk]
Assert.AreEqual(3384, error.ErrorNumber)
Assert.AreEqual(tempFile, error.FileName)
| [] -> ()
| errors -> failwithf "Error while parsing script with assumeDotNetFramework:%b, useSdkRefs:%b, and otherFlags:%A:\n%A" false true flags errors
let expectedReferenceText = match flags |> Array.tryFind(fun f -> f = "--targetprofile:mscorlib") with | Some _ -> "net45" | _ -> "netstandard2.0"
let found = options.OtherOptions |> Array.exists (fun s -> s.Contains(expectedReferenceText) && s.Contains("FSharp.Data.dll"))
Assert.IsTrue(found)