1*333d2b36SAndroid Build Coastguard Worker// Copyright 2023 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 jar 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "android/soong/third_party/zip" 19*333d2b36SAndroid Build Coastguard Worker "bufio" 20*333d2b36SAndroid Build Coastguard Worker "hash/crc32" 21*333d2b36SAndroid Build Coastguard Worker "sort" 22*333d2b36SAndroid Build Coastguard Worker "strings" 23*333d2b36SAndroid Build Coastguard Worker) 24*333d2b36SAndroid Build Coastguard Worker 25*333d2b36SAndroid Build Coastguard Workerconst servicesPrefix = "META-INF/services/" 26*333d2b36SAndroid Build Coastguard Worker 27*333d2b36SAndroid Build Coastguard Worker// Services is used to collect service files from multiple zip files and produce a list of ServiceFiles containing 28*333d2b36SAndroid Build Coastguard Worker// the unique lines from all the input zip entries with the same name. 29*333d2b36SAndroid Build Coastguard Workertype Services struct { 30*333d2b36SAndroid Build Coastguard Worker services map[string]*ServiceFile 31*333d2b36SAndroid Build Coastguard Worker} 32*333d2b36SAndroid Build Coastguard Worker 33*333d2b36SAndroid Build Coastguard Worker// ServiceFile contains the combined contents of all input zip entries with a single name. 34*333d2b36SAndroid Build Coastguard Workertype ServiceFile struct { 35*333d2b36SAndroid Build Coastguard Worker Name string 36*333d2b36SAndroid Build Coastguard Worker FileHeader *zip.FileHeader 37*333d2b36SAndroid Build Coastguard Worker Contents []byte 38*333d2b36SAndroid Build Coastguard Worker Lines []string 39*333d2b36SAndroid Build Coastguard Worker} 40*333d2b36SAndroid Build Coastguard Worker 41*333d2b36SAndroid Build Coastguard Worker// IsServiceFile returns true if the zip entry is in the META-INF/services/ directory. 42*333d2b36SAndroid Build Coastguard Workerfunc (Services) IsServiceFile(entry *zip.File) bool { 43*333d2b36SAndroid Build Coastguard Worker return strings.HasPrefix(entry.Name, servicesPrefix) 44*333d2b36SAndroid Build Coastguard Worker} 45*333d2b36SAndroid Build Coastguard Worker 46*333d2b36SAndroid Build Coastguard Worker// AddServiceFile adds a zip entry in the META-INF/services/ directory to the list of service files that need 47*333d2b36SAndroid Build Coastguard Worker// to be combined. 48*333d2b36SAndroid Build Coastguard Workerfunc (j *Services) AddServiceFile(entry *zip.File) error { 49*333d2b36SAndroid Build Coastguard Worker if j.services == nil { 50*333d2b36SAndroid Build Coastguard Worker j.services = map[string]*ServiceFile{} 51*333d2b36SAndroid Build Coastguard Worker } 52*333d2b36SAndroid Build Coastguard Worker 53*333d2b36SAndroid Build Coastguard Worker service := entry.Name 54*333d2b36SAndroid Build Coastguard Worker serviceFile := j.services[service] 55*333d2b36SAndroid Build Coastguard Worker fh := entry.FileHeader 56*333d2b36SAndroid Build Coastguard Worker if serviceFile == nil { 57*333d2b36SAndroid Build Coastguard Worker serviceFile = &ServiceFile{ 58*333d2b36SAndroid Build Coastguard Worker Name: service, 59*333d2b36SAndroid Build Coastguard Worker FileHeader: &fh, 60*333d2b36SAndroid Build Coastguard Worker } 61*333d2b36SAndroid Build Coastguard Worker j.services[service] = serviceFile 62*333d2b36SAndroid Build Coastguard Worker } 63*333d2b36SAndroid Build Coastguard Worker 64*333d2b36SAndroid Build Coastguard Worker f, err := entry.Open() 65*333d2b36SAndroid Build Coastguard Worker if err != nil { 66*333d2b36SAndroid Build Coastguard Worker return err 67*333d2b36SAndroid Build Coastguard Worker } 68*333d2b36SAndroid Build Coastguard Worker defer f.Close() 69*333d2b36SAndroid Build Coastguard Worker 70*333d2b36SAndroid Build Coastguard Worker scanner := bufio.NewScanner(f) 71*333d2b36SAndroid Build Coastguard Worker for scanner.Scan() { 72*333d2b36SAndroid Build Coastguard Worker line := scanner.Text() 73*333d2b36SAndroid Build Coastguard Worker if line != "" { 74*333d2b36SAndroid Build Coastguard Worker serviceFile.Lines = append(serviceFile.Lines, line) 75*333d2b36SAndroid Build Coastguard Worker } 76*333d2b36SAndroid Build Coastguard Worker } 77*333d2b36SAndroid Build Coastguard Worker 78*333d2b36SAndroid Build Coastguard Worker if err := scanner.Err(); err != nil { 79*333d2b36SAndroid Build Coastguard Worker return err 80*333d2b36SAndroid Build Coastguard Worker } 81*333d2b36SAndroid Build Coastguard Worker 82*333d2b36SAndroid Build Coastguard Worker return nil 83*333d2b36SAndroid Build Coastguard Worker} 84*333d2b36SAndroid Build Coastguard Worker 85*333d2b36SAndroid Build Coastguard Worker// ServiceFiles returns the list of combined service files, each containing all the unique lines from the 86*333d2b36SAndroid Build Coastguard Worker// corresponding service files in the input zip entries. 87*333d2b36SAndroid Build Coastguard Workerfunc (j *Services) ServiceFiles() []ServiceFile { 88*333d2b36SAndroid Build Coastguard Worker services := make([]ServiceFile, 0, len(j.services)) 89*333d2b36SAndroid Build Coastguard Worker 90*333d2b36SAndroid Build Coastguard Worker for _, serviceFile := range j.services { 91*333d2b36SAndroid Build Coastguard Worker serviceFile.Lines = dedupServicesLines(serviceFile.Lines) 92*333d2b36SAndroid Build Coastguard Worker serviceFile.Lines = append(serviceFile.Lines, "") 93*333d2b36SAndroid Build Coastguard Worker serviceFile.Contents = []byte(strings.Join(serviceFile.Lines, "\n")) 94*333d2b36SAndroid Build Coastguard Worker 95*333d2b36SAndroid Build Coastguard Worker serviceFile.FileHeader.UncompressedSize64 = uint64(len(serviceFile.Contents)) 96*333d2b36SAndroid Build Coastguard Worker serviceFile.FileHeader.CRC32 = crc32.ChecksumIEEE(serviceFile.Contents) 97*333d2b36SAndroid Build Coastguard Worker if serviceFile.FileHeader.Method == zip.Store { 98*333d2b36SAndroid Build Coastguard Worker serviceFile.FileHeader.CompressedSize64 = serviceFile.FileHeader.UncompressedSize64 99*333d2b36SAndroid Build Coastguard Worker } 100*333d2b36SAndroid Build Coastguard Worker 101*333d2b36SAndroid Build Coastguard Worker services = append(services, *serviceFile) 102*333d2b36SAndroid Build Coastguard Worker } 103*333d2b36SAndroid Build Coastguard Worker 104*333d2b36SAndroid Build Coastguard Worker sort.Slice(services, func(i, j int) bool { 105*333d2b36SAndroid Build Coastguard Worker return services[i].Name < services[j].Name 106*333d2b36SAndroid Build Coastguard Worker }) 107*333d2b36SAndroid Build Coastguard Worker 108*333d2b36SAndroid Build Coastguard Worker return services 109*333d2b36SAndroid Build Coastguard Worker} 110*333d2b36SAndroid Build Coastguard Worker 111*333d2b36SAndroid Build Coastguard Workerfunc dedupServicesLines(in []string) []string { 112*333d2b36SAndroid Build Coastguard Worker writeIndex := 0 113*333d2b36SAndroid Build Coastguard Workerouter: 114*333d2b36SAndroid Build Coastguard Worker for readIndex := 0; readIndex < len(in); readIndex++ { 115*333d2b36SAndroid Build Coastguard Worker for compareIndex := 0; compareIndex < writeIndex; compareIndex++ { 116*333d2b36SAndroid Build Coastguard Worker if interface{}(in[readIndex]) == interface{}(in[compareIndex]) { 117*333d2b36SAndroid Build Coastguard Worker // The value at readIndex already exists somewhere in the output region 118*333d2b36SAndroid Build Coastguard Worker // of the slice before writeIndex, skip it. 119*333d2b36SAndroid Build Coastguard Worker continue outer 120*333d2b36SAndroid Build Coastguard Worker } 121*333d2b36SAndroid Build Coastguard Worker } 122*333d2b36SAndroid Build Coastguard Worker if readIndex != writeIndex { 123*333d2b36SAndroid Build Coastguard Worker in[writeIndex] = in[readIndex] 124*333d2b36SAndroid Build Coastguard Worker } 125*333d2b36SAndroid Build Coastguard Worker writeIndex++ 126*333d2b36SAndroid Build Coastguard Worker } 127*333d2b36SAndroid Build Coastguard Worker return in[0:writeIndex] 128*333d2b36SAndroid Build Coastguard Worker} 129