blob: 225405cb40f9e709fc6e85cc83d53ca541620f3b [file] [log] [blame]
bralee5a5cce62019-10-22 13:39:18 +08001// 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"
bralee5a5cce62019-10-22 13:39:18 +080020 "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.RegisterSingletonType("ccdeps_generator", ccDepsGeneratorSingleton)
34}
35
36func ccDepsGeneratorSingleton() android.Singleton {
37 return &ccdepsGeneratorSingleton{}
38}
39
40type ccdepsGeneratorSingleton struct {
41}
42
43const (
44 // Environment variables used to control the behavior of this singleton.
45 envVariableCollectCCDeps = "SOONG_COLLECT_CC_DEPS"
46 ccdepsJsonFileName = "module_bp_cc_deps.json"
47 cClang = "clang"
48 cppClang = "clang++"
49)
50
51type ccIdeInfo struct {
52 Path []string `json:"path,omitempty"`
53 Srcs []string `json:"srcs,omitempty"`
54 Global_Common_Flags ccParameters `json:"global_common_flags,omitempty"`
55 Local_Common_Flags ccParameters `json:"local_common_flags,omitempty"`
56 Global_C_flags ccParameters `json:"global_c_flags,omitempty"`
57 Local_C_flags ccParameters `json:"local_c_flags,omitempty"`
58 Global_C_only_flags ccParameters `json:"global_c_only_flags,omitempty"`
59 Local_C_only_flags ccParameters `json:"local_c_only_flags,omitempty"`
60 Global_Cpp_flags ccParameters `json:"global_cpp_flags,omitempty"`
61 Local_Cpp_flags ccParameters `json:"local_cpp_flags,omitempty"`
62 System_include_flags ccParameters `json:"system_include_flags,omitempty"`
63 Module_name string `json:"module_name,omitempty"`
64}
65
66type ccParameters struct {
67 HeaderSearchPath []string `json:"header_search_path,omitempty"`
68 SystemHeaderSearchPath []string `json:"system_search_path,omitempty"`
69 FlagParameters []string `json:"flag,omitempty"`
70 SysRoot string `json:"system_root,omitempty"`
71 RelativeFilePathFlags map[string]string `json:"relative_file_path,omitempty"`
72}
73
74type ccMapIdeInfos map[string]ccIdeInfo
75
76type ccDeps struct {
77 C_clang string `json:"clang,omitempty"`
78 Cpp_clang string `json:"clang++,omitempty"`
79 Modules ccMapIdeInfos `json:"modules,omitempty"`
80}
81
82func (c *ccdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
83 if !ctx.Config().IsEnvTrue(envVariableCollectCCDeps) {
84 return
85 }
86
87 moduleDeps := ccDeps{}
88 moduleInfos := map[string]ccIdeInfo{}
89
90 // Track which projects have already had CMakeLists.txt generated to keep the first
91 // variant for each project.
92 seenProjects := map[string]bool{}
93
94 pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/")
95 moduleDeps.C_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cClang)
96 moduleDeps.Cpp_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cppClang)
97
98 ctx.VisitAllModules(func(module android.Module) {
99 if ccModule, ok := module.(*Module); ok {
100 if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
101 generateCLionProjectData(ctx, compiledModule, ccModule, seenProjects, moduleInfos)
102 }
103 }
104 })
105
106 moduleDeps.Modules = moduleInfos
107
Colin Cross37c5cda2020-01-31 10:07:25 -0800108 ccfpath := android.PathForOutput(ctx, ccdepsJsonFileName)
bralee5a5cce62019-10-22 13:39:18 +0800109 err := createJsonFile(moduleDeps, ccfpath)
110 if err != nil {
111 ctx.Errorf(err.Error())
112 }
113}
114
115func parseCompilerCCParameters(ctx android.SingletonContext, params []string) ccParameters {
116 compilerParams := ccParameters{}
117
118 cparams := []string{}
119 for _, param := range params {
120 param, _ = evalVariable(ctx, param)
121 cparams = append(cparams, param)
122 }
123
124 // Soong does not guarantee that each flag will be in an individual string. e.g: The
125 // input received could be:
126 // params = {"-isystem", "path/to/system"}
127 // or it could be
128 // params = {"-isystem path/to/system"}
129 // To normalize the input, we split all strings with the "space" character and consolidate
130 // all tokens into a flattened parameters list
131 cparams = normalizeParameters(cparams)
132
133 for i := 0; i < len(cparams); i++ {
134 param := cparams[i]
135 if param == "" {
136 continue
137 }
138
139 switch categorizeParameter(param) {
140 case headerSearchPath:
141 compilerParams.HeaderSearchPath =
142 append(compilerParams.HeaderSearchPath, strings.TrimPrefix(param, "-I"))
143 case systemHeaderSearchPath:
braleeadba3c02020-01-08 09:10:53 +0800144 if i < len(cparams)-1 {
bralee5a5cce62019-10-22 13:39:18 +0800145 compilerParams.SystemHeaderSearchPath = append(compilerParams.SystemHeaderSearchPath, cparams[i+1])
146 }
147 i = i + 1
148 case flag:
149 c := cleanupParameter(param)
150 compilerParams.FlagParameters = append(compilerParams.FlagParameters, c)
151 case systemRoot:
152 if i < len(cparams)-1 {
153 compilerParams.SysRoot = cparams[i+1]
154 }
155 i = i + 1
156 case relativeFilePathFlag:
157 flagComponents := strings.Split(param, "=")
158 if len(flagComponents) == 2 {
159 if compilerParams.RelativeFilePathFlags == nil {
160 compilerParams.RelativeFilePathFlags = map[string]string{}
161 }
162 compilerParams.RelativeFilePathFlags[flagComponents[0]] = flagComponents[1]
163 }
164 }
165 }
166 return compilerParams
167}
168
169func generateCLionProjectData(ctx android.SingletonContext, compiledModule CompiledInterface,
170 ccModule *Module, seenProjects map[string]bool, moduleInfos map[string]ccIdeInfo) {
171 srcs := compiledModule.Srcs()
172 if len(srcs) == 0 {
173 return
174 }
175
176 // Only keep the DeviceArch variant module.
177 if ctx.DeviceConfig().DeviceArch() != ccModule.ModuleBase.Arch().ArchType.Name {
178 return
179 }
180
181 clionProjectLocation := getCMakeListsForModule(ccModule, ctx)
182 if seenProjects[clionProjectLocation] {
183 return
184 }
185
186 seenProjects[clionProjectLocation] = true
187
188 name := ccModule.ModuleBase.Name()
189 dpInfo := moduleInfos[name]
190
191 dpInfo.Path = append(dpInfo.Path, path.Dir(ctx.BlueprintFile(ccModule)))
192 dpInfo.Srcs = append(dpInfo.Srcs, srcs.Strings()...)
193 dpInfo.Path = android.FirstUniqueStrings(dpInfo.Path)
194 dpInfo.Srcs = android.FirstUniqueStrings(dpInfo.Srcs)
195
196 dpInfo.Global_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CommonFlags)
197 dpInfo.Local_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CommonFlags)
198 dpInfo.Global_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CFlags)
199 dpInfo.Local_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CFlags)
200 dpInfo.Global_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.ConlyFlags)
201 dpInfo.Local_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.ConlyFlags)
202 dpInfo.Global_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CppFlags)
203 dpInfo.Local_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CppFlags)
204 dpInfo.System_include_flags = parseCompilerCCParameters(ctx, ccModule.flags.SystemIncludeFlags)
205
206 dpInfo.Module_name = name
207
208 moduleInfos[name] = dpInfo
209}
210
211type Deal struct {
212 Name string
213 ideInfo ccIdeInfo
214}
215
216type Deals []Deal
217
218// Ensure it satisfies sort.Interface
219func (d Deals) Len() int { return len(d) }
220func (d Deals) Less(i, j int) bool { return d[i].Name < d[j].Name }
221func (d Deals) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
222
223func sortMap(moduleInfos map[string]ccIdeInfo) map[string]ccIdeInfo {
224 var deals Deals
225 for k, v := range moduleInfos {
226 deals = append(deals, Deal{k, v})
227 }
228
229 sort.Sort(deals)
230
231 m := map[string]ccIdeInfo{}
232 for _, d := range deals {
233 m[d.Name] = d.ideInfo
234 }
235 return m
236}
237
Colin Cross37c5cda2020-01-31 10:07:25 -0800238func createJsonFile(moduleDeps ccDeps, ccfpath android.WritablePath) error {
bralee5a5cce62019-10-22 13:39:18 +0800239 buf, err := json.MarshalIndent(moduleDeps, "", "\t")
240 if err != nil {
Colin Cross37c5cda2020-01-31 10:07:25 -0800241 return fmt.Errorf("JSON marshal of cc deps failed: %s", err)
bralee5a5cce62019-10-22 13:39:18 +0800242 }
Colin Cross37c5cda2020-01-31 10:07:25 -0800243 err = android.WriteFileToOutputDir(ccfpath, buf, 0666)
244 if err != nil {
245 return fmt.Errorf("Writing cc deps to %s failed: %s", ccfpath.String(), err)
246 }
bralee5a5cce62019-10-22 13:39:18 +0800247 return nil
248}