]> git.tdb.fi Git - builder.git/blob - source/androidtools.cpp
Refactor logger to do message formatting internally
[builder.git] / source / androidtools.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/core/environ.h>
3 #include <msp/fs/dir.h>
4 #include <msp/fs/stat.h>
5 #include <msp/strings/format.h>
6 #include <msp/strings/lexicalcast.h>
7 #include <msp/strings/utils.h>
8 #include "androidarchiver.h"
9 #include "androidassetpackagingtool.h"
10 #include "androidcompiler.h"
11 #include "androidlinker.h"
12 #include "androidmanifestgenerator.h"
13 #include "androidtools.h"
14 #include "apkbuilder.h"
15 #include "architecture.h"
16 #include "builder.h"
17 #include "jarsigner.h"
18
19 using namespace std;
20 using namespace Msp;
21
22 // TODO Mark problems somewhere instead of throwing exceptions
23
24 unsigned parse_version(const std::string &version_str)
25 {
26         vector<string> version_parts = split(version_str, '.');
27         unsigned version = lexical_cast<unsigned>(version_parts[0])<<16;
28         if(version_parts.size()>1)
29                 version += lexical_cast<unsigned>(version_parts[1])<<8;
30         if(version_parts.size()>2)
31                 version += lexical_cast<unsigned>(version_parts[2]);
32         return version;
33 }
34
35
36 AndroidDevKit::AndroidDevKit(Builder &b, const string &type, const FS::Path &default_path):
37         builder(b)
38 {
39         string var = format("ANDROID_%s_ROOT", type);
40         root = getenv(var);
41         if(root.empty())
42         {
43                 if(!default_path.empty() && FS::exists(default_path))
44                         root = default_path;
45                 else
46                 {
47                         builder.get_logger().log("problems", "Android %s not found", type);
48                         return;
49                 }
50         }
51
52         FS::Path platforms_dir = root/"platforms";
53         if(!FS::exists(platforms_dir))
54                 return;
55
56         builder.get_logger().log("files", "Traversing %s", platforms_dir.str());
57         supported_api_levels = 0;
58         for(const string &p: list_filtered(platforms_dir, "^android-[1-9][0-9]*$"))
59         {
60                 unsigned api = lexical_cast<unsigned>(p.substr(8));
61                 if(api>63)
62                         builder.get_logger().log("problems", "API level %d is too high", api);
63                 else
64                         supported_api_levels |= 1<<api;
65         }
66 }
67
68 void AndroidDevKit::select_api_level(unsigned api)
69 {
70         if(!(supported_api_levels&(1<<api)))
71                 throw invalid_argument("AndroidDevKit::select_api_level");
72
73         init_api_level(api);
74 }
75
76
77 AndroidSdk::AndroidSdk(Builder &b):
78         AndroidDevKit(b, "SDK")
79 {
80         find_build_tools_dir();
81 }
82
83 void AndroidSdk::find_build_tools_dir()
84 {
85         FS::Path bt_dir = root/"build-tools";
86         if(!FS::exists(bt_dir))
87         {
88                 builder.get_logger().log("problems", "Android build-tools not found");
89                 return;
90         }
91
92         builder.get_logger().log("files", "Traversing %s", bt_dir.str());
93         string use_tools;
94         unsigned latest_version = 0;
95         for(const string &v: list_files(bt_dir))
96         {
97                 unsigned version = parse_version(v);
98                 if(version>latest_version)
99                 {
100                         use_tools = v;
101                         latest_version = version;
102                 }
103         }
104
105         if(use_tools.empty())
106         {
107                 builder.get_logger().log("problems", "Android build-tools not found");
108                 return;
109         }
110
111         build_tools_dir = bt_dir/use_tools;
112         builder.get_logger().log("tools", "Android build-tools found in %s", build_tools_dir.str());
113 }
114
115 void AndroidSdk::init_api_level(unsigned api)
116 {
117         platform_jar = root/"platforms"/format("android-%d", api)/"android.jar";
118 }
119
120
121 AndroidNdk::AndroidNdk(Builder &b, const Architecture &a, const AndroidSdk &sdk):
122         AndroidDevKit(b, "NDK", create_default_path(sdk)),
123         architecture(a)
124 {
125         if(!root.empty())
126         {
127                 FS::Path csr = root/"sysroot";
128                 if(FS::exists(csr))
129                 {
130                         common_sysroot = csr;
131                         builder.get_logger().log("tools", "Android NDK common sysroot is %s", common_sysroot);
132                 }
133         }
134
135         find_toolchain_dir();
136 }
137
138 FS::Path AndroidNdk::create_default_path(const AndroidSdk &sdk)
139 {
140         if(sdk.get_root_dir().empty())
141                 return FS::Path();
142         return sdk.get_root_dir()/"ndk-bundle";
143 }
144
145 void AndroidNdk::find_toolchain_dir()
146 {
147         if(root.empty())
148                 return;
149
150         FS::Path toolchains_dir = root/"toolchains";
151         if(!FS::exists(toolchains_dir))
152         {
153                 builder.get_logger().log("problems", "Android NDK toolchains not found");
154                 return;
155         }
156
157         builder.get_logger().log("files", "Traversing %s", toolchains_dir.str());
158         string prefix = architecture.get_cross_prefix()+"-";
159         string use_toolchain;
160         unsigned latest_version = 0;
161         for(const string &t: list_filtered(toolchains_dir, "^"+prefix))
162         {
163                 string version_str = t.substr(prefix.size());
164                 string compiler = "gcc";
165                 if(!isdigit(version_str[0]))
166                 {
167                         unsigned j;
168                         for(j=1; (j<version_str.size() && !isdigit(version_str[j])); ++j) ;
169                         compiler = version_str.substr(0, j);
170                         version_str = version_str.substr(j);
171                 }
172
173                 if(compiler!="gcc")
174                         continue;
175
176                 unsigned version = parse_version(version_str);
177                 if(version>latest_version)
178                 {
179                         use_toolchain = t;
180                         latest_version = version;
181                 }
182         }
183
184         if(use_toolchain.empty())
185         {
186                 builder.get_logger().log("problems", "No applicable Android NDK toolchains found");
187                 return;
188         }
189
190         const Architecture &native_arch = builder.get_native_arch();
191
192         FS::Path tc_archs_dir = toolchains_dir/use_toolchain/"prebuilt";
193         builder.get_logger().log("files", "Traversing %s", tc_archs_dir.str());
194         string use_arch = native_arch.best_match(list_files(tc_archs_dir));
195
196         if(use_arch.empty())
197         {
198                 builder.get_logger().log("problems", "No applicable Android NDK toolchains found");
199                 return;
200         }
201
202         bin_dir = toolchains_dir/use_toolchain/"prebuilt"/use_arch/"bin";
203         builder.get_logger().log("tools", "Android NDK toolchain binaries found in %s", bin_dir.str());
204 }
205
206 void AndroidNdk::init_api_level(unsigned api)
207 {
208         FS::Path platform_archs_dir = root/"platforms"/format("android-%d", api);
209         builder.get_logger().log("files", "Traversing %s", platform_archs_dir.str());
210         vector<string> platform_archs = list_filtered(platform_archs_dir, "^arch-");
211         for(string &a: platform_archs)
212                 a.erase(0, 5);
213         string use_arch = architecture.best_match(platform_archs);
214
215         if(use_arch.empty())
216         {
217                 builder.get_logger().log("problems", "No matching Android NDK platform found");
218                 return;
219         }
220
221         platform_sysroot = platform_archs_dir/("arch-"+use_arch);
222         builder.get_logger().log("tools", "Android NDK platform sysroot is %s", platform_sysroot);
223 }
224
225
226 AndroidTools::AndroidTools(Builder &builder, const Architecture &arch):
227         sdk(builder),
228         ndk(builder, arch, sdk)
229 {
230         if(uint64_t common_api_levels = sdk.get_supported_api_levels()&ndk.get_supported_api_levels())
231         {
232                 unsigned api = 0;
233                 for(unsigned i=32; i>0; i>>=1)
234                         api += i*!!(common_api_levels>>(api+i));
235                 builder.get_logger().log("tools", "Using Android API level %d", api);
236                 sdk.select_api_level(api);
237                 ndk.select_api_level(api);
238         }
239         else
240                 builder.get_logger().log("problems", "No usable Android platforms found");
241
242         add_tool(new AndroidCompiler(builder, arch, "CC", ndk));
243         add_tool(new AndroidCompiler(builder, arch, "CXX", ndk));
244         add_tool(new AndroidLinker(builder, arch, ndk));
245         add_tool(new AndroidArchiver(builder, arch, ndk));
246         add_tool(new AndroidManifestGenerator(builder));
247         add_tool(new AndroidAssetPackagingTool(builder, sdk));
248         add_tool(new ApkBuilder(builder));
249         add_tool(new JarSigner(builder));
250 }