Skip to content

Commit 7ea7cde

Browse files
authored
Workaround for target assemblies where packages with transitive references contain type providers (#388)
* compute correct referenced assemblies * apply fix * update template paket.lock * add testing * fix lang version
1 parent 6a9f05e commit 7ea7cde

14 files changed

+444
-748
lines changed

src/FSharp.TypeProviders.SDK.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<Description>The core implementation of an F# type provider</Description>
1111
<Tags>F# fsharp typeprovider</Tags>
1212
<GenerateDocumentationFile>true</GenerateDocumentationFile>
13+
<LangVersion>5.0</LangVersion>
1314
</PropertyGroup>
1415
<ItemGroup>
1516
<Compile Include="AssemblyInfo.fs" />

src/ProvidedTypes.fs

Lines changed: 169 additions & 46 deletions
Large diffs are not rendered by default.

src/ProvidedTypes.fsi

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation 2005-2014 and other contributors.
1+
// Copyright (c) Microsoft Corporation 2005-2014 and other contributors.
22
// This sample code is provided "as is" without warranty of any kind.
33
// We disclaim all warranties, either express or implied, including the
44
// warranties of merchantability and fitness for a particular purpose.
@@ -404,9 +404,6 @@ type ProvidedTypesContext =
404404
/// Try to find the given target assembly in the context
405405
member TryBindSimpleAssemblyNameToTarget: assemblyName: string -> Choice<Assembly, exn>
406406

407-
/// Get the list of referenced assemblies determined by the type provider configuration
408-
member ReferencedAssemblyPaths: string list
409-
410407
/// Get the resolved referenced assemblies determined by the type provider configuration
411408
member GetTargetAssemblies : unit -> Assembly[]
412409

@@ -509,8 +506,7 @@ type TypeProviderForNamespaces =
509506

510507
#if !NO_GENERATIVE
511508
/// Register that a given file is a provided generated target assembly, e.g. an assembly produced by an external
512-
/// code generation tool. This assembly should be a target assembly, i.e. use the same asssembly references
513-
/// as given by TargetContext.ReferencedAssemblyPaths
509+
/// code generation tool. This assembly should be a target assembly.
514510
member RegisterGeneratedTargetAssembly: fileName: string -> Assembly
515511
#endif
516512

@@ -557,4 +553,3 @@ module internal AssemblyReader =
557553

558554
val GetWeakReaderCache : unit -> System.Collections.Concurrent.ConcurrentDictionary<(string * string), DateTime * WeakReference<ILModuleReader>>
559555
val GetStrongReaderCache : unit -> System.Collections.Concurrent.ConcurrentDictionary<(string * string), DateTime * int * ILModuleReader>
560-

templates/content/basic/paket.lock

Lines changed: 183 additions & 641 deletions
Large diffs are not rendered by default.

tests/BasicErasedProvisionTests.fs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
#if INTERACTIVE
2-
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
3-
#load "../src/ProvidedTypesTesting.fs"
4-
5-
#else
6-
71
module TPSDK.BasicErasedTests
8-
#endif
92

103
open System
114
open System.Reflection
@@ -451,7 +444,7 @@ type ErasingProviderWithCustomAttributes (config : TypeProviderConfig) as this =
451444
this.AddNamespace(ns, createTypes())
452445

453446
[<Fact>]
454-
let ``ErasingConstructorProvider generates for .NET Standard 2.0 correctly``() : unit =
447+
let ``ErasingConstructorProvider generates for netstandard20 correctly``() : unit =
455448
printfn "--------- Generating code for .NET Standard 2.0 ------"
456449
let res = testCrossTargeting (Targets.DotNetStandard20FSharpRefs()) (fun args -> new ErasingConstructorProvider(args)) [| |]
457450
Assert.False(res.Contains "[FSharp.Core, Version=3.259.4.1")

tests/BasicGenerativeProvisionTests.fs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
#if INTERACTIVE
2-
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
3-
#load "../src/ProvidedTypesTesting.fs"
4-
5-
#else
6-
module TPSDK.Tests.BasicGenerativeTests
7-
#endif
1+
module TPSDK.BasicGenerativeTests
82

93
open System
104
open System.IO

tests/FSharp.TypeProviders.SDK.Tests.fsproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>net5.0</TargetFramework>
55
<IsPackable>false</IsPackable>
6+
<LangVersion>5.0</LangVersion>
67
</PropertyGroup>
78
<ItemGroup>
89
<None Include="paket.references" />
@@ -17,6 +18,7 @@
1718
<Compile Include="GenerativeEnumsProvisionTests.fs" />
1819
<Compile Include="GenerativeInterfacesTests.fs" />
1920
<Compile Include="GenerativeAbstractClassesTests.fs" />
21+
<Compile Include="ReferencedAssemblies.fs" />
2022
</ItemGroup>
2123
<ItemGroup>
2224
<ProjectReference Include="..\src\FSharp.TypeProviders.SDK.fsproj" />

tests/GeneratedCodeTests.fs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
#if INTERACTIVE
2-
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
3-
#load "../src/ProvidedTypesTesting.fs"
4-
5-
#else
6-
71
module TPSDK.GeneratedCodeTests
8-
#endif
92

103
open System
114
open System.Reflection

tests/GeneratedOpTests.fs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
#if INTERACTIVE
2-
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
3-
#load "../src/ProvidedTypesTesting.fs"
4-
5-
#else
6-
71
module TPSDK.GeneratedOpTests
8-
#endif
92

103
open System
114
open System.Reflection

tests/GenerativeAbstractClassesTests.fs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
#if INTERACTIVE
2-
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
3-
#load "../src/ProvidedTypesTesting.fs"
4-
5-
#else
6-
7-
module FSharp.TypeProviders.SDK.Tests.GenerativeAbstractClassesTests
8-
#endif
1+
module TPSDK.GenerativeAbstractClassesTests
92

103
#nowarn "760" // IDisposable needs new
114

tests/GenerativeEnumsProvisionTests.fs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
#if INTERACTIVE
2-
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
3-
#load "../src/ProvidedTypesTesting.fs"
4-
5-
#else
6-
71
module TPSDK.GenerativeEnumsProvisionTests
8-
#endif
92

103
#nowarn "760" // IDisposable needs new
114

tests/GenerativeInterfacesTests.fs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
#if INTERACTIVE
2-
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
3-
#load "../src/ProvidedTypesTesting.fs"
4-
5-
#else
6-
7-
module FSharp.TypeProviders.SDK.Tests.GenerativeInterfacesTests
8-
#endif
1+
module TPSDK.GenerativeInterfacesTests
92

103
#nowarn "760" // IDisposable needs new
114

tests/ProvidedTypesTesting.fs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,61 @@ open Microsoft.FSharp.Quotations
1515
open Microsoft.FSharp.Quotations.Patterns
1616
open Microsoft.FSharp.Reflection
1717

18+
module Utils =
19+
let isNull x = match x with null -> true | _ -> false
20+
21+
22+
/// Simulate a real host of TypeProviderConfig
23+
type internal DllInfo(path: string) =
24+
// Must have a property called FileName
25+
member _.FileName = path
26+
27+
/// Simulate a real host of TypeProviderConfig
28+
// Must be a type called TcImports
29+
type internal TcImports(bas: TcImports option, dllInfosInitial: DllInfo list) =
30+
// Must have a field called "dllInfos"
31+
let mutable dllInfos = dllInfosInitial
32+
member _.Base = bas
33+
member _.DllInfos = dllInfos
34+
member _.AddReferencedDlls paths = dllInfos <- dllInfos @ [ for p in paths -> DllInfo(p) ]
35+
1836
type internal Testing() =
1937

2038
/// Simulates a real instance of TypeProviderConfig
21-
static member MakeSimulatedTypeProviderConfig (resolutionFolder: string, runtimeAssembly: string, runtimeAssemblyRefs: string list, ?isHostedExecution, ?isInvalidationSupported) =
39+
static member MakeSimulatedTypeProviderConfigIncremental (resolutionFolder: string, runtimeAssembly: string, runtimeAssemblyRefs: string list, ?isHostedExecution, ?isInvalidationSupported) =
2240
let runtimeAssemblyRefs = (runtimeAssembly :: runtimeAssemblyRefs) |> List.distinct
23-
let cfg = TypeProviderConfig(fun _ -> failwith "SystemRuntimeContainsType is deprecated and should never be called")
41+
let cfg = TypeProviderConfig(fun _ -> false)
2442
cfg.IsHostedExecution <- defaultArg isHostedExecution false
2543
cfg.IsInvalidationSupported <- defaultArg isInvalidationSupported true
2644
cfg.ResolutionFolder <- resolutionFolder
2745
cfg.RuntimeAssembly <- runtimeAssembly
2846
cfg.ReferencedAssemblies <- Array.ofList runtimeAssemblyRefs
47+
let (?<-) cfg prop value =
48+
let ty = cfg.GetType()
49+
match ty.GetProperty(prop,BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic) with
50+
| null ->
51+
let fld = ty.GetField(prop,BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic)
52+
if fld = null then failwith ("expected TypeProviderConfig to have a property or field "+prop)
53+
fld.SetValue(cfg, value)|> ignore
54+
| p ->
55+
p.GetSetMethod(nonPublic = true).Invoke(cfg, [| box value |]) |> ignore
56+
57+
// Fake an implementation of SystemRuntimeContainsType the shape expected by AssemblyResolver.fs.
58+
let dllInfos = [yield DllInfo(runtimeAssembly); for r in runtimeAssemblyRefs do yield DllInfo(r)]
59+
let tcImports = TcImports(Some(TcImports(None,[])),dllInfos)
60+
let systemRuntimeContainsType = (fun (_s:string) -> if tcImports.DllInfos.Length = 1 then true else true)
61+
cfg?systemRuntimeContainsType <- systemRuntimeContainsType
62+
63+
//Diagnostics.Debugger.Launch() |> ignore
64+
Diagnostics.Debug.Assert(cfg.GetType().GetField("systemRuntimeContainsType",BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Instance) |> isNull |> not)
65+
Diagnostics.Debug.Assert(systemRuntimeContainsType.GetType().GetField("tcImports",BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Instance) |> isNull |> not)
66+
Diagnostics.Debug.Assert(typeof<TcImports>.GetField("dllInfos",BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Instance) |> isNull |> not)
67+
Diagnostics.Debug.Assert(typeof<TcImports>.GetProperty("Base",BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Instance) |> isNull |> not)
68+
Diagnostics.Debug.Assert(typeof<DllInfo>.GetProperty("FileName",BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Instance) |> isNull |> not)
69+
cfg, tcImports
70+
71+
static member MakeSimulatedTypeProviderConfig (resolutionFolder: string, runtimeAssembly: string, runtimeAssemblyRefs: string list, ?isHostedExecution, ?isInvalidationSupported) =
72+
let cfg, _ = Testing.MakeSimulatedTypeProviderConfigIncremental (resolutionFolder, runtimeAssembly, runtimeAssemblyRefs, ?isHostedExecution=isHostedExecution, ?isInvalidationSupported=isInvalidationSupported)
2973
cfg
3074

3175
/// Simulates a real instance of TypeProviderConfig and then creates an instance of the last

tests/ReferencedAssemblies.fs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module TPSDK.ReferencedAssemblies
2+
3+
open System.Reflection
4+
open Microsoft.FSharp.Core.CompilerServices
5+
open Xunit
6+
open ProviderImplementation.ProvidedTypes
7+
open ProviderImplementation.ProvidedTypesTesting
8+
9+
[<Fact>]
10+
// See https://github.com/dotnet/fsharp/issues/13710
11+
let ``test incremental target assemblies are available via simulated tcImports additions``() =
12+
let refs = Targets.DotNetStandard20FSharpRefs()
13+
let config, tcImports = Testing.MakeSimulatedTypeProviderConfigIncremental (resolutionFolder=__SOURCE_DIRECTORY__, runtimeAssembly="whatever.dll", runtimeAssemblyRefs=refs)
14+
use tp1 = new TypeProviderForNamespaces(config)
15+
let ctxt1 = tp1.TargetContext
16+
let ns = "TestNamespace"
17+
let thisAssembly = Assembly.GetExecutingAssembly()
18+
let myType = ProvidedTypeDefinition(thisAssembly, ns, "TestType", Some typeof<obj>)
19+
tp1.AddNamespace(ns, [myType] )
20+
21+
// The initial assembly references should include at least netstandard
22+
printfn "finding netstandard in targets..."
23+
Assert.True(ctxt1.GetTargetAssemblies() |> Array.exists (fun a -> a.GetName().Name = "netstandard"))
24+
25+
// We add a dummy assembly as an additional reference (we use the current assembly for no particular reason)
26+
tcImports.AddReferencedDlls [ thisAssembly.Location ]
27+
28+
// We now evaluate the static parameters, indicating the type provider actually starts to get used
29+
// This causes the type provider to do a one-off re-evaluation of the available referenced DLLs
30+
let ty = tp1.Namespaces.[0].GetTypes().[0]
31+
let tp2 = (tp1 :> ITypeProvider)
32+
let _dummy = tp2.GetStaticParameters(ty)
33+
34+
// After this, the asembly is available in the target assemblies
35+
printfn "finding thisAssembly in targets..."
36+
Assert.True(ctxt1.GetTargetAssemblies() |> Array.exists (fun a -> a.GetName().Name = thisAssembly.GetName().Name))
37+

0 commit comments

Comments
 (0)