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