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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
|
--!A cross-platform build utility based on Lua
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
-- @author ruki
-- @file android.lua
--
-- imports
import("core.theme.theme")
import("core.base.option")
import("core.base.semver")
import("core.project.config")
import("core.project.depend")
import("core.tool.toolchain")
import("lib.detect.find_file")
import("utils.progress")
-- escape path
function _escape_path(p)
return os.args(p, {escape = true})
end
-- deploy application package for android
function main(target, opt)
-- get ndk toolchain
local toolchain_ndk = toolchain.load("ndk", {plat = target:plat(), arch = target:arch()})
-- get target apk path
local target_apk = path.join(target:targetdir(), target:basename() .. ".apk")
-- need re-generate this apk?
local targetfile = target:targetfile()
local dependfile = target:dependfile(target_apk)
local dependinfo = target:is_rebuilt() and {} or (depend.load(dependfile) or {})
if not depend.is_changed(dependinfo, {lastmtime = os.mtime(dependfile)}) then
return
end
-- trace progress info
progress.show(opt.progress, "${color.build.target}generating.qt.app %s.apk", target:basename())
-- get qt sdk
local qt = target:data("qt")
-- get ndk
local ndk = path.translate(assert(toolchain_ndk:config("ndk"), "cannot get NDK!"))
local ndk_sdkver = assert(toolchain_ndk:config("ndk_sdkver"), "cannot get the sdk version of NDK!")
-- get ndk host
local ndk_host = os.host() .. "-" .. os.arch()
if is_host("windows") then
ndk_host = os.arch() == "x64" and "windows-x86_64" or "windows-x86"
elseif is_host("macosx") then
ndk_host = "darwin-x86_64"
elseif is_host("linux") then
ndk_host = "linux-x86_64"
end
-- get androiddeployqt
local search_dirs = {}
if qt.bindir_host then table.insert(search_dirs, qt.bindir_host) end
if qt.bindir then table.insert(search_dirs, qt.bindir) end
local androiddeployqt = find_file("androiddeployqt" .. (is_host("windows") and ".exe" or ""), search_dirs)
assert(os.isexec(androiddeployqt), "androiddeployqt not found!")
-- get working directory
local workdir = path.join(config.builddir(), ".qt", "app", "android", target:name())
-- get android-build directory
local android_buildir = path.join(workdir, "android-build")
-- get android platform
local android_platform = "android-" .. tostring(ndk_sdkver)
-- get java home
local java_home = assert(os.getenv("JAVA_HOME"), "please set $JAVA_HOME environment variable first!")
-- get android sdk directory
local android_sdkdir = path.translate(assert(toolchain_ndk:config("android_sdk"), "please run `xmake f --android_sdk=xxx` to set the android sdk directory!"))
-- get android build-tools version
local android_build_toolver = assert(toolchain_ndk:config("build_toolver"), "please run `xmake f --build_toolver=xxx` to set the android build-tools version!")
-- get qt sdk version
local qt_sdkver = config.get("qt_sdkver")
if qt_sdkver then
qt_sdkver = try { function () return semver.new(qt_sdkver) end}
end
-- get the target architecture
local target_archs =
{
["armv5te"] = "armeabi" -- deprecated
, ["armv7-a"] = "armeabi-v7a" -- deprecated
, ["armeabi"] = "armeabi"
, ["armeabi-v7a"] = "armeabi-v7a"
, ["arm64-v8a"] = "arm64-v8a"
, i386 = "x86"
, x86_64 = "x86_64"
, mips = "mips" -- removed in ndk r71
, mips64 = "mips64" -- removed in ndk r71
}
local target_arch = assert(target_archs[config.arch()], "unsupport target arch(%s)!", config.arch())
-- install target to android-build/libs first
if qt_sdkver and qt_sdkver:ge("5.14") then
-- we need to copy target to android-build/libs/armeabi/libxxx_armeabi.so after Qt 5.14.0
os.cp(target:targetfile(), path.join(android_buildir, "libs", target_arch, "lib" .. target:basename() .. "_" .. target_arch .. ".so"))
else
os.cp(target:targetfile(), path.join(android_buildir, "libs", target_arch, path.filename(target:targetfile())))
end
-- get stdcpp path
local stdcpp_path = path.join(ndk, "sources/cxx-stl/llvm-libc++/libs", target_arch, "libc++_shared.so")
if qt_sdkver and qt_sdkver:ge("5.14") then
local ndk_sysroot = assert(toolchain_ndk:config("ndk_sysroot"), "NDK sysroot directory not found!")
stdcpp_path = path.join(ndk_sysroot, "usr", "lib")
end
-- get toolchain version
local ndk_toolchains_ver = toolchain_ndk:config("ndk_toolchains_ver") or "4.9"
-- generate android-deployment-settings.json file
local android_deployment_settings = path.join(workdir, "android-deployment-settings.json")
local settings_file = io.open(android_deployment_settings, "w")
if settings_file then
settings_file:print('{')
settings_file:print(' "description": "This file is generated by qmake to be read by androiddeployqt and should not be modified by hand.",')
settings_file:print(' "qt": "%s",', _escape_path(qt.sdkdir))
settings_file:print(' "sdk": "%s",', _escape_path(android_sdkdir))
settings_file:print(' "ndk": "%s",', _escape_path(ndk))
settings_file:print(' "sdkBuildToolsRevision": "%s",', android_build_toolver)
settings_file:print(' "toolchain-prefix": "llvm",')
settings_file:print(' "tool-prefix": "llvm",')
settings_file:print(' "toolchain-version": "%s",', ndk_toolchains_ver)
settings_file:print(' "stdcpp-path": "%s",', _escape_path(stdcpp_path))
settings_file:print(' "ndk-host": "%s",', ndk_host)
settings_file:print(' "target-architecture": "%s",', target_arch)
settings_file:print(' "qml-root-path": "%s",', _escape_path(os.projectdir()))
-- for 6.2.x
local search_dirs = {}
if qt.libexecdir_host then table.insert(search_dirs, qt.libexecdir_host) end
if qt.libexecdir then table.insert(search_dirs, qt.libexecdir) end
local qmlimportscanner = find_file("qmlimportscanner" .. (is_host("windows") and ".exe" or ""), search_dirs)
if os.isexec(qmlimportscanner) then
settings_file:print(' "qml-importscanner-binary": "%s",', _escape_path(qmlimportscanner))
end
-- for 6.3.x
local search_dirs = {}
if qt.bindir_host then table.insert(search_dirs, qt.bindir_host) end
if qt.bindir then table.insert(search_dirs, qt.bindir) end
if qt.libexecdir_host then table.insert(search_dirs, qt.libexecdir_host) end
if qt.libexecdir then table.insert(search_dirs, qt.libexecdir) end
local rcc = find_file("rcc" .. (is_host("windows") and ".exe" or ""), search_dirs)
if os.isexec(rcc) then
settings_file:print(' "rcc-binary": "%s",', _escape_path(rcc))
end
local platformplugin = find_file("libplugins_platforms_qtforandroid_" .. target_arch .. "*", path.join(qt.sdkdir, "plugins", "platforms"))
if platformplugin then
settings_file:print(' "deployment-dependencies": {"%s":"%s"},', target_arch, _escape_path(platformplugin))
end
local minsdkversion = target:values("qt.android.minsdkversion")
if minsdkversion then
settings_file:print(' "android-min-sdk-version": "%s",', tostring(minsdkversion))
end
local targetsdkversion = target:values("qt.android.targetsdkversion")
if targetsdkversion then
settings_file:print(' "android-target-sdk-version": "%s",', tostring(targetsdkversion))
end
settings_file:print(' "useLLVM": true,')
if qt_sdkver and qt_sdkver:ge("5.14") then
-- @see https://codereview.qt-project.org/c/qt-creator/qt-creator/+/287145
local triples =
{
["armv5te"] = "arm-linux-androideabi" -- deprecated
, ["armv7-a"] = "arm-linux-androideabi" -- deprecated
, ["armeabi"] = "arm-linux-androideabi" -- removed in ndk r17
, ["armeabi-v7a"] = "arm-linux-androideabi"
, ["arm64-v8a"] = "aarch64-linux-android"
, i386 = "i686-linux-android" -- deprecated
, x86 = "i686-linux-android"
, x86_64 = "x86_64-linux-android"
, mips = "mips-linux-android" -- removed in ndk r17
, mips64 = "mips64-linux-android" -- removed in ndk r17
}
settings_file:print(' "architectures": {"%s":"%s"},', target_arch, triples[target_arch])
settings_file:print(' "application-binary": "%s"', target:basename())
else
settings_file:print(' "application-binary": "%s"', _escape_path(target:targetfile()))
end
settings_file:print('}')
settings_file:close()
end
if option.get("verbose") and option.get("diagnosis") then
io.cat(android_deployment_settings)
end
-- do deploy
local argv = {"--input", android_deployment_settings,
"--output", android_buildir,
"--jdk", java_home,
"--gradle", "--no-gdbserver"}
if option.get("verbose") and option.get("diagnosis") then
table.insert(argv, "--verbose")
end
-- add user flags
local user_flags = target:values("qt.deploy.flags") or {}
if user_flags then
argv = table.join(argv, user_flags)
end
os.vrunv(androiddeployqt, argv)
-- output apk
os.cp(path.join(android_buildir, "build", "outputs", "apk", "debug", "android-build-debug.apk"), target_apk)
-- show apk output path
vprint("the apk output path: %s", target_apk)
-- update files and values to the dependent file
dependinfo.files = {targetfile}
depend.save(dependinfo, dependfile)
end
|