The cached linker results can have multiple identical input assemblies (for
assemblies that show up in both the app and any app extensions), so make sure
we don't load those more than once.
Since the linker can process multiple apps/appex'es at the same time, it also
means it will put together all the required symbols found in _all_ assemblies.
This means that we need to filter out required symbols for other
apps/appex'es.
Change cache invalidation so that if any app extension's cache is invalid,
then invalidate the cache for the container app and all other app extensions.
This is the safest option when we're sharing code.
Don't use the global command line arguments to determine input, because that's
not the input we use for app extensions anymore.
Instead explicitly pass the input arguments when creating the cache.
Since neither mtouch nor mmmp is mkbundled anymore, the installed binary is in
fact a shell script.
This means that it's quite useless to check if the shell script has been
modified; instead check if the executing assembly has been modified (which
works now that we're not mkbundled anymore).
Allow creating temporary apps/extensions multiple times without re-creating temporary directories.
This makes it possible for tests to call CreateTemporaryApp|Extension multiple
times with different code, and have only the actual managed assembly change
between each time (which is useful for tests that verifies that cached builds
are used properly).
We must build each appex bundle before the container bundle, so that we can
compute the frameworks each appex the needs before bundling the container app.
Also there's no need to store the list of frameworks appex's need in a file,
since everything is now done in the same mtouch process.
Implement support for sharing both code and resources between app extensions
and their container app:
* AOT-compiled code. Each shared assembly is only AOT-compiled once, and if
the assembly is built to a framework or dynamic library, it will also only
be included once in the final app (as a framework or dynamic library in the
container app, referenced directly by the app extension). If the assemblies
are built to static objects there won't be any size improvements in the app,
but the build will be much faster, because the assemblies will only be AOT-
compiled once.
* Any resources related to managed assemblies (debug files, config files,
satellite assemblies) will be put in the container app only.
Since these improvements are significant, code sharing will be enabled by
default.
Test results
============
For an extreme test project with 7 extensions (embedded-frameworks)[1]:
with code sharing cycle 9 difference
build time 1m 47s 3m 33s -1m 46s = ~50% faster
app size 26 MB 131 MB -105 MB = ~80% smaller
For a more normal test project (MyTabbedApplication)[2] - this is a simple application with 1 extension:
with code sharing cycle 9 difference
build time 0m 44s 0m 48s -4s = ~ 8% faster
app size 23 MB 37 MB -15 MB = ~40% smaller
Another tvOS app with one extension also show similar gains (MyTVApp)[3]:
with code sharing cycle 9 difference
build time 0m 22s 0m 48s -26s = ~54% faster
app size 22 MB 62 MB -40 MB = ~65% smaller
[1]: https://github.com/rolfbjarne/embedded-frameworks
[2]: https://github.com/xamarin/xamarin-macios/tree/cycle9/msbuild/tests/MyTabbedApplication
[3]: https://github.com/xamarin/xamarin-macios/tree/cycle9/msbuild/tests/MyTVApp
Warn if mtouch loads an assembly from a different location than requested
(which might be because there are multiple assemblies with the same name).
Also rework the MT0023 check a bit by explicitly loading the root assembly
first, and then detecting if any loaded assemblies matches the root assembly.
This results in code that's a bit more obvious, and it also works correctly
with extensions (previously the entire MT0023 check was skipped for
extensions).
Allow the assembly build target name for frameworks to end with '.framework',
so that the following:
--assembly-build-target=@sdk=framework=Xamarin.Sdk.framework
doesn't end up creating Xamarin.Sdk.framework.framework.
If an extension is sharing code with the container app, then assemblies may be
placed in:
* The container app's Framework directory (for assemblies whose code is
shared, and the build action is 'framework'). We already handle this case.
* The container app's root directory (for assemblies whose code is shared, and
the build action is not 'framework'). This case we didn't handle, and we're
now fixing it.
* In the extension (for assemblies whose code is not shared). We already
handle this case.
Assemblies will not end up in the .monotouch-[32|64] subdirectory anymore
(unless they must because they're different), which means that it's not easy
to detect if an assembly really ends up in the subdirectory or not.
So modify tests to accept assemblies in either the root bundle directory, or
in the .monotouch-[32|64] subdirectory.
Assemblies can now be in frameworks.
Assemblies can also be in container apps (in their root directory, as well as
in frameworks in the container app).
So use the simplest brute-force method to find assemblies: look everywhere.