-
Notifications
You must be signed in to change notification settings - Fork 4.8k
/
Copy pathAndroidProject.cs
162 lines (133 loc) · 5.54 KB
/
AndroidProject.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// 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.IO;
using System.Text;
using Microsoft.Android.Build.Ndk;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.Android.Build
{
public 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;
private string targetArchitecture;
public string Abi => abi;
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);
targetArchitecture = GetTargetArchitecture(runtimeIdentifier);
this.logger = logger;
this.projectName = projectName;
}
// builds using NDK toolchain
public void Build(string workingDir, AndroidBuildOptions buildOptions, bool stripDebugSymbols = false, string apiLevel = DefaultMinApiLevel)
{
NdkTools tools = new NdkTools(targetArchitecture, GetHostOS(), apiLevel);
string clangArgs = BuildClangArgs(buildOptions);
Utils.RunProcess(logger, tools.ClangPath, workingDir: workingDir, args: clangArgs);
}
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 BuildClangArgs(AndroidBuildOptions buildOptions)
{
StringBuilder ret = new StringBuilder();
foreach(string compilerArg in buildOptions.CompilerArguments)
{
ret.Append(compilerArg);
ret.Append(' ');
}
foreach(string includeDir in buildOptions.IncludePaths)
{
ret.Append($"-I {includeDir} ");
}
foreach(string linkerArg in buildOptions.LinkerArguments)
{
ret.Append($"-Xlinker {linkerArg} ");
}
foreach(string source in buildOptions.Sources)
{
ret.Append(source);
ret.Append(' ');
}
HashSet<string> libDirs = new HashSet<string>();
foreach(string lib in buildOptions.NativeLibraryPaths)
{
string rootPath = Path.GetDirectoryName(lib)!;
string libName = Path.GetFileName(lib);
if (!libDirs.Contains(rootPath))
{
libDirs.Add(rootPath);
ret.Append($"-L {rootPath} ");
}
ret.Append($"-l:{libName} ");
}
return ret.ToString();
}
private static string GetTargetArchitecture(string runtimeIdentifier)
{
int pos = runtimeIdentifier.IndexOf('-');
return (pos > -1) ? runtimeIdentifier.Substring(pos + 1) : "";
}
private static string GetHostOS()
{
if (Utils.IsWindows())
return "windows";
else if (Utils.IsMacOS())
return "osx";
else
return "linux";
}
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"),
};
}
}