diff --git a/Directory.Build.props b/Directory.Build.props
index 7309f8db887f5..93b2837263a0a 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -149,6 +149,7 @@
$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)'))
$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)', 'publish'))
$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WorkloadBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'LibraryBuilder', 'Debug', '$(NetCoreAppToolCurrent)'))
$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoAOTCompiler', 'Debug', '$(NetCoreAppToolCurrent)'))
$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoTargetsTasks', 'Debug', '$(NetCoreAppToolCurrent)'))
$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'TestExclusionListTasks', 'Debug', '$(NetCoreAppToolCurrent)'))
@@ -162,6 +163,7 @@
$([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll'))
$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppHost', 'wasm', '$(Configuration)'))
$([MSBuild]::NormalizePath('$(WorkloadBuildTasksDir)', 'WorkloadBuildTasks.dll'))
+ $([MSBuild]::NormalizePath('$(LibraryBuilderDir)', 'LibraryBuilder.dll'))
$([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll'))
$([MSBuild]::NormalizePath('$(MonoTargetsTasksDir)', 'MonoTargetsTasks.dll'))
$([MSBuild]::NormalizePath('$(TestExclusionListTasksDir)', 'TestExclusionListTasks.dll'))
diff --git a/eng/testing/tests.android.targets b/eng/testing/tests.android.targets
index a8b279bf85317..2000806646e3c 100644
--- a/eng/testing/tests.android.targets
+++ b/eng/testing/tests.android.targets
@@ -3,12 +3,12 @@
$(BundleTestAppTargets);BundleTestAndroidApp
+
PrepareForAndroidBuildApp;$(AndroidBuildAppDependsOn);_CopyTestArchive
-
AndroidBuildApp
diff --git a/eng/testing/tests.ioslike.targets b/eng/testing/tests.ioslike.targets
index 8b46d2d90122d..c5a8972dd6bf0 100644
--- a/eng/testing/tests.ioslike.targets
+++ b/eng/testing/tests.ioslike.targets
@@ -30,6 +30,8 @@
$(_AOTBuildCommand) $(_AfterBuildCommands)
+
$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'msbuild', 'apple', 'build'))
+ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'msbuild', 'common'))
$(TargetOS)-
https://netcorenativeassets.blob.core.windows.net/resource-packages/external/macos/cmake/cmake-3.16.4-Darwin-x86_64.tar.gz
@@ -65,6 +66,7 @@
+
diff --git a/src/mono/msbuild/android/build/AndroidApp.InTree.targets b/src/mono/msbuild/android/build/AndroidApp.InTree.targets
index 2b374f617ae53..5e5e5606ebeef 100644
--- a/src/mono/msbuild/android/build/AndroidApp.InTree.targets
+++ b/src/mono/msbuild/android/build/AndroidApp.InTree.targets
@@ -4,8 +4,12 @@
+
+
+
+
diff --git a/src/mono/msbuild/android/build/AndroidApp.props b/src/mono/msbuild/android/build/AndroidApp.props
index 8bcbe0dcc2818..a3aea9c5a8a0d 100644
--- a/src/mono/msbuild/android/build/AndroidApp.props
+++ b/src/mono/msbuild/android/build/AndroidApp.props
@@ -4,6 +4,8 @@
true
true
+ <_IsLibraryMode Condition="'$(NativeLib)' != ''">true
+
Publish
_InitializeCommonProperties;
@@ -11,6 +13,7 @@
_AndroidResolveReferences;
_AndroidPrepareProfiledAot;
_AndroidAotCompileApp;
+ _BuildNativeLibrary;
_AndroidGenerateAppBundle;
_AfterAndroidBuildApp
diff --git a/src/mono/msbuild/android/build/AndroidApp.targets b/src/mono/msbuild/android/build/AndroidApp.targets
index 03f4f04be047f..d40e5a2784004 100644
--- a/src/mono/msbuild/android/build/AndroidApp.targets
+++ b/src/mono/msbuild/android/build/AndroidApp.targets
@@ -4,6 +4,8 @@
true
+
+ false
@@ -16,6 +18,36 @@
<_MobileIntermediateOutputPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'mobile'))
+
+
+ $(AndroidAppBundleDir)
+ <_MonoHeaderPath>$(MicrosoftNetCoreAppRuntimePackNativeDir)include\mono-2.0
+ <_AotModuleTablePath>$(AndroidAppBundleDir)\modules.c
+
+
+
+ marshal-ilgen
+
+
+
+
+
+
+
+
+ <_UsedComponents
+ Condition="'$(RuntimeComponents)' != '' and '$(RuntimeComponents)' != '*'"
+ Include="$(RuntimeComponents)" />
+
+ <_RuntimeLibraries
+ Include="$(AndroidAppDir)\*-stub-static.a" />
+ <_RuntimeLibraries
+ Include="$(AndroidAppDir)\*.a"
+ Exclude="$(AndroidAppDir)\*-static.a" />
+
+ <_RuntimeLibraries Remove="$(AndroidAppDir)\libmono-component-%(_UsedComponents.Identity)-stub-static.a" />
+ <_RuntimeLibraries Include="$(AndroidAppDir)\libmono-component-%(_UsedComponents.Identity)-static.a" />
+
@@ -29,11 +61,11 @@
- <_AndroidAssembliesInternal Remove="@(_AndroidAssembliesInternal)" />
- <_AndroidAssembliesInternal Include="@(AndroidAssembliesToBundle)">
+
+
<_InternalForceInterpret>%(AndroidAssembliesToBundle._InternalForceInterpret)
<_IsNative>%(AndroidAssembliesToBundle._IsNative)
-
+
@@ -52,6 +84,7 @@
+
@@ -67,14 +100,14 @@
- <_AotInputAssemblies Include="@(_AndroidAssembliesInternal)"
- Condition="'%(_AndroidAssembliesInternal._InternalForceInterpret)' != 'true'">
+ <_AotInputAssemblies Include="@(AppAssembliesInternal)"
+ Condition="'%(AppAssembliesInternal._InternalForceInterpret)' != 'true'">
$(AotArguments)
$(ProcessArguments)
- <_AOT_InternalForceInterpretAssemblies Include="@(_AndroidAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" />
- <_AndroidAssembliesInternal Remove="@(_AndroidAssembliesInternal)" />
+ <_AOT_InternalForceInterpretAssemblies Include="@(AppAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" />
+
@@ -104,22 +137,39 @@
+
+
+
+
+
+
+
+
+ <_EnableUnmanagedCallersOnlyMethodsExport Condition="'$(_IsLibraryMode)' == 'true'">true
+
+
-
+ Mode="$(_AOTMode)"
+ OutputDir="$(_MobileIntermediateOutputPath)"
+ OutputType="AsmOnly"
+ UseLLVM="$(MonoEnableLLVM)">
+
- <_AndroidAssembliesInternal Include="@(_AOT_InternalForceInterpretAssemblies)" />
+
@@ -129,27 +179,30 @@
Condition="$(AndroidGenerateAppBundle) == 'true'"
DependsOnTargets="_AndroidGenerateRuntimeConfig">
-
- marshal-ilgen
-
+
+ <_NativeDependencies Include="$(LibraryOutputPath)" />
+
-
+ ProjectName="$(AssemblyName)"
+ RuntimeComponents="$(RuntimeComponents)"
+ RuntimeIdentifier="$(RuntimeIdentifier)"
+ StripDebugSymbols="False">
+
diff --git a/src/mono/msbuild/apple/build/AppleApp.InTree.targets b/src/mono/msbuild/apple/build/AppleApp.InTree.targets
index 292e4e76a1fc1..e037114aa4124 100644
--- a/src/mono/msbuild/apple/build/AppleApp.InTree.targets
+++ b/src/mono/msbuild/apple/build/AppleApp.InTree.targets
@@ -8,8 +8,7 @@
adhoc
-
-
+
@@ -24,22 +23,4 @@
-
-
-
- $(MSBuildProjectName)
-
-
- $(TargetOS).AnyCPU.$(Configuration)
- $([MSBuild]::NormalizeDirectory($(ArtifactsDir), 'helix'))
- $([MSBuild]::NormalizeDirectory($(HelixArchiveRoot), 'runonly'))
- $([MSBuild]::NormalizeDirectory($(HelixArchiveRunOnlyRoot), $(OSPlatformConfig), $(WasmHelixTestAppRelativeDir)))
- $(OutputPath)$(AssemblyName).zip
-
-
-
-
diff --git a/src/mono/msbuild/apple/build/AppleApp.LocalBuild.props b/src/mono/msbuild/apple/build/AppleApp.LocalBuild.props
index ee65219e605bf..70685bc1b0770 100644
--- a/src/mono/msbuild/apple/build/AppleApp.LocalBuild.props
+++ b/src/mono/msbuild/apple/build/AppleApp.LocalBuild.props
@@ -34,6 +34,7 @@
$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackLocationToUse), 'runtimes', '$(TargetOS)-$(TargetArchitecture.ToLowerInvariant())', 'native'))
$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'AppleAppBuilder', 'Debug', '$(_NetCoreAppToolCurrent)'))
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'LibraryBuilder', 'Debug', '$(_NetCoreAppToolCurrent)'))
$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoAOTCompiler', 'Debug', '$(_NetCoreAppToolCurrent)'))
$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoTargetsTasks', 'Debug', '$(_NetCoreAppToolCurrent)'))
@@ -52,6 +53,7 @@
$([MSBuild]::NormalizeDirectory($(BuildBaseDir), 'MonoAOTCompiler'))
$([MSBuild]::NormalizeDirectory($(BuildBaseDir), 'MonoTargetsTasks'))
$([MSBuild]::NormalizeDirectory($(BuildBaseDir), 'AppleAppBuilder'))
+ $([MSBuild]::NormalizeDirectory($(BuildBaseDir), 'LibraryBuilder'))
$([MSBuild]::NormalizePath($(BuildBaseDir), 'cross'))
<_MonoAotCrossCompilerPath>$([MSBuild]::NormalizePath($(MonoAotCrossDir), 'mono-aot-cross'))
@@ -66,6 +68,7 @@
$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackLocationToUse)))
$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackDir), 'runtimes', '$(TargetOS)-$(TargetArchitecture.ToLowerInvariant())'))
$([MSBuild]::NormalizePath('$(AppleAppBuilderDir)', 'AppleAppBuilder.dll'))
+ $([MSBuild]::NormalizePath('$(LibraryBuilderDir)', 'LibraryBuilder.dll'))
$([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll'))
$([MSBuild]::NormalizePath('$(MonoTargetsTasksDir)', 'MonoTargetsTasks.dll'))
diff --git a/src/mono/msbuild/apple/build/AppleApp.LocalBuild.targets b/src/mono/msbuild/apple/build/AppleApp.LocalBuild.targets
index 593371e69c01c..93d272cab4c1f 100644
--- a/src/mono/msbuild/apple/build/AppleApp.LocalBuild.targets
+++ b/src/mono/msbuild/apple/build/AppleApp.LocalBuild.targets
@@ -34,18 +34,6 @@
false
-
-
-
@@ -71,6 +59,7 @@
$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackLocationToUse)))
$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackDir), 'runtimes', '$(TargetOS)-$(TargetArchitecture.ToLowerInvariant())'))
$([MSBuild]::NormalizePath('$(AppleAppBuilderDir)', 'AppleAppBuilder.dll'))
+ $([MSBuild]::NormalizePath('$(LibraryBuilderDir)', 'LibraryBuilder.dll'))
$([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll'))
$([MSBuild]::NormalizePath('$(MonoTargetsTasksDir)', 'RuntimeConfigParser.dll'))
diff --git a/src/mono/msbuild/apple/build/AppleApp.props b/src/mono/msbuild/apple/build/AppleApp.props
index b86e481943d73..cbeea202428c6 100644
--- a/src/mono/msbuild/apple/build/AppleApp.props
+++ b/src/mono/msbuild/apple/build/AppleApp.props
@@ -17,12 +17,16 @@
+ <_IsLibraryMode Condition="'$(NativeLib)' != ''">true
+ <_NativeLibraryTarget Condition="'$(_IsLibraryMode)' == 'true'">_BuildNativeLibrary;
+
Publish
_InitializeCommonProperties;
_BeforeAppleBuildApp;
_AppleResolveReferences;
_AppleAotCompileApp;
+ $(_NativeLibraryTarget)
_AppleGenerateAppBundle;
_AfterAppleBuildApp
diff --git a/src/mono/msbuild/apple/build/AppleApp.targets b/src/mono/msbuild/apple/build/AppleApp.targets
index 30d72c6b916a5..6e97c7b016c84 100644
--- a/src/mono/msbuild/apple/build/AppleApp.targets
+++ b/src/mono/msbuild/apple/build/AppleApp.targets
@@ -5,6 +5,17 @@
TaskName="ILStrip"
AssemblyFile="$(MonoTargetsTasksAssemblyPath)" />
+
+ true
+
+ false
+
+
+
+
+
+
@@ -17,6 +28,40 @@
<_MobileIntermediateOutputPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'mobile'))
+
+ $(AppleAppBundleDir)
+ <_MonoHeaderPath>$(MicrosoftNetCoreAppRuntimePackNativeDir)include\mono-2.0
+ <_AotModuleTablePath>$(AppleAppBundleDir)\modules.m
+
+
+
+
+ <_CommonLinkerArgs Condition="'$(TargetOS)' != 'tvos'" Include="-framework GSS" />
+
+
+
+ marshal-ilgen
+
+
+
+
+
+
+
+
+ <_UsedComponents
+ Condition="'$(RuntimeComponents)' != '' and '$(RuntimeComponents)' != '*'"
+ Include="$(RuntimeComponents)" />
+
+ <_RuntimeLibraries
+ Include="$(AppleAppDir)\*-stub-static.a" />
+ <_RuntimeLibraries
+ Include="$(AppleAppDir)\*.a"
+ Exclude="$(AppleAppDir)\*-static.a" />
+
+ <_RuntimeLibraries Remove="$(AppleAppDir)\libmono-component-%(_UsedComponents.Identity)-stub-static.a" />
+ <_RuntimeLibraries Include="$(AppleAppDir)\libmono-component-%(_UsedComponents.Identity)-static.a" />
+
@@ -30,11 +75,11 @@
- <_AppleAssembliesInternal Remove="@(_AppleAssembliesInternal)" />
- <_AppleAssembliesInternal Include="@(AppleAssembliesToBundle)">
+
+
<_InternalForceInterpret>%(AppleAssembliesToBundle._InternalForceInterpret)
<_IsNative>%(AppleAssembliesToBundle._IsNative)
-
+
@@ -46,16 +91,18 @@
<_AOTMode Condition="'$(UseMonoJustInterp)' == 'true'">JustInterp
+
+ <_EnableUnmanagedCallersOnlyMethodsExport Condition="'$(_IsLibraryMode)' == 'true'">true
+
+
-
-
-
+
@@ -76,14 +123,14 @@
<_AotExcludeAssemblies Include="*System.Runtime.WindowsRuntime.dll" />
- <_AotInputAssemblies Include="@(_AppleAssembliesInternal)"
- Condition="'%(_AppleAssembliesInternal._InternalForceInterpret)' != 'true'">
+ <_AotInputAssemblies Include="@(AppAssembliesInternal)"
+ Condition="'%(AppAssembliesInternal._InternalForceInterpret)' != 'true'">
$(AotArguments)
$(ProcessArguments)
- <_AOT_InternalForceInterpretAssemblies Include="@(_AppleAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" />
- <_AppleAssembliesInternal Remove="@(_AppleAssembliesInternal)" />
+ <_AOT_InternalForceInterpretAssemblies Include="@(AppAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" />
+
@@ -108,60 +155,64 @@
-
+ EnableUnmanagedCallersOnlyMethodsExport="$(_EnableUnmanagedCallersOnlyMethodsExport)"
+ IntermediateOutputPath="$(_MobileIntermediateOutputPath)"
+ LLVMPath="$(MonoAotCrossDir)"
+ Mode="$(_AOTMode)"
+ OutputDir="$(_MobileIntermediateOutputPath)"
+ OutputType="AsmOnly"
+ UseLLVM="$(MonoEnableLLVM)">
+
-
+
- <_AppleAssembliesInternal Include="@(_AOT_InternalForceInterpretAssemblies)" />
+
-
+
-
- marshal-ilgen
-
+
+
+
+ AppDir="$(AppleAppDir)"
+ Arch="$(TargetArchitecture)"
+ Assemblies="@(AppAssembliesInternal)"
+ BuildAppBundle="$(GenerateXcodeProject)"
+ DevTeamProvisioning="$(DevTeamProvisioning)"
+ DiagnosticPorts="$(DiagnosticPorts)"
+ EnableAppSandbox="$(EnableAppSandbox)"
+ ExtraLinkerArguments="@(ExtraAppLinkerArgs)"
+ ForceAOT="$(RunAOTCompilation)"
+ ForceInterpreter="$(MonoForceInterpreter)"
+ GenerateCMakeProject="$(GenerateCMakeProject)"
+ GenerateXcodeProject="$(GenerateXcodeProject)"
+ InvariantGlobalization="$(InvariantGlobalization)"
+ MainLibraryFileName="$(MainLibraryFileName)"
+ MonoRuntimeHeaders="$(_MonoHeaderPath)"
+ NativeMainSource="$(NativeMainSource)"
+ Optimized="$(Optimized)"
+ OutputDirectory="$(AppleAppBundleDir)"
+ ProjectName="$(AssemblyName)"
+ RuntimeComponents="$(RuntimeComponents)"
+ TargetOS="$(TargetOS)"
+ UseConsoleUITemplate="True">
diff --git a/src/mono/msbuild/apple/data/Directory.Build.props b/src/mono/msbuild/apple/data/Directory.Build.props
index 51a42316d05ee..87c180074df54 100644
--- a/src/mono/msbuild/apple/data/Directory.Build.props
+++ b/src/mono/msbuild/apple/data/Directory.Build.props
@@ -2,13 +2,17 @@
$(HELIX_CORRELATION_PAYLOAD)\build\
<_AppleTargetsDir>$(AppleBuildSupportDir)\apple\
+ <_LibraryTargetsDir>$(AppleBuildSupportDir)\common\
<_AppleTargetsDir Condition="'$(_AppleTargetsDir)' == '' and '$(RuntimeSrcDir)' != ''">$(RuntimeSrcDir)\src\mono\msbuild\apple\build\
<_AppleTargetsDir Condition="'$(_AppleTargetsDir)' != ''">$([MSBuild]::EnsureTrailingSlash($(_AppleTargetsDir)))
+ <_LibraryTargetsDir Condition="'$(_LibraryTargetsDir)' == '' and '$(RuntimeSrcDir)' != ''">$(RuntimeSrcDir)\src\mono\msbuild\common\
+ <_LibraryTargetsDir Condition="'$(_LibraryTargetsDir)' != ''">$([MSBuild]::EnsureTrailingSlash($(_LibraryTargetsDir)))
+
PrepareForAppleBuild;$(AppleBuildAppDependsOn)
diff --git a/src/mono/msbuild/apple/data/Directory.Build.targets b/src/mono/msbuild/apple/data/Directory.Build.targets
index 1cf9e918b3702..6f95f6c29428c 100644
--- a/src/mono/msbuild/apple/data/Directory.Build.targets
+++ b/src/mono/msbuild/apple/data/Directory.Build.targets
@@ -14,5 +14,6 @@
+
diff --git a/src/mono/msbuild/common/LibraryBuilder.props b/src/mono/msbuild/common/LibraryBuilder.props
new file mode 100644
index 0000000000000..c1df2220ddc6e
--- /dev/null
+++ b/src/mono/msbuild/common/LibraryBuilder.props
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/src/mono/msbuild/common/LibraryBuilder.targets b/src/mono/msbuild/common/LibraryBuilder.targets
new file mode 100644
index 0000000000000..ae920b8dec07a
--- /dev/null
+++ b/src/mono/msbuild/common/LibraryBuilder.targets
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+ <_IsSharedLibrary>false
+ <_IsSharedLibrary Condition="'$(NativeLib)' == 'shared'">true
+
+
+
+ <_ExtraLibrarySources Include="$(_AotModuleTablePath)" />
+ <_ExtraLinkerArgs Include="@(_CommonLinkerArgs)" />
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj
index 895af7d5e6306..e06a8c70a206d 100644
--- a/src/mono/sample/Android/AndroidSampleApp.csproj
+++ b/src/mono/sample/Android/AndroidSampleApp.csproj
@@ -79,18 +79,19 @@
+ ProjectName="HelloAndroid"
+ RuntimeComponents="$(RuntimeComponents)"
+ RuntimeIdentifier="$(RuntimeIdentifier)"
+ StripDebugSymbols="$(StripDebugSymbols)">
diff --git a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs
index c953c49a2e33a..ddbbe5bc64d19 100644
--- a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs
+++ b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs
@@ -33,6 +33,11 @@ public class AndroidAppBuilderTask : Task
///
public ITaskItem[] EnvironmentVariables { get; set; } = Array.Empty();
+ ///
+ /// Additional linker arguments that apply to the app being built
+ ///
+ public ITaskItem[] ExtraLinkerArguments { get; set; } = Array.Empty();
+
///
/// Prefer FullAOT mode for Emulator over JIT
///
@@ -43,6 +48,16 @@ public class AndroidAppBuilderTask : Task
///
public bool ForceFullAOT { get; set; }
+ ///
+ /// Mode to control whether runtime is a self-contained library or not
+ ///
+ public bool IsLibraryMode { get; set; }
+
+ ///
+ /// Extra native dependencies to link into the app
+ ///
+ public string[] NativeDependencies { get; set; } = Array.Empty();
+
///
/// Static linked runtime
///
@@ -99,8 +114,6 @@ public class AndroidAppBuilderTask : Task
public override bool Execute()
{
- string abi = DetermineAbi();
-
var apkBuilder = new ApkBuilder(Log);
apkBuilder.ProjectName = ProjectName;
apkBuilder.AppDir = AppDir;
@@ -122,18 +135,11 @@ public override bool Execute()
apkBuilder.RuntimeComponents = RuntimeComponents;
apkBuilder.DiagnosticPorts = DiagnosticPorts;
apkBuilder.Assemblies = Assemblies;
- (ApkBundlePath, ApkPackageId) = apkBuilder.BuildApk(abi, MainLibraryFileName, MonoRuntimeHeaders);
+ apkBuilder.IsLibraryMode = IsLibraryMode;
+ apkBuilder.NativeDependencies = NativeDependencies;
+ apkBuilder.ExtraLinkerArguments = ExtraLinkerArguments;
+ (ApkBundlePath, ApkPackageId) = apkBuilder.BuildApk(RuntimeIdentifier, MainLibraryFileName, MonoRuntimeHeaders);
return true;
}
-
- private string DetermineAbi() =>
- RuntimeIdentifier switch
- {
- "android-x86" => "x86",
- "android-x64" => "x86_64",
- "android-arm" => "armeabi-v7a",
- "android-arm64" => "arm64-v8a",
- _ => throw new ArgumentException($"{RuntimeIdentifier} is not supported for Android"),
- };
}
diff --git a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj
index 69c1877a575cf..da369e51790dd 100644
--- a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj
+++ b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj
@@ -18,6 +18,7 @@
+
diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs
index 9b73fae5c7c9e..605ab70e19b5b 100644
--- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs
+++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs
@@ -36,7 +36,10 @@ public partial class ApkBuilder
public bool StaticLinkedRuntime { get; set; }
public string? RuntimeComponents { get; set; }
public string? DiagnosticPorts { get; set; }
+ public bool IsLibraryMode { get; set; }
public ITaskItem[] Assemblies { get; set; } = Array.Empty();
+ public ITaskItem[] ExtraLinkerArguments { get; set; } = Array.Empty();
+ public string[] NativeDependencies { get; set; } = Array.Empty();
private TaskLoggingHelper logger;
@@ -46,7 +49,7 @@ public ApkBuilder(TaskLoggingHelper logger)
}
public (string apk, string packageId) BuildApk(
- string abi,
+ string runtimeIdentifier,
string mainLibraryFileName,
string monoRuntimeHeaders)
{
@@ -60,9 +63,9 @@ public ApkBuilder(TaskLoggingHelper logger)
throw new ArgumentException($"MainLibraryFileName='{mainLibraryFileName}' was not found in AppDir='{AppDir}'");
}
- if (string.IsNullOrEmpty(abi))
+ if (string.IsNullOrEmpty(runtimeIdentifier))
{
- throw new ArgumentException("abi should not be empty (e.g. x86, x86_64, armeabi-v7a or arm64-v8a");
+ throw new ArgumentException("RuntimeIdentifier should not be empty and should contain a valid android RID");
}
if (!string.IsNullOrEmpty(ProjectName) && ProjectName.Contains(' '))
@@ -149,37 +152,41 @@ public ApkBuilder(TaskLoggingHelper logger)
var assemblerFiles = new StringBuilder();
var assemblerFilesToLink = new StringBuilder();
var aotLibraryFiles = new List();
- foreach (ITaskItem file in Assemblies)
- {
- // use AOT files if available
- var obj = file.GetMetadata("AssemblerFile");
- var llvmObj = file.GetMetadata("LlvmObjectFile");
- var lib = file.GetMetadata("LibraryFile");
- if (!string.IsNullOrEmpty(obj))
+ if (!IsLibraryMode)
+ {
+ foreach (ITaskItem file in Assemblies)
{
- var name = Path.GetFileNameWithoutExtension(obj);
- assemblerFiles.AppendLine($"add_library({name} OBJECT {obj})");
- assemblerFilesToLink.AppendLine($" {name}");
- }
+ // use AOT files if available
+ var obj = file.GetMetadata("AssemblerFile");
+ var llvmObj = file.GetMetadata("LlvmObjectFile");
+ var lib = file.GetMetadata("LibraryFile");
- if (!string.IsNullOrEmpty(llvmObj))
- {
- var name = Path.GetFileNameWithoutExtension(llvmObj);
- assemblerFilesToLink.AppendLine($" {llvmObj}");
+ if (!string.IsNullOrEmpty(obj))
+ {
+ var name = Path.GetFileNameWithoutExtension(obj);
+ assemblerFiles.AppendLine($"add_library({name} OBJECT {obj})");
+ assemblerFilesToLink.AppendLine($" {name}");
+ }
+
+ if (!string.IsNullOrEmpty(llvmObj))
+ {
+ var name = Path.GetFileNameWithoutExtension(llvmObj);
+ assemblerFilesToLink.AppendLine($" {llvmObj}");
+ }
+
+ if (!string.IsNullOrEmpty(lib))
+ {
+ aotLibraryFiles.Add(lib);
+ }
}
- if (!string.IsNullOrEmpty(lib))
+ if (ForceAOT && assemblerFiles.Length == 0 && aotLibraryFiles.Count == 0)
{
- aotLibraryFiles.Add(lib);
+ throw new InvalidOperationException("Need list of AOT files.");
}
}
- if (ForceAOT && assemblerFiles.Length == 0 && aotLibraryFiles.Count == 0)
- {
- throw new InvalidOperationException("Need list of AOT files.");
- }
-
Directory.CreateDirectory(OutputDir);
Directory.CreateDirectory(Path.Combine(OutputDir, "bin"));
Directory.CreateDirectory(Path.Combine(OutputDir, "obj"));
@@ -231,7 +238,6 @@ public ApkBuilder(TaskLoggingHelper logger)
string androidJar = Path.Combine(AndroidSdk, "platforms", "android-" + BuildApiLevel, "android.jar");
string androidToolchain = Path.Combine(AndroidNdk, "build", "cmake", "android.toolchain.cmake");
string javac = "javac";
- string cmake = "cmake";
string zip = "zip";
Utils.RunProcess(logger, zip, workingDir: assetsToZipDirectory, args: "-q -r ../assets/assets.zip .");
@@ -243,73 +249,86 @@ public ApkBuilder(TaskLoggingHelper logger)
// 1. Build libmonodroid.so via cmake
string nativeLibraries = "";
- string monoRuntimeLib = "";
- if (StaticLinkedRuntime)
+ if (IsLibraryMode)
{
- monoRuntimeLib = Path.Combine(AppDir, "libmonosgen-2.0.a");
+ nativeLibraries = string.Join("\n ", NativeDependencies.Select(dep => dep));
}
else
{
- monoRuntimeLib = Path.Combine(AppDir, "libmonosgen-2.0.so");
- }
+ string monoRuntimeLib = "";
+ if (StaticLinkedRuntime)
+ {
+ monoRuntimeLib = Path.Combine(AppDir, "libmonosgen-2.0.a");
+ }
+ else
+ {
+ monoRuntimeLib = Path.Combine(AppDir, "libmonosgen-2.0.so");
+ }
- if (!File.Exists(monoRuntimeLib))
- {
- throw new ArgumentException($"{monoRuntimeLib} was not found");
- }
- else
- {
- nativeLibraries += $"{monoRuntimeLib}{Environment.NewLine}";
- }
+ if (!File.Exists(monoRuntimeLib))
+ {
+ throw new ArgumentException($"{monoRuntimeLib} was not found");
+ }
+ else
+ {
+ nativeLibraries += $"{monoRuntimeLib}{Environment.NewLine}";
+ }
- if (StaticLinkedRuntime)
- {
- string[] staticComponentStubLibs = Directory.GetFiles(AppDir, "libmono-component-*-stub-static.a");
- bool staticLinkAllComponents = false;
- string[] staticLinkedComponents = Array.Empty();
-
- if (!string.IsNullOrEmpty(RuntimeComponents) && RuntimeComponents.Equals("*", StringComparison.OrdinalIgnoreCase))
- staticLinkAllComponents = true;
- else if (!string.IsNullOrEmpty(RuntimeComponents))
- staticLinkedComponents = RuntimeComponents.Split(";");
-
- // by default, component stubs will be linked and depending on how mono runtime has been build,
- // stubs can disable or dynamic load components.
- foreach (string staticComponentStubLib in staticComponentStubLibs)
+ if (StaticLinkedRuntime)
{
- string componentLibToLink = staticComponentStubLib;
- if (staticLinkAllComponents)
- {
- // static link component.
- componentLibToLink = componentLibToLink.Replace("-stub-static.a", "-static.a", StringComparison.OrdinalIgnoreCase);
- }
- else
+ string[] staticComponentStubLibs = Directory.GetFiles(AppDir, "libmono-component-*-stub-static.a");
+ bool staticLinkAllComponents = false;
+ string[] staticLinkedComponents = Array.Empty();
+
+ if (!string.IsNullOrEmpty(RuntimeComponents) && RuntimeComponents.Equals("*", StringComparison.OrdinalIgnoreCase))
+ staticLinkAllComponents = true;
+ else if (!string.IsNullOrEmpty(RuntimeComponents))
+ staticLinkedComponents = RuntimeComponents.Split(";");
+
+ // by default, component stubs will be linked and depending on how mono runtime has been build,
+ // stubs can disable or dynamic load components.
+ foreach (string staticComponentStubLib in staticComponentStubLibs)
{
- foreach (string staticLinkedComponent in staticLinkedComponents)
+ string componentLibToLink = staticComponentStubLib;
+ if (staticLinkAllComponents)
+ {
+ // static link component.
+ componentLibToLink = componentLibToLink.Replace("-stub-static.a", "-static.a", StringComparison.OrdinalIgnoreCase);
+ }
+ else
{
- if (componentLibToLink.Contains(staticLinkedComponent, StringComparison.OrdinalIgnoreCase))
+ foreach (string staticLinkedComponent in staticLinkedComponents)
{
- // static link component.
- componentLibToLink = componentLibToLink.Replace("-stub-static.a", "-static.a", StringComparison.OrdinalIgnoreCase);
- break;
+ if (componentLibToLink.Contains(staticLinkedComponent, StringComparison.OrdinalIgnoreCase))
+ {
+ // static link component.
+ componentLibToLink = componentLibToLink.Replace("-stub-static.a", "-static.a", StringComparison.OrdinalIgnoreCase);
+ break;
+ }
}
}
- }
- // if lib doesn't exist (primarily due to runtime build without static lib support), fallback linking stub lib.
- if (!File.Exists(componentLibToLink))
- {
- logger.LogMessage(MessageImportance.High, $"\nCouldn't find static component library: {componentLibToLink}, linking static component stub library: {staticComponentStubLib}.\n");
- componentLibToLink = staticComponentStubLib;
+ // if lib doesn't exist (primarily due to runtime build without static lib support), fallback linking stub lib.
+ if (!File.Exists(componentLibToLink))
+ {
+ logger.LogMessage(MessageImportance.High, $"\nCouldn't find static component library: {componentLibToLink}, linking static component stub library: {staticComponentStubLib}.\n");
+ componentLibToLink = staticComponentStubLib;
+ }
+
+ nativeLibraries += $" {componentLibToLink}{Environment.NewLine}";
}
- nativeLibraries += $" {componentLibToLink}{Environment.NewLine}";
+ // There's a circular dependency between static mono runtime lib and static component libraries.
+ // Adding mono runtime lib before and after component libs will resolve issues with undefined symbols
+ // due to circular dependency.
+ nativeLibraries += $" {monoRuntimeLib}{Environment.NewLine}";
}
+ }
- // There's a circular dependency between static mono runtime lib and static component libraries.
- // Adding mono runtime lib before and after component libs will resolve issues with undefined symbols
- // due to circular dependency.
- nativeLibraries += $" {monoRuntimeLib}{Environment.NewLine}";
+ StringBuilder extraLinkerArgs = new StringBuilder();
+ foreach (ITaskItem item in ExtraLinkerArguments)
+ {
+ extraLinkerArgs.AppendLine($" \"{item.ItemSpec}\"");
}
nativeLibraries += assemblerFilesToLink.ToString();
@@ -317,10 +336,12 @@ public ApkBuilder(TaskLoggingHelper logger)
string aotSources = assemblerFiles.ToString();
string cmakeLists = Utils.GetEmbeddedResource("CMakeLists-android.txt")
+ .Replace("%ProjectName%", ProjectName)
.Replace("%MonoInclude%", monoRuntimeHeaders)
.Replace("%NativeLibrariesToLink%", nativeLibraries)
.Replace("%AotSources%", aotSources)
- .Replace("%AotModulesSource%", string.IsNullOrEmpty(aotSources) ? "" : "modules.c");
+ .Replace("%AotModulesSource%", string.IsNullOrEmpty(aotSources) ? "" : "modules.c")
+ .Replace("%APP_LINKER_ARGS%", extraLinkerArgs.ToString());
var defines = new StringBuilder();
if (ForceInterpreter)
@@ -352,25 +373,11 @@ public ApkBuilder(TaskLoggingHelper logger)
File.WriteAllText(Path.Combine(OutputDir, "monodroid.c"), Utils.GetEmbeddedResource("monodroid.c"));
- string cmakeGenArgs = $"-DCMAKE_TOOLCHAIN_FILE={androidToolchain} -DANDROID_ABI=\"{abi}\" -DANDROID_STL=none " +
- $"-DANDROID_PLATFORM=android-{MinApiLevel} -B monodroid";
+ AndroidProject project = new AndroidProject("monodroid", runtimeIdentifier, AndroidNdk, logger);
+ project.GenerateCMake(OutputDir, MinApiLevel, StripDebugSymbols);
+ project.BuildCMake(OutputDir, StripDebugSymbols);
- string cmakeBuildArgs = "--build monodroid";
-
- if (StripDebugSymbols)
- {
- // Use "-s" to strip debug symbols, it complains it's unused but it works
- cmakeGenArgs+= " -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_C_FLAGS=\"-s -Wno-unused-command-line-argument\"";
- cmakeBuildArgs += " --config MinSizeRel";
- }
- else
- {
- cmakeGenArgs += " -DCMAKE_BUILD_TYPE=Debug";
- cmakeBuildArgs += " --config Debug";
- }
-
- Utils.RunProcess(logger, cmake, workingDir: OutputDir, args: cmakeGenArgs);
- Utils.RunProcess(logger, cmake, workingDir: OutputDir, args: cmakeBuildArgs);
+ string abi = project.Abi;
// 2. Compile Java files
@@ -389,6 +396,7 @@ public ApkBuilder(TaskLoggingHelper logger)
File.WriteAllText(javaActivityPath,
Utils.GetEmbeddedResource("MainActivity.java")
.Replace("%EntryPointLibName%", Path.GetFileName(mainLibraryFileName)));
+
if (!string.IsNullOrEmpty(NativeMainSource))
File.Copy(NativeMainSource, javaActivityPath, true);
@@ -400,8 +408,10 @@ public ApkBuilder(TaskLoggingHelper logger)
envVariables += $"\t\tsetEnv(\"{name}\", \"{value}\");\n";
}
+ string jniLibraryName = (IsLibraryMode) ? ProjectName! : "System.Security.Cryptography.Native.Android";
string monoRunner = Utils.GetEmbeddedResource("MonoRunner.java")
.Replace("%EntryPointLibName%", Path.GetFileName(mainLibraryFileName))
+ .Replace("%JNI_LIBRARY_NAME%", jniLibraryName)
.Replace("%EnvVariables%", envVariables);
File.WriteAllText(monoRunnerPath, monoRunner);
@@ -438,14 +448,21 @@ public ApkBuilder(TaskLoggingHelper logger)
var dynamicLibs = new List();
dynamicLibs.Add(Path.Combine(OutputDir, "monodroid", "libmonodroid.so"));
- dynamicLibs.AddRange(Directory.GetFiles(AppDir, "*.so").Where(file => Path.GetFileName(file) != "libmonodroid.so"));
- // add all *.so files to lib/%abi%/
+ if (IsLibraryMode)
+ {
+ dynamicLibs.AddRange(NativeDependencies);
+ }
+ else
+ {
+ dynamicLibs.AddRange(Directory.GetFiles(AppDir, "*.so").Where(file => Path.GetFileName(file) != "libmonodroid.so"));
+ }
+ // add all *.so files to lib/%abi%/
string[] dynamicLinkedComponents = Array.Empty();
bool dynamicLinkAllComponents = false;
if (!StaticLinkedRuntime && !string.IsNullOrEmpty(RuntimeComponents) && RuntimeComponents.Equals("*", StringComparison.OrdinalIgnoreCase))
- dynamicLinkAllComponents = true;
+ dynamicLinkAllComponents = true;
if (!string.IsNullOrEmpty(RuntimeComponents) && !StaticLinkedRuntime)
dynamicLinkedComponents = RuntimeComponents.Split(";");
diff --git a/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt b/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt
index 0c3eedab918e6..edeb9981eeedf 100644
--- a/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt
+++ b/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt
@@ -24,16 +24,8 @@ include_directories("%MonoInclude%")
target_link_libraries(
monodroid
PRIVATE
- %NativeLibrariesToLink%
libz.so
log
- "-u GlobalizationNative_LoadICU"
- "-u GlobalizationNative_GetLatestJapaneseEra"
- "-u GlobalizationNative_ChangeCase"
- "-u GlobalizationNative_CloseSortHandle"
- "-u GlobalizationNative_GetLocales"
- "-u GlobalizationNative_GetLocaleInfoInt"
- "-u GlobalizationNative_GetLocaleTimeFormat"
- "-u GlobalizationNative_ToUnicode"
- "-u GlobalizationNative_NormalizeString"
- "-u GlobalizationNative_GetTimeZoneDisplayName")
+ %NativeLibrariesToLink%
+ %APP_LINKER_ARGS%
+)
diff --git a/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java b/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java
index bd91f93f6b760..695d91068a261 100644
--- a/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java
+++ b/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java
@@ -34,7 +34,7 @@ public class MonoRunner extends Instrumentation
{
static {
// loadLibrary triggers JNI_OnLoad in these libs
- System.loadLibrary("System.Security.Cryptography.Native.Android");
+ System.loadLibrary("%JNI_LIBRARY_NAME%");
System.loadLibrary("monodroid");
}
diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
index 30db2633c3a72..4de8c5b785753 100644
--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
+++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
@@ -66,6 +66,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
/// - AotDataFile (when using UseAotDataFile=true)
/// - LlvmObjectFile (if using LLVM)
/// - LlvmBitcodeFile (if using LLVM-only)
+ /// - ExportsFile (used in LibraryMode only)
///
[Output]
public ITaskItem[]? CompiledAssemblies { get; set; }
diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.props b/src/tasks/AotCompilerTask/MonoAOTCompiler.props
index 97f84e34bebe9..1c7bc065d2a47 100644
--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.props
+++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.props
@@ -2,13 +2,11 @@
-
-
-
+
diff --git a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs
index 785c406faf4d7..f81357930a2e6 100644
--- a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs
+++ b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs
@@ -62,6 +62,11 @@ public string TargetOS
[Required]
public ITaskItem[] Assemblies { get; set; } = Array.Empty();
+ ///
+ /// Additional linker arguments that apply to the app being built
+ ///
+ public ITaskItem[] ExtraLinkerArguments { get; set; } = Array.Empty();
+
///
/// Target arch, can be "arm64", "arm" or "x64" at the moment
///
@@ -302,11 +307,17 @@ public override bool Execute()
throw new ArgumentException("DevTeamProvisioning must be set to a valid value when App Sandbox is enabled, using '-' is not supported.");
}
+ List extraLinkerArgs = new List();
+ foreach(ITaskItem item in ExtraLinkerArguments)
+ {
+ extraLinkerArgs.Add(item.ItemSpec);
+ }
+
var generator = new Xcode(Log, TargetOS, Arch);
if (GenerateXcodeProject)
{
- XcodeProjectPath = generator.GenerateXCode(ProjectName, MainLibraryFileName, assemblerFiles, assemblerDataFiles, assemblerFilesToLink,
+ XcodeProjectPath = generator.GenerateXCode(ProjectName, MainLibraryFileName, assemblerFiles, assemblerDataFiles, assemblerFilesToLink, extraLinkerArgs,
AppDir, binDir, MonoRuntimeHeaders, !isDevice, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource, UseNativeAOTRuntime);
if (BuildAppBundle)
@@ -318,13 +329,21 @@ public override bool Execute()
}
else
{
- AppBundlePath = generator.BuildAppBundle(XcodeProjectPath, Optimized, StripSymbolTable, DevTeamProvisioning);
+ string appDir = generator.BuildAppBundle(XcodeProjectPath, Optimized, DevTeamProvisioning);
+ AppBundlePath = Xcode.GetAppPath(appDir, XcodeProjectPath);
+
+ if (StripSymbolTable)
+ {
+ generator.StripApp(XcodeProjectPath, AppBundlePath);
+ }
+
+ generator.LogAppSize(AppBundlePath);
}
}
}
else if (GenerateCMakeProject)
{
- generator.GenerateCMake(ProjectName, MainLibraryFileName, assemblerFiles, assemblerDataFiles, assemblerFilesToLink,
+ generator.GenerateCMake(ProjectName, MainLibraryFileName, assemblerFiles, assemblerDataFiles, assemblerFilesToLink, extraLinkerArgs,
AppDir, binDir, MonoRuntimeHeaders, !isDevice, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource, UseNativeAOTRuntime);
}
diff --git a/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template b/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template
index 0703c29c2818c..a95bf1da9a550 100644
--- a/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template
+++ b/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template
@@ -72,11 +72,11 @@ target_link_libraries(
"-framework Foundation"
"-framework Security"
"-framework UIKit"
- %FrameworksToLink%
"-lz"
"-lc++"
"-liconv"
%NativeLibrariesToLink%
+%APP_LINKER_ARGS%
)
if(%UseNativeAOTRuntime%)
diff --git a/src/tasks/AppleAppBuilder/Xcode.cs b/src/tasks/AppleAppBuilder/Xcode.cs
index 4561df0c4f7a7..ed9fbf8c97886 100644
--- a/src/tasks/AppleAppBuilder/Xcode.cs
+++ b/src/tasks/AppleAppBuilder/Xcode.cs
@@ -109,7 +109,19 @@ public string TargetOS
public override bool Execute()
{
- new Xcode(Log, TargetOS, Arch).BuildAppBundle(XcodeProjectPath, Optimized, StripSymbolTable, DevTeamProvisioning, DestinationFolder);
+ Xcode project = new Xcode(Log, TargetOS, Arch);
+ string appDir = project.BuildAppBundle(XcodeProjectPath, Optimized, DevTeamProvisioning);
+
+ string appPath = Xcode.GetAppPath(appDir, XcodeProjectPath);
+ string newAppPath = Xcode.GetAppPath(DestinationFolder!, XcodeProjectPath);
+ Directory.Move(appPath, newAppPath);
+
+ if (StripSymbolTable)
+ {
+ project.StripApp(XcodeProjectPath, newAppPath);
+ }
+
+ project.LogAppSize(newAppPath);
return true;
}
@@ -122,12 +134,33 @@ internal sealed class Xcode
private string XcodeArch { get; set; }
private TaskLoggingHelper Logger { get; set; }
+ public Xcode(TaskLoggingHelper logger, string runtimeIdentifier)
+ {
+ Logger = logger;
+
+ string[] runtimeIds = runtimeIdentifier.Split('-');
+
+ if (runtimeIds.Length != 2)
+ {
+ throw new ArgumentException("A valid runtime identifier was not specified (os-arch)");
+ }
+
+ RuntimeIdentifier = runtimeIdentifier;
+ Target = runtimeIds[0];
+ XcodeArch = SetArch(runtimeIds[1]);
+ }
+
public Xcode(TaskLoggingHelper logger, string target, string arch)
{
Logger = logger;
Target = target;
RuntimeIdentifier = $"{Target}-{arch}";
- XcodeArch = arch switch {
+ XcodeArch = SetArch(arch);
+ }
+
+ private static string SetArch(string arch)
+ {
+ return arch switch {
"x64" => "x86_64",
"arm" => "armv7",
_ => arch
@@ -140,6 +173,7 @@ public string GenerateXCode(
IEnumerable asmFiles,
IEnumerable asmDataFiles,
IEnumerable asmLinkFiles,
+ IEnumerable extraLinkerArgs,
string workspace,
string binDir,
string monoInclude,
@@ -156,7 +190,7 @@ public string GenerateXCode(
string? nativeMainSource = null,
bool useNativeAOTRuntime = false)
{
- var cmakeDirectoryPath = GenerateCMake(projectName, entryPointLib, asmFiles, asmDataFiles, asmLinkFiles, workspace, binDir, monoInclude, preferDylibs, useConsoleUiTemplate, forceAOT, forceInterpreter, invariantGlobalization, optimized, enableRuntimeLogging, enableAppSandbox, diagnosticPorts, runtimeComponents, nativeMainSource, useNativeAOTRuntime);
+ var cmakeDirectoryPath = GenerateCMake(projectName, entryPointLib, asmFiles, asmDataFiles, asmLinkFiles, extraLinkerArgs, workspace, binDir, monoInclude, preferDylibs, useConsoleUiTemplate, forceAOT, forceInterpreter, invariantGlobalization, optimized, enableRuntimeLogging, enableAppSandbox, diagnosticPorts, runtimeComponents, nativeMainSource, useNativeAOTRuntime);
CreateXcodeProject(projectName, cmakeDirectoryPath);
return Path.Combine(binDir, projectName, projectName + ".xcodeproj");
}
@@ -187,6 +221,7 @@ public void CreateXcodeProject(string projectName, string cmakeDirectoryPath)
.Append("-S.")
.Append(" -B").Append(projectName)
.Append(" -GXcode")
+ .Append(" -DTARGETS_APPLE_MOBILE=1")
.Append(" -DCMAKE_SYSTEM_NAME=").Append(targetName)
.Append(deployTarget);
@@ -199,6 +234,7 @@ public string GenerateCMake(
IEnumerable asmFiles,
IEnumerable asmDataFiles,
IEnumerable asmLinkFiles,
+ IEnumerable extraLinkerArgs,
string workspace,
string binDir,
string monoInclude,
@@ -364,14 +400,14 @@ public string GenerateCMake(
toLink += $" {asmLinkFile}{Environment.NewLine}";
}
- string frameworks = "";
- if ((Target == TargetNames.iOS) || (Target == TargetNames.iOSsim) || (Target == TargetNames.MacCatalyst))
+ string appLinkerArgs = "";
+ foreach(string linkerArg in extraLinkerArgs)
{
- frameworks = "\"-framework GSS\"";
+ appLinkerArgs += $" \"{linkerArg}\"{Environment.NewLine}";
}
- cmakeLists = cmakeLists.Replace("%FrameworksToLink%", frameworks);
cmakeLists = cmakeLists.Replace("%NativeLibrariesToLink%", toLink);
+ cmakeLists = cmakeLists.Replace("%APP_LINKER_ARGS%", appLinkerArgs);
cmakeLists = cmakeLists.Replace("%AotSources%", aotSources);
cmakeLists = cmakeLists.Replace("%AotTargetsList%", aotList);
cmakeLists = cmakeLists.Replace("%AotModulesSource%", string.IsNullOrEmpty(aotSources) ? "" : "modules.m");
@@ -463,9 +499,9 @@ public string GenerateCMake(
}
public string BuildAppBundle(
- string xcodePrjPath, bool optimized, bool stripSymbolTable, string? devTeamProvisioning = null, string? destination = null)
+ string xcodePrjPath, bool optimized, string? devTeamProvisioning = null)
{
- string sdk = "";
+ string sdk;
var args = new StringBuilder();
args.Append("ONLY_ACTIVE_ARCH=YES");
@@ -559,28 +595,27 @@ public string BuildAppBundle(
Directory.Move(appDirectoryWithoutSdk, appDirectory);
}
- string appPath = Path.Combine(appDirectory, Path.GetFileNameWithoutExtension(xcodePrjPath) + ".app");
-
- if (destination != null)
- {
- var newAppPath = Path.Combine(destination, Path.GetFileNameWithoutExtension(xcodePrjPath) + ".app");
- Directory.Move(appPath, newAppPath);
- appPath = newAppPath;
- }
-
- if (stripSymbolTable)
- {
- string filename = Path.GetFileNameWithoutExtension(appPath);
- Utils.RunProcess(Logger, "dsymutil", $"{appPath}/{filename} -o {Path.GetDirectoryName(xcodePrjPath)}/{filename}.dSYM", workingDir: Path.GetDirectoryName(appPath));
- Utils.RunProcess(Logger, "strip", $"-no_code_signature_warning -x {appPath}/{filename}", workingDir: Path.GetDirectoryName(appPath));
- }
+ return appDirectory;
+ }
+ public void LogAppSize(string appPath)
+ {
long appSize = new DirectoryInfo(appPath)
.EnumerateFiles("*", SearchOption.AllDirectories)
.Sum(file => file.Length);
Logger.LogMessage(MessageImportance.High, $"\nAPP size: {(appSize / 1000_000.0):0.#} Mb.\n");
+ }
- return appPath;
+ public void StripApp(string xcodePrjPath, string appPath)
+ {
+ string filename = Path.GetFileNameWithoutExtension(appPath);
+ Utils.RunProcess(Logger, "dsymutil", $"{appPath}/{filename} -o {Path.GetDirectoryName(xcodePrjPath)}/{filename}.dSYM", workingDir: Path.GetDirectoryName(appPath));
+ Utils.RunProcess(Logger, "strip", $"-no_code_signature_warning -x {appPath}/{filename}", workingDir: Path.GetDirectoryName(appPath));
+ }
+
+ public static string GetAppPath(string appDirectory, string xcodePrjPath)
+ {
+ return Path.Combine(appDirectory, Path.GetFileNameWithoutExtension(xcodePrjPath) + ".app");
}
}
diff --git a/src/tasks/Common/Builders/AndroidProject.cs b/src/tasks/Common/Builders/AndroidProject.cs
new file mode 100644
index 0000000000000..7620cda6b9abd
--- /dev/null
+++ b/src/tasks/Common/Builders/AndroidProject.cs
@@ -0,0 +1,91 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Text;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+internal sealed class AndroidProject
+{
+ private const string DefaultMinApiLevel = "21";
+ private const string Cmake = "cmake";
+
+ private TaskLoggingHelper logger;
+
+ private string abi;
+ private string androidToolchainPath;
+ private string projectName;
+
+ public string Abi => abi;
+
+ // set the project name to something generic.
+ // return the output path
+ // let the builder figure out the name + extension
+
+ public AndroidProject(string projectName, string runtimeIdentifier, TaskLoggingHelper logger) :
+ this(projectName, runtimeIdentifier, Environment.GetEnvironmentVariable("ANDROID_NDK_ROOT")!, logger)
+ {
+ }
+
+ public AndroidProject(string projectName, string runtimeIdentifier, string androidNdkPath, TaskLoggingHelper logger)
+ {
+ androidToolchainPath = Path.Combine(androidNdkPath, "build", "cmake", "android.toolchain.cmake");
+ abi = DetermineAbi(runtimeIdentifier);
+
+ this.logger = logger;
+ this.projectName = projectName;
+ }
+
+ public void GenerateCMake(string workingDir, bool stripDebugSymbols)
+ {
+ GenerateCMake(workingDir, DefaultMinApiLevel, stripDebugSymbols);
+ }
+
+ public void GenerateCMake(string workingDir, string apiLevel = DefaultMinApiLevel, bool stripDebugSymbols = false)
+ {
+ string cmakeGenArgs = $"-DCMAKE_TOOLCHAIN_FILE={androidToolchainPath} -DANDROID_ABI=\"{Abi}\" -DANDROID_STL=none -DTARGETS_ANDROID=1 " +
+ $"-DANDROID_PLATFORM=android-{apiLevel} -B {projectName}";
+
+ if (stripDebugSymbols)
+ {
+ // Use "-s" to strip debug symbols, it complains it's unused but it works
+ cmakeGenArgs += " -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_C_FLAGS=\"-s -Wno-unused-command-line-argument\"";
+ }
+ else
+ {
+ cmakeGenArgs += " -DCMAKE_BUILD_TYPE=Debug";
+ }
+
+ Utils.RunProcess(logger, Cmake, workingDir: workingDir, args: cmakeGenArgs);
+ }
+
+ public string BuildCMake(string workingDir, bool stripDebugSymbols = false)
+ {
+ string cmakeBuildArgs = $"--build {projectName}";
+
+ if (stripDebugSymbols)
+ {
+ cmakeBuildArgs += " --config MinSizeRel";
+ }
+ else
+ {
+ cmakeBuildArgs += " --config Debug";
+ }
+
+ Utils.RunProcess(logger, Cmake, workingDir: workingDir, args: cmakeBuildArgs);
+
+ return Path.Combine(workingDir, projectName);
+ }
+
+ private static string DetermineAbi(string runtimeIdentifier) =>
+ runtimeIdentifier switch
+ {
+ "android-x86" => "x86",
+ "android-x64" => "x86_64",
+ "android-arm" => "armeabi-v7a",
+ "android-arm64" => "arm64-v8a",
+ _ => throw new ArgumentException($"{runtimeIdentifier} is not supported for Android"),
+ };
+}
diff --git a/src/tasks/Common/Builders/AppBuilderTask.cs b/src/tasks/Common/Builders/AppBuilderTask.cs
new file mode 100644
index 0000000000000..3a970ba8ce997
--- /dev/null
+++ b/src/tasks/Common/Builders/AppBuilderTask.cs
@@ -0,0 +1,98 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Text;
+using System.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+public class AppBuilderTask : Task
+{
+ ///
+ /// List of paths to assemblies.
+ /// For AOT builds, the following metadata could be set:
+ /// - AssemblerFile (when using OutputType=AsmOnly)
+ /// - ObjectFile (when using OutputType=Normal)
+ /// - LibraryFile (when using OutputType=Library)
+ /// - AotDataFile (when using UseAotDataFile=true)
+ /// - LlvmObjectFile (if using LLVM)
+ /// - LlvmBitcodeFile (if using LLVM-only)
+ ///
+ [Required]
+ public ITaskItem[] Assemblies { get; set; } = Array.Empty();
+
+ ///
+ /// Path to Mono public headers (*.h)
+ ///
+ [Required]
+ public string MonoRuntimeHeaders { get; set; } = ""!;
+
+ ///
+ /// Path to store build artifacts
+ ///
+ [Required]
+ [NotNull]
+ public string? OutputDirectory { get; set; }
+
+ ///
+ /// OS + architecture runtime identifier
+ ///
+ [Required]
+ public string RuntimeIdentifier { get; set; } = ""!;
+
+ ///
+ /// List of libraries to link against
+ ///
+ [Required]
+ public ITaskItem[] RuntimeLibraries { get; set; } = Array.Empty();
+
+ ///
+ /// Files to be ignored in AppDir
+ ///
+ public ITaskItem[]? ExcludeFromAppDir { get; set; }
+
+ ///
+ /// Diagnostic ports configuration string
+ ///
+ public string? DiagnosticPorts { get; set; } = ""!;
+
+ protected List CompiledAssemblies { get; set; }
+
+ public AppBuilderTask()
+ {
+ CompiledAssemblies = new List(Assemblies.Length);
+ }
+
+ public override bool Execute()
+ {
+ GatherCompiledAssemblies();
+
+ return true;
+ }
+
+ private void GatherCompiledAssemblies()
+ {
+ Directory.CreateDirectory(OutputDirectory);
+
+ foreach (ITaskItem file in Assemblies)
+ {
+ CompiledAssembly compiledAssembly = new CompiledAssembly();
+ compiledAssembly.Path = file.ItemSpec;
+
+ compiledAssembly.AssemblerFile = file.GetMetadata("AssemblerFile");
+ compiledAssembly.ObjectFile = file.GetMetadata("ObjectFile");
+ compiledAssembly.LibraryFile = file.GetMetadata("LibraryFile");
+ compiledAssembly.DataFile = file.GetMetadata("AotDataFile");
+ compiledAssembly.LlvmObjectFile = file.GetMetadata("LlvmObjectFile");
+ compiledAssembly.LlvmBitCodeFile = file.GetMetadata("LlvmBitcodeFile");
+ compiledAssembly.ExportsFile = file.GetMetadata("ExportSymbolsFile");
+
+ CompiledAssemblies.Add(compiledAssembly);
+ }
+ }
+}
diff --git a/src/tasks/Common/Builders/CompiledAssembly.cs b/src/tasks/Common/Builders/CompiledAssembly.cs
new file mode 100644
index 0000000000000..33e76c91b309b
--- /dev/null
+++ b/src/tasks/Common/Builders/CompiledAssembly.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+public class CompiledAssembly
+{
+ public string Path { get; set; } = ""!;
+
+ ///
+ /// The full path to the assembly file when aot mode is AsmOnly
+ ///
+ public string AssemblerFile { get; set; } = ""!;
+
+ ///
+ /// The full path to the object file when aot mode is Normal
+ ///
+ public string ObjectFile { get; set; } = ""!;
+
+ ///
+ /// The full path to the library file (.dylib, .so, .dll) when aot mode is Library
+ ///
+ public string LibraryFile { get; set; } = ""!;
+
+ ///
+ /// The full path to the aot data file when UseAotData is set to true
+ ///
+ public string DataFile { get; set; } = ""!;
+
+ ///
+ /// The full path to the LLVM object file when LLVM is used
+ ///
+ public string LlvmObjectFile { get; set; } = ""!;
+
+ ///
+ /// The full path to the LLVM bitcode file when LLVM only is specified
+ ///
+ public string LlvmBitCodeFile { get; set; } = ""!;
+
+ ///
+ /// The full path of symbols to export when building in library mode
+ ///
+ public string ExportsFile { get; set; } = ""!;
+}
diff --git a/src/tasks/LibraryBuilder/LibraryBuilder.cs b/src/tasks/LibraryBuilder/LibraryBuilder.cs
new file mode 100644
index 0000000000000..0b3489db177ff
--- /dev/null
+++ b/src/tasks/LibraryBuilder/LibraryBuilder.cs
@@ -0,0 +1,317 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Text;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+public class LibraryBuilderTask : AppBuilderTask
+{
+ private bool isSharedLibrary = true;
+ private string nativeLibraryType = "SHARED";
+
+ private string cmakeProjectLanguages = "";
+ private string targetOS = "";
+
+ ///
+ /// The name of the library being generated
+ ///
+ [Required]
+ [NotNull]
+ public string? Name { get; set; }
+
+ ///
+ /// The name of the OS being targeted
+ ///
+ [Required]
+ public string TargetOS
+ {
+ get
+ {
+ return targetOS;
+ }
+ set
+ {
+ targetOS = value.ToLowerInvariant();
+ }
+ }
+
+ ///
+ /// Extra native sources to be added to the library
+ ///
+ public ITaskItem[] ExtraSources { get; set; } = Array.Empty();
+
+ ///
+ /// Additional linker arguments that apply to the library being built
+ ///
+ public ITaskItem[] ExtraLinkerArguments { get; set; } = Array.Empty();
+
+ ///
+ /// Determines if the library is static or shared
+ ///
+ public bool IsSharedLibrary
+ {
+ get => isSharedLibrary;
+ set
+ {
+ isSharedLibrary = value;
+ nativeLibraryType = (isSharedLibrary) ? "SHARED" : "STATIC";
+ }
+ }
+
+ public bool StripDebugSymbols { get; set; }
+
+ ///
+ /// The location of the cmake file output
+ ///
+ [Output]
+ public string OutputPath { get; set; } = ""!;
+
+ private string MobileSymbolFileName
+ {
+ get => Path.Combine(OutputDirectory, "mobile_symbols.txt");
+ }
+
+ private string CMakeProjectLanguages
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(cmakeProjectLanguages))
+ {
+ cmakeProjectLanguages = (TargetOS == "android") ? "C ASM" : "OBJC ASM";
+ }
+
+ return cmakeProjectLanguages;
+ }
+ }
+
+ public override bool Execute()
+ {
+ StringBuilder aotSources = new StringBuilder();
+ StringBuilder aotObjects = new StringBuilder();
+ StringBuilder extraSources = new StringBuilder();
+ StringBuilder linkerArgs = new StringBuilder();
+
+ if (!ValidateValidTargetOS())
+ {
+ throw new ArgumentException($"{TargetOS} is not yet supported by the librarybuilder task.");
+ }
+
+ if (!base.Execute())
+ {
+ // log something here
+ return false;
+ }
+
+ GatherAotSourcesObjects(aotSources, aotObjects, extraSources, linkerArgs);
+ GatherLinkerArgs(linkerArgs);
+
+ WriteCMakeFileFromTemplate(aotSources.ToString(), aotObjects.ToString(), extraSources.ToString(), linkerArgs.ToString());
+ OutputPath = BuildLibrary();
+
+ return true;
+ }
+
+ private void GatherAotSourcesObjects(StringBuilder aotSources, StringBuilder aotObjects, StringBuilder extraSources, StringBuilder linkerArgs)
+ {
+ List exportedSymbols = new List();
+ List exportedAssemblies = new List();
+ bool hasExports = false;
+
+ foreach (CompiledAssembly compiledAssembly in CompiledAssemblies)
+ {
+ if (!string.IsNullOrEmpty(compiledAssembly.AssemblerFile))
+ {
+ aotSources.AppendLine(compiledAssembly.AssemblerFile);
+ }
+
+ if (!string.IsNullOrEmpty(compiledAssembly.LlvmObjectFile))
+ {
+ aotObjects.AppendLine($" {compiledAssembly.LlvmObjectFile}");
+ }
+
+ if (!string.IsNullOrEmpty(compiledAssembly.ExportsFile))
+ {
+ hasExports = true;
+
+ int symbolsAdded = GatherExportedSymbols(compiledAssembly.ExportsFile, exportedSymbols);
+
+ if (symbolsAdded > 0)
+ {
+ exportedAssemblies.Add(Path.GetFileName(compiledAssembly.Path));
+ }
+ }
+ }
+
+ // for android, all symbols to keep go in one linker script
+ //
+ // for ios, multiple files can be specified
+ if (hasExports && TargetOS == "android")
+ {
+ WriteLinkerScriptFile(MobileSymbolFileName, exportedSymbols);
+ WriteLinkerScriptArg(MobileSymbolFileName, linkerArgs);
+ }
+ else if (hasExports && exportedSymbols.Count > 0)
+ {
+ File.WriteAllText(
+ MobileSymbolFileName,
+ string.Join("\n", exportedSymbols.Select(symbol => symbol))
+ );
+ WriteExportedSymbolsArg(MobileSymbolFileName, linkerArgs);
+ }
+
+ WriteAssembliesToLoadList(exportedAssemblies);
+
+ foreach (ITaskItem item in ExtraSources)
+ {
+ extraSources.AppendLine(item.ItemSpec);
+ }
+ }
+
+ private void GatherLinkerArgs(StringBuilder linkerArgs)
+ {
+ string libForceLoad = "";
+
+ if (TargetOS != "android")
+ {
+ libForceLoad = "-force_load ";
+ }
+
+ foreach (ITaskItem item in RuntimeLibraries)
+ {
+ linkerArgs.AppendLine($" \"{libForceLoad}{item.ItemSpec}\"");
+ }
+
+ foreach (ITaskItem item in ExtraLinkerArguments)
+ {
+ linkerArgs.AppendLine($" \"{item.ItemSpec}\"");
+ }
+ }
+
+ private static int GatherExportedSymbols(string exportsFile, List exportedSymbols)
+ {
+ int count = 0;
+
+ foreach (string symbol in File.ReadLines(exportsFile))
+ {
+ exportedSymbols.Add(symbol);
+ count++;
+ }
+
+ return count;
+ }
+
+ private static void WriteExportedSymbolsArg(string exportsFile, StringBuilder linkerArgs)
+ {
+ linkerArgs.AppendLine($" \"-Wl,-exported_symbols_list {exportsFile}\"");
+ }
+
+ private static void WriteLinkerScriptArg(string exportsFile, StringBuilder linkerArgs)
+ {
+ linkerArgs.AppendLine($" \"-Wl,--version-script={exportsFile}\"");
+ }
+
+ private static void WriteLinkerScriptFile(string exportsFile, List exportedSymbols)
+ {
+ string globalExports = string.Join(";\n", exportedSymbols.Select(symbol => symbol));
+ File.WriteAllText(exportsFile,
+ Utils.GetEmbeddedResource("linker-script.txt")
+ .Replace("%GLOBAL_SYMBOLS%", globalExports));
+ }
+
+ private void WriteCMakeFileFromTemplate(string aotSources, string aotObjects, string extraSources, string linkerArgs)
+ {
+ // BundleDir
+ File.WriteAllText(Path.Combine(OutputDirectory, "CMakeLists.txt"),
+ Utils.GetEmbeddedResource("CMakeLists.txt.template")
+ .Replace("%LIBRARY_NAME%", Name)
+ .Replace("%LIBRARY_TYPE%", nativeLibraryType)
+ .Replace("%CMAKE_LANGS%", CMakeProjectLanguages)
+ .Replace("%MonoInclude%", MonoRuntimeHeaders)
+ .Replace("%AotSources%", aotSources)
+ .Replace("%AotObjects%", aotObjects)
+ .Replace("%ExtraSources%", extraSources)
+ .Replace("%LIBRARY_LINKER_ARGS%", linkerArgs));
+ }
+
+ private void WriteAssembliesToLoadList(List assemblies)
+ {
+ string content;
+
+ if (assemblies.Count == 0)
+ {
+ content = " return NULL;";
+ }
+ else
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine($" char** assembly_list = (char**)malloc({assemblies.Count.ToString()} * sizeof(char*));");
+
+ for (int i = 0; i < assemblies.Count; i++)
+ {
+ sb.AppendLine($" assembly_list[{i.ToString()}] = \"{assemblies[i]}\";");
+ }
+
+ sb.AppendLine(" return assembly_list;");
+ content = sb.ToString();
+ }
+
+ File.WriteAllText(Path.Combine(OutputDirectory, "assembly_list.c"),
+ Utils.GetEmbeddedResource("assembly_list.c")
+ .Replace("%LOADABLE_ASSEMBLIES%", content));
+ }
+
+ private string BuildLibrary()
+ {
+ string libraryOutputPath;
+
+ if (TargetOS == "android")
+ {
+ AndroidProject project = new AndroidProject("netlibrary", RuntimeIdentifier, Log);
+ project.GenerateCMake(OutputDirectory, StripDebugSymbols);
+ libraryOutputPath = project.BuildCMake(OutputDirectory, StripDebugSymbols);
+ }
+ else
+ {
+ Xcode project = new Xcode(Log, RuntimeIdentifier);
+ project.CreateXcodeProject("netlibrary", OutputDirectory);
+
+ string xcodeProjectPath = Path.Combine(OutputDirectory, "netlibrary", $"{Name}.xcodeproj");
+ libraryOutputPath = project.BuildAppBundle(xcodeProjectPath, StripDebugSymbols, "-");
+ }
+
+ return Path.Combine(libraryOutputPath, GetLibraryName());
+ }
+
+ private string GetLibraryName()
+ {
+ string libPrefix, libExtension;
+
+ if (TargetOS == "android")
+ {
+ libPrefix = "lib";
+ libExtension = (isSharedLibrary) ? ".so" : ".a";
+ }
+ else
+ {
+ libPrefix = "lib";
+ libExtension = (isSharedLibrary) ? ".dylib" : ".a";
+ }
+
+ return $"{libPrefix}{Name}{libExtension}";
+ }
+
+ private bool ValidateValidTargetOS() =>
+ TargetOS switch
+ {
+ "android" or "ios" or "tvos" or "maccatalyst" => true,
+ _ => false
+ };
+}
diff --git a/src/tasks/LibraryBuilder/LibraryBuilder.csproj b/src/tasks/LibraryBuilder/LibraryBuilder.csproj
new file mode 100644
index 0000000000000..c2a8ae77a3c51
--- /dev/null
+++ b/src/tasks/LibraryBuilder/LibraryBuilder.csproj
@@ -0,0 +1,28 @@
+
+
+ $(TargetFrameworkForNETCoreTasks)
+ Library
+ true
+ false
+ enable
+ $(NoWarn),CA1050,CA1850
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tasks/LibraryBuilder/Templates/CMakeLists.txt.template b/src/tasks/LibraryBuilder/Templates/CMakeLists.txt.template
new file mode 100644
index 0000000000000..1fa43e84a9ede
--- /dev/null
+++ b/src/tasks/LibraryBuilder/Templates/CMakeLists.txt.template
@@ -0,0 +1,57 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(%LIBRARY_NAME%)
+
+enable_language(%CMAKE_LANGS%)
+
+set(DOTNET_AOT_SOURCES
+ %AotSources%
+)
+set(DOTNET_AOT_OBJECTS
+ %AotObjects%
+)
+set(DOTNET_EXTRA_SOURCES
+ %ExtraSources%
+ assembly_list.c
+)
+
+include_directories("%MonoInclude%")
+
+add_library(
+ aot_library STATIC
+ ${DOTNET_AOT_SOURCES}
+)
+target_link_libraries(
+ aot_library
+ PUBLIC
+ ${DOTNET_AOT_OBJECTS}
+)
+
+add_library(
+ %LIBRARY_NAME% %LIBRARY_TYPE%
+ ${DOTNET_EXTRA_SOURCES}
+)
+
+if(TARGETS_ANDROID)
+ set(MOBILE_SYSTEM_LIBS
+ libz.so
+ log
+ )
+else()
+ set(MOBILE_SYSTEM_LIBS
+ "-framework Foundation"
+ "-framework Security"
+ "-framework UIKit"
+ "-lz"
+ "-lc++"
+ "-liconv"
+ )
+endif()
+
+target_link_libraries(
+ %LIBRARY_NAME%
+ PUBLIC
+ aot_library
+ ${MOBILE_SYSTEM_LIBS}
+ %LIBRARY_LINKER_ARGS%
+)
diff --git a/src/tasks/LibraryBuilder/Templates/assembly_list.c b/src/tasks/LibraryBuilder/Templates/assembly_list.c
new file mode 100644
index 0000000000000..ef2168d3e50bf
--- /dev/null
+++ b/src/tasks/LibraryBuilder/Templates/assembly_list.c
@@ -0,0 +1,9 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+#include
+#include
+
+char** get_loadable_assemblies ()
+{
+%LOADABLE_ASSEMBLIES%
+}
\ No newline at end of file
diff --git a/src/tasks/LibraryBuilder/Templates/linker-script.txt b/src/tasks/LibraryBuilder/Templates/linker-script.txt
new file mode 100644
index 0000000000000..c981474c2d4de
--- /dev/null
+++ b/src/tasks/LibraryBuilder/Templates/linker-script.txt
@@ -0,0 +1,5 @@
+NETLIBRARY
+{
+ global: %GLOBAL_SYMBOLS%;
+ local: *;
+};
\ No newline at end of file
diff --git a/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj b/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj
index a717c45f42c5a..2fad90bd30617 100644
--- a/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj
+++ b/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj
@@ -11,6 +11,7 @@
+
diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj
index e74ef15e96ba3..49ddce3238059 100644
--- a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj
+++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj
@@ -11,6 +11,11 @@
true
+
+
+
+
+
diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/ILLink.Descriptors.xml b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/ILLink.Descriptors.xml
new file mode 100644
index 0000000000000..b1259ec37acd9
--- /dev/null
+++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/ILLink.Descriptors.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs
index 9587bb1b57f80..916d1f1aa03b8 100644
--- a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs
+++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs
@@ -2,9 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Runtime.InteropServices;
public static class Program
{
+ [UnmanagedCallersOnly(EntryPoint = nameof(SayHello))]
+ public static void SayHello()
+ {
+ Console.WriteLine("Called from native! Hello!");
+ }
+
public static int Main()
{
Console.WriteLine("Hello, Android!"); // logcat
diff --git a/src/tests/FunctionalTests/iOS/Device/AOT-LLVM/ILLink.Descriptors.xml b/src/tests/FunctionalTests/iOS/Device/AOT-LLVM/ILLink.Descriptors.xml
new file mode 100644
index 0000000000000..5ddcdac6d9f45
--- /dev/null
+++ b/src/tests/FunctionalTests/iOS/Device/AOT-LLVM/ILLink.Descriptors.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/iOS/Device/AOT-LLVM/Program.cs b/src/tests/FunctionalTests/iOS/Device/AOT-LLVM/Program.cs
index 437f6843a36fc..5d5c656367b75 100644
--- a/src/tests/FunctionalTests/iOS/Device/AOT-LLVM/Program.cs
+++ b/src/tests/FunctionalTests/iOS/Device/AOT-LLVM/Program.cs
@@ -11,6 +11,12 @@ public static class Program
[DllImport("__Internal")]
public static extern void mono_ios_set_summary (string value);
+ [UnmanagedCallersOnly(EntryPoint = nameof(SayHello))]
+ public static void SayHello()
+ {
+ Console.WriteLine("Called from native! Hello!");
+ }
+
public static async Task Main(string[] args)
{
mono_ios_set_summary($"Starting functional test");
diff --git a/src/tests/FunctionalTests/iOS/Device/AOT-LLVM/iOS.Device.Aot-Llvm.Test.csproj b/src/tests/FunctionalTests/iOS/Device/AOT-LLVM/iOS.Device.Aot-Llvm.Test.csproj
index e4d28d6235995..1ece8333a99f1 100644
--- a/src/tests/FunctionalTests/iOS/Device/AOT-LLVM/iOS.Device.Aot-Llvm.Test.csproj
+++ b/src/tests/FunctionalTests/iOS/Device/AOT-LLVM/iOS.Device.Aot-Llvm.Test.csproj
@@ -10,10 +10,15 @@
iOS.Device.Aot-Llvm.Test.dll
false
42
- true
+ true
true
+
+
+
+
+
diff --git a/src/tests/build.proj b/src/tests/build.proj
index 56a58aaca9f80..597ec67d803da 100644
--- a/src/tests/build.proj
+++ b/src/tests/build.proj
@@ -236,15 +236,15 @@
DestinationFolder="$(BuildDir)" />
+ MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackDir)/native/include/mono-2.0"
+ OutputDir="$(AppDir)"
+ ProjectName="$(Category)"
+ RuntimeComponents="$(RuntimeComponents)"
+ RuntimeIdentifier="$(RuntimeIdentifier)"
+ StripDebugSymbols="$(StripDebugSymbols)">