1// Copyright (C) 2021 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package aidl 16 17import ( 18 "android/soong/android" 19 20 "fmt" 21 "strings" 22 23 "github.com/google/blueprint" 24 "github.com/google/blueprint/proptools" 25) 26 27var ( 28 aidlMetadataRule = pctx.StaticRule("aidlMetadataRule", blueprint.RuleParams{ 29 Command: `rm -f ${out} && { ` + 30 `echo '{' && ` + 31 `echo "\"name\": \"${name}\"," && ` + 32 `echo "\"stability\": \"${stability}\"," && ` + 33 `echo "\"types\": [${types}]," && ` + 34 `echo "\"hashes\": [${hashes}]" && ` + 35 `echo '}' ` + 36 `;} >> ${out}`, 37 Description: "AIDL metadata: ${out}", 38 }, "name", "stability", "types", "hashes") 39 40 joinJsonObjectsToArrayRule = pctx.StaticRule("joinJsonObjectsToArrayRule", blueprint.RuleParams{ 41 Rspfile: "$out.rsp", 42 RspfileContent: "$files", 43 Command: "rm -rf ${out} && " + 44 // Start the output array with an opening bracket. 45 "echo '[' >> ${out} && " + 46 // Append each input file and a comma to the output. 47 "for file in $$(cat ${out}.rsp); do " + 48 "cat $$file >> ${out}; echo ',' >> ${out}; " + 49 "done && " + 50 // Remove the last comma, replacing it with the closing bracket. 51 "sed -i '$$d' ${out} && echo ']' >> ${out}", 52 Description: "Joining JSON objects into array ${out}", 53 }, "files") 54) 55 56func init() { 57 android.RegisterModuleType("aidl_interfaces_metadata", aidlInterfacesMetadataSingletonFactory) 58} 59 60func aidlInterfacesMetadataSingletonFactory() android.Module { 61 i := &aidlInterfacesMetadataSingleton{} 62 android.InitAndroidModule(i) 63 return i 64} 65 66type aidlInterfacesMetadataSingleton struct { 67 android.ModuleBase 68 69 metadataPath android.WritablePath 70} 71 72var _ android.OutputFileProducer = (*aidlInterfacesMetadataSingleton)(nil) 73 74func (m *aidlInterfacesMetadataSingleton) GenerateAndroidBuildActions(ctx android.ModuleContext) { 75 if m.Name() != aidlMetadataSingletonName { 76 ctx.PropertyErrorf("name", "must be %s", aidlMetadataSingletonName) 77 return 78 } 79 80 type ModuleInfo struct { 81 Stability string 82 ComputedTypes []string 83 HashFiles []string 84 } 85 86 // name -> ModuleInfo 87 moduleInfos := map[string]ModuleInfo{} 88 ctx.VisitDirectDeps(func(m android.Module) { 89 if !m.ExportedToMake() { 90 return 91 } 92 93 switch t := m.(type) { 94 case *aidlInterface: 95 info := moduleInfos[t.ModuleBase.Name()] 96 info.Stability = proptools.StringDefault(t.properties.Stability, "") 97 info.ComputedTypes = t.computedTypes 98 moduleInfos[t.ModuleBase.Name()] = info 99 case *aidlGenRule: 100 info := moduleInfos[t.properties.BaseName] 101 if t.hashFile != nil { 102 info.HashFiles = append(info.HashFiles, t.hashFile.String()) 103 } 104 moduleInfos[t.properties.BaseName] = info 105 } 106 107 }) 108 109 var metadataOutputs android.Paths 110 for _, name := range android.SortedStringKeys(moduleInfos) { 111 info := moduleInfos[name] 112 metadataPath := android.PathForModuleOut(ctx, "metadata_"+name) 113 metadataOutputs = append(metadataOutputs, metadataPath) 114 115 // There is one aidlGenRule per-version per-backend. If we had 116 // objects per version and sub-objects per backend, we could 117 // avoid needing to filter out duplicates. 118 info.HashFiles = android.FirstUniqueStrings(info.HashFiles) 119 120 implicits := android.PathsForSource(ctx, info.HashFiles) 121 122 ctx.Build(pctx, android.BuildParams{ 123 Rule: aidlMetadataRule, 124 Implicits: implicits, 125 Output: metadataPath, 126 Args: map[string]string{ 127 "name": name, 128 "stability": info.Stability, 129 "types": strings.Join(wrap(`\"`, info.ComputedTypes, `\"`), ", "), 130 "hashes": strings.Join( 131 wrap(`\"$$(read -r < `, 132 info.HashFiles, 133 ` hash extra; printf '%s' $$hash)\"`), ", "), 134 }, 135 }) 136 } 137 138 m.metadataPath = android.PathForModuleOut(ctx, "aidl_metadata.json") 139 140 ctx.Build(pctx, android.BuildParams{ 141 Rule: joinJsonObjectsToArrayRule, 142 Inputs: metadataOutputs, 143 Output: m.metadataPath, 144 Args: map[string]string{ 145 "files": strings.Join(metadataOutputs.Strings(), " "), 146 }, 147 }) 148} 149 150func (m *aidlInterfacesMetadataSingleton) OutputFiles(tag string) (android.Paths, error) { 151 if tag != "" { 152 return nil, fmt.Errorf("unsupported tag %q", tag) 153 } 154 155 return android.Paths{m.metadataPath}, nil 156} 157