Skip to content

Workaround for target assemblies where packages with transitive references contain type providers #388

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 17, 2022
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
1 change: 1 addition & 0 deletions src/FSharp.TypeProviders.SDK.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<Description>The core implementation of an F# type provider</Description>
<Tags>F# fsharp typeprovider</Tags>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<LangVersion>5.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
Expand Down
215 changes: 169 additions & 46 deletions src/ProvidedTypes.fs

Large diffs are not rendered by default.

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

/// Get the list of referenced assemblies determined by the type provider configuration
member ReferencedAssemblyPaths: string list

/// Get the resolved referenced assemblies determined by the type provider configuration
member GetTargetAssemblies : unit -> Assembly[]

Expand Down Expand Up @@ -509,8 +506,7 @@ type TypeProviderForNamespaces =

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

Expand Down Expand Up @@ -557,4 +553,3 @@ module internal AssemblyReader =

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

824 changes: 183 additions & 641 deletions templates/content/basic/paket.lock

Large diffs are not rendered by default.

9 changes: 1 addition & 8 deletions tests/BasicErasedProvisionTests.fs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
#if INTERACTIVE
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
#load "../src/ProvidedTypesTesting.fs"

#else

module TPSDK.BasicErasedTests
#endif

open System
open System.Reflection
Expand Down Expand Up @@ -451,7 +444,7 @@ type ErasingProviderWithCustomAttributes (config : TypeProviderConfig) as this =
this.AddNamespace(ns, createTypes())

[<Fact>]
let ``ErasingConstructorProvider generates for .NET Standard 2.0 correctly``() : unit =
let ``ErasingConstructorProvider generates for netstandard20 correctly``() : unit =
printfn "--------- Generating code for .NET Standard 2.0 ------"
let res = testCrossTargeting (Targets.DotNetStandard20FSharpRefs()) (fun args -> new ErasingConstructorProvider(args)) [| |]
Assert.False(res.Contains "[FSharp.Core, Version=3.259.4.1")
Expand Down
8 changes: 1 addition & 7 deletions tests/BasicGenerativeProvisionTests.fs
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
#if INTERACTIVE
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
#load "../src/ProvidedTypesTesting.fs"

#else
module TPSDK.Tests.BasicGenerativeTests
#endif
module TPSDK.BasicGenerativeTests

open System
open System.IO
Expand Down
2 changes: 2 additions & 0 deletions tests/FSharp.TypeProviders.SDK.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>5.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<None Include="paket.references" />
Expand All @@ -17,6 +18,7 @@
<Compile Include="GenerativeEnumsProvisionTests.fs" />
<Compile Include="GenerativeInterfacesTests.fs" />
<Compile Include="GenerativeAbstractClassesTests.fs" />
<Compile Include="ReferencedAssemblies.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\FSharp.TypeProviders.SDK.fsproj" />
Expand Down
7 changes: 0 additions & 7 deletions tests/GeneratedCodeTests.fs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
#if INTERACTIVE
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
#load "../src/ProvidedTypesTesting.fs"

#else

module TPSDK.GeneratedCodeTests
#endif

open System
open System.Reflection
Expand Down
7 changes: 0 additions & 7 deletions tests/GeneratedOpTests.fs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
#if INTERACTIVE
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
#load "../src/ProvidedTypesTesting.fs"

#else

module TPSDK.GeneratedOpTests
#endif

open System
open System.Reflection
Expand Down
9 changes: 1 addition & 8 deletions tests/GenerativeAbstractClassesTests.fs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
#if INTERACTIVE
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
#load "../src/ProvidedTypesTesting.fs"

#else

module FSharp.TypeProviders.SDK.Tests.GenerativeAbstractClassesTests
#endif
module TPSDK.GenerativeAbstractClassesTests

#nowarn "760" // IDisposable needs new

Expand Down
7 changes: 0 additions & 7 deletions tests/GenerativeEnumsProvisionTests.fs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
#if INTERACTIVE
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
#load "../src/ProvidedTypesTesting.fs"

#else

module TPSDK.GenerativeEnumsProvisionTests
#endif

#nowarn "760" // IDisposable needs new

Expand Down
9 changes: 1 addition & 8 deletions tests/GenerativeInterfacesTests.fs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
#if INTERACTIVE
#load "../src/ProvidedTypes.fsi" "../src/ProvidedTypes.fs"
#load "../src/ProvidedTypesTesting.fs"

#else

module FSharp.TypeProviders.SDK.Tests.GenerativeInterfacesTests
#endif
module TPSDK.GenerativeInterfacesTests

#nowarn "760" // IDisposable needs new

Expand Down
48 changes: 46 additions & 2 deletions tests/ProvidedTypesTesting.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,61 @@ open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Reflection

module Utils =
let isNull x = match x with null -> true | _ -> false


/// Simulate a real host of TypeProviderConfig
type internal DllInfo(path: string) =
// Must have a property called FileName
member _.FileName = path

/// Simulate a real host of TypeProviderConfig
// Must be a type called TcImports
type internal TcImports(bas: TcImports option, dllInfosInitial: DllInfo list) =
// Must have a field called "dllInfos"
let mutable dllInfos = dllInfosInitial
member _.Base = bas
member _.DllInfos = dllInfos
member _.AddReferencedDlls paths = dllInfos <- dllInfos @ [ for p in paths -> DllInfo(p) ]

type internal Testing() =

/// Simulates a real instance of TypeProviderConfig
static member MakeSimulatedTypeProviderConfig (resolutionFolder: string, runtimeAssembly: string, runtimeAssemblyRefs: string list, ?isHostedExecution, ?isInvalidationSupported) =
static member MakeSimulatedTypeProviderConfigIncremental (resolutionFolder: string, runtimeAssembly: string, runtimeAssemblyRefs: string list, ?isHostedExecution, ?isInvalidationSupported) =
let runtimeAssemblyRefs = (runtimeAssembly :: runtimeAssemblyRefs) |> List.distinct
let cfg = TypeProviderConfig(fun _ -> failwith "SystemRuntimeContainsType is deprecated and should never be called")
let cfg = TypeProviderConfig(fun _ -> false)
cfg.IsHostedExecution <- defaultArg isHostedExecution false
cfg.IsInvalidationSupported <- defaultArg isInvalidationSupported true
cfg.ResolutionFolder <- resolutionFolder
cfg.RuntimeAssembly <- runtimeAssembly
cfg.ReferencedAssemblies <- Array.ofList runtimeAssemblyRefs
let (?<-) cfg prop value =
let ty = cfg.GetType()
match ty.GetProperty(prop,BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic) with
| null ->
let fld = ty.GetField(prop,BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic)
if fld = null then failwith ("expected TypeProviderConfig to have a property or field "+prop)
fld.SetValue(cfg, value)|> ignore
| p ->
p.GetSetMethod(nonPublic = true).Invoke(cfg, [| box value |]) |> ignore

// Fake an implementation of SystemRuntimeContainsType the shape expected by AssemblyResolver.fs.
let dllInfos = [yield DllInfo(runtimeAssembly); for r in runtimeAssemblyRefs do yield DllInfo(r)]
let tcImports = TcImports(Some(TcImports(None,[])),dllInfos)
let systemRuntimeContainsType = (fun (_s:string) -> if tcImports.DllInfos.Length = 1 then true else true)
cfg?systemRuntimeContainsType <- systemRuntimeContainsType

//Diagnostics.Debugger.Launch() |> ignore
Diagnostics.Debug.Assert(cfg.GetType().GetField("systemRuntimeContainsType",BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Instance) |> isNull |> not)
Diagnostics.Debug.Assert(systemRuntimeContainsType.GetType().GetField("tcImports",BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Instance) |> isNull |> not)
Diagnostics.Debug.Assert(typeof<TcImports>.GetField("dllInfos",BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Instance) |> isNull |> not)
Diagnostics.Debug.Assert(typeof<TcImports>.GetProperty("Base",BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Instance) |> isNull |> not)
Diagnostics.Debug.Assert(typeof<DllInfo>.GetProperty("FileName",BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Instance) |> isNull |> not)
cfg, tcImports

static member MakeSimulatedTypeProviderConfig (resolutionFolder: string, runtimeAssembly: string, runtimeAssemblyRefs: string list, ?isHostedExecution, ?isInvalidationSupported) =
let cfg, _ = Testing.MakeSimulatedTypeProviderConfigIncremental (resolutionFolder, runtimeAssembly, runtimeAssemblyRefs, ?isHostedExecution=isHostedExecution, ?isInvalidationSupported=isInvalidationSupported)
cfg

/// Simulates a real instance of TypeProviderConfig and then creates an instance of the last
Expand Down
37 changes: 37 additions & 0 deletions tests/ReferencedAssemblies.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module TPSDK.ReferencedAssemblies

open System.Reflection
open Microsoft.FSharp.Core.CompilerServices
open Xunit
open ProviderImplementation.ProvidedTypes
open ProviderImplementation.ProvidedTypesTesting

[<Fact>]
// See https://github.com/dotnet/fsharp/issues/13710
let ``test incremental target assemblies are available via simulated tcImports additions``() =
let refs = Targets.DotNetStandard20FSharpRefs()
let config, tcImports = Testing.MakeSimulatedTypeProviderConfigIncremental (resolutionFolder=__SOURCE_DIRECTORY__, runtimeAssembly="whatever.dll", runtimeAssemblyRefs=refs)
use tp1 = new TypeProviderForNamespaces(config)
let ctxt1 = tp1.TargetContext
let ns = "TestNamespace"
let thisAssembly = Assembly.GetExecutingAssembly()
let myType = ProvidedTypeDefinition(thisAssembly, ns, "TestType", Some typeof<obj>)
tp1.AddNamespace(ns, [myType] )

// The initial assembly references should include at least netstandard
printfn "finding netstandard in targets..."
Assert.True(ctxt1.GetTargetAssemblies() |> Array.exists (fun a -> a.GetName().Name = "netstandard"))

// We add a dummy assembly as an additional reference (we use the current assembly for no particular reason)
tcImports.AddReferencedDlls [ thisAssembly.Location ]

// We now evaluate the static parameters, indicating the type provider actually starts to get used
// This causes the type provider to do a one-off re-evaluation of the available referenced DLLs
let ty = tp1.Namespaces.[0].GetTypes().[0]
let tp2 = (tp1 :> ITypeProvider)
let _dummy = tp2.GetStaticParameters(ty)

// After this, the asembly is available in the target assemblies
printfn "finding thisAssembly in targets..."
Assert.True(ctxt1.GetTargetAssemblies() |> Array.exists (fun a -> a.GetName().Name = thisAssembly.GetName().Name))