• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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