blob: d328876ed283975266a541a055e8d5c3d5209150 [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 Choudhary70fb37e2023-11-16 19:52:44 +000080func writeNewlineToOutputFile(outputFile string) {
81 file, err := os.Create(outputFile)
82 data := "\n"
83 if err != nil {
84 log.Fatal(err)
85 }
86 defer file.Close()
87
88 _, err = file.Write([]byte(data))
89 if err != nil {
90 log.Fatal(err)
91 }
92}
93
Aditya Choudharya96ce322023-11-15 11:02:37 +000094func processTestSpecProtobuf(
Aditya Choudharyd96041a2023-11-20 10:16:42 +000095 filePath string, ownershipMetadataMap *sync.Map, keyLocks *keyToLocksMap,
96 errCh chan error, wg *sync.WaitGroup,
Aditya Choudhary51f97c12023-11-02 11:18:40 +000097) {
98 defer wg.Done()
99
100 fileContent := strings.TrimRight(readFileToString(filePath), "\n")
101 testData := test_spec_proto.TestSpec{}
102 err := proto.Unmarshal([]byte(fileContent), &testData)
103 if err != nil {
104 errCh <- err
105 return
106 }
107
108 ownershipMetadata := testData.GetOwnershipMetadataList()
109 for _, metadata := range ownershipMetadata {
110 key := metadata.GetTargetName()
111 lock := keyLocks.GetLockForKey(key)
112 lock.Lock()
113
114 value, loaded := ownershipMetadataMap.LoadOrStore(
115 key, []*test_spec_proto.TestSpec_OwnershipMetadata{metadata},
116 )
117 if loaded {
118 existingMetadata := value.([]*test_spec_proto.TestSpec_OwnershipMetadata)
119 isDuplicate := false
120 for _, existing := range existingMetadata {
121 if metadata.GetTrendyTeamId() != existing.GetTrendyTeamId() {
122 errCh <- fmt.Errorf(
123 "Conflicting trendy team IDs found for %s at:\n%s with teamId"+
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000124 ": %s,\n%s with teamId: %s",
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000125 key,
126 metadata.GetPath(), metadata.GetTrendyTeamId(), existing.GetPath(),
127 existing.GetTrendyTeamId(),
128 )
129
130 lock.Unlock()
131 return
132 }
133 if metadata.GetTrendyTeamId() == existing.GetTrendyTeamId() && metadata.GetPath() == existing.GetPath() {
134 isDuplicate = true
135 break
136 }
137 }
138 if !isDuplicate {
139 existingMetadata = append(existingMetadata, metadata)
140 ownershipMetadataMap.Store(key, existingMetadata)
141 }
142 }
143
144 lock.Unlock()
145 }
146}
147
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000148// processCodeMetadataProtobuf processes CodeMetadata protobuf files
149func processCodeMetadataProtobuf(
150 filePath string, ownershipMetadataMap *sync.Map, sourceFileMetadataMap *sync.Map, keyLocks *keyToLocksMap,
151 errCh chan error, wg *sync.WaitGroup,
152) {
153 defer wg.Done()
154
155 fileContent := strings.TrimRight(readFileToString(filePath), "\n")
156 internalCodeData := code_metadata_internal_proto.CodeMetadataInternal{}
157 err := proto.Unmarshal([]byte(fileContent), &internalCodeData)
158 if err != nil {
159 errCh <- err
160 return
161 }
162
163 // Process each TargetOwnership entry
164 for _, internalMetadata := range internalCodeData.GetTargetOwnershipList() {
165 key := internalMetadata.GetTargetName()
166 lock := keyLocks.GetLockForKey(key)
167 lock.Lock()
168
169 for _, srcFile := range internalMetadata.GetSourceFiles() {
170 srcFileKey := srcFile
171 srcFileLock := keyLocks.GetLockForKey(srcFileKey)
172 srcFileLock.Lock()
173 attributes := sourceFileAttributes{
174 TeamID: internalMetadata.GetTrendyTeamId(),
175 MultiOwnership: internalMetadata.GetMultiOwnership(),
176 Path: internalMetadata.GetPath(),
177 }
178
179 existingAttributes, exists := sourceFileMetadataMap.Load(srcFileKey)
180 if exists {
181 existing := existingAttributes.(sourceFileAttributes)
182 if attributes.TeamID != existing.TeamID && (!attributes.MultiOwnership || !existing.MultiOwnership) {
183 errCh <- fmt.Errorf(
184 "Conflict found for source file %s covered at %s with team ID: %s. Existing team ID: %s and path: %s."+
185 " If multi-ownership is required, multiOwnership should be set to true in all test_spec modules using this target. "+
186 "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.",
187 srcFile, internalMetadata.GetPath(), attributes.TeamID, existing.TeamID, existing.Path,
188 )
189 srcFileLock.Unlock()
190 lock.Unlock()
191 return
192 }
193 } else {
194 // Store the metadata if no conflict
195 sourceFileMetadataMap.Store(srcFileKey, attributes)
196 }
197 srcFileLock.Unlock()
198 }
199
200 value, loaded := ownershipMetadataMap.LoadOrStore(
201 key, []*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{internalMetadata},
202 )
203 if loaded {
204 existingMetadata := value.([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership)
205 isDuplicate := false
206 for _, existing := range existingMetadata {
207 if internalMetadata.GetTrendyTeamId() == existing.GetTrendyTeamId() && internalMetadata.GetPath() == existing.GetPath() {
208 isDuplicate = true
209 break
210 }
211 }
212 if !isDuplicate {
213 existingMetadata = append(existingMetadata, internalMetadata)
214 ownershipMetadataMap.Store(key, existingMetadata)
215 }
216 }
217
218 lock.Unlock()
219 }
220}
221
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000222func main() {
223 inputFile := flag.String("inputFile", "", "Input file path")
224 outputFile := flag.String("outputFile", "", "Output file path")
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000225 rule := flag.String(
226 "rule", "", "Metadata rule (Hint: test_spec or code_metadata)",
227 )
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000228 flag.Parse()
229
Aditya Choudharya96ce322023-11-15 11:02:37 +0000230 if *inputFile == "" || *outputFile == "" || *rule == "" {
231 fmt.Println("Usage: metadata -rule <rule> -inputFile <input file path> -outputFile <output file path>")
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000232 os.Exit(1)
233 }
234
235 inputFileData := strings.TrimRight(readFileToString(*inputFile), "\n")
Aditya Choudhary93cd9f62023-11-21 14:01:41 +0000236 filePaths := strings.Split(inputFileData, " ")
Aditya Choudhary70fb37e2023-11-16 19:52:44 +0000237 if len(filePaths) == 1 && filePaths[0] == "" {
238 writeNewlineToOutputFile(*outputFile)
239 return
240 }
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000241 ownershipMetadataMap := &sync.Map{}
242 keyLocks := &keyToLocksMap{}
243 errCh := make(chan error, len(filePaths))
244 var wg sync.WaitGroup
245
Aditya Choudharya96ce322023-11-15 11:02:37 +0000246 switch *rule {
247 case "test_spec":
248 for _, filePath := range filePaths {
249 wg.Add(1)
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000250 go processTestSpecProtobuf(
251 filePath, ownershipMetadataMap, keyLocks, errCh, &wg,
252 )
Aditya Choudharya96ce322023-11-15 11:02:37 +0000253 }
254
255 wg.Wait()
256 close(errCh)
257
258 for err := range errCh {
259 log.Fatal(err)
260 }
261
262 allKeys := getSortedKeys(ownershipMetadataMap)
263 var allMetadata []*test_spec_proto.TestSpec_OwnershipMetadata
264
265 for _, key := range allKeys {
266 value, _ := ownershipMetadataMap.Load(key)
267 metadataList := value.([]*test_spec_proto.TestSpec_OwnershipMetadata)
268 allMetadata = append(allMetadata, metadataList...)
269 }
270
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000271 testSpec := &test_spec_proto.TestSpec{
272 OwnershipMetadataList: allMetadata,
273 }
274 writeProtoToFile(*outputFile, testSpec)
Aditya Choudharya96ce322023-11-15 11:02:37 +0000275 break
276 case "code_metadata":
Aditya Choudharyd96041a2023-11-20 10:16:42 +0000277 sourceFileMetadataMap := &sync.Map{}
278 for _, filePath := range filePaths {
279 wg.Add(1)
280 go processCodeMetadataProtobuf(
281 filePath, ownershipMetadataMap, sourceFileMetadataMap, keyLocks, errCh, &wg,
282 )
283 }
284
285 wg.Wait()
286 close(errCh)
287
288 for err := range errCh {
289 log.Fatal(err)
290 }
291
292 sortedKeys := getSortedKeys(ownershipMetadataMap)
293 allMetadata := make([]*code_metadata_proto.CodeMetadata_TargetOwnership, 0)
294 for _, key := range sortedKeys {
295 value, _ := ownershipMetadataMap.Load(key)
296 metadata := value.([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership)
297 for _, m := range metadata {
298 targetName := m.GetTargetName()
299 path := m.GetPath()
300 trendyTeamId := m.GetTrendyTeamId()
301
302 allMetadata = append(allMetadata, &code_metadata_proto.CodeMetadata_TargetOwnership{
303 TargetName: &targetName,
304 Path: &path,
305 TrendyTeamId: &trendyTeamId,
306 SourceFiles: m.GetSourceFiles(),
307 })
308 }
309 }
310
311 finalMetadata := &code_metadata_proto.CodeMetadata{
312 TargetOwnershipList: allMetadata,
313 }
314 writeProtoToFile(*outputFile, finalMetadata)
315 break
Aditya Choudharya96ce322023-11-15 11:02:37 +0000316 default:
317 log.Fatalf("No specific processing implemented for rule '%s'.\n", *rule)
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000318 }
Aditya Choudhary51f97c12023-11-02 11:18:40 +0000319}