summaryrefslogtreecommitdiff
path: root/xmake/rules/platform/android/package.lua
blob: d38fcecea93cdd5f22ec5b80519447f48f6efcb5 (plain)
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
--!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      keosu
-- @file        package.lua
--

-- imports
import("core.project.depend")
import("utils.progress")

function _get_libname(android_manifest)
    local libname = "libmain.so"
    local manifest_xml = io.readfile(android_manifest)
    if manifest_xml then
        local lib_name_meta = manifest_xml:match('android:name="android.app.lib_name"%s+android:value="([^"]+)"')
        if lib_name_meta then
            libname = "lib" .. lib_name_meta .. ".so"
        end
    end
    return libname
end

function _pack_resources(target, aapt, android_manifest, android_res, android_assets, android_sdkdir, android_sdk_version, tmp_path)
    local resonly_apk = path.join(tmp_path, "res_only.apk")
    local androidjar = path.join(android_sdkdir, "platforms", string.format("android-%s", android_sdk_version), "android.jar")
    assert(os.isfile(androidjar), "%s not found", androidjar)
    
    local aapt_argv = {"package", "-f", "-M", android_manifest, "-I", androidjar, "-F", resonly_apk}
    if android_res and not os.emptydir(android_res) then
        table.insert(aapt_argv, "-S")
        table.insert(aapt_argv, android_res)
    end
    if android_assets and not os.emptydir(android_assets) then
        table.insert(aapt_argv, "-A")
        table.insert(aapt_argv, android_assets)
    end
    os.vrunv(aapt, aapt_argv)
end

function _pack_libs(target, aapt, libname, tmp_path)
    os.vrunv(aapt, {"add", "res_only.apk", "lib/" .. target:arch() .."/" .. libname},  {curdir = tmp_path})
end

function _align_apk(zipalign, tmp_path)
    local aligned_apk = path.join(tmp_path, "unsigned.apk")
    local zipalign_argv = {"-f", "4", "res_only.apk", "unsigned.apk"}
    os.vrunv(zipalign, zipalign_argv, {curdir = tmp_path})
end

function _sign_apk(apksigner, keystore, keystore_pass, final_apk, tmp_path)
    local aligned_apk = path.join(tmp_path, "unsigned.apk")
    local apksigner_argv = {"sign", "--ks", keystore, "--ks-pass", string.format("pass:%s", keystore_pass), "--out", final_apk, "--in", aligned_apk}
    os.vrunv(apksigner, apksigner_argv)
end

function main(target, opt)
    local conf = target:extraconf("rules", "android.native_app")
    local android_sdk_version = conf.android_sdk_version
    local android_manifest = conf.android_manifest
    local android_res = conf.android_res
    local android_assets = conf.android_assets
    local keystore = conf.keystore
    local keystore_pass = conf.keystore_pass

    assert(android_sdk_version, "android sdk version not set")
    assert(android_manifest, "android manifest not set")

    if not path.is_absolute(android_manifest) then
        android_manifest = path.join(target:scriptdir(), android_manifest)
    end
    if keystore and not path.is_absolute(keystore) then
        keystore = path.join(target:scriptdir(), keystore)
    end
    if android_res and not path.is_absolute(android_res) then
        android_res = path.join(target:scriptdir(), android_res)
    end
    if android_assets and not path.is_absolute(android_assets) then
        android_assets = path.join(target:scriptdir(), android_assets)
    end

    local final_apk = path.join(target:targetdir(), target:basename() .. ".apk")

    -- add dependencies
    local depfiles = {target:targetfile(), android_manifest, keystore}
    if android_res and os.isdir(android_res) then
        table.join2(depfiles, os.files(path.join(android_res, "**")))
    end
    if android_assets and os.isdir(android_assets) then
        table.join2(depfiles, os.files(path.join(android_assets, "**")))
    end

    depend.on_changed(function ()
        progress.show(opt.progress, "${color.build.target}packing.apk %s", final_apk)

        local tmp_path = path.join(target:autogendir(), "packing")
        os.tryrm(tmp_path)
        os.mkdir(tmp_path)
        os.mkdir(path.join(tmp_path, "lib"))
        os.mkdir(path.join(tmp_path, "lib", target:arch()))

        -- copy the target library
        local libname = _get_libname(android_manifest)
        local libfile = path.join(tmp_path, "lib", target:arch(), libname)
        os.cp(target:targetfile(), libfile)

        -- get android tool path
        local android_sdkdir = target:toolchain("ndk"):config("android_sdk")
        local android_build_toolver = target:toolchain("ndk"):config("build_toolver")
        local sdk_tool_path = path.join(android_sdkdir, "build-tools", android_build_toolver)

        local aapt = path.join(sdk_tool_path, "aapt" .. (is_host("windows") and ".exe" or ""))
        local zipalign = path.join(sdk_tool_path, "zipalign" .. (is_host("windows") and ".exe" or ""))
        local apksigner = path.join(sdk_tool_path, "apksigner" .. (is_host("windows") and ".bat" or ""))

        _pack_resources(target, aapt, android_manifest, android_res, android_assets, android_sdkdir, android_sdk_version, tmp_path)
        _pack_libs(target, aapt, libname, tmp_path)
        _align_apk(zipalign, tmp_path)
        _sign_apk(apksigner, keystore, keystore_pass, final_apk, tmp_path)

    end, {dependfile = target:dependfile(final_apk), files = depfiles, values = {android_sdk_version, keystore_pass}})
end