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 "google.golang.org/protobuf/proto" 13 14 "github.com/google/blueprint" 15) 16 17// CodegenMetrics represents information about the Blueprint-to-BUILD 18// conversion process. 19// Use CreateCodegenMetrics() to get a properly initialized instance 20type CodegenMetrics struct { 21 serialized *bp2build_metrics_proto.Bp2BuildMetrics 22 // List of modules with unconverted deps 23 // NOTE: NOT in the .proto 24 moduleWithUnconvertedDepsMsgs []string 25 26 // List of modules with missing deps 27 // NOTE: NOT in the .proto 28 moduleWithMissingDepsMsgs []string 29 30 // Map of converted modules and paths to call 31 // NOTE: NOT in the .proto 32 convertedModulePathMap map[string]string 33} 34 35func CreateCodegenMetrics() CodegenMetrics { 36 return CodegenMetrics{ 37 serialized: &bp2build_metrics_proto.Bp2BuildMetrics{ 38 RuleClassCount: make(map[string]uint64), 39 ConvertedModuleTypeCount: make(map[string]uint64), 40 TotalModuleTypeCount: make(map[string]uint64), 41 }, 42 convertedModulePathMap: make(map[string]string), 43 } 44} 45 46// Serialize returns the protoized version of CodegenMetrics: bp2build_metrics_proto.Bp2BuildMetrics 47func (metrics *CodegenMetrics) Serialize() *bp2build_metrics_proto.Bp2BuildMetrics { 48 return metrics.serialized 49} 50 51// Print the codegen metrics to stdout. 52func (metrics *CodegenMetrics) Print() { 53 generatedTargetCount := uint64(0) 54 for _, ruleClass := range android.SortedKeys(metrics.serialized.RuleClassCount) { 55 count := metrics.serialized.RuleClassCount[ruleClass] 56 fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count) 57 generatedTargetCount += count 58 } 59 fmt.Printf( 60 `[bp2build] Converted %d Android.bp modules to %d total generated BUILD targets. Included %d handcrafted BUILD targets. There are %d total Android.bp modules. 61%d converted modules have unconverted deps: 62 %s 63%d converted modules have missing deps: 64 %s 65`, 66 metrics.serialized.GeneratedModuleCount, 67 generatedTargetCount, 68 metrics.serialized.HandCraftedModuleCount, 69 metrics.TotalModuleCount(), 70 len(metrics.moduleWithUnconvertedDepsMsgs), 71 strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"), 72 len(metrics.moduleWithMissingDepsMsgs), 73 strings.Join(metrics.moduleWithMissingDepsMsgs, "\n\t"), 74 ) 75} 76 77const bp2buildMetricsFilename = "bp2build_metrics.pb" 78 79// fail prints $PWD to stderr, followed by the given printf string and args (vals), 80// then the given alert, and then exits with 1 for failure 81func fail(err error, alertFmt string, vals ...interface{}) { 82 cwd, wderr := os.Getwd() 83 if wderr != nil { 84 cwd = "FAILED TO GET $PWD: " + wderr.Error() 85 } 86 fmt.Fprintf(os.Stderr, "\nIn "+cwd+":\n"+alertFmt+"\n"+err.Error()+"\n", vals...) 87 os.Exit(1) 88} 89 90// Write the bp2build-protoized codegen metrics into the given directory 91func (metrics *CodegenMetrics) Write(dir string) { 92 if _, err := os.Stat(dir); os.IsNotExist(err) { 93 // The metrics dir doesn't already exist, so create it (and parents) 94 if err := os.MkdirAll(dir, 0755); err != nil { // rx for all; w for user 95 fail(err, "Failed to `mkdir -p` %s", dir) 96 } 97 } else if err != nil { 98 fail(err, "Failed to `stat` %s", dir) 99 } 100 metricsFile := filepath.Join(dir, bp2buildMetricsFilename) 101 if err := metrics.dump(metricsFile); err != nil { 102 fail(err, "Error outputting %s", metricsFile) 103 } 104 if _, err := os.Stat(metricsFile); err != nil { 105 if os.IsNotExist(err) { 106 fail(err, "MISSING BP2BUILD METRICS OUTPUT: %s", metricsFile) 107 } else { 108 fail(err, "FAILED TO `stat` BP2BUILD METRICS OUTPUT: %s", metricsFile) 109 } 110 } 111} 112 113// ReadCodegenMetrics loads CodegenMetrics from `dir` 114// returns a nil pointer if the file doesn't exist 115func ReadCodegenMetrics(dir string) *CodegenMetrics { 116 metricsFile := filepath.Join(dir, bp2buildMetricsFilename) 117 if _, err := os.Stat(metricsFile); err != nil { 118 if os.IsNotExist(err) { 119 return nil 120 } else { 121 fail(err, "FAILED TO `stat` BP2BUILD METRICS OUTPUT: %s", metricsFile) 122 panic("unreachable after fail") 123 } 124 } 125 if buf, err := os.ReadFile(metricsFile); err != nil { 126 fail(err, "FAILED TO READ BP2BUILD METRICS OUTPUT: %s", metricsFile) 127 panic("unreachable after fail") 128 } else { 129 bp2BuildMetrics := bp2build_metrics_proto.Bp2BuildMetrics{ 130 RuleClassCount: make(map[string]uint64), 131 ConvertedModuleTypeCount: make(map[string]uint64), 132 TotalModuleTypeCount: make(map[string]uint64), 133 } 134 if err := proto.Unmarshal(buf, &bp2BuildMetrics); err != nil { 135 fail(err, "FAILED TO PARSE BP2BUILD METRICS OUTPUT: %s", metricsFile) 136 } 137 return &CodegenMetrics{ 138 serialized: &bp2BuildMetrics, 139 convertedModulePathMap: make(map[string]string), 140 } 141 } 142} 143 144func (metrics *CodegenMetrics) IncrementRuleClassCount(ruleClass string) { 145 metrics.serialized.RuleClassCount[ruleClass] += 1 146} 147 148func (metrics *CodegenMetrics) AddEvent(event *bp2build_metrics_proto.Event) { 149 metrics.serialized.Events = append(metrics.serialized.Events, event) 150} 151 152func (metrics *CodegenMetrics) AddUnconvertedModule(moduleType string) { 153 metrics.serialized.UnconvertedModuleCount += 1 154 metrics.serialized.TotalModuleTypeCount[moduleType] += 1 155} 156 157func (metrics *CodegenMetrics) SetSymlinkCount(n uint64) { 158 if m := metrics.serialized.WorkspaceSymlinkCount; m != 0 { 159 fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceSymlinkCount of %d", m) 160 } 161 metrics.serialized.WorkspaceSymlinkCount = n 162} 163 164func (metrics *CodegenMetrics) SetMkDirCount(n uint64) { 165 if m := metrics.serialized.WorkspaceMkDirCount; m != 0 { 166 fmt.Fprintf(os.Stderr, "unexpected non-zero workspaceDirCount of %d", m) 167 } 168 metrics.serialized.WorkspaceMkDirCount = n 169} 170 171func (metrics *CodegenMetrics) TotalModuleCount() uint64 { 172 return metrics.serialized.HandCraftedModuleCount + 173 metrics.serialized.GeneratedModuleCount + 174 metrics.serialized.UnconvertedModuleCount 175} 176 177// Dump serializes the metrics to the given filename 178func (metrics *CodegenMetrics) dump(filename string) (err error) { 179 ser := metrics.Serialize() 180 return shared.Save(ser, filename) 181} 182 183type ConversionType int 184 185const ( 186 Generated ConversionType = iota 187 Handcrafted 188) 189 190func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, dir string, conversionType ConversionType) { 191 //a package module has empty name 192 if moduleType == "package" { 193 return 194 } 195 // Undo prebuilt_ module name prefix modifications 196 moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name()) 197 metrics.serialized.ConvertedModules = append(metrics.serialized.ConvertedModules, moduleName) 198 metrics.convertedModulePathMap[moduleName] = "//" + dir 199 metrics.serialized.ConvertedModuleTypeCount[moduleType] += 1 200 metrics.serialized.TotalModuleTypeCount[moduleType] += 1 201 202 if conversionType == Handcrafted { 203 metrics.serialized.HandCraftedModuleCount += 1 204 } else if conversionType == Generated { 205 metrics.serialized.GeneratedModuleCount += 1 206 } 207} 208