-
Notifications
You must be signed in to change notification settings - Fork 128
Support "type granularity" option #854
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
Support "type granularity" option #854
Conversation
Your experience with approach 1 mirrors a post I made about the problems with link.xml awhile back. In that post I proposed new attributes that would be better suited than link.xml for scenarios like this. Just thought I'd mention that in case you were holding out hope for a more maintainable way to strip more than your approach 2 would allow. Your approach 2 is harmless enough to support in my opinion and does offer an effort/reward ratio that would not be filled by an attribute based api. |
The implementation looks good, nice and simple. I agree that there's a need for some kind of type-level granularity option - it has been suggested or requested a few times already (see also #799). My main concern is that we're introducing another linker "concept". We already have @vitek-karas and I were also talking about how this would influence the diagnostic analyzer we're working on. Now when the linker attempts to detect a pattern like |
I did originally start by adding a new I'm totally happy with doing it that way if you prefer. Let me know and I'll update this PR to work that way. However I do think the orthogonal flag approach will make this much more maintainable in the long run as you don't have to care about differing linking strategies in every place - there's just one concept of |
I kind of look at this as a quick solution which we used to do in the past and migrated away from it because it didn't give us sufficient size saving. Some of the problems can be solved on much more granular bases today but I agree for some of the problems we'll still need to come up with the right approach. Now, to your suggested solution. I'm fine to accept something like this but prefer to implement it in XML descriptors fully instead. They are the bazooka for these kinds of issues if someone does not want to do the extra work (btw, all frameworks dlls we worked with went through the same path). Instead of adding another logic entry point and mess with logical ordering what takes precedence. We could expand existing XML preserve logic with a new attribute which controls marking behaviour. For example The change on your side would be something like bellow with the flexibility to fully override any type with their logic. <linker>
<assembly fullname="Microsoft.AspNetCore.Components, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" preserve="methods" mark="referenced" >
</assembly>
</linker> |
@marek-safar I'm afraid I don't quite understand your comment. The config syntax you're suggesting doesn't seem to say anything about type-level marking granularity. Are you saying that Could you clarify? |
@marek-safar Thinking about this more, my best guess is that you mean the syntax for per-type linking would look something like the following: <linker>
<assembly fullname="Microsoft.AspNetCore.Components">
<type fullname="*" preserve="all" mark="referenced" />
</assembly>
</linker> That would make sense. Is that how it would work? Can you confirm also that it is (or would be) possible to specify |
It does. The XML descriptor already works on the hierarchical level. See for example this test https://github.com/mono/linker/blob/master/test/Mono.Linker.Tests.Cases/LinkXml/AssemblyWithPreserveAll.xml, https://github.com/mono/linker/blob/master/test/Mono.Linker.Tests.Cases/LinkXml/AssemblyWithPreserveAll.cs About |
OK, in that case the syntax |
@SteveSandersonMS I don't see that pattern ambiguous, you can have methods only inside types. Your example will work too as linker supports regex patterns for type names (it's just a bit more verbose). |
OK, thanks for clarifying! I guess what I saw as ambiguous is Regardless, it sounds clear now that you do expect that syntax to behave in the way we need. So, do you think this work could be scheduled to be done on the linker? Is there a particular milestone or ETA we could put this down for? It looks like this is outside the scope of what I can do a PR for, given how much our discussion has shown there are subtleties about how XML config is meant to be interpreted that I don't know. |
I am fine with |
I'm curious about the intended usage of this. Problems like these tend to be "viral" that is they span multiple assemblies (potentially the entire application), and so per-assembly settings can leave holes in the solution. For example in this case if I read it correctly any type which implements |
It sounds like we should take a step back and reevaluate the ideal solution. Going back to the points listed in approach (1).
I described an attribute that would provide almost this exact behavior. See in
While I did not list an attribute that would help with this case in my reply's on #797, I do think this is another case where an attribute would make sense. I want something similar for
Not sure what
addressable by something like the If approach 2 is feeling forced and not like a good fit, why not just shoot for the better approach? |
Thanks to help from @marek-safar we now understand this change is unnecessary, since the linker can already do this quite easily via XML config like the following: <?xml version="1.0" encoding="utf-8" ?>
<linker>
<assembly fullname="SomeAssemblyName">
<type fullname="*" preserve="all" required="false" />
</assembly>
</linker> So, I'll close this as there's no reason to have a separate option to get the same results as this. |
@lewing @marek-safar @rynowak Following our discussions this week, I've tried two different approaches to linking more from the
Microsoft.AspNetCore.*
assemblies in Blazor apps.Approach 1: Use XML configs
I created some new build tooling inside the aspnetcore repo that generates and embeds linker XML config files based on known patterns for Blazor. Its rules are:
IComponent
types completely, since their constructors and property-setters are used through reflection only[JSinterop]
methods, since they are called through reflection onlyEventArgs
subclasses, since their property setters are called through reflection onlyThis does work, and allows us to enable linking for
Microsoft.AspNetCore.*
assemblies. It doesn't require any new linker features. However, it has two main drawbacks:Mono.Cecil
and hard-code duplicate copies of some logic from the linker to generate method signature strings in exactly the same format as the linker expects. It also complicates our build process somewhat.IComponent
types completely, but only if there is a reference to the type somewhere in user code". Since we have no choice but to just preserve allIComponent
types completely, we end up with sizeable chains of references.Microsoft.AspNetCore.Components.Forms
assembly even though it contains no forms components, simply because one of the types in that assembly is referenced by a component inMicrosoft.AspNetCore.Components.Web
that is not actually being used by default.Approach 2: Define a new "type-level granularity" option
This is implemented by this PR, and the core logic is very simple. It's a per-assembly flag that means "if a type in this assembly is referenced at all, preserve that type completely". This is a perfect fit for common patterns in ASP.NET and Blazor applications, because for things like
IComponent
types and DI services, you will have a rooted reference to the type itself (e.g., in DI config) and not to any members in that type, but you do need to preserve those members.When enabling this for the
Microsoft.AspNetCore.*
assemblies, we get two major benefits compared with the XML approach:Microsoft.AspNetCore.Components.Forms
assembly completely.Based on this, I consider the "type-level granularity" option to be preferable.
Size comparisons
These numbers are for the Blazor StandaloneApp test case in the ASP.NET Core repo, which is pretty much identical to a File->New Blazor WebAssembly app, built in Release configuration.
blazor-wasm
branch)Note that for all of these, 0.95 MB of the compressed transferred data is things other than .NET dlls, so the percentage gains are higher if you only consider .dlls.
PR review notes
I know we didn't make a specific plan to add this feature to the linker. If you feel it's not an appropriate feature, or something is wrong with the implementation, I've very happy to discuss other options!
In particular, I tracked the "per-assembly granularity" option in a general-purpose dictionary of per-assembly flags, just to avoid over-specializing for this one feature. For example, on the command line you can pass
-f typegranularity MyApp.SomeAssembly
.This might be useful for other per-assembly flags in the future. However if you disagree and think it's over-generalized, I'm happy to take the
ActionFlags
concept out and reduce it to a specialized option like-usetypegranularity MyApp.SomeAssembly
.