Skip to content
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

Ship a .framework version of Mono #53370

Merged
merged 24 commits into from
Jul 8, 2021
Merged

Conversation

directhex
Copy link
Member

This is based on initial work by @akoeplinger, who did most of the CMake bits

Fixes #42846

set_property(TARGET mono-framework APPEND_STRING PROPERTY LINKER_LANGUAGE CXX)
endif ()
set_property(TARGET mono-framework APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-compatibility_version -Wl,2.0 -Wl,-current_version -Wl,2.0")
target_sources(mono-framework PRIVATE "${mono-components-objects}")
Copy link
Member

@lambdageek lambdageek May 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is going to be a mono framework for ios/tvos/catalyst that always statically links in the full non-stub versions of all the components. Is that what we want? We always want to support hot reload and diagnostics, @rolfbjarne ?

Or should each component be its own dynamic .framework, too? (in which case... it would be upto the xamarin-macios embedding code to hook mono_dl_open and locate the components for us)

Or maybe one mono.framework with all the components, and one with only stubs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

statically links

I have not read the PR yet... but the goal of a .framework is that it's all dynamic (not static) linking

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea mono is built as a dynamic thing, but it will include all the components statically as part of it. I left some comments on the Paper.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't there a hard limit to the number of .frameworks an app can have?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We always want to support hot reload and diagnostics,

Debug

  • We do not care about the app bundle size [1]
    • IOW all features (including HR and diagnostics) can be available in the binary (and be enabled/disabled by the app if required)
  • We care about build and deploy times
    • dynamic linking (used for frameworks) makes the build faster
    • the framework (binary) will rarely change (deployed to sim/device once)

[1] unless it starts affecting build/deploy times :-)

Release

  • We care about the app bundle size
    • by default, we won't be using the .framework
      • using the static libraries .a allows the native linker / strip to remove a lot of code. Using the framework would create much larger apps.
    • a release .framework is needed for app extensions
      • why ? it's smaller to share a single, larger .framework than to have a (even partial) runtime in 2 (or more) executables - main app and one/several extension(s)
  • We do not care (within limits) about build times. Release builds being less common than debug ones
  • We do not care about deploy times. Since release build are smaller then generally deploy faster anyway.

I do not see "hot reload" needed for release builds. Not entirely sure about "diagnostics" ...

Modularity is more important for the static libraries. The majority of apps ships without extensions (but that could change).

Or should each component be its own dynamic .framework, too?

There's a limit (at least a suggested one, never seen a rejection) to the number of user frameworks that an app should have. However a framework can have several dynamic libraries inside it... so we could control what's in or out.

Not sure if that's really needed...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given there are quite a number of conditions and cases I wonder if it'd make more sense for Xamarin.iOS to create the .framework as part of the build when it is needed with just the right components? E.g. I've heard that we have some requirements to not ship the diagnostics TCP server code in release builds, even if it is not used.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can create Mono.framework, but afaik we can't create the corresponding dSYM, that has to be done on the machine that compiled the object files.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done some work relating to debug symbol generation in #53240

dsyms for anything we generate and put in a nupkg should be available from the MS debug symbol service

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rolfbjarne we do have the dSYM's and can put them into the runtime pack (or the symbols nuget), would you mind trying if you can create the .framework based off of that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akoeplinger I created a very simple test case, and I was able to create a .framework and the corresponding .framework.dSYM from a .dylib and its corresponding .dylib.dSYM. I ran an executable that consumed the .framework in lldb, and lldb was able to symbolicate functions in the .framework. I wasn't able to make macOS' crash reporter symbolicate the framework functions though, so I'm not sure if the end result is actually correct, or if it just happens to work in lldb. One point is that Apple identifies .dSYM directories using a UUID (which shows up in the crash report), and if we create the .framework.dSYM from the .dylib.dSYM, those share the same UUID, and potentially things break.

Copy link
Contributor

@spouliot spouliot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the PR itself it's not clear to me what ends up inside that directory ?

Any chance for someone to zip this up and attach it here ? thanks!

@directhex
Copy link
Member Author

@spouliot

unzip -lf /Users/directhex/Projects/runtime/artifacts/packages/Debug/Shipping/Microsoft.NETCore.App.Runtime.Mono.ios-arm64.6.0.0-dev.nupkg | grep mo
no-framework
      765  05-27-2021 16:30   runtimes/ios-arm64/native/mono-framework.framework/Info.plist
  8100608  05-27-2021 16:30   runtimes/ios-arm64/native/mono-framework.framework/mono-framework

Except it's different on Catalyst, which is why Catalyst builds are breaking (I'm not accounting for stuff winding up in a more complex tree)



if(TARGET_DARWIN)
add_library(mono-framework SHARED $<TARGET_OBJECTS:monosgen-objects>)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a conditional flag so its only built when building from mono.proj ?

@lambdageek
Copy link
Member

lambdageek commented Jun 18, 2021

We had a call about runtime components, and basically I think this is what we want:

iOS Simulator runtime pack should include:

  1. Mono.framework that includes all the components linked into the libmono that is in the framework. (can do dynamic linking on the simulator, we can put shared libs of the components into the Mono.framework so we build the runtime the same way for .framework and non-.framework)
  2. the same native directory as today (libmono built with dynamic component support, native libs, shared libraries of the components) to be used if users choose non-default options

iOS Device runtime pack should include:

  1. Mono.framework for release/publish (libmono built with static component support and with only component stubs linked into it) that will be used for the app extension scenario that Sebastien outlined.
  2. Mono.framework for debug (libmono built with static component support and with all the components, not the stubs, linked into it)
  3. the same native directory as today (libmono, native libs, static libs of the components and of the stubs) to be used by default for release builds that don't need to share mono between an app and extensions. (Usually they will link the component stubs)

@directhex
Copy link
Member Author

@lambdageek @rolfbjarne okay, how's that?

@lewing
Copy link
Member

lewing commented Jul 6, 2021

Are you sure about using Zip, a format which doens't really handle symlinks

The zip format handles symlinks just fine in my experience. We already use it to compress frameworks into a zip file when dealing with user frameworks in binding libraries.

It's the BCL's API for working with zip files that can't handle symlinks, the command line zip tool works.

It would be really nice to fix this. It is causing problems in several places that are difficult to resolve any other way.

@directhex
Copy link
Member Author

@lewing probably comes under the heading of #54253

@lewing
Copy link
Member

lewing commented Jul 6, 2021

@lewing probably comes under the heading of #54253

and NuGet/Home#10734 for some of the other cases

@directhex
Copy link
Member Author

Official build working as planned

unzip -lf ~/Downloads/Microsoft.NETCore.App.Runtime.Mono.maccatalyst-arm64.6.0.0-preview.7.21357.6.nupkg | grep fram
      763  07-07-2021 14:17   runtimes/maccatalyst-arm64/native/Mono.release.framework/Info.plist
  6475728  07-07-2021 14:21   runtimes/maccatalyst-arm64/native/Mono.release.framework/Mono
      761  07-07-2021 14:17   runtimes/maccatalyst-arm64/native/Mono.debug.framework/Info.plist
  6188776  07-07-2021 14:21   runtimes/maccatalyst-arm64/native/Mono.debug.framework/Mono

@@ -381,6 +381,42 @@ if(NOT DISABLE_SHARED_LIBS)
add_library(monosgen-shared-dac SHARED "mini-windows-dlldac.c")
set_target_properties(monosgen-shared-dac PROPERTIES OUTPUT_NAME ${MONO_SHARED_LIB_NAME}-dac)
endif()

if(BUILD_DARWIN_FRAMEWORKS)
if(TARGET_DARWIN)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be TARGET_DARWIN AND STATIC_COMPONENTS.
I think this version of the code is only right if the components are static archives, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we should never build a .framework in the case of .dylib components? Didn't you say iOS Simulator should ship dylibs & build against stubs in #53370 (comment) ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I meant to me this looks like the code only supports the static components case. I assumed a dynamic components version will be added in a follow-up PR. Maybe I misunderstood what's happening here.

Copy link
Member

@lambdageek lambdageek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, LGTM.

I think there are a couple of follow-up things we need to do (/cc @steveisok):

  1. i'm still unclear if this supports dynamic (.dylib) components inside the .framework (which we want for the simulator case). if it doesn't that's a follow-up item (together with switching simulator runs to using dynamic components)
  2. We should update AppleAppBuilder to support frameworks and add a functional test.

@directhex
Copy link
Member Author

@lambdageek we can't afford to wait on Rolf returning before iterating on this, given the snap date, so I'll merge it as-is and we can follow up for any changes as it's shown they're needed.

Of your two followup concerns:

  1. Dynamic components are copied into the .framework if they exist, but it's not clear they're in the correct path to be usable (I can't find a consistent reference on the correct path for dylibs in iOS-style unversioned frameworks)
  2. Not included in this PR, will need to be future work

@directhex directhex merged commit d841f5e into dotnet:main Jul 8, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Aug 7, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mono: the runtime packages for .NET 6+ do not contain the runtime as a .framework for iOS/tvOS/watchOS
9 participants