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