1package cc
2
3import (
4	"fmt"
5	"sort"
6	"strings"
7
8	"github.com/google/blueprint/proptools"
9
10	"android/soong/android"
11)
12
13func init() {
14	android.RegisterSingletonType("cflag_artifacts_text", cflagArtifactsTextFactory)
15}
16
17var (
18	TrackedCFlags = []string{
19		"-Wall",
20		"-Werror",
21		"-Wextra",
22		"-Wthread-safety",
23		"-O3",
24	}
25
26	TrackedCFlagsDir = []string{
27		"device/google/",
28		"vendor/google/",
29	}
30)
31
32const FileBP = 50
33
34// Stores output files.
35type cflagArtifactsText struct {
36	interOutputs map[string]android.WritablePaths
37	outputs      android.WritablePaths
38}
39
40// allowedDir verifies if the directory/project is part of the TrackedCFlagsDir
41// filter.
42func allowedDir(subdir string) bool {
43	subdir += "/"
44	return android.HasAnyPrefix(subdir, TrackedCFlagsDir)
45}
46
47func (s *cflagArtifactsText) genFlagFilename(flag string) string {
48	return fmt.Sprintf("module_cflags%s.txt", flag)
49}
50
51// incrementFile is used to generate an output path object with the passed in flag
52// and part number.
53// e.g. FLAG + part # -> out/soong/cflags/module_cflags-FLAG.txt.0
54func (s *cflagArtifactsText) incrementFile(ctx android.SingletonContext,
55	flag string, part int) (string, android.OutputPath) {
56
57	filename := fmt.Sprintf("%s.%d", s.genFlagFilename(flag), part)
58	filepath := android.PathForOutput(ctx, "cflags", filename)
59	s.interOutputs[flag] = append(s.interOutputs[flag], filepath)
60	return filename, filepath
61}
62
63// GenCFlagArtifactParts is used to generate the build rules which produce the
64// intermediary files for each desired C Flag artifact
65// e.g. module_cflags-FLAG.txt.0, module_cflags-FLAG.txt.1, ...
66func (s *cflagArtifactsText) GenCFlagArtifactParts(ctx android.SingletonContext,
67	flag string, using bool, modules []string, part int) int {
68
69	cleanedName := strings.Replace(flag, "=", "_", -1)
70	filename, filepath := s.incrementFile(ctx, cleanedName, part)
71	rule := android.NewRuleBuilder()
72	rule.Command().Textf("rm -f %s", filepath.String())
73
74	if using {
75		rule.Command().
76			Textf("echo '# Modules using %s'", flag).
77			FlagWithOutput(">> ", filepath)
78	} else {
79		rule.Command().
80			Textf("echo '# Modules not using %s'", flag).
81			FlagWithOutput(">> ", filepath)
82	}
83
84	length := len(modules)
85
86	if length == 0 {
87		rule.Build(pctx, ctx, filename, "gen "+filename)
88		part++
89	}
90
91	// Following loop splits the module list for each tracked C Flag into
92	// chunks of length FileBP (file breakpoint) and generates a partial artifact
93	// (intermediary file) build rule for each split.
94	moduleShards := android.ShardStrings(modules, FileBP)
95	for index, shard := range moduleShards {
96		rule.Command().
97			Textf("for m in %s; do echo $m",
98				strings.Join(proptools.ShellEscapeList(shard), " ")).
99			FlagWithOutput(">> ", filepath).
100			Text("; done")
101		rule.Build(pctx, ctx, filename, "gen "+filename)
102
103		if index+1 != len(moduleShards) {
104			filename, filepath = s.incrementFile(ctx, cleanedName, part+index+1)
105			rule = android.NewRuleBuilder()
106			rule.Command().Textf("rm -f %s", filepath.String())
107		}
108	}
109
110	return part + len(moduleShards)
111}
112
113// GenCFlagArtifacts is used to generate build rules which combine the
114// intermediary files of a specific tracked flag into a single C Flag artifact
115// for each tracked flag.
116// e.g. module_cflags-FLAG.txt.0 + module_cflags-FLAG.txt.1 = module_cflags-FLAG.txt
117func (s *cflagArtifactsText) GenCFlagArtifacts(ctx android.SingletonContext) {
118	// Scans through s.interOutputs and creates a build rule for each tracked C
119	// Flag that concatenates the associated intermediary file into a single
120	// artifact.
121	for _, flag := range TrackedCFlags {
122		// Generate build rule to combine related intermediary files into a
123		// C Flag artifact
124		rule := android.NewRuleBuilder()
125		filename := s.genFlagFilename(flag)
126		outputpath := android.PathForOutput(ctx, "cflags", filename)
127		rule.Command().
128			Text("cat").
129			Inputs(s.interOutputs[flag].Paths()).
130			FlagWithOutput("> ", outputpath)
131		rule.Build(pctx, ctx, filename, "gen "+filename)
132		s.outputs = append(s.outputs, outputpath)
133	}
134}
135
136func (s *cflagArtifactsText) GenerateBuildActions(ctx android.SingletonContext) {
137	modulesWithCFlag := make(map[string][]string)
138
139	// Scan through all modules, selecting the ones that are part of the filter,
140	// and then storing into a map which tracks whether or not tracked C flag is
141	// used or not.
142	ctx.VisitAllModules(func(module android.Module) {
143		if ccModule, ok := module.(*Module); ok {
144			if allowedDir(ctx.ModuleDir(ccModule)) {
145				cflags := ccModule.flags.Local.CFlags
146				cppflags := ccModule.flags.Local.CppFlags
147				module := fmt.Sprintf("%s:%s (%s)",
148					ctx.BlueprintFile(ccModule),
149					ctx.ModuleName(ccModule),
150					ctx.ModuleSubDir(ccModule))
151				for _, flag := range TrackedCFlags {
152					if inList(flag, cflags) || inList(flag, cppflags) {
153						modulesWithCFlag[flag] = append(modulesWithCFlag[flag], module)
154					} else {
155						modulesWithCFlag["!"+flag] = append(modulesWithCFlag["!"+flag], module)
156					}
157				}
158			}
159		}
160	})
161
162	// Traversing map and setting up rules to produce intermediary files which
163	// contain parts of each expected C Flag artifact.
164	for _, flag := range TrackedCFlags {
165		sort.Strings(modulesWithCFlag[flag])
166		part := s.GenCFlagArtifactParts(ctx, flag, true, modulesWithCFlag[flag], 0)
167		sort.Strings(modulesWithCFlag["!"+flag])
168		s.GenCFlagArtifactParts(ctx, flag, false, modulesWithCFlag["!"+flag], part)
169	}
170
171	// Combine intermediary files into a single C Flag artifact.
172	s.GenCFlagArtifacts(ctx)
173}
174
175func cflagArtifactsTextFactory() android.Singleton {
176	return &cflagArtifactsText{
177		interOutputs: make(map[string]android.WritablePaths),
178	}
179}
180
181func (s *cflagArtifactsText) MakeVars(ctx android.MakeVarsContext) {
182	ctx.Strict("SOONG_MODULES_CFLAG_ARTIFACTS", strings.Join(s.outputs.Strings(), " "))
183}
184