blob: 9b89110b9d8fa70a7648886f2ca9c9b7bc03afbd [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"
20 "os"
21 "path"
22 "sort"
23 "strings"
24
25 "android/soong/android"
26)
27
28// This singleton collects cc modules' source and flags into to a json file.
29// It does so for generating CMakeLists.txt project files needed data when
30// either make, mm, mma, mmm or mmma is called.
31// The info file is generated in $OUT/module_bp_cc_depend.json.
32
33func init() {
34 android.RegisterSingletonType("ccdeps_generator", ccDepsGeneratorSingleton)
35}
36
37func ccDepsGeneratorSingleton() android.Singleton {
38 return &ccdepsGeneratorSingleton{}
39}
40
41type ccdepsGeneratorSingleton struct {
42}
43
44const (
45 // Environment variables used to control the behavior of this singleton.
46 envVariableCollectCCDeps = "SOONG_COLLECT_CC_DEPS"
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 if !ctx.Config().IsEnvTrue(envVariableCollectCCDeps) {
85 return
86 }
87
88 moduleDeps := ccDeps{}
89 moduleInfos := map[string]ccIdeInfo{}
90
91 // Track which projects have already had CMakeLists.txt generated to keep the first
92 // variant for each project.
93 seenProjects := map[string]bool{}
94
95 pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/")
96 moduleDeps.C_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cClang)
97 moduleDeps.Cpp_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cppClang)
98
99 ctx.VisitAllModules(func(module android.Module) {
100 if ccModule, ok := module.(*Module); ok {
101 if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
102 generateCLionProjectData(ctx, compiledModule, ccModule, seenProjects, moduleInfos)
103 }
104 }
105 })
106
107 moduleDeps.Modules = moduleInfos
108
109 ccfpath := android.PathForOutput(ctx, ccdepsJsonFileName).String()
110 err := createJsonFile(moduleDeps, ccfpath)
111 if err != nil {
112 ctx.Errorf(err.Error())
113 }
114}
115
116func parseCompilerCCParameters(ctx android.SingletonContext, params []string) ccParameters {
117 compilerParams := ccParameters{}
118
119 cparams := []string{}
120 for _, param := range params {
121 param, _ = evalVariable(ctx, param)
122 cparams = append(cparams, param)
123 }
124
125 // Soong does not guarantee that each flag will be in an individual string. e.g: The
126 // input received could be:
127 // params = {"-isystem", "path/to/system"}
128 // or it could be
129 // params = {"-isystem path/to/system"}
130 // To normalize the input, we split all strings with the "space" character and consolidate
131 // all tokens into a flattened parameters list
132 cparams = normalizeParameters(cparams)
133
134 for i := 0; i < len(cparams); i++ {
135 param := cparams[i]
136 if param == "" {
137 continue
138 }
139
140 switch categorizeParameter(param) {
141 case headerSearchPath:
142 compilerParams.HeaderSearchPath =
143 append(compilerParams.HeaderSearchPath, strings.TrimPrefix(param, "-I"))
144 case systemHeaderSearchPath:
145 if i < len(params)-1 {
146 compilerParams.SystemHeaderSearchPath = append(compilerParams.SystemHeaderSearchPath, cparams[i+1])
147 }
148 i = i + 1
149 case flag:
150 c := cleanupParameter(param)
151 compilerParams.FlagParameters = append(compilerParams.FlagParameters, c)
152 case systemRoot:
153 if i < len(cparams)-1 {
154 compilerParams.SysRoot = cparams[i+1]
155 }
156 i = i + 1
157 case relativeFilePathFlag:
158 flagComponents := strings.Split(param, "=")
159 if len(flagComponents) == 2 {
160 if compilerParams.RelativeFilePathFlags == nil {
161 compilerParams.RelativeFilePathFlags = map[string]string{}
162 }
163 compilerParams.RelativeFilePathFlags[flagComponents[0]] = flagComponents[1]
164 }
165 }
166 }
167 return compilerParams
168}
169
170func generateCLionProjectData(ctx android.SingletonContext, compiledModule CompiledInterface,
171 ccModule *Module, seenProjects map[string]bool, moduleInfos map[string]ccIdeInfo) {
172 srcs := compiledModule.Srcs()
173 if len(srcs) == 0 {
174 return
175 }
176
177 // Only keep the DeviceArch variant module.
178 if ctx.DeviceConfig().DeviceArch() != ccModule.ModuleBase.Arch().ArchType.Name {
179 return
180 }
181
182 clionProjectLocation := getCMakeListsForModule(ccModule, ctx)
183 if seenProjects[clionProjectLocation] {
184 return
185 }
186
187 seenProjects[clionProjectLocation] = true
188
189 name := ccModule.ModuleBase.Name()
190 dpInfo := moduleInfos[name]
191
192 dpInfo.Path = append(dpInfo.Path, path.Dir(ctx.BlueprintFile(ccModule)))
193 dpInfo.Srcs = append(dpInfo.Srcs, srcs.Strings()...)
194 dpInfo.Path = android.FirstUniqueStrings(dpInfo.Path)
195 dpInfo.Srcs = android.FirstUniqueStrings(dpInfo.Srcs)
196
197 dpInfo.Global_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CommonFlags)
198 dpInfo.Local_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CommonFlags)
199 dpInfo.Global_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CFlags)
200 dpInfo.Local_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CFlags)
201 dpInfo.Global_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.ConlyFlags)
202 dpInfo.Local_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.ConlyFlags)
203 dpInfo.Global_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CppFlags)
204 dpInfo.Local_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CppFlags)
205 dpInfo.System_include_flags = parseCompilerCCParameters(ctx, ccModule.flags.SystemIncludeFlags)
206
207 dpInfo.Module_name = name
208
209 moduleInfos[name] = dpInfo
210}
211
212type Deal struct {
213 Name string
214 ideInfo ccIdeInfo
215}
216
217type Deals []Deal
218
219// Ensure it satisfies sort.Interface
220func (d Deals) Len() int { return len(d) }
221func (d Deals) Less(i, j int) bool { return d[i].Name < d[j].Name }
222func (d Deals) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
223
224func sortMap(moduleInfos map[string]ccIdeInfo) map[string]ccIdeInfo {
225 var deals Deals
226 for k, v := range moduleInfos {
227 deals = append(deals, Deal{k, v})
228 }
229
230 sort.Sort(deals)
231
232 m := map[string]ccIdeInfo{}
233 for _, d := range deals {
234 m[d.Name] = d.ideInfo
235 }
236 return m
237}
238
239func createJsonFile(moduleDeps ccDeps, ccfpath string) error {
240 file, err := os.Create(ccfpath)
241 if err != nil {
242 return fmt.Errorf("Failed to create file: %s, relative: %v", ccdepsJsonFileName, err)
243 }
244 defer file.Close()
245 moduleDeps.Modules = sortMap(moduleDeps.Modules)
246 buf, err := json.MarshalIndent(moduleDeps, "", "\t")
247 if err != nil {
248 return fmt.Errorf("Write file failed: %s, relative: %v", ccdepsJsonFileName, err)
249 }
250 fmt.Fprintf(file, string(buf))
251 return nil
252}