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
15package main
16
17import (
18	"flag"
19	"fmt"
20	"io/ioutil"
21	"os"
22	"runtime"
23	"runtime/pprof"
24	"runtime/trace"
25	"strconv"
26	"strings"
27
28	"android/soong/zip"
29)
30
31type uniqueSet map[string]bool
32
33func (u *uniqueSet) String() string {
34	return `""`
35}
36
37func (u *uniqueSet) Set(s string) error {
38	if _, found := (*u)[s]; found {
39		return fmt.Errorf("File %q was specified twice as a file to not deflate", s)
40	} else {
41		(*u)[s] = true
42	}
43
44	return nil
45}
46
47type file struct{}
48
49func (file) String() string { return `""` }
50
51func (file) Set(s string) error {
52	fileArgsBuilder.File(s)
53	return nil
54}
55
56type listFiles struct{}
57
58func (listFiles) String() string { return `""` }
59
60func (listFiles) Set(s string) error {
61	fileArgsBuilder.List(s)
62	return nil
63}
64
65type dir struct{}
66
67func (dir) String() string { return `""` }
68
69func (dir) Set(s string) error {
70	fileArgsBuilder.Dir(s)
71	return nil
72}
73
74type relativeRoot struct{}
75
76func (relativeRoot) String() string { return "" }
77
78func (relativeRoot) Set(s string) error {
79	fileArgsBuilder.SourcePrefixToStrip(s)
80	return nil
81}
82
83type junkPaths struct{}
84
85func (junkPaths) IsBoolFlag() bool { return true }
86func (junkPaths) String() string   { return "" }
87
88func (junkPaths) Set(s string) error {
89	v, err := strconv.ParseBool(s)
90	fileArgsBuilder.JunkPaths(v)
91	return err
92}
93
94type rootPrefix struct{}
95
96func (rootPrefix) String() string { return "" }
97
98func (rootPrefix) Set(s string) error {
99	fileArgsBuilder.PathPrefixInZip(s)
100	return nil
101}
102
103var (
104	fileArgsBuilder  = zip.NewFileArgsBuilder()
105	nonDeflatedFiles = make(uniqueSet)
106)
107
108func main() {
109	var expandedArgs []string
110	for _, arg := range os.Args {
111		if strings.HasPrefix(arg, "@") {
112			bytes, err := ioutil.ReadFile(strings.TrimPrefix(arg, "@"))
113			if err != nil {
114				fmt.Fprintln(os.Stderr, err.Error())
115				os.Exit(1)
116			}
117			respArgs := zip.ReadRespFile(bytes)
118			expandedArgs = append(expandedArgs, respArgs...)
119		} else {
120			expandedArgs = append(expandedArgs, arg)
121		}
122	}
123
124	flags := flag.NewFlagSet("flags", flag.ExitOnError)
125	flags.Usage = func() {
126		fmt.Fprintf(os.Stderr, "usage: soong_zip -o zipfile [-m manifest] [-C dir] [-f|-l file] [-D dir]...\n")
127		flags.PrintDefaults()
128		os.Exit(2)
129	}
130
131	out := flags.String("o", "", "file to write zip file to")
132	manifest := flags.String("m", "", "input jar manifest file name")
133	directories := flags.Bool("d", false, "include directories in zip")
134	compLevel := flags.Int("L", 5, "deflate compression level (0-9)")
135	emulateJar := flags.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
136	writeIfChanged := flags.Bool("write_if_changed", false, "only update resultant .zip if it has changed")
137	ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "continue if a requested file does not exist")
138	symlinks := flags.Bool("symlinks", true, "store symbolic links in zip instead of following them")
139	srcJar := flags.Bool("srcjar", false, "move .java files to locations that match their package statement")
140
141	parallelJobs := flags.Int("parallel", runtime.NumCPU(), "number of parallel threads to use")
142	cpuProfile := flags.String("cpuprofile", "", "write cpu profile to file")
143	traceFile := flags.String("trace", "", "write trace to file")
144
145	flags.Var(&rootPrefix{}, "P", "path prefix within the zip at which to place files")
146	flags.Var(&listFiles{}, "l", "file containing list of .class files")
147	flags.Var(&dir{}, "D", "directory to include in zip")
148	flags.Var(&file{}, "f", "file to include in zip")
149	flags.Var(&nonDeflatedFiles, "s", "file path to be stored within the zip without compression")
150	flags.Var(&relativeRoot{}, "C", "path to use as relative root of files in following -f, -l, or -D arguments")
151	flags.Var(&junkPaths{}, "j", "junk paths, zip files without directory names")
152
153	flags.Parse(expandedArgs[1:])
154
155	if flags.NArg() > 0 {
156		fmt.Fprintf(os.Stderr, "unexpected arguments %s\n", strings.Join(flags.Args(), " "))
157		flags.Usage()
158	}
159
160	if *cpuProfile != "" {
161		f, err := os.Create(*cpuProfile)
162		if err != nil {
163			fmt.Fprintln(os.Stderr, err.Error())
164			os.Exit(1)
165		}
166		defer f.Close()
167		pprof.StartCPUProfile(f)
168		defer pprof.StopCPUProfile()
169	}
170
171	if *traceFile != "" {
172		f, err := os.Create(*traceFile)
173		if err != nil {
174			fmt.Fprintln(os.Stderr, err.Error())
175			os.Exit(1)
176		}
177		defer f.Close()
178		err = trace.Start(f)
179		if err != nil {
180			fmt.Fprintln(os.Stderr, err.Error())
181			os.Exit(1)
182		}
183		defer trace.Stop()
184	}
185
186	if fileArgsBuilder.Error() != nil {
187		fmt.Fprintln(os.Stderr, fileArgsBuilder.Error())
188		os.Exit(1)
189	}
190
191	err := zip.Zip(zip.ZipArgs{
192		FileArgs:                 fileArgsBuilder.FileArgs(),
193		OutputFilePath:           *out,
194		EmulateJar:               *emulateJar,
195		SrcJar:                   *srcJar,
196		AddDirectoryEntriesToZip: *directories,
197		CompressionLevel:         *compLevel,
198		ManifestSourcePath:       *manifest,
199		NumParallelJobs:          *parallelJobs,
200		NonDeflatedFiles:         nonDeflatedFiles,
201		WriteIfChanged:           *writeIfChanged,
202		StoreSymlinks:            *symlinks,
203		IgnoreMissingFiles:       *ignoreMissingFiles,
204	})
205	if err != nil {
206		fmt.Fprintln(os.Stderr, "error:", err.Error())
207		os.Exit(1)
208	}
209}
210