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 which projects have already had CMakeLists.txt generated to keep the first 89 // variant for each project. 90 seenProjects := map[string]bool{} 91 92 pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/") 93 moduleDeps.C_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cClang) 94 moduleDeps.Cpp_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cppClang) 95 96 ctx.VisitAllModules(func(module android.Module) { 97 if ccModule, ok := module.(*Module); ok { 98 if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok { 99 generateCLionProjectData(ctx, compiledModule, ccModule, seenProjects, moduleInfos) 100 } 101 } 102 }) 103 104 moduleDeps.Modules = moduleInfos 105 106 ccfpath := android.PathForOutput(ctx, ccdepsJsonFileName) 107 err := createJsonFile(moduleDeps, ccfpath) 108 if err != nil { 109 ctx.Errorf(err.Error()) 110 } 111 c.outputPath = ccfpath 112 113 // This is necessary to satisfy the dangling rules check as this file is written by Soong rather than a rule. 114 ctx.Build(pctx, android.BuildParams{ 115 Rule: android.Touch, 116 Output: ccfpath, 117 }) 118} 119 120func (c *ccdepsGeneratorSingleton) MakeVars(ctx android.MakeVarsContext) { 121 if c.outputPath == nil { 122 return 123 } 124 125 ctx.DistForGoal("general-tests", c.outputPath) 126} 127 128func parseCompilerCCParameters(ctx android.SingletonContext, params []string) ccParameters { 129 compilerParams := ccParameters{} 130 131 cparams := []string{} 132 for _, param := range params { 133 param, _ = evalVariable(ctx, param) 134 cparams = append(cparams, param) 135 } 136 137 // Soong does not guarantee that each flag will be in an individual string. e.g: The 138 // input received could be: 139 // params = {"-isystem", "path/to/system"} 140 // or it could be 141 // params = {"-isystem path/to/system"} 142 // To normalize the input, we split all strings with the "space" character and consolidate 143 // all tokens into a flattened parameters list 144 cparams = normalizeParameters(cparams) 145 146 for i := 0; i < len(cparams); i++ { 147 param := cparams[i] 148 if param == "" { 149 continue 150 } 151 152 switch categorizeParameter(param) { 153 case headerSearchPath: 154 compilerParams.HeaderSearchPath = 155 append(compilerParams.HeaderSearchPath, strings.TrimPrefix(param, "-I")) 156 case systemHeaderSearchPath: 157 if i < len(cparams)-1 { 158 compilerParams.SystemHeaderSearchPath = append(compilerParams.SystemHeaderSearchPath, cparams[i+1]) 159 } 160 i = i + 1 161 case flag: 162 c := cleanupParameter(param) 163 compilerParams.FlagParameters = append(compilerParams.FlagParameters, c) 164 case systemRoot: 165 if i < len(cparams)-1 { 166 compilerParams.SysRoot = cparams[i+1] 167 } 168 i = i + 1 169 case relativeFilePathFlag: 170 flagComponents := strings.Split(param, "=") 171 if len(flagComponents) == 2 { 172 if compilerParams.RelativeFilePathFlags == nil { 173 compilerParams.RelativeFilePathFlags = map[string]string{} 174 } 175 compilerParams.RelativeFilePathFlags[flagComponents[0]] = flagComponents[1] 176 } 177 } 178 } 179 return compilerParams 180} 181 182func generateCLionProjectData(ctx android.SingletonContext, compiledModule CompiledInterface, 183 ccModule *Module, seenProjects map[string]bool, moduleInfos map[string]ccIdeInfo) { 184 srcs := compiledModule.Srcs() 185 if len(srcs) == 0 { 186 return 187 } 188 189 // Only keep the DeviceArch variant module. 190 if ctx.DeviceConfig().DeviceArch() != ccModule.ModuleBase.Arch().ArchType.Name { 191 return 192 } 193 194 clionProjectLocation := getCMakeListsForModule(ccModule, ctx) 195 if seenProjects[clionProjectLocation] { 196 return 197 } 198 199 seenProjects[clionProjectLocation] = true 200 201 name := ccModule.ModuleBase.Name() 202 dpInfo := moduleInfos[name] 203 204 dpInfo.Path = append(dpInfo.Path, path.Dir(ctx.BlueprintFile(ccModule))) 205 dpInfo.Srcs = append(dpInfo.Srcs, srcs.Strings()...) 206 dpInfo.Path = android.FirstUniqueStrings(dpInfo.Path) 207 dpInfo.Srcs = android.FirstUniqueStrings(dpInfo.Srcs) 208 209 dpInfo.Global_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CommonFlags) 210 dpInfo.Local_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CommonFlags) 211 dpInfo.Global_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CFlags) 212 dpInfo.Local_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CFlags) 213 dpInfo.Global_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.ConlyFlags) 214 dpInfo.Local_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.ConlyFlags) 215 dpInfo.Global_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CppFlags) 216 dpInfo.Local_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CppFlags) 217 dpInfo.System_include_flags = parseCompilerCCParameters(ctx, ccModule.flags.SystemIncludeFlags) 218 219 dpInfo.Module_name = name 220 221 moduleInfos[name] = dpInfo 222} 223 224type Deal struct { 225 Name string 226 ideInfo ccIdeInfo 227} 228 229type Deals []Deal 230 231// Ensure it satisfies sort.Interface 232func (d Deals) Len() int { return len(d) } 233func (d Deals) Less(i, j int) bool { return d[i].Name < d[j].Name } 234func (d Deals) Swap(i, j int) { d[i], d[j] = d[j], d[i] } 235 236func sortMap(moduleInfos map[string]ccIdeInfo) map[string]ccIdeInfo { 237 var deals Deals 238 for k, v := range moduleInfos { 239 deals = append(deals, Deal{k, v}) 240 } 241 242 sort.Sort(deals) 243 244 m := map[string]ccIdeInfo{} 245 for _, d := range deals { 246 m[d.Name] = d.ideInfo 247 } 248 return m 249} 250 251func createJsonFile(moduleDeps ccDeps, ccfpath android.WritablePath) error { 252 buf, err := json.MarshalIndent(moduleDeps, "", "\t") 253 if err != nil { 254 return fmt.Errorf("JSON marshal of cc deps failed: %s", err) 255 } 256 err = android.WriteFileToOutputDir(ccfpath, buf, 0666) 257 if err != nil { 258 return fmt.Errorf("Writing cc deps to %s failed: %s", ccfpath.String(), err) 259 } 260 return nil 261} 262