Skip to content

Commit a5730e5

Browse files
authored
Use path for loading native dependencies (#10347)
* Use path for loading native dependencies * feedback * Update Microsoft.DotNet.DependencyManager.fsproj * Update VisualFSharpFull.csproj
1 parent d7f6732 commit a5730e5

File tree

8 files changed

+105
-31
lines changed

8 files changed

+105
-31
lines changed

setup/Swix/Microsoft.FSharp.Compiler.MSBuild/Microsoft.FSharp.Compiler.MSBuild.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ folder "InstallDir:Common7\IDE\CommonExtensions\Microsoft\FSharp"
101101
file source="$(BinariesFolder)\FSharp.Core\$(Configuration)\netstandard2.0\FSharp.Core.dll" vs.file.ngen=yes vs.file.ngenArchitecture=All vs.file.ngenPriority=2
102102
file source="$(BinariesFolder)\FSharp.Core\$(Configuration)\netstandard2.0\FSharp.Core.xml"
103103
file source="$(BinariesFolder)\FSharp.Build\$(Configuration)\$(TargetFramework)\FSharp.Build.dll" vs.file.ngen=yes vs.file.ngenArchitecture=All vs.file.ngenPriority=2
104-
file source="$(BinariesFolder)\Microsoft.DotNet.DependencyManager\$(Configuration)\net472\Microsoft.DotNet.DependencyManager.dll" vs.file.ngen=yes vs.file.ngenArchitecture=All vs.file.ngenPriority=2
104+
file source="$(BinariesFolder)\Microsoft.DotNet.DependencyManager\$(Configuration)\netstandard2.0\Microsoft.DotNet.DependencyManager.dll" vs.file.ngen=yes vs.file.ngenArchitecture=All vs.file.ngenPriority=2
105105
file source="$(BinariesFolder)\FSharp.Build\$(Configuration)\$(TargetFramework)\Microsoft.Build.Framework.dll"
106106
file source="$(BinariesFolder)\FSharp.Build\$(Configuration)\$(TargetFramework)\Microsoft.Build.Tasks.Core.dll"
107107
file source="$(BinariesFolder)\FSharp.Build\$(Configuration)\$(TargetFramework)\Microsoft.Build.Utilities.Core.dll"

src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,13 @@ module internal ProjectFile =
7474
resolutions
7575
|> Array.filter(fun r ->
7676
not(String.IsNullOrEmpty(r.NugetPackageId) ||
77-
String.IsNullOrEmpty(r.NativePath)) &&
78-
Directory.Exists(r.NativePath))
79-
|> Array.map(fun r -> r.NativePath)
77+
String.IsNullOrEmpty(r.NativePath)))
78+
|> Array.map(fun r ->
79+
if Directory.Exists(r.NativePath) then Some (r.NativePath)
80+
elif File.Exists(r.NativePath) then Some (Path.GetDirectoryName(r.NativePath).Replace('\\', '/'))
81+
else None)
82+
|> Array.filter(fun r -> r.IsSome)
83+
|> Array.map(fun r -> r.Value)
8084

8185
Array.concat [|managedRoots; nativeRoots|] |> Array.distinct
8286

src/fsharp/Microsoft.DotNet.DependencyManager/DependencyProvider.fs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ type ReflectionDependencyManagerProvider(theType: Type,
124124
resolveDeps: MethodInfo option,
125125
resolveDepsEx: MethodInfo option,
126126
outputDir: string option) =
127-
128127
let instance = Activator.CreateInstance(theType, [| outputDir :> obj |])
129128
let nameProperty = nameProperty.GetValue >> string
130129
let keyProperty = keyProperty.GetValue >> string
@@ -226,7 +225,6 @@ type ReflectionDependencyManagerProvider(theType: Type,
226225

227226
/// Resolve the dependencies for the given arguments
228227
member this.ResolveDependencies(scriptDir, mainScriptName, scriptName, scriptExt, packageManagerTextLines, tfm, rid): IResolveDependenciesResult =
229-
230228
// The ResolveDependencies method, has two signatures, the original signaature in the variable resolveDeps and the updated signature resolveDepsEx
231229
// the resolve method can return values in two different tuples:
232230
// (bool * string list * string list * string list)
@@ -275,10 +273,7 @@ type ReflectionDependencyManagerProvider(theType: Type,
275273
type DependencyProvider (assemblyProbingPaths: AssemblyResolutionProbe, nativeProbingRoots: NativeResolutionProbe) =
276274

277275
// Note: creating a NativeDllResolveHandler currently installs process-wide handlers
278-
let dllResolveHandler =
279-
match nativeProbingRoots with
280-
| null -> { new IDisposable with member _.Dispose() = () }
281-
| _ -> new NativeDllResolveHandler(nativeProbingRoots) :> IDisposable
276+
let dllResolveHandler = new NativeDllResolveHandler(nativeProbingRoots)
282277

283278
// Note: creating a AssemblyResolveHandler currently installs process-wide handlers
284279
let assemblyResolveHandler =
@@ -384,7 +379,6 @@ type DependencyProvider (assemblyProbingPaths: AssemblyResolutionProbe, nativePr
384379

385380
/// Fetch a dependencymanager that supports a specific key
386381
member _.TryFindDependencyManagerByKey (compilerTools: string seq, outputDir: string, reportError: ResolvingErrorReport, key: string): IDependencyManagerProvider =
387-
388382
try
389383
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) reportError
390384
|> Map.tryFind key
@@ -407,7 +401,7 @@ type DependencyProvider (assemblyProbingPaths: AssemblyResolutionProbe, nativePr
407401
[<Optional;DefaultParameterValue("")>]implicitIncludeDir: string,
408402
[<Optional;DefaultParameterValue("")>]mainScriptName: string,
409403
[<Optional;DefaultParameterValue("")>]fileName: string): IResolveDependenciesResult =
410-
404+
411405
let key = (packageManager.Key, scriptExt, Seq.toArray packageManagerTextLines, executionTfm, executionRid, implicitIncludeDir, mainScriptName, fileName)
412406

413407
let result =
@@ -424,8 +418,10 @@ type DependencyProvider (assemblyProbingPaths: AssemblyResolutionProbe, nativePr
424418
let e = stripTieWrapper e
425419
Error (DependencyManager.SR.packageManagerError(e.Message))
426420
))
427-
match result with
428-
| Ok res -> res
421+
match result with
422+
| Ok res ->
423+
dllResolveHandler.RefreshPathsInEnvironment(res.Roots)
424+
res
429425
| Error (errorNumber, errorData) ->
430426
reportError.Invoke(ErrorReportType.Error, errorNumber, errorData)
431427
ReflectionDependencyManagerProvider.MakeResultFromFields(false, arrEmpty, arrEmpty, seqEmpty, seqEmpty, seqEmpty)
@@ -436,5 +432,5 @@ type DependencyProvider (assemblyProbingPaths: AssemblyResolutionProbe, nativePr
436432

437433
// Unregister everything
438434
registeredDependencyManagers <- None
439-
dllResolveHandler.Dispose()
435+
(dllResolveHandler :> IDisposable).Dispose()
440436
assemblyResolveHandler.Dispose()

src/fsharp/Microsoft.DotNet.DependencyManager/Microsoft.DotNet.DependencyManager.fsproj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44

55
<PropertyGroup>
66
<OutputType>Library</OutputType>
7-
<TargetFrameworks>netstandard2.0;net472</TargetFrameworks>
7+
<TargetFrameworks>netstandard2.0</TargetFrameworks>
88
<IsPackable>true</IsPackable>
9-
<TargetFrameworks Condition="'$(OS)' == 'Unix'">netstandard2.0</TargetFrameworks>
109
<AssemblyName>Microsoft.DotNet.DependencyManager</AssemblyName>
1110
<NoWarn>$(NoWarn);45;55;62;75;1204</NoWarn>
1211
<AllowCrossTargeting>true</AllowCrossTargeting>

src/fsharp/Microsoft.DotNet.DependencyManager/NativeDllResolveHandler.fs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Microsoft.DotNet.DependencyManager
44

55
open System
6+
open System.Collections.Concurrent
67
open System.IO
78
open System.Reflection
89
open System.Runtime.InteropServices
@@ -13,7 +14,6 @@ open Internal.Utilities.FSharpEnvironment
1314
/// host implements this, it's job is to return a list of package roots to probe.
1415
type NativeResolutionProbe = delegate of Unit -> seq<string>
1516

16-
#if NETSTANDARD
1717
open System.Runtime.Loader
1818

1919
// Cut down AssemblyLoadContext, for loading native libraries
@@ -28,7 +28,6 @@ type NativeAssemblyLoadContext () =
2828

2929
static member NativeLoadContext = new NativeAssemblyLoadContext()
3030

31-
3231
/// Type that encapsulates Native library probing for managed packages
3332
type NativeDllResolveHandlerCoreClr (nativeProbingRoots: NativeResolutionProbe) =
3433
let probingFileNames (name: string) =
@@ -65,7 +64,6 @@ type NativeDllResolveHandlerCoreClr (nativeProbingRoots: NativeResolutionProbe)
6564
|]
6665

6766
let _resolveUnmanagedDll (_: Assembly) (name: string): IntPtr =
68-
6967
// Enumerate probing roots looking for a dll that matches the probing name in the probed locations
7068
let probeForNativeLibrary root rid name =
7169
// Look for name in root
@@ -79,7 +77,7 @@ type NativeDllResolveHandlerCoreClr (nativeProbingRoots: NativeResolutionProbe)
7977
let probe =
8078
match nativeProbingRoots with
8179
| null -> None
82-
| _ ->
80+
| _ ->
8381
nativeProbingRoots.Invoke()
8482
|> Seq.tryPick(fun root ->
8583
probingFileNames name |> Seq.tryPick(fun name ->
@@ -98,28 +96,54 @@ type NativeDllResolveHandlerCoreClr (nativeProbingRoots: NativeResolutionProbe)
9896
let eventInfo = typeof<AssemblyLoadContext>.GetEvent("ResolvingUnmanagedDll")
9997
let handler = Func<Assembly, string, IntPtr> (_resolveUnmanagedDll)
10098

101-
do if not (isNull eventInfo) then eventInfo.AddEventHandler(AssemblyLoadContext.Default, handler)
99+
do
100+
if not (isNull eventInfo) then
101+
eventInfo.AddEventHandler(AssemblyLoadContext.Default, handler)
102102

103103
interface IDisposable with
104104
member _x.Dispose() =
105105
if not (isNull eventInfo) then
106106
eventInfo.RemoveEventHandler(AssemblyLoadContext.Default, handler)
107107
()
108108

109-
#endif
110-
111-
type NativeDllResolveHandler (_nativeProbingRoots: NativeResolutionProbe) =
112-
109+
type NativeDllResolveHandler (nativeProbingRoots: NativeResolutionProbe) =
113110
let handler:IDisposable option =
114-
#if NETSTANDARD
115111
if isRunningOnCoreClr then
116-
Some (new NativeDllResolveHandlerCoreClr(_nativeProbingRoots) :> IDisposable)
112+
Some (new NativeDllResolveHandlerCoreClr(nativeProbingRoots) :> IDisposable)
117113
else
118-
#endif
119114
None
120115

116+
let appendSemiColon (p:string) =
117+
if not(p.EndsWith(";", StringComparison.OrdinalIgnoreCase)) then
118+
p + ";"
119+
else
120+
p
121+
122+
let addedPaths = ConcurrentBag<string>()
123+
124+
let addProbeToProcessPath probePath =
125+
let probe = appendSemiColon probePath
126+
let path = appendSemiColon (Environment.GetEnvironmentVariable("PATH"))
127+
if not (path.Contains(probe)) then
128+
Environment.SetEnvironmentVariable("PATH", path + probe)
129+
addedPaths.Add probe
130+
131+
let removeProbeFromProcessPath probePath =
132+
if not(String.IsNullOrWhiteSpace(probePath)) then
133+
let probe = appendSemiColon probePath
134+
let path = appendSemiColon (Environment.GetEnvironmentVariable("PATH"))
135+
if path.Contains(probe) then Environment.SetEnvironmentVariable("PATH", path.Replace(probe, ""))
136+
137+
member internal _.RefreshPathsInEnvironment(roots: string seq) =
138+
for probePath in roots do
139+
addProbeToProcessPath probePath
140+
121141
interface IDisposable with
122142
member _.Dispose() =
123143
match handler with
124144
| None -> ()
125145
| Some handler -> handler.Dispose()
146+
147+
let mutable probe:string = null
148+
while (addedPaths.TryTake(&probe)) do
149+
removeProbeFromProcessPath probe

src/fsharp/Microsoft.DotNet.DependencyManager/NativeDllResolveHandler.fsi

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ type NativeResolutionProbe = delegate of Unit -> seq<string>
1212
type NativeDllResolveHandler =
1313

1414
/// Construct a new NativeDllResolveHandler
15-
new: _nativeProbingRoots: NativeResolutionProbe -> NativeDllResolveHandler
15+
new: nativeProbingRoots: NativeResolutionProbe -> NativeDllResolveHandler
16+
17+
member internal RefreshPathsInEnvironment: string seq -> unit
1618

1719
interface IDisposable

tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,55 @@ x |> Seq.iter(fun r ->
657657
try Assembly.Load("NoneSuchAssembly") |> ignore with _ -> ()
658658
Assert.False (assemblyFound, "Invoke the assemblyProbingRoots callback -- Error the AssemblyResolve still fired ")
659659

660+
[<Fact>]
661+
member __.``Verify that Dispose cleans up the native paths added``() =
662+
let nativeProbingRoots () = Seq.empty<string>
663+
664+
let appendSemiColon (p:string) =
665+
if not(p.EndsWith(";", StringComparison.OrdinalIgnoreCase)) then
666+
p + ";"
667+
else
668+
p
669+
670+
let reportError =
671+
let report errorType code message =
672+
match errorType with
673+
| ErrorReportType.Error -> printfn "PackageManagementError %d : %s" code message
674+
| ErrorReportType.Warning -> printfn "PackageManagementWarning %d : %s" code message
675+
ResolvingErrorReport (report)
676+
677+
let mutable initialPath:string = null
678+
let mutable currentPath:string = null
679+
let mutable finalPath:string = null
680+
do
681+
initialPath <- appendSemiColon (Environment.GetEnvironmentVariable("PATH"))
682+
use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots))
683+
let idm = dp.TryFindDependencyManagerByKey(Seq.empty, "", reportError, "nuget")
684+
let mutable currentPath:string = null
685+
if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then
686+
let result = dp.Resolve(idm, ".fsx", [|"r", "Microsoft.Data.Sqlite,3.1.7"|], reportError, "netstandard2.0")
687+
Assert.Equal(true, result.Success)
688+
currentPath <- appendSemiColon (Environment.GetEnvironmentVariable("PATH"))
689+
finalPath <- appendSemiColon (Environment.GetEnvironmentVariable("PATH"))
690+
Assert.True(currentPath <> initialPath) // The path was modified by #r "nuget: ..."
691+
Assert.Equal(finalPath, initialPath) // IDispose correctly cleaned up the path
692+
693+
initialPath <- null
694+
currentPath <- null
695+
finalPath <- null
696+
do
697+
initialPath <- appendSemiColon (Environment.GetEnvironmentVariable("PATH"))
698+
let mutable currentPath:string = null
699+
use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots))
700+
let idm = dp.TryFindDependencyManagerByKey(Seq.empty, "", reportError, "nuget")
701+
let result = dp.Resolve(idm, ".fsx", [|"r", "Microsoft.Data.Sqlite,3.1.7"|], reportError, "netcoreapp3.1")
702+
Assert.Equal(true, result.Success)
703+
currentPath <- appendSemiColon (Environment.GetEnvironmentVariable("PATH"))
704+
finalPath <- appendSemiColon (Environment.GetEnvironmentVariable("PATH"))
705+
Assert.True(currentPath <> initialPath) // The path was modified by #r "nuget: ..."
706+
Assert.Equal(finalPath, initialPath) // IDispose correctly cleaned up the path
707+
708+
()
660709

661710
[<Fact>]
662711
member __.``Verify that #help produces help text for fsi + dependency manager``() =

vsintegration/Vsix/VisualFSharpFull/VisualFSharpFull.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595
<NgenArchitecture>All</NgenArchitecture>
9696
<NgenPriority>2</NgenPriority>
9797
<Private>True</Private>
98-
<AdditionalProperties>TargetFramework=net472</AdditionalProperties>
98+
<AdditionalProperties>TargetFramework=netstandard2.0</AdditionalProperties>
9999
</ProjectReference>
100100
<ProjectReference Include="$(FSharpSourcesRoot)\fsharp\FSharp.Core\FSharp.Core.fsproj">
101101
<Project>{DED3BBD7-53F4-428A-8C9F-27968E768605}</Project>

0 commit comments

Comments
 (0)