1package bp2build 2 3import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "android/soong/android" 10 "android/soong/shared" 11 "android/soong/ui/metrics/bp2build_metrics_proto" 12 "github.com/google/blueprint" 13) 14 15// Simple metrics struct to collect information about a Blueprint to BUILD 16// conversion process. 17type CodegenMetrics struct { 18 // Total number of Soong modules converted to generated targets 19 generatedModuleCount uint64 20 21 // Total number of Soong modules converted to handcrafted targets 22 handCraftedModuleCount uint64 23 24 // Total number of unconverted Soong modules 25 unconvertedModuleCount uint64 26 27 // Counts of generated Bazel targets per Bazel rule class 28 ruleClassCount map[string]uint64 29 30 // List of modules with unconverted deps 31 // NOTE: NOT in the .proto 32 moduleWithUnconvertedDepsMsgs []string 33 34 // List of modules with missing deps 35 // NOTE: NOT in the .proto 36 moduleWithMissingDepsMsgs []string 37 38 // List of converted modules 39 convertedModules []string 40 41 // Counts of converted modules by module type. 42 convertedModuleTypeCount map[string]uint64 43 44 // Counts of total modules by module type. 45 totalModuleTypeCount map[string]uint64 46 47 Events []*bp2build_metrics_proto.Event 48} 49 50// Serialize returns the protoized version of CodegenMetrics: bp2build_metrics_proto.Bp2BuildMetrics 51func (metrics *CodegenMetrics) Serialize() bp2build_metrics_proto.Bp2BuildMetrics { 52 return bp2build_metrics_proto.Bp2BuildMetrics{ 53 GeneratedModuleCount: metrics.generatedModuleCount, 54 HandCraftedModuleCount: metrics.handCraftedModuleCount, 55 UnconvertedModuleCount: metrics.unconvertedModuleCount, 56 RuleClassCount: metrics.ruleClassCount, 57 ConvertedModules: metrics.convertedModules, 58 ConvertedModuleTypeCount: metrics.convertedModuleTypeCount, 59 TotalModuleTypeCount: metrics.totalModuleTypeCount, 60 Events: metrics.Events, 61 } 62} 63 64// Print the codegen metrics to stdout. 65func (metrics *CodegenMetrics) Print() { 66 generatedTargetCount := uint64(0) 67 for _, ruleClass := range android.SortedStringKeys(metrics.ruleClassCount) { 68 count := metrics.ruleClassCount[ruleClass] 69 fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count) 70 generatedTargetCount += count 71 } 72 fmt.Printf( 73 `[bp2build] Converted %d Android.bp modules to %d total generated BUILD targets. Included %d handcrafted BUILD targets. There are %d total Android.bp modules. 74%d converted modules have unconverted deps: 75 %s 76%d converted modules have missing deps: 77 %s 78`, 79 metrics.generatedModuleCount, 80 generatedTargetCount, 81 metrics.handCraftedModuleCount, 82 metrics.TotalModuleCount(), 83 len(metrics.moduleWithUnconvertedDepsMsgs), 84 strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"), 85 len(metrics.moduleWithMissingDepsMsgs), 86 strings.Join(metrics.moduleWithMissingDepsMsgs, "\n\t"), 87 ) 88} 89 90const bp2buildMetricsFilename = "bp2build_metrics.pb" 91 92// fail prints $PWD to stderr, followed by the given printf string and args (vals), 93// then the given alert, and then exits with 1 for failure 94func fail(err error, alertFmt string, vals ...interface{}) { 95 cwd, wderr := os.Getwd() 96 if wderr != nil { 97 cwd = "FAILED TO GET $PWD: " + wderr.Error() 98 } 99 fmt.Fprintf(os.Stderr, "\nIn "+cwd+":\n"+alertFmt+"\n"+err.Error()+"\n", vals...) 100 os.Exit(1) 101} 102 103// Write the bp2build-protoized codegen metrics into the given directory 104func (metrics *CodegenMetrics) Write(dir string) { 105 if _, err := os.Stat(dir); os.IsNotExist(err) { 106 // The metrics dir doesn't already exist, so create it (and parents) 107 if err := os.MkdirAll(dir, 0755); err != nil { // rx for all; w for user 108 fail(err, "Failed to `mkdir -p` %s", dir) 109 } 110 } else if err != nil { 111 fail(err, "Failed to `stat` %s", dir) 112 } 113 metricsFile := filepath.Join(dir, bp2buildMetricsFilename) 114 if err := metrics.dump(metricsFile); err != nil { 115 fail(err, "Error outputting %s", metricsFile) 116 } 117 if _, err := os.Stat(metricsFile); err != nil { 118 fail(err, "MISSING BP2BUILD METRICS OUTPUT: Failed to `stat` %s", metricsFile) 119 } else { 120 fmt.Printf("\nWrote bp2build metrics to: %s\n", metricsFile) 121 } 122} 123 124func (metrics *CodegenMetrics) IncrementRuleClassCount(ruleClass string) { 125 metrics.ruleClassCount[ruleClass] += 1 126} 127 128func (metrics *CodegenMetrics) AddUnconvertedModule(moduleType string) { 129 metrics.unconvertedModuleCount += 1 130 metrics.totalModuleTypeCount[moduleType] += 1 131} 132 133func (metrics *CodegenMetrics) TotalModuleCount() uint64 { 134 return metrics.handCraftedModuleCount + 135 metrics.generatedModuleCount + 136 metrics.unconvertedModuleCount 137} 138 139// Dump serializes the metrics to the given filename 140func (metrics *CodegenMetrics) dump(filename string) (err error) { 141 ser := metrics.Serialize() 142 return shared.Save(&ser, filename) 143} 144 145type ConversionType int 146 147const ( 148 Generated ConversionType = iota 149 Handcrafted 150) 151 152func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, conversionType ConversionType) { 153 // Undo prebuilt_ module name prefix modifications 154 moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name()) 155 metrics.convertedModules = append(metrics.convertedModules, moduleName) 156 metrics.convertedModuleTypeCount[moduleType] += 1 157 metrics.totalModuleTypeCount[moduleType] += 1 158 159 if conversionType == Handcrafted { 160 metrics.handCraftedModuleCount += 1 161 } else if conversionType == Generated { 162 metrics.generatedModuleCount += 1 163 } 164} 165