• 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	"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