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 "strings" 21 22 "github.com/google/blueprint" 23 "github.com/google/blueprint/proptools" 24) 25 26var ( 27 aidlMetadataRule = pctx.StaticRule("aidlMetadataRule", blueprint.RuleParams{ 28 Command: `rm -f ${out} && { ` + 29 `echo '{' && ` + 30 `echo "\"name\": \"${name}\"," && ` + 31 `echo "\"stability\": \"${stability}\"," && ` + 32 `echo "\"types\": [${types}]," && ` + 33 `echo "\"hashes\": [${hashes}]," && ` + 34 `echo "\"has_development\": ${has_development}," && ` + 35 `echo "\"use_unfrozen\": ${use_unfrozen}," && ` + 36 `echo "\"versions\": [${versions}]" && ` + 37 `echo '}' ` + 38 `;} >> ${out}`, 39 Description: "AIDL metadata: ${out}", 40 }, "name", "stability", "types", "hashes", "has_development", "use_unfrozen", "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 72func (m *aidlInterfacesMetadataSingleton) GenerateAndroidBuildActions(ctx android.ModuleContext) { 73 if m.Name() != aidlMetadataSingletonName { 74 ctx.PropertyErrorf("name", "must be %s", aidlMetadataSingletonName) 75 return 76 } 77 78 type ModuleInfo struct { 79 Stability string 80 ComputedTypes []string 81 HashFiles []string 82 HasDevelopment android.Path 83 UseUnfrozen bool 84 Versions []string 85 } 86 87 // name -> ModuleInfo 88 moduleInfos := map[string]ModuleInfo{} 89 ctx.VisitDirectDeps(func(m android.Module) { 90 if !m.ExportedToMake() { 91 return 92 } 93 94 switch t := m.(type) { 95 case *aidlInterface: 96 apiInfo := expectOtherModuleProvider(ctx, t, aidlApiProvider) 97 info := moduleInfos[t.ModuleBase.Name()] 98 info.Stability = proptools.StringDefault(t.properties.Stability, "") 99 info.ComputedTypes = t.computedTypes 100 info.Versions = t.getVersions() 101 info.UseUnfrozen = t.useUnfrozen(ctx) 102 info.HasDevelopment = apiInfo.HasDevelopment 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 } 111 }) 112 113 var metadataOutputs android.Paths 114 for _, name := range android.SortedKeys(moduleInfos) { 115 info := moduleInfos[name] 116 metadataPath := android.PathForModuleOut(ctx, "metadata_"+name) 117 metadataOutputs = append(metadataOutputs, metadataPath) 118 119 // There is one aidlGenRule per-version per-backend. If we had 120 // objects per version and sub-objects per backend, we could 121 // avoid needing to filter out duplicates. 122 info.HashFiles = android.FirstUniqueStrings(info.HashFiles) 123 readHashes := "" 124 if len(info.HashFiles) > 0 { 125 readHashes = "$$(sed 's/.*/\"&\",/' " + strings.Join(info.HashFiles, " ") + 126 "| tr '\\n' ' ' | sed 's/, $$//')" 127 } 128 129 implicits := android.PathsForSource(ctx, info.HashFiles) 130 hasDevelopmentValue := "true" 131 if info.HasDevelopment != nil { 132 hasDevelopmentValue = "$$(if [ \"$$(cat " + info.HasDevelopment.String() + 133 ")\" = \"1\" ]; then echo true; else echo false; fi)" 134 } 135 136 useUnfrozenValue := "false" 137 if info.UseUnfrozen { 138 useUnfrozenValue = "true" 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 "use_unfrozen": useUnfrozenValue, 153 "versions": strings.Join(info.Versions, ", "), 154 }, 155 }) 156 } 157 158 output := android.PathForModuleOut(ctx, "aidl_metadata.json") 159 160 ctx.Build(pctx, android.BuildParams{ 161 Rule: joinJsonObjectsToArrayRule, 162 Inputs: metadataOutputs, 163 Output: output, 164 Args: map[string]string{ 165 "files": strings.Join(metadataOutputs.Strings(), " "), 166 }, 167 }) 168 169 ctx.SetOutputFiles(android.Paths{output}, "") 170} 171