xref: /aosp_15_r20/build/soong/zip/cmd/main.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright 2015 Google Inc. 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// soong_zip is a utility used during the build to create a zip archive by pulling the entries from
16// various sources:
17//  * explicitly specified files
18//  * files whose paths are read from a file
19//  * directories traversed recursively
20// It can optionally change the recorded path of an entry.
21
22package main
23
24import (
25	"flag"
26	"fmt"
27	"os"
28	"runtime"
29	"runtime/pprof"
30	"runtime/trace"
31	"strconv"
32	"strings"
33
34	"android/soong/response"
35	"android/soong/zip"
36)
37
38type uniqueSet map[string]bool
39
40func (u *uniqueSet) String() string {
41	return `""`
42}
43
44func (u *uniqueSet) Set(s string) error {
45	if _, found := (*u)[s]; found {
46		return fmt.Errorf("File %q was specified twice as a file to not deflate", s)
47	} else {
48		(*u)[s] = true
49	}
50
51	return nil
52}
53
54type file struct{}
55
56func (file) String() string { return `""` }
57
58func (file) Set(s string) error {
59	fileArgsBuilder.File(s)
60	return nil
61}
62
63type listFiles struct{}
64
65func (listFiles) String() string { return `""` }
66
67func (listFiles) Set(s string) error {
68	fileArgsBuilder.List(s)
69	return nil
70}
71
72type rspFiles struct{}
73
74func (rspFiles) String() string { return `""` }
75
76func (rspFiles) Set(s string) error {
77	fileArgsBuilder.RspFile(s)
78	return nil
79}
80
81type explicitFile struct{}
82
83func (explicitFile) String() string { return `""` }
84
85func (explicitFile) Set(s string) error {
86	fileArgsBuilder.ExplicitPathInZip(s)
87	return nil
88}
89
90type dir struct{}
91
92func (dir) String() string { return `""` }
93
94func (dir) Set(s string) error {
95	fileArgsBuilder.Dir(s)
96	return nil
97}
98
99type relativeRoot struct{}
100
101func (relativeRoot) String() string { return "" }
102
103func (relativeRoot) Set(s string) error {
104	fileArgsBuilder.SourcePrefixToStrip(s)
105	return nil
106}
107
108type junkPaths struct{}
109
110func (junkPaths) IsBoolFlag() bool { return true }
111func (junkPaths) String() string   { return "" }
112
113func (junkPaths) Set(s string) error {
114	v, err := strconv.ParseBool(s)
115	fileArgsBuilder.JunkPaths(v)
116	return err
117}
118
119type rootPrefix struct{}
120
121func (rootPrefix) String() string { return "" }
122
123func (rootPrefix) Set(s string) error {
124	fileArgsBuilder.PathPrefixInZip(s)
125	return nil
126}
127
128var (
129	fileArgsBuilder  = zip.NewFileArgsBuilder()
130	nonDeflatedFiles = make(uniqueSet)
131)
132
133func main() {
134	var expandedArgs []string
135	for _, arg := range os.Args {
136		if strings.HasPrefix(arg, "@") {
137			f, err := os.Open(strings.TrimPrefix(arg, "@"))
138			if err != nil {
139				fmt.Fprintln(os.Stderr, err.Error())
140				os.Exit(1)
141			}
142
143			respArgs, err := response.ReadRspFile(f)
144			f.Close()
145			if err != nil {
146				fmt.Fprintln(os.Stderr, err.Error())
147				os.Exit(1)
148			}
149			expandedArgs = append(expandedArgs, respArgs...)
150		} else {
151			expandedArgs = append(expandedArgs, arg)
152		}
153	}
154
155	flags := flag.NewFlagSet("flags", flag.ExitOnError)
156	flags.Usage = func() {
157		fmt.Fprintf(os.Stderr, "usage: soong_zip -o zipfile [-m manifest] [-C dir] [-f|-l file] [-D dir]...\n")
158		flags.PrintDefaults()
159		os.Exit(2)
160	}
161
162	out := flags.String("o", "", "file to write zip file to")
163	manifest := flags.String("m", "", "input jar manifest file name")
164	directories := flags.Bool("d", false, "include directories in zip")
165	compLevel := flags.Int("L", 5, "deflate compression level (0-9)")
166	emulateJar := flags.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
167	writeIfChanged := flags.Bool("write_if_changed", false, "only update resultant .zip if it has changed")
168	ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "continue if a requested file does not exist")
169	symlinks := flags.Bool("symlinks", true, "store symbolic links in zip instead of following them")
170	srcJar := flags.Bool("srcjar", false, "move .java files to locations that match their package statement")
171
172	parallelJobs := flags.Int("parallel", runtime.NumCPU(), "number of parallel threads to use")
173	cpuProfile := flags.String("cpuprofile", "", "write cpu profile to file")
174	traceFile := flags.String("trace", "", "write trace to file")
175	sha256Checksum := flags.Bool("sha256", false, "add a zip header to each file containing its SHA256 digest")
176	doNotWrite := flags.Bool("n", false, "Nothing is written to disk -- all other work happens")
177	quiet := flags.Bool("quiet", false, "do not print warnings to console")
178
179	flags.Var(&rootPrefix{}, "P", "path prefix within the zip at which to place files")
180	flags.Var(&listFiles{}, "l", "file containing list of files to zip")
181	flags.Var(&rspFiles{}, "r", "file containing list of files to zip with Ninja rsp file escaping")
182	flags.Var(&dir{}, "D", "directory to include in zip")
183	flags.Var(&file{}, "f", "file to include in zip")
184	flags.Var(&nonDeflatedFiles, "s", "file path to be stored within the zip without compression")
185	flags.Var(&relativeRoot{}, "C", "path to use as relative root of files in following -f, -l, or -D arguments")
186	flags.Var(&junkPaths{}, "j", "junk paths, zip files without directory names")
187	flags.Var(&explicitFile{}, "e", "filename to use in the zip file for the next -f argument")
188
189	flags.Parse(expandedArgs[1:])
190
191	if flags.NArg() > 0 {
192		fmt.Fprintf(os.Stderr, "unexpected arguments %s\n", strings.Join(flags.Args(), " "))
193		flags.Usage()
194	}
195
196	if *cpuProfile != "" {
197		f, err := os.Create(*cpuProfile)
198		if err != nil {
199			fmt.Fprintln(os.Stderr, err.Error())
200			os.Exit(1)
201		}
202		defer f.Close()
203		pprof.StartCPUProfile(f)
204		defer pprof.StopCPUProfile()
205	}
206
207	if *traceFile != "" {
208		f, err := os.Create(*traceFile)
209		if err != nil {
210			fmt.Fprintln(os.Stderr, err.Error())
211			os.Exit(1)
212		}
213		defer f.Close()
214		err = trace.Start(f)
215		if err != nil {
216			fmt.Fprintln(os.Stderr, err.Error())
217			os.Exit(1)
218		}
219		defer trace.Stop()
220	}
221
222	if fileArgsBuilder.Error() != nil {
223		fmt.Fprintln(os.Stderr, fileArgsBuilder.Error())
224		os.Exit(1)
225	}
226
227	err := zip.Zip(zip.ZipArgs{
228		FileArgs:                 fileArgsBuilder.FileArgs(),
229		OutputFilePath:           *out,
230		EmulateJar:               *emulateJar,
231		SrcJar:                   *srcJar,
232		AddDirectoryEntriesToZip: *directories,
233		CompressionLevel:         *compLevel,
234		ManifestSourcePath:       *manifest,
235		NumParallelJobs:          *parallelJobs,
236		NonDeflatedFiles:         nonDeflatedFiles,
237		WriteIfChanged:           *writeIfChanged,
238		StoreSymlinks:            *symlinks,
239		IgnoreMissingFiles:       *ignoreMissingFiles,
240		Sha256Checksum:           *sha256Checksum,
241		DoNotWrite:               *doNotWrite,
242		Quiet:                    *quiet,
243	})
244	if err != nil {
245		fmt.Fprintln(os.Stderr, "error:", err.Error())
246		os.Exit(1)
247	}
248}
249