1// Copyright 2022 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package main
6
7// This file contains functions and apis to support the "merge"
8// subcommand of "go tool covdata".
9
10import (
11	"flag"
12	"fmt"
13	"internal/coverage"
14	"internal/coverage/cmerge"
15	"internal/coverage/decodecounter"
16	"internal/coverage/decodemeta"
17	"internal/coverage/pods"
18	"os"
19)
20
21var outdirflag *string
22var pcombineflag *bool
23
24func makeMergeOp() covOperation {
25	outdirflag = flag.String("o", "", "Output directory to write")
26	pcombineflag = flag.Bool("pcombine", false, "Combine profiles derived from distinct program executables")
27	m := &mstate{
28		mm: newMetaMerge(),
29	}
30	return m
31}
32
33// mstate encapsulates state and provides methods for implementing the
34// merge operation. This type implements the CovDataVisitor interface,
35// and is designed to be used in concert with the CovDataReader
36// utility, which abstracts away most of the grubby details of reading
37// coverage data files. Most of the heavy lifting for merging is done
38// using apis from 'metaMerge' (this is mainly a wrapper around that
39// functionality).
40type mstate struct {
41	mm *metaMerge
42}
43
44func (m *mstate) Usage(msg string) {
45	if len(msg) > 0 {
46		fmt.Fprintf(os.Stderr, "error: %s\n", msg)
47	}
48	fmt.Fprintf(os.Stderr, "usage: go tool covdata merge -i=<directories> -o=<dir>\n\n")
49	flag.PrintDefaults()
50	fmt.Fprintf(os.Stderr, "\nExamples:\n\n")
51	fmt.Fprintf(os.Stderr, "  go tool covdata merge -i=dir1,dir2,dir3 -o=outdir\n\n")
52	fmt.Fprintf(os.Stderr, "  \tmerges all files in dir1/dir2/dir3\n")
53	fmt.Fprintf(os.Stderr, "  \tinto output dir outdir\n")
54	Exit(2)
55}
56
57func (m *mstate) Setup() {
58	if *indirsflag == "" {
59		m.Usage("select input directories with '-i' option")
60	}
61	if *outdirflag == "" {
62		m.Usage("select output directory with '-o' option")
63	}
64	m.mm.SetModeMergePolicy(cmerge.ModeMergeRelaxed)
65}
66
67func (m *mstate) BeginPod(p pods.Pod) {
68	m.mm.beginPod()
69}
70
71func (m *mstate) EndPod(p pods.Pod) {
72	m.mm.endPod(*pcombineflag)
73}
74
75func (m *mstate) BeginCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) {
76	dbgtrace(2, "visit counter data file %s dirIdx %d", cdf, dirIdx)
77	m.mm.beginCounterDataFile(cdr)
78}
79
80func (m *mstate) EndCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) {
81}
82
83func (m *mstate) VisitFuncCounterData(data decodecounter.FuncPayload) {
84	m.mm.visitFuncCounterData(data)
85}
86
87func (m *mstate) EndCounters() {
88}
89
90func (m *mstate) VisitMetaDataFile(mdf string, mfr *decodemeta.CoverageMetaFileReader) {
91	m.mm.visitMetaDataFile(mdf, mfr)
92}
93
94func (m *mstate) BeginPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32) {
95	dbgtrace(3, "VisitPackage(pk=%d path=%s)", pkgIdx, pd.PackagePath())
96	m.mm.visitPackage(pd, pkgIdx, *pcombineflag)
97}
98
99func (m *mstate) EndPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32) {
100}
101
102func (m *mstate) VisitFunc(pkgIdx uint32, fnIdx uint32, fd *coverage.FuncDesc) {
103	m.mm.visitFunc(pkgIdx, fnIdx, fd, mergeMode, *pcombineflag)
104}
105
106func (m *mstate) Finish() {
107	if *pcombineflag {
108		finalHash := m.mm.emitMeta(*outdirflag, true)
109		m.mm.emitCounters(*outdirflag, finalHash)
110	}
111}
112