Merge META-INF/services/* files in merge_zips -jar
kotlinx_coroutines_test and kotlinx_coroutine_android each provide a META-INF/services/kotlinx.coroutines.CoroutineExceptionHandler with different contents, and the final contents needs to be the combination of the two files. Implement service merging in merge_zips when the -jar argument is provided. Bug: 290933559 Test: TestMergeZips Change-Id: I69f80d1265c64c671d308ef4cdccfa1564abe056
This commit is contained in:
@@ -21,6 +21,7 @@ bootstrap_go_package {
|
||||
pkgPath: "android/soong/jar",
|
||||
srcs: [
|
||||
"jar.go",
|
||||
"services.go",
|
||||
],
|
||||
testSrcs: [
|
||||
"jar_test.go",
|
||||
|
128
jar/services.go
Normal file
128
jar/services.go
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright 2023 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package jar
|
||||
|
||||
import (
|
||||
"android/soong/third_party/zip"
|
||||
"bufio"
|
||||
"hash/crc32"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const servicesPrefix = "META-INF/services/"
|
||||
|
||||
// Services is used to collect service files from multiple zip files and produce a list of ServiceFiles containing
|
||||
// the unique lines from all the input zip entries with the same name.
|
||||
type Services struct {
|
||||
services map[string]*ServiceFile
|
||||
}
|
||||
|
||||
// ServiceFile contains the combined contents of all input zip entries with a single name.
|
||||
type ServiceFile struct {
|
||||
Name string
|
||||
FileHeader *zip.FileHeader
|
||||
Contents []byte
|
||||
Lines []string
|
||||
}
|
||||
|
||||
// IsServiceFile returns true if the zip entry is in the META-INF/services/ directory.
|
||||
func (Services) IsServiceFile(entry *zip.File) bool {
|
||||
return strings.HasPrefix(entry.Name, servicesPrefix)
|
||||
}
|
||||
|
||||
// AddServiceFile adds a zip entry in the META-INF/services/ directory to the list of service files that need
|
||||
// to be combined.
|
||||
func (j *Services) AddServiceFile(entry *zip.File) error {
|
||||
if j.services == nil {
|
||||
j.services = map[string]*ServiceFile{}
|
||||
}
|
||||
|
||||
service := entry.Name
|
||||
serviceFile := j.services[service]
|
||||
fh := entry.FileHeader
|
||||
if serviceFile == nil {
|
||||
serviceFile = &ServiceFile{
|
||||
Name: service,
|
||||
FileHeader: &fh,
|
||||
}
|
||||
j.services[service] = serviceFile
|
||||
}
|
||||
|
||||
f, err := entry.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line != "" {
|
||||
serviceFile.Lines = append(serviceFile.Lines, line)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServiceFiles returns the list of combined service files, each containing all the unique lines from the
|
||||
// corresponding service files in the input zip entries.
|
||||
func (j *Services) ServiceFiles() []ServiceFile {
|
||||
services := make([]ServiceFile, 0, len(j.services))
|
||||
|
||||
for _, serviceFile := range j.services {
|
||||
serviceFile.Lines = dedupServicesLines(serviceFile.Lines)
|
||||
serviceFile.Lines = append(serviceFile.Lines, "")
|
||||
serviceFile.Contents = []byte(strings.Join(serviceFile.Lines, "\n"))
|
||||
|
||||
serviceFile.FileHeader.UncompressedSize64 = uint64(len(serviceFile.Contents))
|
||||
serviceFile.FileHeader.CRC32 = crc32.ChecksumIEEE(serviceFile.Contents)
|
||||
if serviceFile.FileHeader.Method == zip.Store {
|
||||
serviceFile.FileHeader.CompressedSize64 = serviceFile.FileHeader.UncompressedSize64
|
||||
}
|
||||
|
||||
services = append(services, *serviceFile)
|
||||
}
|
||||
|
||||
sort.Slice(services, func(i, j int) bool {
|
||||
return services[i].Name < services[j].Name
|
||||
})
|
||||
|
||||
return services
|
||||
}
|
||||
|
||||
func dedupServicesLines(in []string) []string {
|
||||
writeIndex := 0
|
||||
outer:
|
||||
for readIndex := 0; readIndex < len(in); readIndex++ {
|
||||
for compareIndex := 0; compareIndex < writeIndex; compareIndex++ {
|
||||
if interface{}(in[readIndex]) == interface{}(in[compareIndex]) {
|
||||
// The value at readIndex already exists somewhere in the output region
|
||||
// of the slice before writeIndex, skip it.
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
if readIndex != writeIndex {
|
||||
in[writeIndex] = in[readIndex]
|
||||
}
|
||||
writeIndex++
|
||||
}
|
||||
return in[0:writeIndex]
|
||||
}
|
Reference in New Issue
Block a user