1// Copyright 2019 Google Inc. All rights reserved. 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 cc 16 17import ( 18 "encoding/json" 19 "fmt" 20 "path" 21 "sort" 22 "strings" 23 24 "android/soong/android" 25) 26 27// This singleton collects cc modules' source and flags into to a json file. 28// It does so for generating CMakeLists.txt project files needed data when 29// either make, mm, mma, mmm or mmma is called. 30// The info file is generated in $OUT/module_bp_cc_depend.json. 31 32func init() { 33 android.RegisterParallelSingletonType("ccdeps_generator", ccDepsGeneratorSingleton) 34} 35 36func ccDepsGeneratorSingleton() android.Singleton { 37 return &ccdepsGeneratorSingleton{} 38} 39 40type ccdepsGeneratorSingleton struct { 41 outputPath android.Path 42} 43 44const ( 45 ccdepsJsonFileName = "module_bp_cc_deps.json" 46 cClang = "clang" 47 cppClang = "clang++" 48) 49 50type ccIdeInfo struct { 51 Path []string `json:"path,omitempty"` 52 Srcs []string `json:"srcs,omitempty"` 53 Global_Common_Flags ccParameters `json:"global_common_flags,omitempty"` 54 Local_Common_Flags ccParameters `json:"local_common_flags,omitempty"` 55 Global_C_flags ccParameters `json:"global_c_flags,omitempty"` 56 Local_C_flags ccParameters `json:"local_c_flags,omitempty"` 57 Global_C_only_flags ccParameters `json:"global_c_only_flags,omitempty"` 58 Local_C_only_flags ccParameters `json:"local_c_only_flags,omitempty"` 59 Global_Cpp_flags ccParameters `json:"global_cpp_flags,omitempty"` 60 Local_Cpp_flags ccParameters `json:"local_cpp_flags,omitempty"` 61 System_include_flags ccParameters `json:"system_include_flags,omitempty"` 62 Module_name string `json:"module_name,omitempty"` 63} 64 65type ccParameters struct { 66 HeaderSearchPath []string `json:"header_search_path,omitempty"` 67 SystemHeaderSearchPath []string `json:"system_search_path,omitempty"` 68 FlagParameters []string `json:"flag,omitempty"` 69 SysRoot string `json:"system_root,omitempty"` 70 RelativeFilePathFlags map[string]string `json:"relative_file_path,omitempty"` 71} 72 73type ccMapIdeInfos map[string]ccIdeInfo 74 75type ccDeps struct { 76 C_clang string `json:"clang,omitempty"` 77 Cpp_clang string `json:"clang++,omitempty"` 78 Modules ccMapIdeInfos `json:"modules,omitempty"` 79} 80 81func (c *ccdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) { 82 // (b/204397180) Generate module_bp_cc_deps.json by default. 83 moduleDeps := ccDeps{} 84 moduleInfos := map[string]ccIdeInfo{} 85 86 // Track if best variant (device arch match) has been found. 87 bestVariantFound := map[string]bool{} 88 89 pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/") 90 moduleDeps.C_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cClang) 91 moduleDeps.Cpp_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cppClang) 92 93 ctx.VisitAllModules(func(module android.Module) { 94 if ccModule, ok := module.(*Module); ok { 95 if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok { 96 generateCLionProjectData(ctx, compiledModule, ccModule, bestVariantFound, moduleInfos) 97 } 98 } 99 }) 100 101 moduleDeps.Modules = moduleInfos 102 103 ccfpath := android.PathForOutput(ctx, ccdepsJsonFileName) 104 err := createJsonFile(moduleDeps, ccfpath) 105 if err != nil { 106 ctx.Errorf(err.Error()) 107 } 108 c.outputPath = ccfpath 109 110 // This is necessary to satisfy the dangling rules check as this file is written by Soong rather than a rule. 111 ctx.Build(pctx, android.BuildParams{ 112 Rule: android.Touch, 113 Output: ccfpath, 114 }) 115 ctx.DistForGoal("general-tests", c.outputPath) 116} 117 118func parseCompilerCCParameters(ctx android.SingletonContext, params []string) ccParameters { 119 compilerParams := ccParameters{} 120 121 cparams := []string{} 122 for _, param := range params { 123 param, _ = evalVariable(ctx, param) 124 cparams = append(cparams, param) 125 } 126 127 // Soong does not guarantee that each flag will be in an individual string. e.g: The 128 // input received could be: 129 // params = {"-isystem", "path/to/system"} 130 // or it could be 131 // params = {"-isystem path/to/system"} 132 // To normalize the input, we split all strings with the "space" character and consolidate 133 // all tokens into a flattened parameters list 134 cparams = normalizeParameters(cparams) 135 136 for i := 0; i < len(cparams); i++ { 137 param := cparams[i] 138 if param == "" { 139 continue 140 } 141 142 switch categorizeParameter(param) { 143 case headerSearchPath: 144 compilerParams.HeaderSearchPath = 145 append(compilerParams.HeaderSearchPath, strings.TrimPrefix(param, "-I")) 146 case systemHeaderSearchPath: 147 if i < len(cparams)-1 { 148 compilerParams.SystemHeaderSearchPath = append(compilerParams.SystemHeaderSearchPath, cparams[i+1]) 149 } 150 i = i + 1 151 case flag: 152 c := cleanupParameter(param) 153 compilerParams.FlagParameters = append(compilerParams.FlagParameters, c) 154 case systemRoot: 155 if i < len(cparams)-1 { 156 compilerParams.SysRoot = cparams[i+1] 157 } 158 i = i + 1 159 case relativeFilePathFlag: 160 flagComponents := strings.Split(param, "=") 161 if len(flagComponents) == 2 { 162 if compilerParams.RelativeFilePathFlags == nil { 163 compilerParams.RelativeFilePathFlags = map[string]string{} 164 } 165 compilerParams.RelativeFilePathFlags[flagComponents[0]] = flagComponents[1] 166 } 167 } 168 } 169 return compilerParams 170} 171 172func generateCLionProjectData(ctx android.SingletonContext, compiledModule CompiledInterface, 173 ccModule *Module, bestVariantFound map[string]bool, moduleInfos map[string]ccIdeInfo) { 174 moduleName := ccModule.ModuleBase.Name() 175 srcs := compiledModule.Srcs() 176 177 // Skip if best variant has already been found. 178 if bestVariantFound[moduleName] { 179 return 180 } 181 182 // Skip if sources are empty. 183 if len(srcs) == 0 { 184 return 185 } 186 187 // Check if device arch matches, in which case this is the best variant and takes precedence. 188 if ccModule.Device() && ccModule.ModuleBase.Arch().ArchType.Name == ctx.DeviceConfig().DeviceArch() { 189 bestVariantFound[moduleName] = true 190 } else if _, ok := moduleInfos[moduleName]; ok { 191 // Skip because this isn't the best variant and a previous one has already been added. 192 // Heuristically, ones that appear first are likely to be more relevant. 193 return 194 } 195 196 dpInfo := ccIdeInfo{} 197 198 dpInfo.Path = append(dpInfo.Path, path.Dir(ctx.BlueprintFile(ccModule))) 199 dpInfo.Srcs = append(dpInfo.Srcs, srcs.Strings()...) 200 dpInfo.Path = android.FirstUniqueStrings(dpInfo.Path) 201 dpInfo.Srcs = android.FirstUniqueStrings(dpInfo.Srcs) 202 203 dpInfo.Global_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CommonFlags) 204 dpInfo.Local_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CommonFlags) 205 dpInfo.Global_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CFlags) 206 dpInfo.Local_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CFlags) 207 dpInfo.Global_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.ConlyFlags) 208 dpInfo.Local_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.ConlyFlags) 209 dpInfo.Global_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CppFlags) 210 dpInfo.Local_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CppFlags) 211 dpInfo.System_include_flags = parseCompilerCCParameters(ctx, ccModule.flags.SystemIncludeFlags) 212 213 dpInfo.Module_name = moduleName 214 215 moduleInfos[moduleName] = dpInfo 216} 217 218type Deal struct { 219 Name string 220 ideInfo ccIdeInfo 221} 222 223type Deals []Deal 224 225// Ensure it satisfies sort.Interface 226func (d Deals) Len() int { return len(d) } 227func (d Deals) Less(i, j int) bool { return d[i].Name < d[j].Name } 228func (d Deals) Swap(i, j int) { d[i], d[j] = d[j], d[i] } 229 230func sortMap(moduleInfos map[string]ccIdeInfo) map[string]ccIdeInfo { 231 var deals Deals 232 for k, v := range moduleInfos { 233 deals = append(deals, Deal{k, v}) 234 } 235 236 sort.Sort(deals) 237 238 m := map[string]ccIdeInfo{} 239 for _, d := range deals { 240 m[d.Name] = d.ideInfo 241 } 242 return m 243} 244 245func createJsonFile(moduleDeps ccDeps, ccfpath android.WritablePath) error { 246 buf, err := json.MarshalIndent(moduleDeps, "", "\t") 247 if err != nil { 248 return fmt.Errorf("JSON marshal of cc deps failed: %s", err) 249 } 250 err = android.WriteFileToOutputDir(ccfpath, buf, 0666) 251 if err != nil { 252 return fmt.Errorf("Writing cc deps to %s failed: %s", ccfpath.String(), err) 253 } 254 return nil 255} 256