-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
[.NET9 + WASM] LinkerConfig.xml no longer respected?!? #110181
Comments
I'm not aware of any changes around warning suppressions between 8 and 9. Blazor WASM defaults to setting the documented
The warnings that are generated have no relationship with the LinkerConfig.xml file. Consider: class Program
{
static int Main()
{
return Activator.CreateInstance(Type.GetType(Console.ReadLine())).GetHashCode();
}
} Above is going to generate a warning because analysis doesn't know what type this is going to ask about, since we literally We cannot "If it's safe to trim off code then do so, if not then leave it.". You as a user requested trimming. The tool says it will probably produce something broken because we don't know what type name the user types. Maybe you specified all the types the user can type in your LinkConfig.xml. Maybe you didn't. The tool has no clue, there's no way to connect that XML to here. We could in lieu of generating the warning just not trim anything at all, but then people would file bugs saying they requested trimming and nothing happened (the tool literally doesn't know what to keep so it just must keep everything). If you don't want trimming you can just leave it off. If you don't mind the app is probably broken, you can suppress the warnings with the documented switch. |
@MichalStrehovsky thanks for getting back to me. Let me try to sort the feedback you provided:
|
Can you be more specific about what you mean by this?
I'm not sure i understand. Let's take the example I posted above. The suspicious code is Type.GetType(Console.ReadLine()). The suspicious code is not getting trimmed at all. We keep the entire method intact, nothing is touched. What does get trimmed is everything else. And that's the problem the warning is telling you. If someone types System.Net.Http.WebClient to the ReadLine call, we probably trimmed it and the type won't be found, returning null. The only way to implement your proposal would be instead of emitting a warning, to keep everything. But I don't see much point in that - this is something you as a user can do too - you see a warning, decide you don't want to address it and just turn off trimming and problem solved. |
@MichalStrehovsky thanks again for your feedback.
Thanks for clarification. This was not clear to me so far. I now understand that code which throws warnings in fact is not trimmed at all.
Sure, if you unzipped the repo you will find these lines in the file
... which means I'm excluding the assembly However, when you run Why do I get that warning although the |
@MichalStrehovsky to clarify: so I wouldn't even need Is my understanding correct? |
With net9, we're using the official tool chain now, and the original behavior applies, which includes linker validations which were enabled in net8 for blazor. We have linker warnings that are showing up inside Uno assemblies which we are fixing for an upcoming release and @DierkDroth you can disable those warnings to get to the Uno net8 behavior. You only need to wait for the next uno update to be released. As I mentioned earlier, I don't think what you're reporting a bug that explicitly excluded assemblies still provide warnings, mainly because linker warnings are transitive based on what methods are using. |
To clarify here, the method where a warning originates will not be trimmed, but the warning is saying "there is a dependency here that I can't understand, and I might remove what the code needs". In this example, the trimmer sees a code path that calls To reiterate, trimmer warnings aren't saying "You could trim more if you do this", it's saying "I can't analyze this statically, but I'm still going to remove stuff and your app might break".
This means that the linker will not trim anything inside the assembly
You still need LinkerConfig.xml. Without it, the linker would aggressively trim everything that it doesn't see static dependencies for, and the app would be much more likely to fail. Trimming with a LinkerConfig.xml and suppressing warnings is telling the linker, "I've put everything in the LinkerConfig.xml that I'll need. Keep those assemblies/types/methods, and the app will work, even though you can't guarantee it." Regarding the rant section, the WASM trimmer is the exact same tool as the native C# trimmer. The problem is that UI and Web frameworks in C# rely heavily on reflection which cannot be statically analyzed. The framework authors do their best to provide LinkerConfig.xml files to keep everything that should be needed, and suppress warnings when they are confident that they have manually kept the metadata needed, but it can be fragile. |
@jtschuster thanks for providing your feedback.
This is exactly what I'm 'complaining' about: there then just is no way to tell if the exclusion actually worked or not - since there always will be the warnings, correct?
Actually, wait a minute: are you saying there could be legit C# code (= works natively no problem) but might fail in WASM even without any trimming involved at all ... and that's what these warnings are for?!?
Got it. Thanks, I understand that reasoning... |
@jtschuster |
The warnings can be fixed (see here) or suppressed by the author of the assembly with the UnconditionalSuppressMessageAttribute (see here), or you can set the PublishTrimmed property to false in your Wasm csproj: Exclusion isn't the right way to think about this; "fully preserved" is more accurate. The assembly in LinkerConfig.xml preserved, but other assemblies in the app will be trimmed. The code causing the warning can reflect over arbitrary types in the app from any assembly, so the preserving the current assembly doesn't guarantee that the metadata it requires will not be trimmed.
No, any warnings from the linker are only related to trimming. If you don't use the linker, you should have identical behaviors (or at least any warnings would come from the compiler instead).
It looks like the default Windows build doesn't run the linker at all. |
Thanks again @jtschuster. The confusion starts to clear up...
This actually concerns my again since - if I understand you correctly - preserving the assembly in Now, if that is true then the question is: what needed to be done to resolve the warnings? I admit I haven't looked into yet at all - but rather am digesting it from a conceptual aspect at this point... since this implies that 'ignoring' or 'masking away' these warnings bears the risk of unknown failure at runtime even when excluding/preserving the hosting assembly in |
Yes, that's accurate.
For this particular warning, you need to add a using System.Diagnostics.CodeAnalysis;
public sealed partial class MainPage : Page
{
public MainPage()
{
Activator.CreateInstance(TestType);
Content = new TextBlock { Text = "Hi there" };
}
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
public Type TestType
=> typeof(string);
} The DynamicallyAccessedMembersAttribute tells the trimmer that the members of any time assigned to this value must be preserved. The fixing warning documentation linked can provide more of an explanation of what's going on, but let me know if something there isn't clear. |
Closing the issue as expected behavior, but feel free to comment if you have anymore questions. |
Thanks @jtschuster for walking me through the issue. I will review my code for these (and other) warnings and will come back to in case... |
@jtschuster some quick feedback after I invested a few hours ripping through the 100s of warnings (from preserved/excluded assemblies) in my code. This feels like the old days of FxCop where after spending days and weeks on working through warnings you wonder if you actually had fixed any true issue at all... |
Do you have an example? I sometimes see people do things like this: class Program
{
public static int ExitCode => 0;
// This generates IL2070 warning since we're accessing an unknown property on an unknown type
static PropertyInfo GetUsedProperty(Type t, string name) => t.GetProperty(name)!;
static int Main()
{
Console.WriteLine(GetUsedProperty(typeof(Program), nameof(ExitCode)));
return ExitCode;
}
} And then go "I'm going to suppress this warning since this is only accessing properties that are already used in the app". Those people are then surprised to find out that trimming still removed the |
Here are some samples @MichalStrehovsky. Please don't forget that I do have all my app assemblies preserved/excluded and I'm not implementing any library which might be used by any 3rd party. This is a typical one: I control
Here is another one: I control
And yet another one: I control
Sure, I could rip through my code and flag all those 100s of warnings as "don't worry, I know what I'm doing here", but at the end of the day it would not improve the quality of my code (rare - but yet unseen - cases where an actual issue might be fixed aside...). Am I missing something here? |
Without knowing the implementation I can't be sure, but this one probably can be fixed by adding
This one cannot be fixed.
This one may be fixable depending on what the type of the var propValue = instance switch {
MyType1 => typeof(MyType1).GetProperty(propertyName).GetValue(instance),
MyType2 => typeof(MyType2).GetProperty(propertyName).GetValue(instance),
...
}; Otherwise, if you know you have manually preserved all possible metadata required, you can suppress this warning.
You should be doing everything you can to fix the warnings rather than manually preserving metadata and suppressing warnings. Ideally you would use dependencies that are trim compatible and fix each warning with annotations or restructuring the code. The guarantee of having no trim warnings is that the runtime behavior of the trimmed app will be the same as the untrimmed app. If that's not feasible, manual preservation and suppressions are a workaround without the same runtime behavior guarantees. If you make sure that you have manually preserved all the metadata that your code will need, your code may not change, but you can be more confident the app will work when trimmed. But again, you're already off the "happy path" for safe trimming, and there isn't the guarantee that runtime behavior will be the same as untrimmed, so it's up to you to determine the risk/reward of putting more effort into the workaround. If trimming is a requirement for your app, I'd strongly recommend going through each warning and fixing it, or at least thinking about all possible metadata the code would need and manually preserving it before suppressing the warning. If you're not as concerned about app size and would rather have your app "just work", you can always turn off trimming. |
Thanks again for your extensive reply @jtschuster Here is what I don't get: why should I invest the time "to make the linker happy" if I told the linker that it should not touch any (!) of my assemblies at all? To me this sounds like a waste of time (similar to the old FxCop days), since I'm absolutely sure that
Nevertheless the linker can and should trim any anything if feels could be trimmed off the UNO assemblies (in my particular case) and anything else in any other 3rd part assemblies (including .NET itself). |
The trimmer will still warn because the code in your preserved assemblies can reflect over any type in the app, from any assembly (including assemblies that are trimmed). For example, consider this code: class MyType
{
void DoThing(object myObject, string myPropertyName)
{
object result = myObject.GetType().GetProperty(myPropertyName).GetValue(myObject);
Activator.CreateInstance(Globals.GetType(WorkInstance.TypeName));
}
} You might know that |
@jtschuster thanks. Yes, I now understand why the warnings are there even on my preserved/excluded assemblies. However, what I'm challenging (still) is, which I should not simply ignore those warnings in my scenario where all the types/classes involved are in my custom assemblies and I'm preserving all of my assemblies anyway. |
You can always just suppress all the warnings. The warnings are there and enabled by default because trimming is potentially a destructive operation. Whenever there is a warning, it means trimming was potentially destructive because the thing the flagged reflection operates on might have been removed and we don't know what. By all means you can just specify the documented I would not do it in a shipping project. Shipping projects change. Someone might add e.g. a field of type System.Drawing.Point to one of the classes that are serialized at some point in the future. Looks like an innocent change, but it breaks the invariant that the app only reflects on app types. App will be broken and there will be nothing to detect it. |
Thanks for your feedback @MichalStrehovsky. Like I said: I clearly separate my own custom assemblies - where I control the warning cases 100% (at least those which I have reviewed so far, which is why I'm making my point above by comparing it to the chore of going through gazillions of FxCop warnings) and which all are excluded - and the 3rd party assemblies, including .NET where I liked to keep trimming active |
Description
Originally I posted this issue on the Platform UNO GitHub. However, I learnt from @jeromelaban (CTO, Platform UNO) that this is a .NET runtime issue which is why I'm posting this here. Please relocate this issue as your see need and fit ... thanks.
After upgrading .NET8 -> .NET9 I get tons of linker warnings on my code although all my assemblies are (still) excluded in
LinkerConfig.xml
.Please see repo below:
LinkerConfig.xml
Note: there are a couple of warnings resulting from the UNO assemblies. Please ignore those for now, but focus on the single warning resulting from my custom repo code.
Minimal.9.zip
Here is my question:
Why am getting the warning as my custom assembly is excluded in
LinkerConfig.xml
? How could I now be sure that the exclusion works as expected? This makes no sense to me. If I wanted to know what warnings my custom assembly had, then I would just remove the exclusion, no? The current situation just causes confusion to the user and should be rectified.< rant on >
Also: why do I get these warnings at all? Why can't the WASM linker "just shut up and do its job" similar to the linker technology with the native C# stack? As a user I don't want to be bothered with that stuff. If it's safe to trim off code then do so, if not then leave it. There still could be an option for the experienced/interested user like 'ok mister super-duper-user, here you could trim your binary even more if you did this' ... but as an ordinary user I just "don't give a sh...". I strongly recommend providing the same level of convenience with the WASM linker as with the linker in the native C# stack.
< rant off >
What am I missing here?
Reproduction Steps
please see above
Expected behavior
please see above
Actual behavior
please see above
Regression?
I think so
Known Workarounds
No response
Configuration
No response
Other information
No response
The text was updated successfully, but these errors were encountered: