• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright (C) 2021 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import argparse
18import os
19import shutil
20import subprocess
21import sys
22import tempfile
23
24def _create_apex(args, work_dir):
25
26    image_apex_dir = "image.apex"
27
28    # Used for creating canned_fs_config, since every file and dir in the APEX are represented
29    # by an entry in the fs_config.
30    apex_subdirs = []
31    apex_filepaths = []
32
33    input_dir = os.path.join(work_dir, image_apex_dir)
34    os.makedirs(input_dir, exist_ok=True)
35    bazel_apexer_wrapper_manifest = open(args.bazel_apexer_wrapper_manifest, 'r')
36    file_lines = bazel_apexer_wrapper_manifest.readlines()
37    for line in file_lines:
38        line = line.strip()
39        if (len(line) == 0):
40            continue
41        apex_dirname, apex_filename, bazel_input_file = line.split(",")
42        full_apex_dirname = "/".join([input_dir, apex_dirname])
43        os.makedirs(full_apex_dirname, exist_ok=True)
44
45        apex_filepath = "/".join([apex_dirname, apex_filename])
46        apex_filepaths.append(apex_filepath)
47        apex_subdirs.append(apex_dirname)
48
49        full_apex_filepath = "/".join([input_dir, apex_filepath])
50        # Because Bazel execution root is a symlink forest, all the input files are symlinks, these
51        # include the dependency files declared in the BUILD files as well as the files declared
52        # and created in the bzl files. For sandbox runs the former are two or more level symlinks and
53        # latter are one level symlinks. For non-sandbox runs, the former are one level symlinks
54        # and the latter are actual files. Here are some examples:
55        #
56        # Two level symlinks:
57        # system/timezone/output_data/version/tz_version ->
58        # /usr/local/google/home/...out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
59        # execroot/__main__/system/timezone/output_data/version/tz_version ->
60        # /usr/local/google/home/.../system/timezone/output_data/version/tz_version
61        #
62        # Three level symlinks:
63        # bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/libcrypto.so ->
64        # /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
65        # execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/libcrypto.so ->
66        # /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
67        # execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/
68        # liblibcrypto_stripped.so ->
69        # /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
70        # execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/
71        # liblibcrypto_unstripped.so
72        #
73        # One level symlinks:
74        # bazel-out/android_target-fastbuild/bin/system/timezone/apex/apex_manifest.pb ->
75        # /usr/local/google/home/.../out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
76        # execroot/__main__/bazel-out/android_target-fastbuild/bin/system/timezone/apex/
77        # apex_manifest.pb
78
79        if os.path.islink(bazel_input_file):
80            bazel_input_file = os.readlink(bazel_input_file)
81
82            # For sandbox run these are the 2nd level symlinks and we need to resolve
83            while os.path.islink(bazel_input_file) and 'execroot/__main__' in bazel_input_file:
84                bazel_input_file = os.readlink(bazel_input_file)
85
86        shutil.copyfile(bazel_input_file, full_apex_filepath, follow_symlinks=False)
87
88    # Make sure subdirs are unique
89    apex_subdirs_set = set()
90    for d in apex_subdirs:
91        apex_subdirs_set.add(d)
92
93        # Make sure all the parent dirs of the current subdir are in the set, too
94        dirs = d.split("/")
95        for i in range(0, len(dirs)):
96            apex_subdirs_set.add("/".join(dirs[:i]))
97
98    canned_fs_config = _generate_canned_fs_config(work_dir, apex_subdirs_set, apex_filepaths)
99
100    # Construct the main apexer command.
101    cmd = [args.apexer_path]
102    cmd.append('--verbose')
103    cmd.append('--force')
104    cmd.append('--include_build_info')
105    cmd.extend(['--file_contexts', args.file_contexts])
106    cmd.extend(['--canned_fs_config', canned_fs_config])
107    cmd.extend(['--key', args.key])
108    cmd.extend(['--payload_type', 'image'])
109    cmd.extend(['--target_sdk_version', '10000'])
110    cmd.extend(['--payload_fs_type', 'ext4'])
111    cmd.extend(['--apexer_tool_path', args.apexer_tool_paths])
112
113    if args.android_manifest != None:
114        cmd.extend(['--android_manifest', args.android_manifest])
115
116    if args.pubkey != None:
117        cmd.extend(['--pubkey', args.pubkey])
118
119    if args.manifest != None:
120        cmd.extend(['--manifest', args.manifest])
121
122    if args.min_sdk_version != None:
123        cmd.extend(['--min_sdk_version', args.min_sdk_version])
124
125    if args.android_jar_path != None:
126        cmd.extend(['--android_jar_path', args.android_jar_path])
127
128    cmd.append(input_dir)
129    cmd.append(args.apex_output_file)
130
131    popen = subprocess.Popen(cmd)
132    popen.wait()
133
134    return True
135
136# Generate filesystem config. This encodes the filemode, uid, and gid of each
137# file in the APEX, including apex_manifest.json and apex_manifest.pb.
138#
139# NOTE: every file must have an entry.
140def _generate_canned_fs_config(work_dir, dirs, filepaths):
141    with tempfile.NamedTemporaryFile(mode = 'w+', dir=work_dir, delete=False) as canned_fs_config:
142        config_lines = []
143        config_lines += ["/ 1000 1000 0755"]
144        config_lines += ["/apex_manifest.json 1000 1000 0644"]
145        config_lines += ["/apex_manifest.pb 1000 1000 0644"]
146        config_lines += ["/" + filepath + " 1000 1000 0644" for filepath in filepaths]
147        config_lines += ["/" + d + " 0 2000 0755" for d in dirs]
148        canned_fs_config.write("\n".join(config_lines))
149
150    return canned_fs_config.name
151
152def _parse_args(argv):
153    parser = argparse.ArgumentParser(description='Build an APEX file')
154
155    parser.add_argument(
156        '--manifest',
157        help='path to the APEX manifest file (.pb)')
158    parser.add_argument(
159        '--apex_output_file',
160        required=True,
161        help='path to the APEX image file')
162    parser.add_argument(
163        '--bazel_apexer_wrapper_manifest',
164        required=True,
165        help='path to the manifest file that stores the info about the files to be packaged by apexer')
166    parser.add_argument(
167        '--android_manifest',
168        help='path to the AndroidManifest file. If omitted, a default one is created and used')
169    parser.add_argument(
170        '--file_contexts',
171        required=True,
172        help='selinux file contexts file.')
173    parser.add_argument(
174        '--key',
175        required=True,
176        help='path to the private key file.')
177    parser.add_argument(
178        '--pubkey',
179        help='path to the public key file. Used to bundle the public key in APEX for testing.')
180    parser.add_argument(
181        '--apexer_path',
182        required=True,
183        help='Path to the apexer binary.')
184    parser.add_argument(
185        '--apexer_tool_paths',
186        required=True,
187        help='Directories containing all the tools used by apexer, separated by ":" character.')
188    parser.add_argument(
189        '--min_sdk_version',
190        help='Default Min SDK version to use for AndroidManifest.xml')
191    parser.add_argument(
192        '--android_jar_path',
193        help='path to use as the source of the android API.')
194
195    return parser.parse_args(argv)
196
197def main(argv):
198    args = _parse_args(argv)
199
200    with tempfile.TemporaryDirectory() as work_dir:
201        success = _create_apex(args, work_dir)
202
203    if not success:
204        sys.exit(1)
205
206if __name__ == '__main__':
207    main(sys.argv[1:])
208