• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
44const (
45	ccdepsJsonFileName = "module_bp_cc_deps.json"
46	cClang             = "clang"
47	cppClang           = "clang++"
48)
49
50type ccIdeInfo struct {
51	Path                 []string     `json:"path,omitempty"`
52	Srcs                 []string     `json:"srcs,omitempty"`
53	Global_Common_Flags  ccParameters `json:"global_common_flags,omitempty"`
54	Local_Common_Flags   ccParameters `json:"local_common_flags,omitempty"`
55	Global_C_flags       ccParameters `json:"global_c_flags,omitempty"`
56	Local_C_flags        ccParameters `json:"local_c_flags,omitempty"`
57	Global_C_only_flags  ccParameters `json:"global_c_only_flags,omitempty"`
58	Local_C_only_flags   ccParameters `json:"local_c_only_flags,omitempty"`
59	Global_Cpp_flags     ccParameters `json:"global_cpp_flags,omitempty"`
60	Local_Cpp_flags      ccParameters `json:"local_cpp_flags,omitempty"`
61	System_include_flags ccParameters `json:"system_include_flags,omitempty"`
62	Module_name          string       `json:"module_name,omitempty"`
63}
64
65type ccParameters struct {
66	HeaderSearchPath       []string          `json:"header_search_path,omitempty"`
67	SystemHeaderSearchPath []string          `json:"system_search_path,omitempty"`
68	FlagParameters         []string          `json:"flag,omitempty"`
69	SysRoot                string            `json:"system_root,omitempty"`
70	RelativeFilePathFlags  map[string]string `json:"relative_file_path,omitempty"`
71}
72
73type ccMapIdeInfos map[string]ccIdeInfo
74
75type ccDeps struct {
76	C_clang   string        `json:"clang,omitempty"`
77	Cpp_clang string        `json:"clang++,omitempty"`
78	Modules   ccMapIdeInfos `json:"modules,omitempty"`
79}
80
81func (c *ccdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
82	// (b/204397180) Generate module_bp_cc_deps.json by default.
83	moduleDeps := ccDeps{}
84	moduleInfos := map[string]ccIdeInfo{}
85
86	// Track if best variant (device arch match) has been found.
87	bestVariantFound := map[string]bool{}
88
89	pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/")
90	moduleDeps.C_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cClang)
91	moduleDeps.Cpp_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cppClang)
92
93	ctx.VisitAllModules(func(module android.Module) {
94		if ccModule, ok := module.(*Module); ok {
95			if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
96				generateCLionProjectData(ctx, compiledModule, ccModule, bestVariantFound, moduleInfos)
97			}
98		}
99	})
100
101	moduleDeps.Modules = moduleInfos
102
103	ccfpath := android.PathForOutput(ctx, ccdepsJsonFileName)
104	err := createJsonFile(moduleDeps, ccfpath)
105	if err != nil {
106		ctx.Errorf(err.Error())
107	}
108	c.outputPath = ccfpath
109
110	// This is necessary to satisfy the dangling rules check as this file is written by Soong rather than a rule.
111	ctx.Build(pctx, android.BuildParams{
112		Rule:   android.Touch,
113		Output: ccfpath,
114	})
115	ctx.DistForGoal("general-tests", c.outputPath)
116}
117
118func parseCompilerCCParameters(ctx android.SingletonContext, params []string) ccParameters {
119	compilerParams := ccParameters{}
120
121	cparams := []string{}
122	for _, param := range params {
123		param, _ = evalVariable(ctx, param)
124		cparams = append(cparams, param)
125	}
126
127	// Soong does not guarantee that each flag will be in an individual string. e.g: The
128	// input received could be:
129	// params = {"-isystem", "path/to/system"}
130	// or it could be
131	// params = {"-isystem path/to/system"}
132	// To normalize the input, we split all strings with the "space" character and consolidate
133	// all tokens into a flattened parameters list
134	cparams = normalizeParameters(cparams)
135
136	for i := 0; i < len(cparams); i++ {
137		param := cparams[i]
138		if param == "" {
139			continue
140		}
141
142		switch categorizeParameter(param) {
143		case headerSearchPath:
144			compilerParams.HeaderSearchPath =
145				append(compilerParams.HeaderSearchPath, strings.TrimPrefix(param, "-I"))
146		case systemHeaderSearchPath:
147			if i < len(cparams)-1 {
148				compilerParams.SystemHeaderSearchPath = append(compilerParams.SystemHeaderSearchPath, cparams[i+1])
149			}
150			i = i + 1
151		case flag:
152			c := cleanupParameter(param)
153			compilerParams.FlagParameters = append(compilerParams.FlagParameters, c)
154		case systemRoot:
155			if i < len(cparams)-1 {
156				compilerParams.SysRoot = cparams[i+1]
157			}
158			i = i + 1
159		case relativeFilePathFlag:
160			flagComponents := strings.Split(param, "=")
161			if len(flagComponents) == 2 {
162				if compilerParams.RelativeFilePathFlags == nil {
163					compilerParams.RelativeFilePathFlags = map[string]string{}
164				}
165				compilerParams.RelativeFilePathFlags[flagComponents[0]] = flagComponents[1]
166			}
167		}
168	}
169	return compilerParams
170}
171
172func generateCLionProjectData(ctx android.SingletonContext, compiledModule CompiledInterface,
173	ccModule *Module, bestVariantFound map[string]bool, moduleInfos map[string]ccIdeInfo) {
174	moduleName := ccModule.ModuleBase.Name()
175	srcs := compiledModule.Srcs()
176
177	// Skip if best variant has already been found.
178	if bestVariantFound[moduleName] {
179		return
180	}
181
182	// Skip if sources are empty.
183	if len(srcs) == 0 {
184		return
185	}
186
187	// Check if device arch matches, in which case this is the best variant and takes precedence.
188	if ccModule.Device() && ccModule.ModuleBase.Arch().ArchType.Name == ctx.DeviceConfig().DeviceArch() {
189		bestVariantFound[moduleName] = true
190	} else if _, ok := moduleInfos[moduleName]; ok {
191		// Skip because this isn't the best variant and a previous one has already been added.
192		// Heuristically, ones that appear first are likely to be more relevant.
193		return
194	}
195
196	dpInfo := ccIdeInfo{}
197
198	dpInfo.Path = append(dpInfo.Path, path.Dir(ctx.BlueprintFile(ccModule)))
199	dpInfo.Srcs = append(dpInfo.Srcs, srcs.Strings()...)
200	dpInfo.Path = android.FirstUniqueStrings(dpInfo.Path)
201	dpInfo.Srcs = android.FirstUniqueStrings(dpInfo.Srcs)
202
203	dpInfo.Global_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CommonFlags)
204	dpInfo.Local_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CommonFlags)
205	dpInfo.Global_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CFlags)
206	dpInfo.Local_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CFlags)
207	dpInfo.Global_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.ConlyFlags)
208	dpInfo.Local_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.ConlyFlags)
209	dpInfo.Global_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CppFlags)
210	dpInfo.Local_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CppFlags)
211	dpInfo.System_include_flags = parseCompilerCCParameters(ctx, ccModule.flags.SystemIncludeFlags)
212
213	dpInfo.Module_name = moduleName
214
215	moduleInfos[moduleName] = dpInfo
216}
217
218type Deal struct {
219	Name    string
220	ideInfo ccIdeInfo
221}
222
223type Deals []Deal
224
225// Ensure it satisfies sort.Interface
226func (d Deals) Len() int           { return len(d) }
227func (d Deals) Less(i, j int) bool { return d[i].Name < d[j].Name }
228func (d Deals) Swap(i, j int)      { d[i], d[j] = d[j], d[i] }
229
230func sortMap(moduleInfos map[string]ccIdeInfo) map[string]ccIdeInfo {
231	var deals Deals
232	for k, v := range moduleInfos {
233		deals = append(deals, Deal{k, v})
234	}
235
236	sort.Sort(deals)
237
238	m := map[string]ccIdeInfo{}
239	for _, d := range deals {
240		m[d.Name] = d.ideInfo
241	}
242	return m
243}
244
245func createJsonFile(moduleDeps ccDeps, ccfpath android.WritablePath) error {
246	buf, err := json.MarshalIndent(moduleDeps, "", "\t")
247	if err != nil {
248		return fmt.Errorf("JSON marshal of cc deps failed: %s", err)
249	}
250	err = android.WriteFileToOutputDir(ccfpath, buf, 0666)
251	if err != nil {
252		return fmt.Errorf("Writing cc deps to %s failed: %s", ccfpath.String(), err)
253	}
254	return nil
255}
256