-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Please provide an API to locate an assembly file by name without loading #23546
Comments
Actually, almost the exact inverse of System.Runtime.Loader.AssemblyLoadContext.GetAssemblyName. |
(We don't seem to have a label for System.Runtime.Loader so tagging it as System.Runtime, before it gets auto-tagged as System.Reflection) A few thoughts:
|
@atsushikan I'm fine being in charge of the assembly binding policy, so long as I can delegate that burden to the BCL and runtime. You say this may not be the best use case, but what would the ideal alternative then be? That I manually duplicate some subset of the runtime's assembly loading behavior? |
Well in general, I'm used to thinking of tooling binding environment as a separate concept from the runtime binding environment (Think cross-compiler scenarios.) so mingling them raises red flags in my mind. But yeah, sometimes, it's just handy to say "the runtime's policy is good enough for the task at hand - just let me at it." |
cc: @kouvel |
I'm completely blocked here whether my utility targets .NET Core or .NET Framework. My console app is reading a .NET Standard 1.3 assembly which references an enum in When my console app is targeting When my console app is targeting I need to be unblocked on .NET Framework so that this can run as an MSBuild task rather than a console app, and I need to be unblocked on .NET Core because my .NET Core integration tests are failing. |
@atsushikan I get your point about cross-compiler scenarios and all, but I'm worried that I'm up against reverse-engineering military grade assembly binding logic on both .NET Framework and .NET Core. I shouldn't have to know about shared frameworks, the GAC, binding redirection, and whatever specialized logic and edge cases are handled on each platform. I need to be able to resolve assembly references from .NET Core, .NET Framework and .NET Standard assemblies– all three from a .NET Core process, and all three from a .NET Framework process. |
@joperezr, @kouvel, @karelz, just to set expectations, I consider this a System.Runtime.Loader request, not a Reflection request so other than the 2 cents I've already thrown in, I'm not planning on driving this further as I'm not the area owner for the loader. System.Runtime was the closest area match available. |
I tried what you described and I'm also seeing the same thing. I had to add this NuGet package: However, it looks like that those version redirects don't apply to ReflectionOnlyLoad and an assembly resolver may be necessary for that, which probably doesn't work for your scenario. So on netfx one thought is to use Assembly.Load, maybe in a separate AppDomain if you need to unload it later. CC @AlexGhiondea if he has any other guidance. Regarding using MetadataReader, I'm not familiar with it, but since many types are forwarded to other assemblies, it may be necessary to walk the forwards to find the actual type in, say, mscorlib. @russellhadley, I understand that your team owns the assembly loader now, is there someone who would be driving that area? |
Another thought is to publish the netstandard1.3 project so that all of the non-runtime dependencies are in a known location. For runtime dependencies, you could just use Assembly.Load. |
😞 I'm really trying to minimize overhead. Also, what if I'm an MSBuild task analyzing a .NET Core assembly? I don't want to load what .NET Framework would load, I want to follow the logic of whichever .NET Core runtime the assembly was compiled against. Same again, what if I'm a .NET Core console app analyzing a .NET Framework assembly? I don't want to load what .NET Core would load, I want to follow the logic of whichever .NET Framework version the assembly was compiled against. See the points in my starting comment for more reasons this is infeasible- not least of which is that I'm in-process with MSBuild and loading multiple versions of system assemblies.
That's very easy to do, but I'm still stuck on the part where I can't read the type forwards because I can't locate the file they're in. 😄 |
If it's a netstandard type, does it matter which runtime it's coming from? But if you need the type from the same runtime as the assembly being inspected, I don't see how the API you suggested adding would provide you with that location. The runtime you're running on doesn't know anything about the runtime the assembly being inspected is targeted for, or where that runtime would be available on the machine, it may not even be installed on the machine. Maybe the CLI tools could have some API surface that provides that info since they deal with runtimes and how to locate them. |
With .NET Standard assemblies we should be fine, but I already know I'm going to have to deal with multitargeted projects that target .NET Framework and I don't want to close off the possibility ot handling any platform target.
This is true, and I'm no longer sure how to solve the problem in a general fashion. Seems it would require a NuGet package shipping the resolution logic for each platform which sounds like a huge endeavor. |
What about:
At least then, any non-standard types would be covered by published binaries that you can easily inspect and the only remaining types would be (probably) netstandard types for which you can use Assembly.Load and the number of these assemblies should be very limited. ? |
That's an okay starting point, but that brings me back to analyzing .NET Standard and .NET Framework dlls (which don't have system DLLs published in the same folder) from MSBuild and also analyzing both from a .NET Core Console app. Problems with Assembly.Load:
So the API would be preferable. |
If I have to use Assembly.Load, the next best thing honestly would be shelling out to a transient .NET Framework exe to locate the assembly out of process. But I'm still not sure how I could respect binding redirects this way. |
Following that logic, either .NET Core has to know all the logic in .NET Framework (that's obviously a non-starter given how complex the logic is and given the code duplication it would lead to), or .NET Framework needs to expose hosting APIs, providing answer (which I think it does).
You need to know anyway how the app is supposed to be executed - how it is hosted, which config it uses, etc. You should be able to use it as input for the spin-off process. Overall, I think I understand your scenario. |
Perhaps I'll wait and see what comes of this discussion: https://github.com/dotnet/coreclr/issues/14263#issuecomment-335045286 |
Here's my use case for being "tied to the runtime's binding policy:" I've got a compiler that uses Trying to put things into a different AppDomain so they can be unloaded turns out to be problematic because of various quirks in how VS works. So what I need is to replace the external type system with something else, while keeping all observable behavior exactly the same. This means I need the method @jnm2 is asking for, which will let me resolve an assembly's dependencies into exactly the same ones that |
Isn't that solved by the |
@teo-tsirpanis I believe so. @elinor-fung or @vitek-karas Can we close this? |
Reading the above it seems that the actual ask is for the last item - a way within the context of the SDK to get the full list of assemblies which make up the application. Doing that is an SDK functionality and we should not be adding any API into the runtime for this. |
To answer @masonwheeler: That is basically what
Note that it's a NuGet package which is available for pretty any version of .NET. |
@vitek-karas Everything looks great right up until the point where it can't run any code from the assembly. And then the compiler comes crashing into a big brick wall because the entire metaprogramming system falls apart. |
@masonwheeler So you have a tool which runs as part of the build which wants to run at least parts of the app being built? I see only two ways around this:
The last alternative would be to have some kind of IL interpreter, but I'm not aware of any readily available (which doesn't mean it doesn't exist). But that would likely come with other limitations anyway. |
FWIW I have the exact same thing and it runs in-process within an MSBuild task if the build runs on modern .NET, and out-of-process if it runs on .NET Framework. I didn't place any constraints on the framework the compiled app targets; if it targets an incompatible framework, it will make a best-effort try to load the assembly and its dependencies. The dependencies are the same passed to Roslyn, minus the reference assemblies. |
Thanks @vitek-karas. Your answer makes sense to me. I don't really want to take the current environment into consideration for my use case, and of course there is no exact way to locate an assembly's dependencies the way the assembly's containing app would do so at runtime. The only path forward would be to build and improve heuristics as needed. Feel free to close when you consider the other folks' use cases here to be answered. |
Is any "what would happen in some other containing app" scenario actually being requested? My use case isn't for a hypothetical, but a fact about the current moment: "if I tried to load this assembly name right here right now in this process, what file would it resolve to?" |
@masonwheeler as an API request "what would a given name resolve to" is perfectly valid. But the above discussion is about the usefulness of such API. There are not that many scenario I can think of where the API is helpful. I thought that your scenario is a compiler like tool, which runs during app's build and tries to run parts of the app's code. In such case, loading the app's code into the compiler-like tool is a rather tricky proposition. And even if you decided to do it, you mention that you need to run that code, so why not load it fully as you'll need to run it anyway.
Please understand that for us to do a good job of designing a new API (and also prioritizing the work), it's best if we have a solid understanding of the scenarios where such API would be used. |
@vitek-karas Basically, there are two related but distinct use cases. One is to find files to be loaded in a metadata-only context. |
I'm using the MetadataReader to generate public API surface files such as https://pastebin.com/nLSzWPBA. For the most part, it's a breeze.
When it comes to deserializing enum attribute arguments, the System.Reflection.Metadata API requires me to provide the underlying type and then of course I want to be able to output that as the appropriate field name rather than as an integer cast to the enum's name. This requires me to look up an assembly on disk given (1) the path to the assembly I'm currently examining and (2) an AssemblyName instance which I create from the AssemblyReferenceHandle.
It's easy enough to use
Assembly.ReflectionOnlyLoad
and non-Metadata reflection API to locate the enum field, but I see evenReflectionOnlyLoad
as undesirable:Would you please expose an API which locates the assembly that would be loaded if the running process were the assembly being examined, given the
AssemblyName
, and passes back a file path which I can then open with the MetadataReader? Is there a better way to achieve what I want without this API, and without the list of issues that I see withLoad
andReflectionOnlyLoad
?I'm imagining it could be something like this:
[updated per comment]
Or if it could be useful to the location logic to know not only the base path but some metadata in the assembly that the assembly reference came from:
The text was updated successfully, but these errors were encountered: