From 88318ed49b6ee6f3bfe9cdba2cf7262629b96637 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Mon, 6 Mar 2023 16:55:14 -0500 Subject: [PATCH] [android][ios] Add LibraryBuilder task to support aot library mode (#81919) This change adds a build task to generate a self-contained shared or static library when using the aot compiler and will only export symbols for methods decorated with UnmanagedCallersOnly attributes (similar to NativeAOT). Contributes to #79377 --- Directory.Build.props | 2 + eng/testing/tests.android.targets | 2 +- eng/testing/tests.ioslike.targets | 2 + src/libraries/sendtohelix-mobile.targets | 2 + .../android/build/AndroidApp.InTree.targets | 4 + .../msbuild/android/build/AndroidApp.props | 3 + .../msbuild/android/build/AndroidApp.targets | 111 ++++-- .../apple/build/AppleApp.InTree.targets | 21 +- .../apple/build/AppleApp.LocalBuild.props | 3 + .../apple/build/AppleApp.LocalBuild.targets | 13 +- src/mono/msbuild/apple/build/AppleApp.props | 4 + src/mono/msbuild/apple/build/AppleApp.targets | 145 +++++--- .../msbuild/apple/data/Directory.Build.props | 4 + .../apple/data/Directory.Build.targets | 1 + src/mono/msbuild/common/LibraryBuilder.props | 2 + .../msbuild/common/LibraryBuilder.targets | 33 ++ .../sample/Android/AndroidSampleApp.csproj | 17 +- .../AndroidAppBuilder/AndroidAppBuilder.cs | 32 +- .../AndroidAppBuilder.csproj | 1 + src/tasks/AndroidAppBuilder/ApkBuilder.cs | 213 ++++++------ .../Templates/CMakeLists-android.txt | 14 +- .../Templates/MonoRunner.java | 2 +- src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 1 + .../AotCompilerTask/MonoAOTCompiler.props | 4 +- src/tasks/AppleAppBuilder/AppleAppBuilder.cs | 25 +- .../Templates/CMakeLists.txt.template | 2 +- src/tasks/AppleAppBuilder/Xcode.cs | 85 +++-- src/tasks/Common/Builders/AndroidProject.cs | 91 +++++ src/tasks/Common/Builders/AppBuilderTask.cs | 98 ++++++ src/tasks/Common/Builders/CompiledAssembly.cs | 44 +++ src/tasks/LibraryBuilder/LibraryBuilder.cs | 317 ++++++++++++++++++ .../LibraryBuilder/LibraryBuilder.csproj | 28 ++ .../Templates/CMakeLists.txt.template | 57 ++++ .../LibraryBuilder/Templates/assembly_list.c | 9 + .../Templates/linker-script.txt | 5 + .../TestExclusionListTasks.csproj | 1 + ...droid.Device_Emulator.Aot_Llvm.Test.csproj | 5 + .../AOT_LLVM/ILLink.Descriptors.xml | 7 + .../Device_Emulator/AOT_LLVM/Program.cs | 7 + .../Device/AOT-LLVM/ILLink.Descriptors.xml | 7 + .../iOS/Device/AOT-LLVM/Program.cs | 6 + .../AOT-LLVM/iOS.Device.Aot-Llvm.Test.csproj | 7 +- src/tests/build.proj | 14 +- 43 files changed, 1171 insertions(+), 280 deletions(-) create mode 100644 src/mono/msbuild/common/LibraryBuilder.props create mode 100644 src/mono/msbuild/common/LibraryBuilder.targets create mode 100644 src/tasks/Common/Builders/AndroidProject.cs create mode 100644 src/tasks/Common/Builders/AppBuilderTask.cs create mode 100644 src/tasks/Common/Builders/CompiledAssembly.cs create mode 100644 src/tasks/LibraryBuilder/LibraryBuilder.cs create mode 100644 src/tasks/LibraryBuilder/LibraryBuilder.csproj create mode 100644 src/tasks/LibraryBuilder/Templates/CMakeLists.txt.template create mode 100644 src/tasks/LibraryBuilder/Templates/assembly_list.c create mode 100644 src/tasks/LibraryBuilder/Templates/linker-script.txt create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/ILLink.Descriptors.xml create mode 100644 src/tests/FunctionalTests/iOS/Device/AOT-LLVM/ILLink.Descriptors.xml 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)">