1*333d2b36SAndroid Build Coastguard Worker// Copyright 2017 Google Inc. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage main 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "errors" 19*333d2b36SAndroid Build Coastguard Worker "flag" 20*333d2b36SAndroid Build Coastguard Worker "fmt" 21*333d2b36SAndroid Build Coastguard Worker "hash/crc32" 22*333d2b36SAndroid Build Coastguard Worker "io" 23*333d2b36SAndroid Build Coastguard Worker "io/ioutil" 24*333d2b36SAndroid Build Coastguard Worker "log" 25*333d2b36SAndroid Build Coastguard Worker "os" 26*333d2b36SAndroid Build Coastguard Worker "path/filepath" 27*333d2b36SAndroid Build Coastguard Worker "sort" 28*333d2b36SAndroid Build Coastguard Worker "strings" 29*333d2b36SAndroid Build Coastguard Worker 30*333d2b36SAndroid Build Coastguard Worker "android/soong/response" 31*333d2b36SAndroid Build Coastguard Worker 32*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint/pathtools" 33*333d2b36SAndroid Build Coastguard Worker 34*333d2b36SAndroid Build Coastguard Worker "android/soong/jar" 35*333d2b36SAndroid Build Coastguard Worker "android/soong/third_party/zip" 36*333d2b36SAndroid Build Coastguard Worker) 37*333d2b36SAndroid Build Coastguard Worker 38*333d2b36SAndroid Build Coastguard Worker// Input zip: we can open it, close it, and obtain an array of entries 39*333d2b36SAndroid Build Coastguard Workertype InputZip interface { 40*333d2b36SAndroid Build Coastguard Worker Name() string 41*333d2b36SAndroid Build Coastguard Worker Open() error 42*333d2b36SAndroid Build Coastguard Worker Close() error 43*333d2b36SAndroid Build Coastguard Worker Entries() []*zip.File 44*333d2b36SAndroid Build Coastguard Worker IsOpen() bool 45*333d2b36SAndroid Build Coastguard Worker} 46*333d2b36SAndroid Build Coastguard Worker 47*333d2b36SAndroid Build Coastguard Worker// An entry that can be written to the output zip 48*333d2b36SAndroid Build Coastguard Workertype ZipEntryContents interface { 49*333d2b36SAndroid Build Coastguard Worker String() string 50*333d2b36SAndroid Build Coastguard Worker IsDir() bool 51*333d2b36SAndroid Build Coastguard Worker CRC32() uint32 52*333d2b36SAndroid Build Coastguard Worker Size() uint64 53*333d2b36SAndroid Build Coastguard Worker WriteToZip(dest string, zw *zip.Writer) error 54*333d2b36SAndroid Build Coastguard Worker} 55*333d2b36SAndroid Build Coastguard Worker 56*333d2b36SAndroid Build Coastguard Worker// a ZipEntryFromZip is a ZipEntryContents that pulls its content from another zip 57*333d2b36SAndroid Build Coastguard Worker// identified by the input zip and the index of the entry in its entries array 58*333d2b36SAndroid Build Coastguard Workertype ZipEntryFromZip struct { 59*333d2b36SAndroid Build Coastguard Worker inputZip InputZip 60*333d2b36SAndroid Build Coastguard Worker index int 61*333d2b36SAndroid Build Coastguard Worker name string 62*333d2b36SAndroid Build Coastguard Worker isDir bool 63*333d2b36SAndroid Build Coastguard Worker crc32 uint32 64*333d2b36SAndroid Build Coastguard Worker size uint64 65*333d2b36SAndroid Build Coastguard Worker} 66*333d2b36SAndroid Build Coastguard Worker 67*333d2b36SAndroid Build Coastguard Workerfunc NewZipEntryFromZip(inputZip InputZip, entryIndex int) *ZipEntryFromZip { 68*333d2b36SAndroid Build Coastguard Worker fi := inputZip.Entries()[entryIndex] 69*333d2b36SAndroid Build Coastguard Worker newEntry := ZipEntryFromZip{inputZip: inputZip, 70*333d2b36SAndroid Build Coastguard Worker index: entryIndex, 71*333d2b36SAndroid Build Coastguard Worker name: fi.Name, 72*333d2b36SAndroid Build Coastguard Worker isDir: fi.FileInfo().IsDir(), 73*333d2b36SAndroid Build Coastguard Worker crc32: fi.CRC32, 74*333d2b36SAndroid Build Coastguard Worker size: fi.UncompressedSize64, 75*333d2b36SAndroid Build Coastguard Worker } 76*333d2b36SAndroid Build Coastguard Worker return &newEntry 77*333d2b36SAndroid Build Coastguard Worker} 78*333d2b36SAndroid Build Coastguard Worker 79*333d2b36SAndroid Build Coastguard Workerfunc (ze ZipEntryFromZip) String() string { 80*333d2b36SAndroid Build Coastguard Worker return fmt.Sprintf("%s!%s", ze.inputZip.Name(), ze.name) 81*333d2b36SAndroid Build Coastguard Worker} 82*333d2b36SAndroid Build Coastguard Worker 83*333d2b36SAndroid Build Coastguard Workerfunc (ze ZipEntryFromZip) IsDir() bool { 84*333d2b36SAndroid Build Coastguard Worker return ze.isDir 85*333d2b36SAndroid Build Coastguard Worker} 86*333d2b36SAndroid Build Coastguard Worker 87*333d2b36SAndroid Build Coastguard Workerfunc (ze ZipEntryFromZip) CRC32() uint32 { 88*333d2b36SAndroid Build Coastguard Worker return ze.crc32 89*333d2b36SAndroid Build Coastguard Worker} 90*333d2b36SAndroid Build Coastguard Worker 91*333d2b36SAndroid Build Coastguard Workerfunc (ze ZipEntryFromZip) Size() uint64 { 92*333d2b36SAndroid Build Coastguard Worker return ze.size 93*333d2b36SAndroid Build Coastguard Worker} 94*333d2b36SAndroid Build Coastguard Worker 95*333d2b36SAndroid Build Coastguard Workerfunc (ze ZipEntryFromZip) WriteToZip(dest string, zw *zip.Writer) error { 96*333d2b36SAndroid Build Coastguard Worker if err := ze.inputZip.Open(); err != nil { 97*333d2b36SAndroid Build Coastguard Worker return err 98*333d2b36SAndroid Build Coastguard Worker } 99*333d2b36SAndroid Build Coastguard Worker entry := ze.inputZip.Entries()[ze.index] 100*333d2b36SAndroid Build Coastguard Worker entry.SetModTime(jar.DefaultTime) 101*333d2b36SAndroid Build Coastguard Worker return zw.CopyFrom(entry, dest) 102*333d2b36SAndroid Build Coastguard Worker} 103*333d2b36SAndroid Build Coastguard Worker 104*333d2b36SAndroid Build Coastguard Worker// a ZipEntryFromBuffer is a ZipEntryContents that pulls its content from a []byte 105*333d2b36SAndroid Build Coastguard Workertype ZipEntryFromBuffer struct { 106*333d2b36SAndroid Build Coastguard Worker fh *zip.FileHeader 107*333d2b36SAndroid Build Coastguard Worker content []byte 108*333d2b36SAndroid Build Coastguard Worker} 109*333d2b36SAndroid Build Coastguard Worker 110*333d2b36SAndroid Build Coastguard Workerfunc (be ZipEntryFromBuffer) String() string { 111*333d2b36SAndroid Build Coastguard Worker return "internal buffer" 112*333d2b36SAndroid Build Coastguard Worker} 113*333d2b36SAndroid Build Coastguard Worker 114*333d2b36SAndroid Build Coastguard Workerfunc (be ZipEntryFromBuffer) IsDir() bool { 115*333d2b36SAndroid Build Coastguard Worker return be.fh.FileInfo().IsDir() 116*333d2b36SAndroid Build Coastguard Worker} 117*333d2b36SAndroid Build Coastguard Worker 118*333d2b36SAndroid Build Coastguard Workerfunc (be ZipEntryFromBuffer) CRC32() uint32 { 119*333d2b36SAndroid Build Coastguard Worker return crc32.ChecksumIEEE(be.content) 120*333d2b36SAndroid Build Coastguard Worker} 121*333d2b36SAndroid Build Coastguard Worker 122*333d2b36SAndroid Build Coastguard Workerfunc (be ZipEntryFromBuffer) Size() uint64 { 123*333d2b36SAndroid Build Coastguard Worker return uint64(len(be.content)) 124*333d2b36SAndroid Build Coastguard Worker} 125*333d2b36SAndroid Build Coastguard Worker 126*333d2b36SAndroid Build Coastguard Workerfunc (be ZipEntryFromBuffer) WriteToZip(dest string, zw *zip.Writer) error { 127*333d2b36SAndroid Build Coastguard Worker w, err := zw.CreateHeaderAndroid(be.fh) 128*333d2b36SAndroid Build Coastguard Worker if err != nil { 129*333d2b36SAndroid Build Coastguard Worker return err 130*333d2b36SAndroid Build Coastguard Worker } 131*333d2b36SAndroid Build Coastguard Worker 132*333d2b36SAndroid Build Coastguard Worker if !be.IsDir() { 133*333d2b36SAndroid Build Coastguard Worker _, err = w.Write(be.content) 134*333d2b36SAndroid Build Coastguard Worker if err != nil { 135*333d2b36SAndroid Build Coastguard Worker return err 136*333d2b36SAndroid Build Coastguard Worker } 137*333d2b36SAndroid Build Coastguard Worker } 138*333d2b36SAndroid Build Coastguard Worker 139*333d2b36SAndroid Build Coastguard Worker return nil 140*333d2b36SAndroid Build Coastguard Worker} 141*333d2b36SAndroid Build Coastguard Worker 142*333d2b36SAndroid Build Coastguard Worker// Processing state. 143*333d2b36SAndroid Build Coastguard Workertype OutputZip struct { 144*333d2b36SAndroid Build Coastguard Worker outputWriter *zip.Writer 145*333d2b36SAndroid Build Coastguard Worker stripDirEntries bool 146*333d2b36SAndroid Build Coastguard Worker emulateJar bool 147*333d2b36SAndroid Build Coastguard Worker sortEntries bool 148*333d2b36SAndroid Build Coastguard Worker ignoreDuplicates bool 149*333d2b36SAndroid Build Coastguard Worker excludeDirs []string 150*333d2b36SAndroid Build Coastguard Worker excludeFiles []string 151*333d2b36SAndroid Build Coastguard Worker sourceByDest map[string]ZipEntryContents 152*333d2b36SAndroid Build Coastguard Worker} 153*333d2b36SAndroid Build Coastguard Worker 154*333d2b36SAndroid Build Coastguard Workerfunc NewOutputZip(outputWriter *zip.Writer, sortEntries, emulateJar, stripDirEntries, ignoreDuplicates bool) *OutputZip { 155*333d2b36SAndroid Build Coastguard Worker return &OutputZip{ 156*333d2b36SAndroid Build Coastguard Worker outputWriter: outputWriter, 157*333d2b36SAndroid Build Coastguard Worker stripDirEntries: stripDirEntries, 158*333d2b36SAndroid Build Coastguard Worker emulateJar: emulateJar, 159*333d2b36SAndroid Build Coastguard Worker sortEntries: sortEntries, 160*333d2b36SAndroid Build Coastguard Worker sourceByDest: make(map[string]ZipEntryContents, 0), 161*333d2b36SAndroid Build Coastguard Worker ignoreDuplicates: ignoreDuplicates, 162*333d2b36SAndroid Build Coastguard Worker } 163*333d2b36SAndroid Build Coastguard Worker} 164*333d2b36SAndroid Build Coastguard Worker 165*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) setExcludeDirs(excludeDirs []string) { 166*333d2b36SAndroid Build Coastguard Worker oz.excludeDirs = make([]string, len(excludeDirs)) 167*333d2b36SAndroid Build Coastguard Worker for i, dir := range excludeDirs { 168*333d2b36SAndroid Build Coastguard Worker oz.excludeDirs[i] = filepath.Clean(dir) 169*333d2b36SAndroid Build Coastguard Worker } 170*333d2b36SAndroid Build Coastguard Worker} 171*333d2b36SAndroid Build Coastguard Worker 172*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) setExcludeFiles(excludeFiles []string) { 173*333d2b36SAndroid Build Coastguard Worker oz.excludeFiles = excludeFiles 174*333d2b36SAndroid Build Coastguard Worker} 175*333d2b36SAndroid Build Coastguard Worker 176*333d2b36SAndroid Build Coastguard Worker// Adds an entry with given name whose source is given ZipEntryContents. Returns old ZipEntryContents 177*333d2b36SAndroid Build Coastguard Worker// if entry with given name already exists. 178*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) addZipEntry(name string, source ZipEntryContents) (ZipEntryContents, error) { 179*333d2b36SAndroid Build Coastguard Worker if existingSource, exists := oz.sourceByDest[name]; exists { 180*333d2b36SAndroid Build Coastguard Worker return existingSource, nil 181*333d2b36SAndroid Build Coastguard Worker } 182*333d2b36SAndroid Build Coastguard Worker oz.sourceByDest[name] = source 183*333d2b36SAndroid Build Coastguard Worker // Delay writing an entry if entries need to be rearranged. 184*333d2b36SAndroid Build Coastguard Worker if oz.emulateJar || oz.sortEntries { 185*333d2b36SAndroid Build Coastguard Worker return nil, nil 186*333d2b36SAndroid Build Coastguard Worker } 187*333d2b36SAndroid Build Coastguard Worker return nil, source.WriteToZip(name, oz.outputWriter) 188*333d2b36SAndroid Build Coastguard Worker} 189*333d2b36SAndroid Build Coastguard Worker 190*333d2b36SAndroid Build Coastguard Worker// Adds an entry for the manifest (META-INF/MANIFEST.MF from the given file 191*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) addManifest(manifestPath string) error { 192*333d2b36SAndroid Build Coastguard Worker if !oz.stripDirEntries { 193*333d2b36SAndroid Build Coastguard Worker if _, err := oz.addZipEntry(jar.MetaDir, ZipEntryFromBuffer{jar.MetaDirFileHeader(), nil}); err != nil { 194*333d2b36SAndroid Build Coastguard Worker return err 195*333d2b36SAndroid Build Coastguard Worker } 196*333d2b36SAndroid Build Coastguard Worker } 197*333d2b36SAndroid Build Coastguard Worker contents, err := ioutil.ReadFile(manifestPath) 198*333d2b36SAndroid Build Coastguard Worker if err == nil { 199*333d2b36SAndroid Build Coastguard Worker fh, buf, err := jar.ManifestFileContents(contents) 200*333d2b36SAndroid Build Coastguard Worker if err == nil { 201*333d2b36SAndroid Build Coastguard Worker _, err = oz.addZipEntry(jar.ManifestFile, ZipEntryFromBuffer{fh, buf}) 202*333d2b36SAndroid Build Coastguard Worker } 203*333d2b36SAndroid Build Coastguard Worker } 204*333d2b36SAndroid Build Coastguard Worker return err 205*333d2b36SAndroid Build Coastguard Worker} 206*333d2b36SAndroid Build Coastguard Worker 207*333d2b36SAndroid Build Coastguard Worker// Adds an entry with given name and contents read from given file 208*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) addZipEntryFromFile(name string, path string) error { 209*333d2b36SAndroid Build Coastguard Worker buf, err := ioutil.ReadFile(path) 210*333d2b36SAndroid Build Coastguard Worker if err == nil { 211*333d2b36SAndroid Build Coastguard Worker fh := &zip.FileHeader{ 212*333d2b36SAndroid Build Coastguard Worker Name: name, 213*333d2b36SAndroid Build Coastguard Worker Method: zip.Store, 214*333d2b36SAndroid Build Coastguard Worker UncompressedSize64: uint64(len(buf)), 215*333d2b36SAndroid Build Coastguard Worker } 216*333d2b36SAndroid Build Coastguard Worker fh.SetMode(0700) 217*333d2b36SAndroid Build Coastguard Worker fh.SetModTime(jar.DefaultTime) 218*333d2b36SAndroid Build Coastguard Worker _, err = oz.addZipEntry(name, ZipEntryFromBuffer{fh, buf}) 219*333d2b36SAndroid Build Coastguard Worker } 220*333d2b36SAndroid Build Coastguard Worker return err 221*333d2b36SAndroid Build Coastguard Worker} 222*333d2b36SAndroid Build Coastguard Worker 223*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) addEmptyEntry(entry string) error { 224*333d2b36SAndroid Build Coastguard Worker var emptyBuf []byte 225*333d2b36SAndroid Build Coastguard Worker fh := &zip.FileHeader{ 226*333d2b36SAndroid Build Coastguard Worker Name: entry, 227*333d2b36SAndroid Build Coastguard Worker Method: zip.Store, 228*333d2b36SAndroid Build Coastguard Worker UncompressedSize64: uint64(len(emptyBuf)), 229*333d2b36SAndroid Build Coastguard Worker } 230*333d2b36SAndroid Build Coastguard Worker fh.SetMode(0700) 231*333d2b36SAndroid Build Coastguard Worker fh.SetModTime(jar.DefaultTime) 232*333d2b36SAndroid Build Coastguard Worker _, err := oz.addZipEntry(entry, ZipEntryFromBuffer{fh, emptyBuf}) 233*333d2b36SAndroid Build Coastguard Worker return err 234*333d2b36SAndroid Build Coastguard Worker} 235*333d2b36SAndroid Build Coastguard Worker 236*333d2b36SAndroid Build Coastguard Worker// Returns true if given entry is to be excluded 237*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) isEntryExcluded(name string) bool { 238*333d2b36SAndroid Build Coastguard Worker for _, dir := range oz.excludeDirs { 239*333d2b36SAndroid Build Coastguard Worker dir = filepath.Clean(dir) 240*333d2b36SAndroid Build Coastguard Worker patterns := []string{ 241*333d2b36SAndroid Build Coastguard Worker dir + "/", // the directory itself 242*333d2b36SAndroid Build Coastguard Worker dir + "/**/*", // files recursively in the directory 243*333d2b36SAndroid Build Coastguard Worker dir + "/**/*/", // directories recursively in the directory 244*333d2b36SAndroid Build Coastguard Worker } 245*333d2b36SAndroid Build Coastguard Worker 246*333d2b36SAndroid Build Coastguard Worker for _, pattern := range patterns { 247*333d2b36SAndroid Build Coastguard Worker match, err := pathtools.Match(pattern, name) 248*333d2b36SAndroid Build Coastguard Worker if err != nil { 249*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("%s: %s", err.Error(), pattern)) 250*333d2b36SAndroid Build Coastguard Worker } 251*333d2b36SAndroid Build Coastguard Worker if match { 252*333d2b36SAndroid Build Coastguard Worker if oz.emulateJar { 253*333d2b36SAndroid Build Coastguard Worker // When merging jar files, don't strip META-INF/MANIFEST.MF even if stripping META-INF is 254*333d2b36SAndroid Build Coastguard Worker // requested. 255*333d2b36SAndroid Build Coastguard Worker // TODO(ccross): which files does this affect? 256*333d2b36SAndroid Build Coastguard Worker if name != jar.MetaDir && name != jar.ManifestFile { 257*333d2b36SAndroid Build Coastguard Worker return true 258*333d2b36SAndroid Build Coastguard Worker } 259*333d2b36SAndroid Build Coastguard Worker } 260*333d2b36SAndroid Build Coastguard Worker return true 261*333d2b36SAndroid Build Coastguard Worker } 262*333d2b36SAndroid Build Coastguard Worker } 263*333d2b36SAndroid Build Coastguard Worker } 264*333d2b36SAndroid Build Coastguard Worker 265*333d2b36SAndroid Build Coastguard Worker for _, pattern := range oz.excludeFiles { 266*333d2b36SAndroid Build Coastguard Worker match, err := pathtools.Match(pattern, name) 267*333d2b36SAndroid Build Coastguard Worker if err != nil { 268*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("%s: %s", err.Error(), pattern)) 269*333d2b36SAndroid Build Coastguard Worker } 270*333d2b36SAndroid Build Coastguard Worker if match { 271*333d2b36SAndroid Build Coastguard Worker return true 272*333d2b36SAndroid Build Coastguard Worker } 273*333d2b36SAndroid Build Coastguard Worker } 274*333d2b36SAndroid Build Coastguard Worker return false 275*333d2b36SAndroid Build Coastguard Worker} 276*333d2b36SAndroid Build Coastguard Worker 277*333d2b36SAndroid Build Coastguard Worker// Creates a zip entry whose contents is an entry from the given input zip. 278*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) copyEntry(inputZip InputZip, index int) error { 279*333d2b36SAndroid Build Coastguard Worker entry := NewZipEntryFromZip(inputZip, index) 280*333d2b36SAndroid Build Coastguard Worker if oz.stripDirEntries && entry.IsDir() { 281*333d2b36SAndroid Build Coastguard Worker return nil 282*333d2b36SAndroid Build Coastguard Worker } 283*333d2b36SAndroid Build Coastguard Worker existingEntry, err := oz.addZipEntry(entry.name, entry) 284*333d2b36SAndroid Build Coastguard Worker if err != nil { 285*333d2b36SAndroid Build Coastguard Worker return err 286*333d2b36SAndroid Build Coastguard Worker } 287*333d2b36SAndroid Build Coastguard Worker if existingEntry == nil { 288*333d2b36SAndroid Build Coastguard Worker return nil 289*333d2b36SAndroid Build Coastguard Worker } 290*333d2b36SAndroid Build Coastguard Worker 291*333d2b36SAndroid Build Coastguard Worker // File types should match 292*333d2b36SAndroid Build Coastguard Worker if existingEntry.IsDir() != entry.IsDir() { 293*333d2b36SAndroid Build Coastguard Worker return fmt.Errorf("Directory/file mismatch at %v from %v and %v\n", 294*333d2b36SAndroid Build Coastguard Worker entry.name, existingEntry, entry) 295*333d2b36SAndroid Build Coastguard Worker } 296*333d2b36SAndroid Build Coastguard Worker 297*333d2b36SAndroid Build Coastguard Worker if oz.ignoreDuplicates || 298*333d2b36SAndroid Build Coastguard Worker // Skip manifest and module info files that are not from the first input file 299*333d2b36SAndroid Build Coastguard Worker (oz.emulateJar && entry.name == jar.ManifestFile || entry.name == jar.ModuleInfoClass) || 300*333d2b36SAndroid Build Coastguard Worker // Identical entries 301*333d2b36SAndroid Build Coastguard Worker (existingEntry.CRC32() == entry.CRC32() && existingEntry.Size() == entry.Size()) || 302*333d2b36SAndroid Build Coastguard Worker // Directory entries 303*333d2b36SAndroid Build Coastguard Worker entry.IsDir() { 304*333d2b36SAndroid Build Coastguard Worker return nil 305*333d2b36SAndroid Build Coastguard Worker } 306*333d2b36SAndroid Build Coastguard Worker 307*333d2b36SAndroid Build Coastguard Worker return fmt.Errorf("Duplicate path %v found in %v and %v\n", entry.name, existingEntry, inputZip.Name()) 308*333d2b36SAndroid Build Coastguard Worker} 309*333d2b36SAndroid Build Coastguard Worker 310*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) entriesArray() []string { 311*333d2b36SAndroid Build Coastguard Worker entries := make([]string, len(oz.sourceByDest)) 312*333d2b36SAndroid Build Coastguard Worker i := 0 313*333d2b36SAndroid Build Coastguard Worker for entry := range oz.sourceByDest { 314*333d2b36SAndroid Build Coastguard Worker entries[i] = entry 315*333d2b36SAndroid Build Coastguard Worker i++ 316*333d2b36SAndroid Build Coastguard Worker } 317*333d2b36SAndroid Build Coastguard Worker return entries 318*333d2b36SAndroid Build Coastguard Worker} 319*333d2b36SAndroid Build Coastguard Worker 320*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) jarSorted() []string { 321*333d2b36SAndroid Build Coastguard Worker entries := oz.entriesArray() 322*333d2b36SAndroid Build Coastguard Worker sort.SliceStable(entries, func(i, j int) bool { return jar.EntryNamesLess(entries[i], entries[j]) }) 323*333d2b36SAndroid Build Coastguard Worker return entries 324*333d2b36SAndroid Build Coastguard Worker} 325*333d2b36SAndroid Build Coastguard Worker 326*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) alphanumericSorted() []string { 327*333d2b36SAndroid Build Coastguard Worker entries := oz.entriesArray() 328*333d2b36SAndroid Build Coastguard Worker sort.Strings(entries) 329*333d2b36SAndroid Build Coastguard Worker return entries 330*333d2b36SAndroid Build Coastguard Worker} 331*333d2b36SAndroid Build Coastguard Worker 332*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) writeEntries(entries []string) error { 333*333d2b36SAndroid Build Coastguard Worker for _, entry := range entries { 334*333d2b36SAndroid Build Coastguard Worker source, _ := oz.sourceByDest[entry] 335*333d2b36SAndroid Build Coastguard Worker if err := source.WriteToZip(entry, oz.outputWriter); err != nil { 336*333d2b36SAndroid Build Coastguard Worker return err 337*333d2b36SAndroid Build Coastguard Worker } 338*333d2b36SAndroid Build Coastguard Worker } 339*333d2b36SAndroid Build Coastguard Worker return nil 340*333d2b36SAndroid Build Coastguard Worker} 341*333d2b36SAndroid Build Coastguard Worker 342*333d2b36SAndroid Build Coastguard Workerfunc (oz *OutputZip) getUninitializedPythonPackages(inputZips []InputZip) ([]string, error) { 343*333d2b36SAndroid Build Coastguard Worker // the runfiles packages needs to be populated with "__init__.py". 344*333d2b36SAndroid Build Coastguard Worker // the runfiles dirs have been treated as packages. 345*333d2b36SAndroid Build Coastguard Worker var allPackages []string // Using a slice to preserve input order. 346*333d2b36SAndroid Build Coastguard Worker seenPkgs := make(map[string]bool) 347*333d2b36SAndroid Build Coastguard Worker initedPackages := make(map[string]bool) 348*333d2b36SAndroid Build Coastguard Worker getPackage := func(path string) string { 349*333d2b36SAndroid Build Coastguard Worker ret := filepath.Dir(path) 350*333d2b36SAndroid Build Coastguard Worker // filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/". 351*333d2b36SAndroid Build Coastguard Worker if ret == "." || ret == "/" { 352*333d2b36SAndroid Build Coastguard Worker return "" 353*333d2b36SAndroid Build Coastguard Worker } 354*333d2b36SAndroid Build Coastguard Worker return ret 355*333d2b36SAndroid Build Coastguard Worker } 356*333d2b36SAndroid Build Coastguard Worker 357*333d2b36SAndroid Build Coastguard Worker // put existing __init__.py files to a set first. This set is used for preventing 358*333d2b36SAndroid Build Coastguard Worker // generated __init__.py files from overwriting existing ones. 359*333d2b36SAndroid Build Coastguard Worker for _, inputZip := range inputZips { 360*333d2b36SAndroid Build Coastguard Worker if err := inputZip.Open(); err != nil { 361*333d2b36SAndroid Build Coastguard Worker return nil, err 362*333d2b36SAndroid Build Coastguard Worker } 363*333d2b36SAndroid Build Coastguard Worker for _, file := range inputZip.Entries() { 364*333d2b36SAndroid Build Coastguard Worker pyPkg := getPackage(file.Name) 365*333d2b36SAndroid Build Coastguard Worker baseName := filepath.Base(file.Name) 366*333d2b36SAndroid Build Coastguard Worker if baseName == "__init__.py" || baseName == "__init__.pyc" { 367*333d2b36SAndroid Build Coastguard Worker if _, found := initedPackages[pyPkg]; found { 368*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q", file.Name)) 369*333d2b36SAndroid Build Coastguard Worker } 370*333d2b36SAndroid Build Coastguard Worker initedPackages[pyPkg] = true 371*333d2b36SAndroid Build Coastguard Worker } 372*333d2b36SAndroid Build Coastguard Worker for pyPkg != "" { 373*333d2b36SAndroid Build Coastguard Worker if _, found := seenPkgs[pyPkg]; found { 374*333d2b36SAndroid Build Coastguard Worker break 375*333d2b36SAndroid Build Coastguard Worker } 376*333d2b36SAndroid Build Coastguard Worker seenPkgs[pyPkg] = true 377*333d2b36SAndroid Build Coastguard Worker allPackages = append(allPackages, pyPkg) 378*333d2b36SAndroid Build Coastguard Worker pyPkg = getPackage(pyPkg) 379*333d2b36SAndroid Build Coastguard Worker } 380*333d2b36SAndroid Build Coastguard Worker } 381*333d2b36SAndroid Build Coastguard Worker } 382*333d2b36SAndroid Build Coastguard Worker noInitPackages := make([]string, 0) 383*333d2b36SAndroid Build Coastguard Worker for _, pyPkg := range allPackages { 384*333d2b36SAndroid Build Coastguard Worker if _, found := initedPackages[pyPkg]; !found { 385*333d2b36SAndroid Build Coastguard Worker noInitPackages = append(noInitPackages, pyPkg) 386*333d2b36SAndroid Build Coastguard Worker } 387*333d2b36SAndroid Build Coastguard Worker } 388*333d2b36SAndroid Build Coastguard Worker return noInitPackages, nil 389*333d2b36SAndroid Build Coastguard Worker} 390*333d2b36SAndroid Build Coastguard Worker 391*333d2b36SAndroid Build Coastguard Worker// An InputZip owned by the InputZipsManager. Opened ManagedInputZip's are chained in the open order. 392*333d2b36SAndroid Build Coastguard Workertype ManagedInputZip struct { 393*333d2b36SAndroid Build Coastguard Worker owner *InputZipsManager 394*333d2b36SAndroid Build Coastguard Worker realInputZip InputZip 395*333d2b36SAndroid Build Coastguard Worker older *ManagedInputZip 396*333d2b36SAndroid Build Coastguard Worker newer *ManagedInputZip 397*333d2b36SAndroid Build Coastguard Worker} 398*333d2b36SAndroid Build Coastguard Worker 399*333d2b36SAndroid Build Coastguard Worker// Maintains the array of ManagedInputZips, keeping track of open input ones. When an InputZip is opened, 400*333d2b36SAndroid Build Coastguard Worker// may close some other InputZip to limit the number of open ones. 401*333d2b36SAndroid Build Coastguard Workertype InputZipsManager struct { 402*333d2b36SAndroid Build Coastguard Worker inputZips []*ManagedInputZip 403*333d2b36SAndroid Build Coastguard Worker nOpenZips int 404*333d2b36SAndroid Build Coastguard Worker maxOpenZips int 405*333d2b36SAndroid Build Coastguard Worker openInputZips *ManagedInputZip 406*333d2b36SAndroid Build Coastguard Worker} 407*333d2b36SAndroid Build Coastguard Worker 408*333d2b36SAndroid Build Coastguard Workerfunc (miz *ManagedInputZip) unlink() { 409*333d2b36SAndroid Build Coastguard Worker olderMiz := miz.older 410*333d2b36SAndroid Build Coastguard Worker newerMiz := miz.newer 411*333d2b36SAndroid Build Coastguard Worker if newerMiz.older != miz || olderMiz.newer != miz { 412*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("removing %p:%#v: broken list between %p:%#v and %p:%#v", 413*333d2b36SAndroid Build Coastguard Worker miz, miz, newerMiz, newerMiz, olderMiz, olderMiz)) 414*333d2b36SAndroid Build Coastguard Worker } 415*333d2b36SAndroid Build Coastguard Worker olderMiz.newer = newerMiz 416*333d2b36SAndroid Build Coastguard Worker newerMiz.older = olderMiz 417*333d2b36SAndroid Build Coastguard Worker miz.newer = nil 418*333d2b36SAndroid Build Coastguard Worker miz.older = nil 419*333d2b36SAndroid Build Coastguard Worker} 420*333d2b36SAndroid Build Coastguard Worker 421*333d2b36SAndroid Build Coastguard Workerfunc (miz *ManagedInputZip) link(olderMiz *ManagedInputZip) { 422*333d2b36SAndroid Build Coastguard Worker if olderMiz.newer != nil || olderMiz.older != nil { 423*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("inputZip is already open")) 424*333d2b36SAndroid Build Coastguard Worker } 425*333d2b36SAndroid Build Coastguard Worker oldOlderMiz := miz.older 426*333d2b36SAndroid Build Coastguard Worker if oldOlderMiz.newer != miz { 427*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("broken list between %p:%#v and %p:%#v", miz, miz, oldOlderMiz, oldOlderMiz)) 428*333d2b36SAndroid Build Coastguard Worker } 429*333d2b36SAndroid Build Coastguard Worker miz.older = olderMiz 430*333d2b36SAndroid Build Coastguard Worker olderMiz.older = oldOlderMiz 431*333d2b36SAndroid Build Coastguard Worker oldOlderMiz.newer = olderMiz 432*333d2b36SAndroid Build Coastguard Worker olderMiz.newer = miz 433*333d2b36SAndroid Build Coastguard Worker} 434*333d2b36SAndroid Build Coastguard Worker 435*333d2b36SAndroid Build Coastguard Workerfunc NewInputZipsManager(nInputZips, maxOpenZips int) *InputZipsManager { 436*333d2b36SAndroid Build Coastguard Worker if maxOpenZips < 3 { 437*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("open zips limit should be above 3")) 438*333d2b36SAndroid Build Coastguard Worker } 439*333d2b36SAndroid Build Coastguard Worker // In the fake element .older points to the most recently opened InputZip, and .newer points to the oldest. 440*333d2b36SAndroid Build Coastguard Worker head := new(ManagedInputZip) 441*333d2b36SAndroid Build Coastguard Worker head.older = head 442*333d2b36SAndroid Build Coastguard Worker head.newer = head 443*333d2b36SAndroid Build Coastguard Worker return &InputZipsManager{ 444*333d2b36SAndroid Build Coastguard Worker inputZips: make([]*ManagedInputZip, 0, nInputZips), 445*333d2b36SAndroid Build Coastguard Worker maxOpenZips: maxOpenZips, 446*333d2b36SAndroid Build Coastguard Worker openInputZips: head, 447*333d2b36SAndroid Build Coastguard Worker } 448*333d2b36SAndroid Build Coastguard Worker} 449*333d2b36SAndroid Build Coastguard Worker 450*333d2b36SAndroid Build Coastguard Worker// InputZip factory 451*333d2b36SAndroid Build Coastguard Workerfunc (izm *InputZipsManager) Manage(inz InputZip) InputZip { 452*333d2b36SAndroid Build Coastguard Worker iz := &ManagedInputZip{owner: izm, realInputZip: inz} 453*333d2b36SAndroid Build Coastguard Worker izm.inputZips = append(izm.inputZips, iz) 454*333d2b36SAndroid Build Coastguard Worker return iz 455*333d2b36SAndroid Build Coastguard Worker} 456*333d2b36SAndroid Build Coastguard Worker 457*333d2b36SAndroid Build Coastguard Worker// Opens or reopens ManagedInputZip. 458*333d2b36SAndroid Build Coastguard Workerfunc (izm *InputZipsManager) reopen(miz *ManagedInputZip) error { 459*333d2b36SAndroid Build Coastguard Worker if miz.realInputZip.IsOpen() { 460*333d2b36SAndroid Build Coastguard Worker if miz != izm.openInputZips { 461*333d2b36SAndroid Build Coastguard Worker miz.unlink() 462*333d2b36SAndroid Build Coastguard Worker izm.openInputZips.link(miz) 463*333d2b36SAndroid Build Coastguard Worker } 464*333d2b36SAndroid Build Coastguard Worker return nil 465*333d2b36SAndroid Build Coastguard Worker } 466*333d2b36SAndroid Build Coastguard Worker if izm.nOpenZips >= izm.maxOpenZips { 467*333d2b36SAndroid Build Coastguard Worker if err := izm.close(izm.openInputZips.older); err != nil { 468*333d2b36SAndroid Build Coastguard Worker return err 469*333d2b36SAndroid Build Coastguard Worker } 470*333d2b36SAndroid Build Coastguard Worker } 471*333d2b36SAndroid Build Coastguard Worker if err := miz.realInputZip.Open(); err != nil { 472*333d2b36SAndroid Build Coastguard Worker return err 473*333d2b36SAndroid Build Coastguard Worker } 474*333d2b36SAndroid Build Coastguard Worker izm.openInputZips.link(miz) 475*333d2b36SAndroid Build Coastguard Worker izm.nOpenZips++ 476*333d2b36SAndroid Build Coastguard Worker return nil 477*333d2b36SAndroid Build Coastguard Worker} 478*333d2b36SAndroid Build Coastguard Worker 479*333d2b36SAndroid Build Coastguard Workerfunc (izm *InputZipsManager) close(miz *ManagedInputZip) error { 480*333d2b36SAndroid Build Coastguard Worker if miz.IsOpen() { 481*333d2b36SAndroid Build Coastguard Worker err := miz.realInputZip.Close() 482*333d2b36SAndroid Build Coastguard Worker izm.nOpenZips-- 483*333d2b36SAndroid Build Coastguard Worker miz.unlink() 484*333d2b36SAndroid Build Coastguard Worker return err 485*333d2b36SAndroid Build Coastguard Worker } 486*333d2b36SAndroid Build Coastguard Worker return nil 487*333d2b36SAndroid Build Coastguard Worker} 488*333d2b36SAndroid Build Coastguard Worker 489*333d2b36SAndroid Build Coastguard Worker// Checks that openInputZips deque is valid 490*333d2b36SAndroid Build Coastguard Workerfunc (izm *InputZipsManager) checkOpenZipsDeque() { 491*333d2b36SAndroid Build Coastguard Worker nReallyOpen := 0 492*333d2b36SAndroid Build Coastguard Worker el := izm.openInputZips 493*333d2b36SAndroid Build Coastguard Worker for { 494*333d2b36SAndroid Build Coastguard Worker elNext := el.older 495*333d2b36SAndroid Build Coastguard Worker if elNext.newer != el { 496*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("Element:\n %p: %v\nNext:\n %p %v", el, el, elNext, elNext)) 497*333d2b36SAndroid Build Coastguard Worker } 498*333d2b36SAndroid Build Coastguard Worker if elNext == izm.openInputZips { 499*333d2b36SAndroid Build Coastguard Worker break 500*333d2b36SAndroid Build Coastguard Worker } 501*333d2b36SAndroid Build Coastguard Worker el = elNext 502*333d2b36SAndroid Build Coastguard Worker if !el.IsOpen() { 503*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("Found unopened element")) 504*333d2b36SAndroid Build Coastguard Worker } 505*333d2b36SAndroid Build Coastguard Worker nReallyOpen++ 506*333d2b36SAndroid Build Coastguard Worker if nReallyOpen > izm.nOpenZips { 507*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("found %d open zips, should be %d", nReallyOpen, izm.nOpenZips)) 508*333d2b36SAndroid Build Coastguard Worker } 509*333d2b36SAndroid Build Coastguard Worker } 510*333d2b36SAndroid Build Coastguard Worker if nReallyOpen > izm.nOpenZips { 511*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("found %d open zips, should be %d", nReallyOpen, izm.nOpenZips)) 512*333d2b36SAndroid Build Coastguard Worker } 513*333d2b36SAndroid Build Coastguard Worker} 514*333d2b36SAndroid Build Coastguard Worker 515*333d2b36SAndroid Build Coastguard Workerfunc (miz *ManagedInputZip) Name() string { 516*333d2b36SAndroid Build Coastguard Worker return miz.realInputZip.Name() 517*333d2b36SAndroid Build Coastguard Worker} 518*333d2b36SAndroid Build Coastguard Worker 519*333d2b36SAndroid Build Coastguard Workerfunc (miz *ManagedInputZip) Open() error { 520*333d2b36SAndroid Build Coastguard Worker return miz.owner.reopen(miz) 521*333d2b36SAndroid Build Coastguard Worker} 522*333d2b36SAndroid Build Coastguard Worker 523*333d2b36SAndroid Build Coastguard Workerfunc (miz *ManagedInputZip) Close() error { 524*333d2b36SAndroid Build Coastguard Worker return miz.owner.close(miz) 525*333d2b36SAndroid Build Coastguard Worker} 526*333d2b36SAndroid Build Coastguard Worker 527*333d2b36SAndroid Build Coastguard Workerfunc (miz *ManagedInputZip) IsOpen() bool { 528*333d2b36SAndroid Build Coastguard Worker return miz.realInputZip.IsOpen() 529*333d2b36SAndroid Build Coastguard Worker} 530*333d2b36SAndroid Build Coastguard Worker 531*333d2b36SAndroid Build Coastguard Workerfunc (miz *ManagedInputZip) Entries() []*zip.File { 532*333d2b36SAndroid Build Coastguard Worker if !miz.IsOpen() { 533*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("%s: is not open", miz.Name())) 534*333d2b36SAndroid Build Coastguard Worker } 535*333d2b36SAndroid Build Coastguard Worker return miz.realInputZip.Entries() 536*333d2b36SAndroid Build Coastguard Worker} 537*333d2b36SAndroid Build Coastguard Worker 538*333d2b36SAndroid Build Coastguard Worker// Actual processing. 539*333d2b36SAndroid Build Coastguard Workerfunc mergeZips(inputZips []InputZip, writer *zip.Writer, manifest, pyMain string, 540*333d2b36SAndroid Build Coastguard Worker sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool, 541*333d2b36SAndroid Build Coastguard Worker excludeFiles, excludeDirs []string, zipsToNotStrip map[string]bool) error { 542*333d2b36SAndroid Build Coastguard Worker 543*333d2b36SAndroid Build Coastguard Worker out := NewOutputZip(writer, sortEntries, emulateJar, stripDirEntries, ignoreDuplicates) 544*333d2b36SAndroid Build Coastguard Worker out.setExcludeFiles(excludeFiles) 545*333d2b36SAndroid Build Coastguard Worker out.setExcludeDirs(excludeDirs) 546*333d2b36SAndroid Build Coastguard Worker if manifest != "" { 547*333d2b36SAndroid Build Coastguard Worker if err := out.addManifest(manifest); err != nil { 548*333d2b36SAndroid Build Coastguard Worker return err 549*333d2b36SAndroid Build Coastguard Worker } 550*333d2b36SAndroid Build Coastguard Worker } 551*333d2b36SAndroid Build Coastguard Worker if pyMain != "" { 552*333d2b36SAndroid Build Coastguard Worker if err := out.addZipEntryFromFile("__main__.py", pyMain); err != nil { 553*333d2b36SAndroid Build Coastguard Worker return err 554*333d2b36SAndroid Build Coastguard Worker } 555*333d2b36SAndroid Build Coastguard Worker } 556*333d2b36SAndroid Build Coastguard Worker 557*333d2b36SAndroid Build Coastguard Worker if emulatePar { 558*333d2b36SAndroid Build Coastguard Worker noInitPackages, err := out.getUninitializedPythonPackages(inputZips) 559*333d2b36SAndroid Build Coastguard Worker if err != nil { 560*333d2b36SAndroid Build Coastguard Worker return err 561*333d2b36SAndroid Build Coastguard Worker } 562*333d2b36SAndroid Build Coastguard Worker for _, uninitializedPyPackage := range noInitPackages { 563*333d2b36SAndroid Build Coastguard Worker if err = out.addEmptyEntry(filepath.Join(uninitializedPyPackage, "__init__.py")); err != nil { 564*333d2b36SAndroid Build Coastguard Worker return err 565*333d2b36SAndroid Build Coastguard Worker } 566*333d2b36SAndroid Build Coastguard Worker } 567*333d2b36SAndroid Build Coastguard Worker } 568*333d2b36SAndroid Build Coastguard Worker 569*333d2b36SAndroid Build Coastguard Worker var jarServices jar.Services 570*333d2b36SAndroid Build Coastguard Worker 571*333d2b36SAndroid Build Coastguard Worker // Finally, add entries from all the input zips. 572*333d2b36SAndroid Build Coastguard Worker for _, inputZip := range inputZips { 573*333d2b36SAndroid Build Coastguard Worker _, copyFully := zipsToNotStrip[inputZip.Name()] 574*333d2b36SAndroid Build Coastguard Worker if err := inputZip.Open(); err != nil { 575*333d2b36SAndroid Build Coastguard Worker return err 576*333d2b36SAndroid Build Coastguard Worker } 577*333d2b36SAndroid Build Coastguard Worker 578*333d2b36SAndroid Build Coastguard Worker for i, entry := range inputZip.Entries() { 579*333d2b36SAndroid Build Coastguard Worker if emulateJar && jarServices.IsServiceFile(entry) { 580*333d2b36SAndroid Build Coastguard Worker // If this is a jar, collect service files to combine instead of adding them to the zip. 581*333d2b36SAndroid Build Coastguard Worker err := jarServices.AddServiceFile(entry) 582*333d2b36SAndroid Build Coastguard Worker if err != nil { 583*333d2b36SAndroid Build Coastguard Worker return err 584*333d2b36SAndroid Build Coastguard Worker } 585*333d2b36SAndroid Build Coastguard Worker continue 586*333d2b36SAndroid Build Coastguard Worker } 587*333d2b36SAndroid Build Coastguard Worker if copyFully || !out.isEntryExcluded(entry.Name) { 588*333d2b36SAndroid Build Coastguard Worker if err := out.copyEntry(inputZip, i); err != nil { 589*333d2b36SAndroid Build Coastguard Worker return err 590*333d2b36SAndroid Build Coastguard Worker } 591*333d2b36SAndroid Build Coastguard Worker } 592*333d2b36SAndroid Build Coastguard Worker } 593*333d2b36SAndroid Build Coastguard Worker // Unless we need to rearrange the entries, the input zip can now be closed. 594*333d2b36SAndroid Build Coastguard Worker if !(emulateJar || sortEntries) { 595*333d2b36SAndroid Build Coastguard Worker if err := inputZip.Close(); err != nil { 596*333d2b36SAndroid Build Coastguard Worker return err 597*333d2b36SAndroid Build Coastguard Worker } 598*333d2b36SAndroid Build Coastguard Worker } 599*333d2b36SAndroid Build Coastguard Worker } 600*333d2b36SAndroid Build Coastguard Worker 601*333d2b36SAndroid Build Coastguard Worker if emulateJar { 602*333d2b36SAndroid Build Coastguard Worker // Combine all the service files into a single list of combined service files and add them to the zip. 603*333d2b36SAndroid Build Coastguard Worker for _, serviceFile := range jarServices.ServiceFiles() { 604*333d2b36SAndroid Build Coastguard Worker _, err := out.addZipEntry(serviceFile.Name, ZipEntryFromBuffer{ 605*333d2b36SAndroid Build Coastguard Worker fh: serviceFile.FileHeader, 606*333d2b36SAndroid Build Coastguard Worker content: serviceFile.Contents, 607*333d2b36SAndroid Build Coastguard Worker }) 608*333d2b36SAndroid Build Coastguard Worker if err != nil { 609*333d2b36SAndroid Build Coastguard Worker return err 610*333d2b36SAndroid Build Coastguard Worker } 611*333d2b36SAndroid Build Coastguard Worker } 612*333d2b36SAndroid Build Coastguard Worker return out.writeEntries(out.jarSorted()) 613*333d2b36SAndroid Build Coastguard Worker } else if sortEntries { 614*333d2b36SAndroid Build Coastguard Worker return out.writeEntries(out.alphanumericSorted()) 615*333d2b36SAndroid Build Coastguard Worker } 616*333d2b36SAndroid Build Coastguard Worker return nil 617*333d2b36SAndroid Build Coastguard Worker} 618*333d2b36SAndroid Build Coastguard Worker 619*333d2b36SAndroid Build Coastguard Worker// Process command line 620*333d2b36SAndroid Build Coastguard Workertype fileList []string 621*333d2b36SAndroid Build Coastguard Worker 622*333d2b36SAndroid Build Coastguard Workerfunc (f *fileList) String() string { 623*333d2b36SAndroid Build Coastguard Worker return `""` 624*333d2b36SAndroid Build Coastguard Worker} 625*333d2b36SAndroid Build Coastguard Worker 626*333d2b36SAndroid Build Coastguard Workerfunc (f *fileList) Set(name string) error { 627*333d2b36SAndroid Build Coastguard Worker *f = append(*f, filepath.Clean(name)) 628*333d2b36SAndroid Build Coastguard Worker 629*333d2b36SAndroid Build Coastguard Worker return nil 630*333d2b36SAndroid Build Coastguard Worker} 631*333d2b36SAndroid Build Coastguard Worker 632*333d2b36SAndroid Build Coastguard Workertype zipsToNotStripSet map[string]bool 633*333d2b36SAndroid Build Coastguard Worker 634*333d2b36SAndroid Build Coastguard Workerfunc (s zipsToNotStripSet) String() string { 635*333d2b36SAndroid Build Coastguard Worker return `""` 636*333d2b36SAndroid Build Coastguard Worker} 637*333d2b36SAndroid Build Coastguard Worker 638*333d2b36SAndroid Build Coastguard Workerfunc (s zipsToNotStripSet) Set(path string) error { 639*333d2b36SAndroid Build Coastguard Worker s[path] = true 640*333d2b36SAndroid Build Coastguard Worker return nil 641*333d2b36SAndroid Build Coastguard Worker} 642*333d2b36SAndroid Build Coastguard Worker 643*333d2b36SAndroid Build Coastguard Workervar ( 644*333d2b36SAndroid Build Coastguard Worker sortEntries = flag.Bool("s", false, "sort entries (defaults to the order from the input zip files)") 645*333d2b36SAndroid Build Coastguard Worker emulateJar = flag.Bool("j", false, "sort zip entries using jar ordering (META-INF first)") 646*333d2b36SAndroid Build Coastguard Worker emulatePar = flag.Bool("p", false, "merge zip entries based on par format") 647*333d2b36SAndroid Build Coastguard Worker excludeDirs fileList 648*333d2b36SAndroid Build Coastguard Worker excludeFiles fileList 649*333d2b36SAndroid Build Coastguard Worker zipsToNotStrip = make(zipsToNotStripSet) 650*333d2b36SAndroid Build Coastguard Worker stripDirEntries = flag.Bool("D", false, "strip directory entries from the output zip file") 651*333d2b36SAndroid Build Coastguard Worker manifest = flag.String("m", "", "manifest file to insert in jar") 652*333d2b36SAndroid Build Coastguard Worker pyMain = flag.String("pm", "", "__main__.py file to insert in par") 653*333d2b36SAndroid Build Coastguard Worker prefix = flag.String("prefix", "", "A file to prefix to the zip file") 654*333d2b36SAndroid Build Coastguard Worker ignoreDuplicates = flag.Bool("ignore-duplicates", false, "take each entry from the first zip it exists in and don't warn") 655*333d2b36SAndroid Build Coastguard Worker) 656*333d2b36SAndroid Build Coastguard Worker 657*333d2b36SAndroid Build Coastguard Workerfunc init() { 658*333d2b36SAndroid Build Coastguard Worker flag.Var(&excludeDirs, "stripDir", "directories to be excluded from the output zip, accepts wildcards") 659*333d2b36SAndroid Build Coastguard Worker flag.Var(&excludeFiles, "stripFile", "files to be excluded from the output zip, accepts wildcards") 660*333d2b36SAndroid Build Coastguard Worker flag.Var(&zipsToNotStrip, "zipToNotStrip", "the input zip file which is not applicable for stripping") 661*333d2b36SAndroid Build Coastguard Worker} 662*333d2b36SAndroid Build Coastguard Worker 663*333d2b36SAndroid Build Coastguard Workertype FileInputZip struct { 664*333d2b36SAndroid Build Coastguard Worker name string 665*333d2b36SAndroid Build Coastguard Worker reader *zip.ReadCloser 666*333d2b36SAndroid Build Coastguard Worker} 667*333d2b36SAndroid Build Coastguard Worker 668*333d2b36SAndroid Build Coastguard Workerfunc (fiz *FileInputZip) Name() string { 669*333d2b36SAndroid Build Coastguard Worker return fiz.name 670*333d2b36SAndroid Build Coastguard Worker} 671*333d2b36SAndroid Build Coastguard Worker 672*333d2b36SAndroid Build Coastguard Workerfunc (fiz *FileInputZip) Close() error { 673*333d2b36SAndroid Build Coastguard Worker if fiz.IsOpen() { 674*333d2b36SAndroid Build Coastguard Worker reader := fiz.reader 675*333d2b36SAndroid Build Coastguard Worker fiz.reader = nil 676*333d2b36SAndroid Build Coastguard Worker return reader.Close() 677*333d2b36SAndroid Build Coastguard Worker } 678*333d2b36SAndroid Build Coastguard Worker return nil 679*333d2b36SAndroid Build Coastguard Worker} 680*333d2b36SAndroid Build Coastguard Worker 681*333d2b36SAndroid Build Coastguard Workerfunc (fiz *FileInputZip) Entries() []*zip.File { 682*333d2b36SAndroid Build Coastguard Worker if !fiz.IsOpen() { 683*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("%s: is not open", fiz.Name())) 684*333d2b36SAndroid Build Coastguard Worker } 685*333d2b36SAndroid Build Coastguard Worker return fiz.reader.File 686*333d2b36SAndroid Build Coastguard Worker} 687*333d2b36SAndroid Build Coastguard Worker 688*333d2b36SAndroid Build Coastguard Workerfunc (fiz *FileInputZip) IsOpen() bool { 689*333d2b36SAndroid Build Coastguard Worker return fiz.reader != nil 690*333d2b36SAndroid Build Coastguard Worker} 691*333d2b36SAndroid Build Coastguard Worker 692*333d2b36SAndroid Build Coastguard Workerfunc (fiz *FileInputZip) Open() error { 693*333d2b36SAndroid Build Coastguard Worker if fiz.IsOpen() { 694*333d2b36SAndroid Build Coastguard Worker return nil 695*333d2b36SAndroid Build Coastguard Worker } 696*333d2b36SAndroid Build Coastguard Worker var err error 697*333d2b36SAndroid Build Coastguard Worker if fiz.reader, err = zip.OpenReader(fiz.Name()); err != nil { 698*333d2b36SAndroid Build Coastguard Worker return fmt.Errorf("%s: %s", fiz.Name(), err.Error()) 699*333d2b36SAndroid Build Coastguard Worker } 700*333d2b36SAndroid Build Coastguard Worker return nil 701*333d2b36SAndroid Build Coastguard Worker} 702*333d2b36SAndroid Build Coastguard Worker 703*333d2b36SAndroid Build Coastguard Workerfunc main() { 704*333d2b36SAndroid Build Coastguard Worker flag.Usage = func() { 705*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [--prefix script] [-pm __main__.py] OutputZip [inputs...]") 706*333d2b36SAndroid Build Coastguard Worker flag.PrintDefaults() 707*333d2b36SAndroid Build Coastguard Worker } 708*333d2b36SAndroid Build Coastguard Worker 709*333d2b36SAndroid Build Coastguard Worker // parse args 710*333d2b36SAndroid Build Coastguard Worker flag.Parse() 711*333d2b36SAndroid Build Coastguard Worker args := flag.Args() 712*333d2b36SAndroid Build Coastguard Worker if len(args) < 1 { 713*333d2b36SAndroid Build Coastguard Worker flag.Usage() 714*333d2b36SAndroid Build Coastguard Worker os.Exit(1) 715*333d2b36SAndroid Build Coastguard Worker } 716*333d2b36SAndroid Build Coastguard Worker outputPath := args[0] 717*333d2b36SAndroid Build Coastguard Worker inputs := make([]string, 0) 718*333d2b36SAndroid Build Coastguard Worker for _, input := range args[1:] { 719*333d2b36SAndroid Build Coastguard Worker if input[0] == '@' { 720*333d2b36SAndroid Build Coastguard Worker f, err := os.Open(strings.TrimPrefix(input[1:], "@")) 721*333d2b36SAndroid Build Coastguard Worker if err != nil { 722*333d2b36SAndroid Build Coastguard Worker log.Fatal(err) 723*333d2b36SAndroid Build Coastguard Worker } 724*333d2b36SAndroid Build Coastguard Worker 725*333d2b36SAndroid Build Coastguard Worker rspInputs, err := response.ReadRspFile(f) 726*333d2b36SAndroid Build Coastguard Worker f.Close() 727*333d2b36SAndroid Build Coastguard Worker if err != nil { 728*333d2b36SAndroid Build Coastguard Worker log.Fatal(err) 729*333d2b36SAndroid Build Coastguard Worker } 730*333d2b36SAndroid Build Coastguard Worker inputs = append(inputs, rspInputs...) 731*333d2b36SAndroid Build Coastguard Worker } else { 732*333d2b36SAndroid Build Coastguard Worker inputs = append(inputs, input) 733*333d2b36SAndroid Build Coastguard Worker } 734*333d2b36SAndroid Build Coastguard Worker } 735*333d2b36SAndroid Build Coastguard Worker 736*333d2b36SAndroid Build Coastguard Worker log.SetFlags(log.Lshortfile) 737*333d2b36SAndroid Build Coastguard Worker 738*333d2b36SAndroid Build Coastguard Worker // make writer 739*333d2b36SAndroid Build Coastguard Worker outputZip, err := os.Create(outputPath) 740*333d2b36SAndroid Build Coastguard Worker if err != nil { 741*333d2b36SAndroid Build Coastguard Worker log.Fatal(err) 742*333d2b36SAndroid Build Coastguard Worker } 743*333d2b36SAndroid Build Coastguard Worker defer outputZip.Close() 744*333d2b36SAndroid Build Coastguard Worker 745*333d2b36SAndroid Build Coastguard Worker var offset int64 746*333d2b36SAndroid Build Coastguard Worker if *prefix != "" { 747*333d2b36SAndroid Build Coastguard Worker prefixFile, err := os.Open(*prefix) 748*333d2b36SAndroid Build Coastguard Worker if err != nil { 749*333d2b36SAndroid Build Coastguard Worker log.Fatal(err) 750*333d2b36SAndroid Build Coastguard Worker } 751*333d2b36SAndroid Build Coastguard Worker offset, err = io.Copy(outputZip, prefixFile) 752*333d2b36SAndroid Build Coastguard Worker if err != nil { 753*333d2b36SAndroid Build Coastguard Worker log.Fatal(err) 754*333d2b36SAndroid Build Coastguard Worker } 755*333d2b36SAndroid Build Coastguard Worker } 756*333d2b36SAndroid Build Coastguard Worker 757*333d2b36SAndroid Build Coastguard Worker writer := zip.NewWriter(outputZip) 758*333d2b36SAndroid Build Coastguard Worker defer func() { 759*333d2b36SAndroid Build Coastguard Worker err := writer.Close() 760*333d2b36SAndroid Build Coastguard Worker if err != nil { 761*333d2b36SAndroid Build Coastguard Worker log.Fatal(err) 762*333d2b36SAndroid Build Coastguard Worker } 763*333d2b36SAndroid Build Coastguard Worker }() 764*333d2b36SAndroid Build Coastguard Worker writer.SetOffset(offset) 765*333d2b36SAndroid Build Coastguard Worker 766*333d2b36SAndroid Build Coastguard Worker if *manifest != "" && !*emulateJar { 767*333d2b36SAndroid Build Coastguard Worker log.Fatal(errors.New("must specify -j when specifying a manifest via -m")) 768*333d2b36SAndroid Build Coastguard Worker } 769*333d2b36SAndroid Build Coastguard Worker 770*333d2b36SAndroid Build Coastguard Worker if *pyMain != "" && !*emulatePar { 771*333d2b36SAndroid Build Coastguard Worker log.Fatal(errors.New("must specify -p when specifying a Python __main__.py via -pm")) 772*333d2b36SAndroid Build Coastguard Worker } 773*333d2b36SAndroid Build Coastguard Worker 774*333d2b36SAndroid Build Coastguard Worker // do merge 775*333d2b36SAndroid Build Coastguard Worker inputZipsManager := NewInputZipsManager(len(inputs), 1000) 776*333d2b36SAndroid Build Coastguard Worker inputZips := make([]InputZip, len(inputs)) 777*333d2b36SAndroid Build Coastguard Worker for i, input := range inputs { 778*333d2b36SAndroid Build Coastguard Worker inputZips[i] = inputZipsManager.Manage(&FileInputZip{name: input}) 779*333d2b36SAndroid Build Coastguard Worker } 780*333d2b36SAndroid Build Coastguard Worker err = mergeZips(inputZips, writer, *manifest, *pyMain, *sortEntries, *emulateJar, *emulatePar, 781*333d2b36SAndroid Build Coastguard Worker *stripDirEntries, *ignoreDuplicates, []string(excludeFiles), []string(excludeDirs), 782*333d2b36SAndroid Build Coastguard Worker map[string]bool(zipsToNotStrip)) 783*333d2b36SAndroid Build Coastguard Worker if err != nil { 784*333d2b36SAndroid Build Coastguard Worker log.Fatal(err) 785*333d2b36SAndroid Build Coastguard Worker } 786*333d2b36SAndroid Build Coastguard Worker} 787