Skip to content

Commit 4a09dd4

Browse files
authored
[nativeaot] Next steps for the NAOT runtime (#10476)
Context: #10461 A follow up to #10461 which adds some more compatibility with the "standard" MonoVM and CoreCLR runtime hosts, namely: * Support for setting environment variables via the environment file (`<AndroidEnvironment>` MSBuild item) * Support for setting **local** (that is, application level, not system level) system properties In addition to the above, it makes NativeAOT use static libc++ instead of the shared one, which not only shrinks the package size (`libc++_shared.so` is around 9mb), but it also makes startup slightly faster.
1 parent 32fcc8b commit 4a09dd4

File tree

18 files changed

+219
-105
lines changed

18 files changed

+219
-105
lines changed

samples/NativeAOT/NativeAOT.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@
1212
<!-- Only property required to opt into NativeAOT -->
1313
<PublishAot>true</PublishAot>
1414
</PropertyGroup>
15-
</Project>
15+
16+
<ItemGroup>
17+
<AndroidEnvironment Include="environment.txt" />
18+
</ItemGroup>
19+
</Project>

samples/NativeAOT/environment.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
naot.system.property=testing 1 2 3

samples/NativeAOT/run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fi
1919

2020
adb uninstall "${PACKAGE}" || true
2121
adb install -r -d --no-streaming --no-fastdeploy "${APK}"
22-
#adb shell setprop debug.mono.log default,assembly,timing=bare
22+
adb shell setprop debug.mono.log default,assembly,timing=bare
2323
adb logcat -G 128M
2424
adb logcat -c
2525
adb shell am start -S --user "0" -a "android.intent.action.MAIN" -c "android.intent.category.LAUNCHER" -n "${PACKAGE}/${ACTIVITY}" -W

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
7272
<CppCompilerAndLinker>clang++</CppCompilerAndLinker>
7373
<ObjCopyName>llvm-objcopy</ObjCopyName>
7474

75+
<!-- We must ensure this is `false`, as it would interfere with statically linking libc++ -->
76+
<LinkStandardCPlusPlusLibrary>false</LinkStandardCPlusPlusLibrary>
77+
7578
<!-- Example settings from: https://github.com/xamarin/xamarin-macios/blob/c43d4ea40bc777969e3b158cf46446df292d8449/dotnet/targets/Xamarin.Shared.Sdk.targets#L541-L550 -->
7679
<RunILLink>true</RunILLink>
7780
<!--
@@ -111,13 +114,19 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
111114
<TrimmerRootAssembly Include="@(_AndroidILLinkAssemblies->'%(Filename)')" Exclude="System.Private.CoreLib" TrimMode="All" />
112115
<!-- Passes linked assemblies to outer MSBuild tasks/targets -->
113116
<ResolvedFileToPublish Include="@(IlcCompileInput);@(_AndroidILLinkAssemblies)" RuntimeIdentifier="$(RuntimeIdentifier)" />
114-
<!-- Include libc++ -->
115-
<ResolvedFileToPublish Include="$(_NdkSysrootDir)libc++_shared.so" RuntimeIdentifier="$(RuntimeIdentifier)" />
117+
116118
<!-- Satellite assemblies -->
117119
<IlcSatelliteAssembly Include="$(_OuterIntermediateSatelliteAssembliesWithTargetPath)" />
118120

119121
<LinkerArg Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" />
120-
<LinkerArg Include="$(_NdkSysrootDir)libc++_shared.so" />
122+
123+
<!-- Include libc++ -->
124+
<LinkerArg Include="$(_NdkSysrootDir)libc++_static.a" />
125+
<LinkerArg Include="$(_NdkSysrootDir)libc++abi.a" />
126+
127+
<!-- This library conflicts with static libc++ -->
128+
<NativeLibrary Remove="$(IlcSdkPath)libstdc++compat.a" />
129+
<LinkerArg Remove="$(IlcSdkPath)libstdc++compat.a" />
121130

122131
<!-- Every p/invoke using the `xa-internal-api` "library" will be called directly -->
123132
<DirectPInvoke Include="xa-internal-api" />

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,11 @@ public void BasicApplicationPublishReadyToRun (bool isComposite, string rid)
159159
var helper = new ArchiveAssemblyHelper (apk, true);
160160
var abi = MonoAndroidHelper.RidToAbi (rid);
161161
Assert.IsTrue (helper.Exists ($"assemblies/{abi}/{assemblyName}.dll"), $"{assemblyName}.dll should exist in apk!");
162-
162+
163163
using var stream = helper.ReadEntry ($"assemblies/{assemblyName}.dll");
164164
stream.Position = 0;
165165
using var peReader = new System.Reflection.PortableExecutable.PEReader (stream);
166-
Assert.IsTrue (peReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory.Size > 0,
166+
Assert.IsTrue (peReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory.Size > 0,
167167
$"ReadyToRun image not found in {assemblyName}.dll! ManagedNativeHeaderDirectory should not be empty!");
168168
}
169169

@@ -193,9 +193,7 @@ public void NativeAOT ()
193193
];
194194
string [] nativeaot_files = [
195195
$"lib/arm64-v8a/lib{proj.ProjectName}.so",
196-
"lib/arm64-v8a/libc++_shared.so",
197196
$"lib/x86_64/lib{proj.ProjectName}.so",
198-
"lib/x86_64/libc++_shared.so",
199197
];
200198

201199
var intermediate = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath);

src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGeneratorCLR.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,10 +332,21 @@ protected override void Construct (LlvmIrModule module)
332332
module.Add (envVars);
333333
module.AddGlobalVariable ("app_environment_variable_contents", envVarsBlob, LlvmIrVariableOptions.GlobalConstant);
334334

335-
var sysProps = new LlvmIrGlobalVariable (systemProperties ?? new SortedDictionary<string, string>(), "app_system_properties") {
335+
// We reuse the same structure as for environment variables, there's no point in adding a new, identical, one
336+
var sysPropsBlob = new LlvmIrStringBlob ();
337+
List<StructureInstance<LlvmIrHelpers.AppEnvironmentVariable>> appSysProps = LlvmIrHelpers.MakeEnvironmentVariableList (
338+
Log,
339+
systemProperties,
340+
sysPropsBlob,
341+
appEnvironmentVariableStructureInfo
342+
);
343+
344+
var sysProps = new LlvmIrGlobalVariable (appSysProps, "app_system_properties") {
336345
Comment = " System properties defined by the application",
346+
Options = LlvmIrVariableOptions.GlobalConstant,
337347
};
338-
module.Add (sysProps, stringGroupName: "sysprop", stringGroupComment: " System properties name:value pairs");
348+
module.Add (sysProps);
349+
module.AddGlobalVariable ("app_system_property_contents", sysPropsBlob, LlvmIrVariableOptions.GlobalConstant);
339350

340351
DsoCacheState dsoState = InitDSOCache ();
341352
var app_cfg = new ApplicationConfigCLR {
@@ -347,7 +358,7 @@ protected override void Construct (LlvmIrModule module)
347358
number_of_runtime_properties = (uint)(runtimeProperties == null ? 0 : runtimeProperties.Count),
348359
package_naming_policy = (uint)PackageNamingPolicy,
349360
environment_variable_count = (uint)(environmentVariables == null ? 0 : environmentVariables.Count),
350-
system_property_count = (uint)(systemProperties == null ? 0 : systemProperties.Count * 2),
361+
system_property_count = (uint)(appSysProps.Count),
351362
number_of_assemblies_in_apk = (uint)NumberOfAssembliesInApk,
352363
number_of_shared_libraries = (uint)NativeLibraries.Count,
353364
bundled_assembly_name_width = (uint)BundledAssemblyNameWidth,

src/Xamarin.Android.Build.Tasks/Utilities/NativeAotEnvironmentNativeAssemblyGenerator.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ protected override void Construct (LlvmIrModule module)
2828
SortedDictionary<string, string>? systemProperties = null;
2929
if (envBuilder.SystemProperties.Count > 0) {
3030
systemProperties = new (envBuilder.SystemProperties, StringComparer.Ordinal);
31+
} else {
32+
systemProperties = new (StringComparer.Ordinal);
3133
}
3234

3335
var envVarsBlob = new LlvmIrStringBlob ();
@@ -47,6 +49,25 @@ protected override void Construct (LlvmIrModule module)
4749
};
4850
module.Add (envVars);
4951
module.AddGlobalVariable ("__naot_android_app_environment_variable_contents", envVarsBlob, LlvmIrVariableOptions.GlobalConstant);
52+
53+
// We reuse the same structure as for environment variables, there's no point in adding a new, identical, one
54+
var sysPropsBlob = new LlvmIrStringBlob ();
55+
List<StructureInstance<LlvmIrHelpers.AppEnvironmentVariable>> appSysProps = LlvmIrHelpers.MakeEnvironmentVariableList (
56+
Log,
57+
systemProperties,
58+
sysPropsBlob,
59+
appEnvironmentVariableStructureInfo
60+
);
61+
62+
var sysPropsCount = new LlvmIrGlobalVariable ((uint)appSysProps.Count, "__naot_android_app_system_property_count");
63+
module.Add (sysPropsCount);
64+
65+
var sysProps = new LlvmIrGlobalVariable (appSysProps, "__naot_android_app_system_properties") {
66+
Comment = " System properties defined by the application",
67+
Options = LlvmIrVariableOptions.GlobalConstant,
68+
};
69+
module.Add (sysProps);
70+
module.AddGlobalVariable ("__naot_android_app_system_property_contents", sysPropsBlob, LlvmIrVariableOptions.GlobalConstant);
5071
}
5172

5273
void MapStructures (LlvmIrModule module)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#pragma once
2+
3+
#include <xamarin-app.hh>
4+
#include <host/host-environment.hh>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#pragma once
2+
3+
#include <cerrno>
4+
#include <cstdlib>
5+
#include <cstring>
6+
#include <string_view>
7+
8+
#include <runtime-base/logger.hh>
9+
10+
struct AppEnvironmentVariable;
11+
12+
namespace xamarin::android {
13+
class HostEnvironment
14+
{
15+
public:
16+
static void init () noexcept;
17+
18+
[[gnu::flatten, gnu::always_inline]]
19+
static void set_variable (const char *name, const char *value) noexcept
20+
{
21+
log_debug (LOG_DEFAULT, " Variable {} = '{}'", optional_string (name), optional_string (value));
22+
if (::setenv (name, value, 1) < 0) {
23+
log_warn (LOG_DEFAULT, "Failed to set environment variable '{}': {}", name, ::strerror (errno));
24+
}
25+
}
26+
27+
[[gnu::flatten, gnu::always_inline]]
28+
static void set_variable (std::string_view const& name, std::string_view const& value) noexcept
29+
{
30+
set_variable (name.data (), value.data ());
31+
}
32+
33+
[[gnu::flatten, gnu::always_inline]]
34+
static void set_system_property (const char *name, const char *value) noexcept
35+
{
36+
// TODO: should we **actually** try to set the system property here? Would that even work? Needs testing
37+
log_debug (LOG_DEFAULT, " System property {} = '{}'", optional_string (name), optional_string (value));
38+
}
39+
40+
[[gnu::flatten, gnu::always_inline]]
41+
static auto lookup_system_property (std::string_view const& name, size_t &value_len,
42+
uint32_t const count, AppEnvironmentVariable const (&entries)[],
43+
const char (&contents)[]) noexcept -> const char*
44+
{
45+
if (count == 0) {
46+
return nullptr;
47+
}
48+
49+
for (size_t i = 0; i < count; i++) {
50+
AppEnvironmentVariable const& sys_prop = entries[i];
51+
const char *prop_name = &contents[sys_prop.name_index];
52+
if (name.compare (prop_name) != 0) {
53+
continue;
54+
}
55+
56+
const char *prop_value = &contents[sys_prop.value_index];
57+
value_len = strlen (prop_value);
58+
return prop_value;
59+
}
60+
61+
return nullptr;
62+
}
63+
64+
template<void (*setter)(const char *name, const char *value) noexcept> [[gnu::flatten, gnu::always_inline]]
65+
static void set_values (uint32_t const& count, AppEnvironmentVariable const (&entries)[], const char (&contents)[]) noexcept
66+
{
67+
for (size_t i = 0; i < count; i++) {
68+
AppEnvironmentVariable const& env_var = entries[i];
69+
const char *var_name = &contents[env_var.name_index];
70+
const char *var_value = &contents[env_var.value_index];
71+
72+
setter (var_name, var_value);
73+
}
74+
}
75+
};
76+
}

src/native/clr/include/xamarin-app.hh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,8 @@ extern "C" {
339339
[[gnu::visibility("default")]] extern const ApplicationConfig application_config;
340340
[[gnu::visibility("default")]] extern const AppEnvironmentVariable app_environment_variables[];
341341
[[gnu::visibility("default")]] extern const char app_environment_variable_contents[];
342-
[[gnu::visibility("default")]] extern const char* const app_system_properties[];
342+
[[gnu::visibility("default")]] extern const AppEnvironmentVariable app_system_properties[];
343+
[[gnu::visibility("default")]] extern const char app_system_property_contents[];
343344

344345
[[gnu::visibility("default")]] extern const char* const mono_aot_mode_name;
345346

0 commit comments

Comments
 (0)