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 "\"has_development\": ${has_development}," && ` + 36 `echo "\"versions\": [${versions}]" && ` + 37 `echo '}' ` + 38 `;} >> ${out}`, 39 Description: "AIDL metadata: ${out}", 40 }, "name", "stability", "types", "hashes", "has_development", "versions") 41 42 joinJsonObjectsToArrayRule = pctx.StaticRule("joinJsonObjectsToArrayRule", blueprint.RuleParams{ 43 Rspfile: "$out.rsp", 44 RspfileContent: "$files", 45 Command: "rm -rf ${out} && " + 46 // Start the output array with an opening bracket. 47 "echo '[' >> ${out} && " + 48 // Append each input file and a comma to the output. 49 "for file in $$(cat ${out}.rsp); do " + 50 "cat $$file >> ${out}; echo ',' >> ${out}; " + 51 "done && " + 52 // Remove the last comma, replacing it with the closing bracket. 53 "sed -i '$$d' ${out} && echo ']' >> ${out}", 54 Description: "Joining JSON objects into array ${out}", 55 }, "files") 56) 57 58func init() { 59 android.RegisterModuleType("aidl_interfaces_metadata", aidlInterfacesMetadataSingletonFactory) 60} 61 62func aidlInterfacesMetadataSingletonFactory() android.Module { 63 i := &aidlInterfacesMetadataSingleton{} 64 android.InitAndroidModule(i) 65 return i 66} 67 68type aidlInterfacesMetadataSingleton struct { 69 android.ModuleBase 70 71 metadataPath android.WritablePath 72} 73 74var _ android.OutputFileProducer = (*aidlInterfacesMetadataSingleton)(nil) 75 76func (m *aidlInterfacesMetadataSingleton) GenerateAndroidBuildActions(ctx android.ModuleContext) { 77 if m.Name() != aidlMetadataSingletonName { 78 ctx.PropertyErrorf("name", "must be %s", aidlMetadataSingletonName) 79 return 80 } 81 82 type ModuleInfo struct { 83 Stability string 84 ComputedTypes []string 85 HashFiles []string 86 HasDevelopment android.WritablePath 87 Versions []string 88 } 89 90 // name -> ModuleInfo 91 moduleInfos := map[string]ModuleInfo{} 92 ctx.VisitDirectDeps(func(m android.Module) { 93 if !m.ExportedToMake() { 94 return 95 } 96 97 switch t := m.(type) { 98 case *aidlInterface: 99 info := moduleInfos[t.ModuleBase.Name()] 100 info.Stability = proptools.StringDefault(t.properties.Stability, "") 101 info.ComputedTypes = t.computedTypes 102 info.Versions = t.getVersions() 103 moduleInfos[t.ModuleBase.Name()] = info 104 case *aidlGenRule: 105 info := moduleInfos[t.properties.BaseName] 106 if t.hashFile != nil { 107 info.HashFiles = append(info.HashFiles, t.hashFile.String()) 108 } 109 moduleInfos[t.properties.BaseName] = info 110 case *aidlApi: 111 info := moduleInfos[t.properties.BaseName] 112 info.HasDevelopment = t.hasDevelopment 113 moduleInfos[t.properties.BaseName] = info 114 } 115 116 }) 117 118 var metadataOutputs android.Paths 119 for _, name := range android.SortedStringKeys(moduleInfos) { 120 info := moduleInfos[name] 121 metadataPath := android.PathForModuleOut(ctx, "metadata_"+name) 122 metadataOutputs = append(metadataOutputs, metadataPath) 123 124 // There is one aidlGenRule per-version per-backend. If we had 125 // objects per version and sub-objects per backend, we could 126 // avoid needing to filter out duplicates. 127 info.HashFiles = android.FirstUniqueStrings(info.HashFiles) 128 readHashes := "" 129 if len(info.HashFiles) > 0 { 130 readHashes = "$$(sed 's/.*/\"&\",/' " + strings.Join(info.HashFiles, " ") + 131 "| tr '\\n' ' ' | sed 's/, $$//')" 132 } 133 134 implicits := android.PathsForSource(ctx, info.HashFiles) 135 hasDevelopmentValue := "true" 136 if info.HasDevelopment != nil { 137 hasDevelopmentValue = "$$(if [ \"$$(cat " + info.HasDevelopment.String() + 138 ")\" = \"1\" ]; then echo true; else echo false; fi)" 139 } 140 141 ctx.Build(pctx, android.BuildParams{ 142 Rule: aidlMetadataRule, 143 Implicits: implicits, 144 Input: info.HasDevelopment, 145 Output: metadataPath, 146 Args: map[string]string{ 147 "name": name, 148 "stability": info.Stability, 149 "types": strings.Join(wrap(`\"`, info.ComputedTypes, `\"`), ", "), 150 "hashes": readHashes, 151 "has_development": hasDevelopmentValue, 152 "versions": strings.Join(info.Versions, ", "), 153 }, 154 }) 155 } 156 157 m.metadataPath = android.PathForModuleOut(ctx, "aidl_metadata.json") 158 159 ctx.Build(pctx, android.BuildParams{ 160 Rule: joinJsonObjectsToArrayRule, 161 Inputs: metadataOutputs, 162 Output: m.metadataPath, 163 Args: map[string]string{ 164 "files": strings.Join(metadataOutputs.Strings(), " "), 165 }, 166 }) 167} 168 169func (m *aidlInterfacesMetadataSingleton) OutputFiles(tag string) (android.Paths, error) { 170 if tag != "" { 171 return nil, fmt.Errorf("unsupported tag %q", tag) 172 } 173 174 return android.Paths{m.metadataPath}, nil 175} 176