Bug 22408 - [assembly:Preserve] does not prevent the entire assembly from being stripped from the application
Summary: [assembly:Preserve] does not prevent the entire assembly from being stripped ...
Status: NEW
Alias: None
Product: iOS
Classification: Xamarin
Component: General (show other bugs)
Version: 7.2.6
Hardware: PC Mac OS
: --- enhancement
Target Milestone: Future Cycle (TBD)
Assignee: Bugzilla
Depends on:
Reported: 2014-08-27 12:01 UTC by Adam Kemp
Modified: 2015-06-19 11:52 UTC (History)
3 users (show)

See Also:
Is this bug a regression?: ---
Last known good build:

Test solution (6.62 MB, application/zip)
2014-08-27 12:01 UTC, Adam Kemp

Description Adam Kemp 2014-08-27 12:01:03 UTC
Created attachment 7823 [details]
Test solution

It is now possible to mark an entire assembly with the Preserve attribute, but unfortunately this does not prevent that entire assembly from being left out of the application if nothing in that assembly is directly referenced. One of the listed reasons for this feature (http://blog.xamarin.com/howto-partially-linking-monotouch-applications/) was for unit tests, but the way that it works now doesn't actually work for our unit testing framework because every one of the test assemblies is entirely removed from the resulting application. We have to put in some code somewhere in our unit test runner app to reference a type in each of our unit test assemblies in order to make it work. This also messes up some of our dependency injection mechanisms in our regular app. Lastly, it makes it impossible to create a component that exists solely to force load an Objective-C library that isn't directly used by the C# code but still has side effects when loaded. For instance, the Reveal library inserts a server into the application for runtime view debugging. I want to be able to just reference a project to make that work, but the current implementation of the build tools makes that impossible. I have to do something in my application itself to force that assembly to be included.

To reproduce the issue see the attached solution. Build the project and then inspect the resulting .app. Notice that Reveal.dll is not included in the .app. It should be included. I have [assembly:Preserve] in the StructsAndEnums.cs file.
Comment 1 Sebastien Pouliot 2014-08-27 14:30:46 UTC
The fact that the assembly is removed is unrelated to the [Preserve] or even the linker. An unreferenced assembly is removed at compile time (mcs), so it's not part of the final assembly (which means it won't be part of the application).

[Preserve] works for Touch.Unit (the nunitlite framework we ship) because it's required to register all the test assemblies with the runner. That's simply requires something like:

> runner.Add (typeof (MonoTouchFixtures.Test.Test).Assembly);

that makes sure the assemblies are real/hard references, not "authorized if you need it" kind of references that the IDE (not .NET) let you create.

Processing unreferenced assemblies is more complex than it seems because:

- mtouch is not aware of (all of) them. It's input can be incomplete (since it's not in the assembly binary), so the unneeded references you see in the IDE are never seen;

- a lot of projects have "default" references to a bunch of BCL assemblies. Loading/processing them can be quite expensive (when we already know they are not used). That would negatively affect most projects (while requiring unreferenced assemblies is rather uncommon);

- it kind of break dependencies checks (since it's not really one) and that might affect some (future) optimizations for the AOT compiler.

IIRC there's another workaround (a side effect) when using --xml for the linker. If you refer an assembly in this fashion you're actually forcing it to be loaded* and that should hit the [Preserve] case.

* that does suffer from most of the previous cons, albeit the list of such assemblies is under your control so the impact should be minimal (and similar to using a hard/code-based reference).
Comment 2 Adam Kemp 2014-08-27 14:46:16 UTC
I may get terminology wrong, but my ask is simple: A referenced assembly should be included in the build output. Whatever you need to do to make that happen is fine with me. If it's not the linker then fix it in whichever component is responsible for removing assemblies it thinks are unused.

We even mark these assemblies as "Copy local" so there's no reason for any step of the build process to remove them. Our project file explicitly says to include it so it should be included. Removing those assemblies is a bug. That's what I'm reporting here.

FWIW, we don't use Touch.Unit. We have our own auto test suite, but our mechanism for working around this issue is very similar. That shouldn't be necessary, though. It's unfortunate that our test application has to reference each test assembly, but I understand that limitation entirely because of the dynamic code restrictions on iOS. However, referencing the assembly should be the only thing necessary to make it included in the output and usuable by the test framework. I shouldn't also have to edit code to register each test assembly. That's a workaround to this bug. If you fix the bug that code would be unnecessary.
Comment 3 Adam Kemp 2014-09-08 10:29:24 UTC
Here is another use case that is made more difficult by this behavior:


It's likely to become more common to have assemblies that are only referenced via XAML, but because of this behavior users have to insert hacky references to types in those assemblies just to force them to be included in the app.
Comment 4 Tommy Baggett 2015-06-19 11:52:37 UTC
+1 on this or any solution that will allow me to somehow let the compiler know to include a class even when it considers it unused. About six weeks of work on my part is mostly wasted due to this issue. See http://forums.xamarin.com/discussion/43867/possible-compiler-bug-with-multiple-levels-of-pcl-dependencies

Note You need to log in before you can comment on or make changes to this bug.