Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 27 additions & 10 deletions dotnet/targets/Xamarin.Shared.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,10 @@
DependsOnTargets="_RunRidSpecificBuild"
>
<ItemGroup>
<!-- If we're creating a runtimeconfig.json file, then the resulting binary runtimeconfig.bin file can be put in the architecture-specific subdirectory -->
<_ArchitectureSpecificFiles Include="$(_RuntimeConfigurationFile)" Condition="'$(GenerateRuntimeConfigurationFiles)' != ''" />
<!-- If we're creating a runtimeconfig.json file, then the resulting binary runtimeconfig.bin file can be put in the architecture-specific subdirectory.
Only MonoVM ships this file; CoreCLR bakes the runtime configuration into the app as C code, and
NativeAOT embeds it into the executable at publish time (neither reads runtimeconfig.bin). -->
<_ArchitectureSpecificFiles Include="$(_RuntimeConfigurationFile)" Condition="'$(GenerateRuntimeConfigurationFiles)' != '' And '$(_XamarinRuntime)' == 'MonoVM'" />
</ItemGroup>

<MergeAppBundles
Expand Down Expand Up @@ -733,6 +735,13 @@
<!-- Set default ValidateObjectPointers value -->
<_ValidateObjectPointers Condition="'$(_ValidateObjectPointers)' == ''">false</_ValidateObjectPointers>

<!-- Only MonoVM ships the binary runtimeconfig format and reads it at startup (locating it by name
via RuntimeConfigurationFile). CoreCLR bakes the runtime configuration into the app as C code
(from the merged json, handed to the linker via RuntimeConfigurationFilePath), and NativeAOT
embeds it into the executable at publish time - neither ships nor reads a runtimeconfig file. -->
<_BundlerRuntimeConfigurationFile Condition="'$(_XamarinRuntime)' == 'MonoVM'">$(_RuntimeConfigurationFile)</_BundlerRuntimeConfigurationFile>
<_BundlerRuntimeConfigurationFilePath Condition="'$(_XamarinRuntime)' == 'CoreCLR' And '$(GenerateRuntimeConfigurationFiles)' == 'true'">$(_MergedRuntimeConfigFilePath)</_BundlerRuntimeConfigurationFilePath>

<_CustomLinkerOptions>
AreAnyAssembliesTrimmed=$(_AreAnyAssembliesTrimmed)
AssemblyName=$(AssemblyName).dll
Expand Down Expand Up @@ -782,7 +791,8 @@
@(ReferenceNativeSymbol -> 'ReferenceNativeSymbol=%(SymbolType):%(SymbolMode):%(Identity)')
RequireLinkWithAttributeForObjectiveCClassSearch=$(RequireLinkWithAttributeForObjectiveCClassSearch)
RequirePInvokeWrappers=$(_RequirePInvokeWrappers)
RuntimeConfigurationFile=$(_RuntimeConfigurationFile)
RuntimeConfigurationFile=$(_BundlerRuntimeConfigurationFile)
RuntimeConfigurationFilePath=$(_BundlerRuntimeConfigurationFilePath)
SdkDevPath=$(_SdkDevPath)
SdkRootDirectory=$(_XamarinSdkRootDirectory)
SdkVersion=$(_SdkVersion)
Expand Down Expand Up @@ -1564,6 +1574,9 @@

<_RuntimeConfigurationFile>runtimeconfig.bin</_RuntimeConfigurationFile>

<!-- The runtimeconfig.json merged with the companion runtimeconfig.dev.json (see _CreateRuntimeConfiguration). -->
<_MergedRuntimeConfigFilePath>$(DeviceSpecificIntermediateOutputPath)runtimeconfig.merged.json</_MergedRuntimeConfigFilePath>

<!-- Path to the file where Objective-C type map information is stored -->
<_TypeMapFilePath Condition="'$(_TypeMapFilePath)' == ''">$(_LinkerCacheDirectory)/type-map.txt</_TypeMapFilePath>

Expand Down Expand Up @@ -1595,12 +1608,9 @@

<Target Name="_CreateRuntimeConfiguration"
Inputs="$(ProjectRuntimeConfigFilePath);$(ProjectRuntimeConfigDevFilePath)"
Outputs="$(_ParsedRuntimeConfigFilePath)"
Outputs="$(_RuntimeConfigurationOutputFile)"
DependsOnTargets="GenerateBuildRuntimeConfigurationFiles;_ComputePublishLocation"
>
<PropertyGroup>
<_MergedRuntimeConfigFilePath>$(DeviceSpecificIntermediateOutputPath)runtimeconfig.merged.json</_MergedRuntimeConfigFilePath>
</PropertyGroup>
<ItemGroup>
<!-- List all the properties passed to xamarin_bridge_vm_initialize in xamarin_vm_initialize (in runtime.m) -->
<_RuntimeConfigReservedProperties Include="APP_PATHS" />
Expand Down Expand Up @@ -1629,7 +1639,7 @@
</ItemGroup>

<RuntimeConfigParserTask
Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true'"
Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true' And '$(_XamarinRuntime)' == 'MonoVM'"
RuntimeConfigFile="$(_MergedRuntimeConfigFilePath)"
OutputFile="$(_ParsedRuntimeConfigFilePath)"
RuntimeConfigReservedProperties="@(_RuntimeConfigReservedProperties)"
Expand Down Expand Up @@ -2543,10 +2553,17 @@
<_DylibPublishDir Condition="'$(SdkIsMobile)' == 'true'">$(_RelativeAppBundlePath)\</_DylibPublishDir>
<_DylibPublishDir Condition="'$(SdkIsDesktop)' == 'true'">$(_RelativeAppBundlePath)\Contents\$(_CustomBundleName)\</_DylibPublishDir>
<_ParsedRuntimeConfigFilePath Condition="'$(_ParsedRuntimeConfigFilePath)' == ''">$(DeviceSpecificIntermediateOutputPath)$(_RuntimeConfigurationFile)</_ParsedRuntimeConfigFilePath>

<!-- The final output of _CreateRuntimeConfiguration: only MonoVM ships the binary runtimeconfig format;
CoreCLR and NativeAOT use the merged json instead (baked into the app as C code by the linker for
CoreCLR, embedded into the executable at publish time for NativeAOT). -->
<_RuntimeConfigurationOutputFile Condition="'$(_XamarinRuntime)' != 'MonoVM'">$(_MergedRuntimeConfigFilePath)</_RuntimeConfigurationOutputFile>
<_RuntimeConfigurationOutputFile Condition="'$(_RuntimeConfigurationOutputFile)' == ''">$(_ParsedRuntimeConfigFilePath)</_RuntimeConfigurationOutputFile>
</PropertyGroup>
<ItemGroup>
<!-- Add runtimeconfig.bin to ResolvedFileToPublish so we process it in ComputeBundlePath and copy the file to the app bundle -->
<ResolvedFileToPublish Include="$(_ParsedRuntimeConfigFilePath)" Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true'">
<!-- Add runtimeconfig.bin to ResolvedFileToPublish so we process it in ComputeBundlePath and copy the file to the app bundle.
Only MonoVM ships this file (CoreCLR bakes the configuration into the app as C code, NativeAOT embeds it at publish time). -->
<ResolvedFileToPublish Include="$(_ParsedRuntimeConfigFilePath)" Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true' And '$(_XamarinRuntime)' == 'MonoVM'">
<RelativePath>$([System.IO.Path]::GetFileName('$(_ParsedRuntimeConfigFilePath)'))</RelativePath>
<PublishFolderType>Assembly</PublishFolderType>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
Expand Down
98 changes: 9 additions & 89 deletions runtime/coreclr-bridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@

#if defined (CORECLR_RUNTIME)

#include <sys/stat.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/mman.h>

#include "product.h"
#include "runtime-internal.h"
Expand Down Expand Up @@ -361,104 +359,29 @@
}

/**
* xamarin_bridge_decode_value:
*
* This implementation is a slightly modified copy (to make it compile) of mono_metadata_decode_value
* https://github.com/dotnet/runtime/blob/08a7b2382799082eedb94d70fca6c66eb75f2872/src/mono/mono/metadata/metadata.c#L1525
* The runtimeconfig.json 'configProperties' are baked into the app as C arrays at build time
* (see the generated main and Xamarin.Shared.Sdk.targets), and made available here through the
* xamarin_runtime_config_property_* globals. This is simpler than the mobile-runtimeconfig binary
* format used by MonoVM: no separate file to ship, mmap and decode at startup.
*/
guint32
xamarin_bridge_decode_value (const char *_ptr, const char **rptr)
{
const unsigned char *ptr = (const unsigned char *) _ptr;
unsigned char b = *ptr;
guint32 len;

if ((b & 0x80) == 0){
len = b;
++ptr;
} else if ((b & 0x40) == 0){
len = (guint32) ((b & 0x3f) << 8 | ptr [1]);
ptr += 2;
} else {
len = (guint32) (((b & 0x1f) << 24) |
(ptr [1] << 16) |
(ptr [2] << 8) |
ptr [3]);
ptr += 4;
}
if (rptr)
*rptr = (char*)ptr;

return len;
}

static char *
xamarin_read_config_string (const char **buf)
{
guint32 configLength = xamarin_bridge_decode_value (*buf, buf);
char *value = strndup (*buf, configLength);
*buf = *buf + configLength;
return value;
}

static void *
xamarin_mmap_runtime_config_file (size_t *length)
{
if (xamarin_runtime_configuration_name == NULL) {
LOG (PRODUCT ": No runtime config file provided at build time.\n");
return NULL;
}

char path [1024];
if (!xamarin_locate_app_resource (xamarin_runtime_configuration_name, path, sizeof (path))) {
LOG (PRODUCT ": Could not locate the runtime config file '%s' in the app bundle.\n", xamarin_runtime_configuration_name);
return NULL;
}

int fd = open (path, O_RDONLY);
if (fd == -1) {
LOG (PRODUCT ": Could not open the runtime config file '%s' in the app bundle: %s\n", path, strerror (errno));
return NULL;
}

struct stat stat_buf = { };
if (fstat (fd, &stat_buf) == -1) {
LOG (PRODUCT ": Could not stat the runtime config file '%s' in the app bundle: %s\n", path, strerror (errno));
close (fd);
return NULL;
}

*length = (size_t) stat_buf.st_size;
void *buffer = mmap (NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0);
close (fd);
return buffer;
}

// Input: the property keys + values passed to xamarin_bridge_vm_initialize
// Output: newly allocated arrays of property keys + values that include those passed to xamarin_bridge_vm_initialize together with those in the runtimeconfig.bin file
// Output: newly allocated arrays of property keys + values that include those passed to xamarin_bridge_vm_initialize together with those baked into the app from the runtimeconfig.json file
// Caller must free the allocated arrays + their elements
void
xamarin_bridge_compute_properties (int inputCount, const char **inputKeys, const char **inputValues, int* outputCount, const char ***outputKeys, const char ***outputValues)
{
size_t fd_len = 0;
const char *buf = (const char *) xamarin_mmap_runtime_config_file (&fd_len);
int runtimeConfigCount = 0;

if (buf != NULL)
runtimeConfigCount = (int) xamarin_bridge_decode_value (buf, &buf);
int runtimeConfigCount = xamarin_runtime_config_property_count;

// Allocate the output arrays
*outputCount = inputCount + runtimeConfigCount;
*outputKeys = (const char **) calloc ((size_t) *outputCount, sizeof (char *));
*outputValues = (const char **) calloc ((size_t) *outputCount, sizeof (char *));

// Read the runtimeconfig properties
// https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/docs/design/mono/mobile-runtimeconfig-json.md#the-encoded-runtimeconfig-format
// Copy the runtimeconfig properties baked into the app
for (int i = 0; i < runtimeConfigCount; i++) {
char *key = xamarin_read_config_string (&buf);
char *value = xamarin_read_config_string (&buf);
(*outputKeys) [i] = key;
(*outputValues) [i] = value;
(*outputKeys) [i] = strdup (xamarin_runtime_config_property_keys [i]);
(*outputValues) [i] = strdup (xamarin_runtime_config_property_values [i]);
}

// Copy the input properties
Expand All @@ -470,9 +393,6 @@
NSLog (@PRODUCT ": No name/value specified for runtime property %s=%s", inputKeys [i], inputValues [i]);
}
}

if (buf != NULL)
munmap ((void *) buf, fd_len);
}

#if !defined (NATIVEAOT)
Expand Down
7 changes: 7 additions & 0 deletions runtime/runtime.m
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@
#endif
const char *xamarin_runtime_configuration_name = NULL;

// The runtimeconfig.json 'configProperties' baked into the app as C arrays (CoreCLR only).
// These are set from the generated main (xamarin_setup_impl) and consumed by
// xamarin_bridge_compute_properties when initializing CoreCLR.
int xamarin_runtime_config_property_count = 0;
const char **xamarin_runtime_config_property_keys = NULL;
const char **xamarin_runtime_config_property_values = NULL;

enum XamarinNativeLinkMode xamarin_libmono_native_link_mode = XamarinNativeLinkModeStaticObject;
const char **xamarin_runtime_libraries = NULL;

Expand Down
3 changes: 3 additions & 0 deletions runtime/xamarin/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ extern enum XamarinLaunchMode xamarin_launch_mode;
extern bool xamarin_supports_dynamic_registration;
#endif
extern const char *xamarin_runtime_configuration_name;
extern int xamarin_runtime_config_property_count;
extern const char **xamarin_runtime_config_property_keys;
extern const char **xamarin_runtime_config_property_values;
extern enum XamarinNativeLinkMode xamarin_libmono_native_link_mode;
extern const char** xamarin_runtime_libraries;

Expand Down
4 changes: 3 additions & 1 deletion tests/dotnet/UnitTests/BundleStructureTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,9 @@ internal static void CheckAppBundleContents (ApplePlatform platform, IEnumerable
expectedFiles.Add (Path.Combine (assemblyDirectory, "Mono.Options.dll"));
AddMultiRidAssembly (platform, expectedFiles, assemblyDirectory, "Touch.Client", runtimeIdentifiers, platform == ApplePlatform.MacOSX || (platform == ApplePlatform.MacCatalyst && !isReleaseBuild), includeDebugFiles: includeDebugFiles);
AddMultiRidAssembly (platform, expectedFiles, assemblyDirectory, Path.GetFileNameWithoutExtension (Configuration.GetBaseLibraryName (platform)), runtimeIdentifiers, platform == ApplePlatform.MacOSX, includeDebugFiles: includeDebugFiles);
expectedFiles.Add (Path.Combine (assemblyDirectory, "runtimeconfig.bin"));
// For CoreCLR the runtime configuration is baked into the app as C code, so there's no runtimeconfig.bin file.
if (!isCoreCLR)
expectedFiles.Add (Path.Combine (assemblyDirectory, "runtimeconfig.bin"));

switch (platform) {
case ApplePlatform.iOS:
Expand Down
47 changes: 47 additions & 0 deletions tests/dotnet/UnitTests/RuntimeConfigurationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.IO;
using System.Linq;

#nullable enable

namespace Xamarin.Tests {
[TestFixture]
public class RuntimeConfigurationTest : TestBaseClass {
[Test]
[TestCase (ApplePlatform.MacOSX, "osx-arm64")]
public void BakedIntoGeneratedMain (ApplePlatform platform, string runtimeIdentifiers)
{
// For CoreCLR the runtimeconfig.json 'configProperties' are baked into the app as C arrays in the
// generated main (and assigned to the xamarin_runtime_config_property_* globals), instead of shipping
// the binary runtimeconfig format and decoding it at startup. Here we build an app with a well-known
// config property (System.Globalization.Invariant, from InvariantGlobalization) and verify both that
// it ends up in the generated main and that there's no runtimeconfig.bin in the app bundle.
var project = "MySimpleApp";
Configuration.IgnoreIfIgnoredPlatform (platform);
Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers);

var project_path = GetProjectPath (project, platform: platform);
Clean (project_path);
var properties = GetDefaultProperties (runtimeIdentifiers);
properties ["InvariantGlobalization"] = "true";

DotNet.AssertBuild (project_path, properties);

var objDir = GetObjDir (project_path, platform, runtimeIdentifiers);
var generatedMains = Directory.GetFiles (objDir, "main.*.mm", SearchOption.AllDirectories);
Assert.That (generatedMains, Is.Not.Empty, "A generated main.mm must exist.");

foreach (var main in generatedMains) {
var contents = File.ReadAllText (main);
Assert.That (contents, Does.Contain ("xamarin_runtime_config_property_keys_array"), $"The generated main '{main}' must bake the runtime configuration properties.");
Assert.That (contents, Does.Contain ("\"System.Globalization.Invariant\""), $"The generated main '{main}' must contain the System.Globalization.Invariant property.");
}

var appPath = GetAppPath (project_path, platform, runtimeIdentifiers);
var runtimeConfigBin = Directory.GetFiles (appPath, "runtimeconfig.bin", SearchOption.AllDirectories);
Assert.That (runtimeConfigBin, Is.Empty, "No runtimeconfig.bin should be shipped for CoreCLR (the configuration is baked into the app).");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ Contents/Info.plist:
1,062 bytes (1.0 KB = 0.0 MB)
Contents/MacOS/SizeTestApp:
8,785,672 bytes (8,579.8 KB = 8.4 MB)
Contents/MonoBundle/runtimeconfig.bin:
1,896 bytes (1.9 KB = 0.0 MB)
Contents/PkgInfo:
8 bytes (0.0 KB = 0.0 MB)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ Contents/Info.plist:
1,062 bytes (1.0 KB = 0.0 MB)
Contents/MacOS/SizeTestApp:
2,739,336 bytes (2,675.1 KB = 2.6 MB)
Contents/MonoBundle/runtimeconfig.bin:
1,808 bytes (1.8 KB = 0.0 MB)
Contents/PkgInfo:
8 bytes (0.0 KB = 0.0 MB)
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,5 @@ Contents/MonoBundle/libSystem.Net.Security.Native.dylib:
151,904 bytes (148.3 KB = 0.1 MB)
Contents/MonoBundle/libSystem.Security.Cryptography.Native.Apple.dylib:
347,888 bytes (339.7 KB = 0.3 MB)
Contents/MonoBundle/runtimeconfig.bin:
1,445 bytes (1.4 KB = 0.0 MB)
Contents/PkgInfo:
8 bytes (0.0 KB = 0.0 MB)
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,5 @@ Contents/MonoBundle/libSystem.Net.Security.Native.dylib:
151,904 bytes (148.3 KB = 0.1 MB)
Contents/MonoBundle/libSystem.Security.Cryptography.Native.Apple.dylib:
347,888 bytes (339.7 KB = 0.3 MB)
Contents/MonoBundle/runtimeconfig.bin:
1,363 bytes (1.3 KB = 0.0 MB)
Contents/PkgInfo:
8 bytes (0.0 KB = 0.0 MB)
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,5 @@ Contents/MonoBundle/libSystem.Net.Security.Native.dylib:
151,904 bytes (148.3 KB = 0.1 MB)
Contents/MonoBundle/libSystem.Security.Cryptography.Native.Apple.dylib:
333,152 bytes (325.3 KB = 0.3 MB)
Contents/MonoBundle/runtimeconfig.bin:
1,848 bytes (1.8 KB = 0.0 MB)
Contents/PkgInfo:
8 bytes (0.0 KB = 0.0 MB)
2 changes: 0 additions & 2 deletions tests/dotnet/UnitTests/expected/MacOSX-NativeAOT-size.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,5 @@ Contents/MonoBundle/libSystem.Net.Security.Native.dylib:
151,904 bytes (148.3 KB = 0.1 MB)
Contents/MonoBundle/libSystem.Security.Cryptography.Native.Apple.dylib:
333,152 bytes (325.3 KB = 0.3 MB)
Contents/MonoBundle/runtimeconfig.bin:
1,766 bytes (1.7 KB = 0.0 MB)
Contents/PkgInfo:
8 bytes (0.0 KB = 0.0 MB)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ Info.plist:
1,128 bytes (1.1 KB = 0.0 MB)
PkgInfo:
8 bytes (0.0 KB = 0.0 MB)
runtimeconfig.bin:
1,889 bytes (1.8 KB = 0.0 MB)
SizeTestApp:
7,801,720 bytes (7,618.9 KB = 7.4 MB)
2 changes: 0 additions & 2 deletions tests/dotnet/UnitTests/expected/TVOS-NativeAOT-size.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ Info.plist:
1,128 bytes (1.1 KB = 0.0 MB)
PkgInfo:
8 bytes (0.0 KB = 0.0 MB)
runtimeconfig.bin:
1,808 bytes (1.8 KB = 0.0 MB)
SizeTestApp:
2,702,696 bytes (2,639.4 KB = 2.6 MB)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ Info.plist:
1,152 bytes (1.1 KB = 0.0 MB)
PkgInfo:
8 bytes (0.0 KB = 0.0 MB)
runtimeconfig.bin:
1,888 bytes (1.8 KB = 0.0 MB)
SizeTestApp:
8,995,264 bytes (8,784.4 KB = 8.6 MB)
2 changes: 0 additions & 2 deletions tests/dotnet/UnitTests/expected/iOS-NativeAOT-size.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ Info.plist:
1,152 bytes (1.1 KB = 0.0 MB)
PkgInfo:
8 bytes (0.0 KB = 0.0 MB)
runtimeconfig.bin:
1,808 bytes (1.8 KB = 0.0 MB)
SizeTestApp:
2,703,032 bytes (2,639.7 KB = 2.6 MB)
Loading
Loading