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