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