1// Copyright 2023 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Tool for building Bundletool modules for apps and SDKs.
16package main
17
18import (
19	"archive/zip"
20	"flag"
21	"log"
22	"os"
23	"strings"
24)
25
26var (
27	internalApkPathFlag  = flag.String("internal_apk_path", "", "Path to an APK that contains the SDK classes and resources.")
28	outputModulePathFlag = flag.String("output_module_path", "", "Path to the resulting module, ready to be sent to Bundletool.")
29)
30
31func main() {
32	flag.Parse()
33	if *internalApkPathFlag == "" {
34		log.Fatal("Missing internal APK path")
35	}
36
37	if *internalApkPathFlag == "" {
38		log.Fatal("Missing ouput module path")
39	}
40	err := unzipApkAndCreateModule(*internalApkPathFlag, *outputModulePathFlag)
41	if err != nil {
42		log.Fatal(err)
43	}
44}
45
46func unzipApkAndCreateModule(internalApkPath, outputModulePath string) error {
47	r, err := zip.OpenReader(internalApkPath)
48	if err != nil {
49		return err
50	}
51	defer r.Close()
52
53	w, err := os.Create(outputModulePath)
54	if err != nil {
55		return err
56	}
57	defer w.Close()
58	zipWriter := zip.NewWriter(w)
59	defer zipWriter.Close()
60
61	for _, f := range r.File {
62		f.Name = fileNameInOutput(f.Name)
63		if err := zipWriter.Copy(f); err != nil {
64			return err
65		}
66	}
67	return nil
68}
69
70func fileNameInOutput(oldName string) string {
71	switch {
72	// Passthrough files. They will just be copied into the output module.
73	case oldName == "resources.pb" ||
74		strings.HasPrefix(oldName, "res/") ||
75		strings.HasPrefix(oldName, "assets/") ||
76		strings.HasPrefix(oldName, "lib/"):
77		return oldName
78	// Manifest should be moved to manifest/ dir.
79	case oldName == "AndroidManifest.xml":
80		return "manifest/AndroidManifest.xml"
81	// Dex files need to be moved under dex/ dir.
82	case strings.HasSuffix(oldName, ".dex"):
83		return "dex/" + oldName
84	// All other files (probably JVM metadata files) should be moved to root/ dir.
85	default:
86		return "root/" + oldName
87	}
88}
89