blob: b7668be44f1f52f837ee567a5edf34600e7b099a [file] [log] [blame]
Aditya Choudhary51f97c12023-11-02 11:18:40 +00001package main
2
3import (
4 "flag"
5 "fmt"
6 "io"
7 "log"
8 "os"
9 "sort"
10 "strings"
11 "sync"
12
Aditya Choudharyd96041a2023-11-20 10:16:42 +000013 "android/soong/testing/code_metadata_internal_proto"
14 "android/soong/testing/code_metadata_proto"
Aditya Choudhary51f97c12023-11-02 11:18:40 +000015 "android/soong/testing/test_spec_proto"
16 "google.golang.org/protobuf/proto"
17)
18
19type keyToLocksMap struct {
20 locks sync.Map
21}
22
23func (kl *keyToLocksMap) GetLockForKey(key string) *sync.Mutex {
24 mutex, _ := kl.locks.LoadOrStore(key, &sync.Mutex{})
25 return mutex.(*sync.Mutex)
26}
27
Aditya Choudharyd96041a2023-11-20 10:16:42 +000028// Define a struct to hold the combination of team ID and multi-ownership flag for validation
29type sourceFileAttributes struct {
30 TeamID string
31 MultiOwnership bool
32 Path string
33}
34
Aditya Choudhary51f97c12023-11-02 11:18:40 +000035func getSortedKeys(syncMap *sync.Map) []string {
36 var allKeys []string
37 syncMap.Range(
38 func(key, _ interface{}) bool {
39 allKeys = append(allKeys, key.(string))
40 return true
41 },
42 )
43
44 sort.Strings(allKeys)
45 return allKeys
46}
47
Aditya Choudharyd96041a2023-11-20 10:16:42 +000048// writeProtoToFile marshals a protobuf message and writes it to a file
49func writeProtoToFile(outputFile string, message proto.Message) {
50 data, err := proto.Marshal(message)
Aditya Choudhary51f97c12023-11-02 11:18:40 +000051 if err != nil {
52 log.Fatal(err)
53 }
54 file, err := os.Create(outputFile)
55 if err != nil {
56 log.Fatal(err)
57 }
58 defer file.Close()
59
60 _, err = file.Write(data)
61 if err != nil {
62 log.Fatal(err)
63 }
64}
65
66func readFileToString(filePath string) string {
67 file, err := os.Open(filePath)
68 if err != nil {
69 log.Fatal(err)
70 }
71 defer file.Close()
72
73 data, err := io.ReadAll(file)
74 if err != nil {
75 log.Fatal(err)
76 }
77 return string(data)
78}
79
Aditya Choudharyf0670e82024-01-11 11:12:44 +000080func writeEmptyOutputProto(outputFile string, metadataRule string) {
Aditya Choudhary70fb37e2023-11-16 19:52:44 +000081 file, err := os.Create(outputFile)
Aditya Choudharyf0670e82024-01-11 11:12:44 +000082 if err != nil {
83 log.Fatal(err)
84 }
85 var message proto.Message
86 if metadataRule == "test_spec" {
87 message = &test_spec_proto.TestSpec{}
88 } else if metadataRule == "code_metadata" {
89 message = &code_metadata_proto.CodeMetadata{}
90 }
91 data, err := proto.Marshal(message)
Aditya Choudhary70fb37e2023-11-16 19:52:44 +000092 if err != nil {
93 log.Fatal(err)
94 }
95 defer file.Close()
96
97 _, err = file.Write([]byte(data))
98 if err != nil {
99 log.Fatal(err)
100 }
101}
102
Aditya Choudharya96ce322023-11-15 11:02:37 +0000103func processTestSpecProtobuf(
Aditya Choudharyf0670e82024-01-11 11:12:44 +0000104 filePath string, ownershipMetadataMap *sync.Map, keyLocks *keyToLocksMap,
105 errCh chan error, wg *sync.WaitGroup,
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000106) {
107 defer wg.Done()
108
109 fileContent := strings.TrimRight(readFileToString(filePath), "\n")
110 testData := test_spec_proto.TestSpec{}
111 err := proto.Unmarshal([]byte(fileContent), &testData)
112 if err != nil {
113 errCh <- err
114 return
115 }
116
117 ownershipMetadata := testData.GetOwnershipMetadataList()
118 for _, metadata := range ownershipMetadata {
119 key := metadata.GetTargetName()
120 lock := keyLocks.GetLockForKey(key)
121 lock.Lock()
122
123 value, loaded := ownershipMetadataMap.LoadOrStore(
124 key, []*test_spec_proto.TestSpec_OwnershipMetadata{metadata},
125 )
126 if loaded {
127 existingMetadata := value.([]*test_spec_proto.TestSpec_OwnershipMetadata)
128 isDuplicate := false
129 for _, existing := range existingMetadata {
130 if metadata.GetTrendyTeamId() != existing.GetTrendyTeamId() {
131 errCh <- fmt.Errorf(
132 "Conflicting trendy team IDs found for %s at:\n%s with teamId"+
Aditya Choudharyf0670e82024-01-11 11:12:44 +0000133 ": %s,\n%s with teamId: %s",
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000134 key,
135 metadata.GetPath(), metadata.GetTrendyTeamId(), existing.GetPath(),
136 existing.GetTrendyTeamId(),
137 )
138
139 lock.Unlock()
140 return
141 }
142 if metadata.GetTrendyTeamId() == existing.GetTrendyTeamId() && metadata.GetPath() == existing.GetPath() {
143 isDuplicate = true
144 break
145 }
146 }
147 if !isDuplicate {
148 existingMetadata = append(existingMetadata, metadata)
149 ownershipMetadataMap.Store(key, existingMetadata)
150 }
151 }
152
153 lock.Unlock()
154 }
155}
156
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000157// processCodeMetadataProtobuf processes CodeMetadata protobuf files
158func processCodeMetadataProtobuf(
Aditya Choudharyf0670e82024-01-11 11:12:44 +0000159 filePath string, ownershipMetadataMap *sync.Map, sourceFileMetadataMap *sync.Map, keyLocks *keyToLocksMap,
160 errCh chan error, wg *sync.WaitGroup,
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000161) {
162 defer wg.Done()
163
164 fileContent := strings.TrimRight(readFileToString(filePath), "\n")
165 internalCodeData := code_metadata_internal_proto.CodeMetadataInternal{}
166 err := proto.Unmarshal([]byte(fileContent), &internalCodeData)
167 if err != nil {
168 errCh <- err
169 return
170 }
171
172 // Process each TargetOwnership entry
173 for _, internalMetadata := range internalCodeData.GetTargetOwnershipList() {
174 key := internalMetadata.GetTargetName()
175 lock := keyLocks.GetLockForKey(key)
176 lock.Lock()
177
178 for _, srcFile := range internalMetadata.GetSourceFiles() {
179 srcFileKey := srcFile
180 srcFileLock := keyLocks.GetLockForKey(srcFileKey)
181 srcFileLock.Lock()
182 attributes := sourceFileAttributes{
183 TeamID: internalMetadata.GetTrendyTeamId(),
184 MultiOwnership: internalMetadata.GetMultiOwnership(),
185 Path: internalMetadata.GetPath(),
186 }
187
188 existingAttributes, exists := sourceFileMetadataMap.Load(srcFileKey)
189 if exists {
190 existing := existingAttributes.(sourceFileAttributes)
191 if attributes.TeamID != existing.TeamID && (!attributes.MultiOwnership || !existing.MultiOwnership) {
192 errCh <- fmt.Errorf(
193 "Conflict found for source file %s covered at %s with team ID: %s. Existing team ID: %s and path: %s."+
Aditya Choudharyf0670e82024-01-11 11:12:44 +0000194 " If multi-ownership is required, multiOwnership should be set to true in all test_spec modules using this target. "+
195 "Multiple-ownership in general is discouraged though as it make infrastructure around android relying on this information pick up a random value when it needs only one.",
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000196 srcFile, internalMetadata.GetPath(), attributes.TeamID, existing.TeamID, existing.Path,
197 )
198 srcFileLock.Unlock()
199 lock.Unlock()
200 return
201 }
202 } else {
203 // Store the metadata if no conflict
204 sourceFileMetadataMap.Store(srcFileKey, attributes)
205 }
206 srcFileLock.Unlock()
207 }
208
209 value, loaded := ownershipMetadataMap.LoadOrStore(
210 key, []*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{internalMetadata},
211 )
212 if loaded {
213 existingMetadata := value.([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership)
214 isDuplicate := false
215 for _, existing := range existingMetadata {
216 if internalMetadata.GetTrendyTeamId() == existing.GetTrendyTeamId() && internalMetadata.GetPath() == existing.GetPath() {
217 isDuplicate = true
218 break
219 }
220 }
221 if !isDuplicate {
222 existingMetadata = append(existingMetadata, internalMetadata)
223 ownershipMetadataMap.Store(key, existingMetadata)
224 }
225 }
226
227 lock.Unlock()
228 }
229}
230
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000231func main() {
232 inputFile := flag.String("inputFile", "", "Input file path")
233 outputFile := flag.String("outputFile", "", "Output file path")
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000234 rule := flag.String(
235 "rule", "", "Metadata rule (Hint: test_spec or code_metadata)",
236 )
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000237 flag.Parse()
238
Aditya Choudharya96ce322023-11-15 11:02:37 +0000239 if *inputFile == "" || *outputFile == "" || *rule == "" {
240 fmt.Println("Usage: metadata -rule <rule> -inputFile <input file path> -outputFile <output file path>")
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000241 os.Exit(1)
242 }
243
244 inputFileData := strings.TrimRight(readFileToString(*inputFile), "\n")
Aditya Choudhary93cd9f62023-11-21 14:01:41 +0000245 filePaths := strings.Split(inputFileData, " ")
Aditya Choudhary70fb37e2023-11-16 19:52:44 +0000246 if len(filePaths) == 1 && filePaths[0] == "" {
Aditya Choudharyf0670e82024-01-11 11:12:44 +0000247 writeEmptyOutputProto(*outputFile, *rule)
Aditya Choudhary70fb37e2023-11-16 19:52:44 +0000248 return
249 }
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000250 ownershipMetadataMap := &sync.Map{}
251 keyLocks := &keyToLocksMap{}
252 errCh := make(chan error, len(filePaths))
253 var wg sync.WaitGroup
254
Aditya Choudharya96ce322023-11-15 11:02:37 +0000255 switch *rule {
256 case "test_spec":
257 for _, filePath := range filePaths {
258 wg.Add(1)
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000259 go processTestSpecProtobuf(
260 filePath, ownershipMetadataMap, keyLocks, errCh, &wg,
261 )
Aditya Choudharya96ce322023-11-15 11:02:37 +0000262 }
263
264 wg.Wait()
265 close(errCh)
266
267 for err := range errCh {
268 log.Fatal(err)
269 }
270
271 allKeys := getSortedKeys(ownershipMetadataMap)
272 var allMetadata []*test_spec_proto.TestSpec_OwnershipMetadata
273
274 for _, key := range allKeys {
275 value, _ := ownershipMetadataMap.Load(key)
276 metadataList := value.([]*test_spec_proto.TestSpec_OwnershipMetadata)
277 allMetadata = append(allMetadata, metadataList...)
278 }
279
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000280 testSpec := &test_spec_proto.TestSpec{
281 OwnershipMetadataList: allMetadata,
282 }
283 writeProtoToFile(*outputFile, testSpec)
Aditya Choudharya96ce322023-11-15 11:02:37 +0000284 break
285 case "code_metadata":
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000286 sourceFileMetadataMap := &sync.Map{}
287 for _, filePath := range filePaths {
288 wg.Add(1)
289 go processCodeMetadataProtobuf(
290 filePath, ownershipMetadataMap, sourceFileMetadataMap, keyLocks, errCh, &wg,
291 )
292 }
293
294 wg.Wait()
295 close(errCh)
296
297 for err := range errCh {
298 log.Fatal(err)
299 }
300
301 sortedKeys := getSortedKeys(ownershipMetadataMap)
302 allMetadata := make([]*code_metadata_proto.CodeMetadata_TargetOwnership, 0)
303 for _, key := range sortedKeys {
304 value, _ := ownershipMetadataMap.Load(key)
305 metadata := value.([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership)
306 for _, m := range metadata {
307 targetName := m.GetTargetName()
308 path := m.GetPath()
309 trendyTeamId := m.GetTrendyTeamId()
310
311 allMetadata = append(allMetadata, &code_metadata_proto.CodeMetadata_TargetOwnership{
312 TargetName: &targetName,
313 Path: &path,
314 TrendyTeamId: &trendyTeamId,
315 SourceFiles: m.GetSourceFiles(),
316 })
317 }
318 }
319
320 finalMetadata := &code_metadata_proto.CodeMetadata{
321 TargetOwnershipList: allMetadata,
322 }
323 writeProtoToFile(*outputFile, finalMetadata)
324 break
Aditya Choudharya96ce322023-11-15 11:02:37 +0000325 default:
326 log.Fatalf("No specific processing implemented for rule '%s'.\n", *rule)
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000327 }
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000328}