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