xref: /aosp_15_r20/build/soong/jar/services.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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