| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 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 |  | 
|  | 15 | package cc | 
|  | 16 |  | 
|  | 17 | import ( | 
|  | 18 | "encoding/json" | 
|  | 19 | "fmt" | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 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 |  | 
|  | 32 | func init() { | 
| LaMont Jones | 0c10e4d | 2023-05-16 00:58:37 +0000 | [diff] [blame] | 33 | android.RegisterParallelSingletonType("ccdeps_generator", ccDepsGeneratorSingleton) | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 34 | } | 
|  | 35 |  | 
|  | 36 | func ccDepsGeneratorSingleton() android.Singleton { | 
|  | 37 | return &ccdepsGeneratorSingleton{} | 
|  | 38 | } | 
|  | 39 |  | 
|  | 40 | type ccdepsGeneratorSingleton struct { | 
| Liz Kammer | 5e07d0c | 2020-06-30 14:37:22 -0700 | [diff] [blame] | 41 | outputPath android.Path | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 42 | } | 
|  | 43 |  | 
| Liz Kammer | 5e07d0c | 2020-06-30 14:37:22 -0700 | [diff] [blame] | 44 | var _ android.SingletonMakeVarsProvider = (*ccdepsGeneratorSingleton)(nil) | 
|  | 45 |  | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 46 | const ( | 
| Jim Tang | c44ba2a | 2021-11-03 15:55:01 +0800 | [diff] [blame] | 47 | ccdepsJsonFileName = "module_bp_cc_deps.json" | 
|  | 48 | cClang             = "clang" | 
|  | 49 | cppClang           = "clang++" | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 50 | ) | 
|  | 51 |  | 
|  | 52 | type 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 |  | 
|  | 67 | type 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 |  | 
|  | 75 | type ccMapIdeInfos map[string]ccIdeInfo | 
|  | 76 |  | 
|  | 77 | type ccDeps struct { | 
|  | 78 | C_clang   string        `json:"clang,omitempty"` | 
|  | 79 | Cpp_clang string        `json:"clang++,omitempty"` | 
|  | 80 | Modules   ccMapIdeInfos `json:"modules,omitempty"` | 
|  | 81 | } | 
|  | 82 |  | 
|  | 83 | func (c *ccdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) { | 
| Jim Tang | c44ba2a | 2021-11-03 15:55:01 +0800 | [diff] [blame] | 84 | // (b/204397180) Generate module_bp_cc_deps.json by default. | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 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 |  | 
| Colin Cross | 37c5cda | 2020-01-31 10:07:25 -0800 | [diff] [blame] | 106 | ccfpath := android.PathForOutput(ctx, ccdepsJsonFileName) | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 107 | err := createJsonFile(moduleDeps, ccfpath) | 
|  | 108 | if err != nil { | 
|  | 109 | ctx.Errorf(err.Error()) | 
|  | 110 | } | 
| Liz Kammer | 5e07d0c | 2020-06-30 14:37:22 -0700 | [diff] [blame] | 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 |  | 
|  | 120 | func (c *ccdepsGeneratorSingleton) MakeVars(ctx android.MakeVarsContext) { | 
|  | 121 | if c.outputPath == nil { | 
|  | 122 | return | 
|  | 123 | } | 
|  | 124 |  | 
|  | 125 | ctx.DistForGoal("general-tests", c.outputPath) | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 126 | } | 
|  | 127 |  | 
|  | 128 | func 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: | 
| bralee | adba3c0 | 2020-01-08 09:10:53 +0800 | [diff] [blame] | 157 | if i < len(cparams)-1 { | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 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 |  | 
|  | 182 | func 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 |  | 
|  | 224 | type Deal struct { | 
|  | 225 | Name    string | 
|  | 226 | ideInfo ccIdeInfo | 
|  | 227 | } | 
|  | 228 |  | 
|  | 229 | type Deals []Deal | 
|  | 230 |  | 
|  | 231 | // Ensure it satisfies sort.Interface | 
|  | 232 | func (d Deals) Len() int           { return len(d) } | 
|  | 233 | func (d Deals) Less(i, j int) bool { return d[i].Name < d[j].Name } | 
|  | 234 | func (d Deals) Swap(i, j int)      { d[i], d[j] = d[j], d[i] } | 
|  | 235 |  | 
|  | 236 | func 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 |  | 
| Colin Cross | 37c5cda | 2020-01-31 10:07:25 -0800 | [diff] [blame] | 251 | func createJsonFile(moduleDeps ccDeps, ccfpath android.WritablePath) error { | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 252 | buf, err := json.MarshalIndent(moduleDeps, "", "\t") | 
|  | 253 | if err != nil { | 
| Colin Cross | 37c5cda | 2020-01-31 10:07:25 -0800 | [diff] [blame] | 254 | return fmt.Errorf("JSON marshal of cc deps failed: %s", err) | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 255 | } | 
| Colin Cross | 37c5cda | 2020-01-31 10:07:25 -0800 | [diff] [blame] | 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 | } | 
| bralee | 5a5cce6 | 2019-10-22 13:39:18 +0800 | [diff] [blame] | 260 | return nil | 
|  | 261 | } |