summaryrefslogtreecommitdiff
path: root/xmake/rules/platform/android/package.lua
blob: 42e4ac80e96f3ade9a6b2e2944f193ea2a1be291 (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
--!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 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:targetdir(), "temp")
        os.mkdir(tmp_path)
        os.mkdir(path.join(tmp_path, "lib"))
        os.mkdir(path.join(tmp_path, "lib", target:arch()))

        -- copy the target library to temp folder with name libmain.so
        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
        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
        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)

        -- pack libs
        os.vrunv(aapt, {"add", "res_only.apk", "lib/" .. target:arch() .."/" .. libname},  {curdir = tmp_path})

        -- align apk
        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})

        -- sign 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, {dependfile = target:dependfile(final_apk), files = depfiles, values = {android_sdk_version, keystore_pass}})
end