xref: /aosp_15_r20/build/soong/java/jacoco.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2017 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Workerpackage java
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Worker// Rules for instrumenting classes using jacoco
18*333d2b36SAndroid Build Coastguard Worker
19*333d2b36SAndroid Build Coastguard Workerimport (
20*333d2b36SAndroid Build Coastguard Worker	"fmt"
21*333d2b36SAndroid Build Coastguard Worker	"path/filepath"
22*333d2b36SAndroid Build Coastguard Worker	"strings"
23*333d2b36SAndroid Build Coastguard Worker
24*333d2b36SAndroid Build Coastguard Worker	"github.com/google/blueprint"
25*333d2b36SAndroid Build Coastguard Worker	"github.com/google/blueprint/proptools"
26*333d2b36SAndroid Build Coastguard Worker
27*333d2b36SAndroid Build Coastguard Worker	"android/soong/android"
28*333d2b36SAndroid Build Coastguard Worker	"android/soong/java/config"
29*333d2b36SAndroid Build Coastguard Worker)
30*333d2b36SAndroid Build Coastguard Worker
31*333d2b36SAndroid Build Coastguard Workervar (
32*333d2b36SAndroid Build Coastguard Worker	jacoco = pctx.AndroidStaticRule("jacoco", blueprint.RuleParams{
33*333d2b36SAndroid Build Coastguard Worker		Command: `rm -rf $tmpDir && mkdir -p $tmpDir && ` +
34*333d2b36SAndroid Build Coastguard Worker			`${config.Zip2ZipCmd} -i $in -o $strippedJar $stripSpec && ` +
35*333d2b36SAndroid Build Coastguard Worker			`${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.JacocoCLIJar} ` +
36*333d2b36SAndroid Build Coastguard Worker			`  instrument --quiet --dest $tmpDir $strippedJar && ` +
37*333d2b36SAndroid Build Coastguard Worker			`${config.MergeZipsCmd} --ignore-duplicates -j $out $tmpJar $in`,
38*333d2b36SAndroid Build Coastguard Worker		CommandDeps: []string{
39*333d2b36SAndroid Build Coastguard Worker			"${config.Zip2ZipCmd}",
40*333d2b36SAndroid Build Coastguard Worker			"${config.JavaCmd}",
41*333d2b36SAndroid Build Coastguard Worker			"${config.JacocoCLIJar}",
42*333d2b36SAndroid Build Coastguard Worker			"${config.MergeZipsCmd}",
43*333d2b36SAndroid Build Coastguard Worker		},
44*333d2b36SAndroid Build Coastguard Worker	},
45*333d2b36SAndroid Build Coastguard Worker		"strippedJar", "stripSpec", "tmpDir", "tmpJar")
46*333d2b36SAndroid Build Coastguard Worker)
47*333d2b36SAndroid Build Coastguard Worker
48*333d2b36SAndroid Build Coastguard Workerfunc jacocoDepsMutator(ctx android.BottomUpMutatorContext) {
49*333d2b36SAndroid Build Coastguard Worker	type instrumentable interface {
50*333d2b36SAndroid Build Coastguard Worker		shouldInstrument(ctx android.BaseModuleContext) bool
51*333d2b36SAndroid Build Coastguard Worker		shouldInstrumentInApex(ctx android.BaseModuleContext) bool
52*333d2b36SAndroid Build Coastguard Worker		setInstrument(value bool)
53*333d2b36SAndroid Build Coastguard Worker	}
54*333d2b36SAndroid Build Coastguard Worker
55*333d2b36SAndroid Build Coastguard Worker	j, ok := ctx.Module().(instrumentable)
56*333d2b36SAndroid Build Coastguard Worker	if !ctx.Module().Enabled(ctx) || !ok {
57*333d2b36SAndroid Build Coastguard Worker		return
58*333d2b36SAndroid Build Coastguard Worker	}
59*333d2b36SAndroid Build Coastguard Worker
60*333d2b36SAndroid Build Coastguard Worker	if j.shouldInstrumentInApex(ctx) {
61*333d2b36SAndroid Build Coastguard Worker		j.setInstrument(true)
62*333d2b36SAndroid Build Coastguard Worker	}
63*333d2b36SAndroid Build Coastguard Worker
64*333d2b36SAndroid Build Coastguard Worker	if j.shouldInstrument(ctx) && ctx.ModuleName() != "jacocoagent" {
65*333d2b36SAndroid Build Coastguard Worker		// We can use AddFarVariationDependencies here because, since this dep
66*333d2b36SAndroid Build Coastguard Worker		// is added as libs only (i.e. a compiletime CLASSPATH entry only),
67*333d2b36SAndroid Build Coastguard Worker		// the first variant of jacocoagent is sufficient to prevent
68*333d2b36SAndroid Build Coastguard Worker		// compile time errors.
69*333d2b36SAndroid Build Coastguard Worker		// At this stage in the build, AddVariationDependencies is not always
70*333d2b36SAndroid Build Coastguard Worker		// able to procure a variant of jacocoagent that matches the calling
71*333d2b36SAndroid Build Coastguard Worker		// module.
72*333d2b36SAndroid Build Coastguard Worker		ctx.AddFarVariationDependencies(ctx.Module().Target().Variations(), libTag, "jacocoagent")
73*333d2b36SAndroid Build Coastguard Worker	}
74*333d2b36SAndroid Build Coastguard Worker}
75*333d2b36SAndroid Build Coastguard Worker
76*333d2b36SAndroid Build Coastguard Worker// Instruments a jar using the Jacoco command line interface.  Uses stripSpec to extract a subset
77*333d2b36SAndroid Build Coastguard Worker// of the classes in inputJar into strippedJar, instruments strippedJar into tmpJar, and then
78*333d2b36SAndroid Build Coastguard Worker// combines the classes in tmpJar with inputJar (preferring the instrumented classes in tmpJar)
79*333d2b36SAndroid Build Coastguard Worker// to produce instrumentedJar.
80*333d2b36SAndroid Build Coastguard Workerfunc jacocoInstrumentJar(ctx android.ModuleContext, instrumentedJar, strippedJar android.WritablePath,
81*333d2b36SAndroid Build Coastguard Worker	inputJar android.Path, stripSpec string) {
82*333d2b36SAndroid Build Coastguard Worker
83*333d2b36SAndroid Build Coastguard Worker	// The basename of tmpJar has to be the same as the basename of strippedJar
84*333d2b36SAndroid Build Coastguard Worker	tmpJar := android.PathForModuleOut(ctx, "jacoco", "tmp", strippedJar.Base())
85*333d2b36SAndroid Build Coastguard Worker
86*333d2b36SAndroid Build Coastguard Worker	ctx.Build(pctx, android.BuildParams{
87*333d2b36SAndroid Build Coastguard Worker		Rule:           jacoco,
88*333d2b36SAndroid Build Coastguard Worker		Description:    "jacoco",
89*333d2b36SAndroid Build Coastguard Worker		Output:         instrumentedJar,
90*333d2b36SAndroid Build Coastguard Worker		ImplicitOutput: strippedJar,
91*333d2b36SAndroid Build Coastguard Worker		Input:          inputJar,
92*333d2b36SAndroid Build Coastguard Worker		Args: map[string]string{
93*333d2b36SAndroid Build Coastguard Worker			"strippedJar": strippedJar.String(),
94*333d2b36SAndroid Build Coastguard Worker			"stripSpec":   stripSpec,
95*333d2b36SAndroid Build Coastguard Worker			"tmpDir":      filepath.Dir(tmpJar.String()),
96*333d2b36SAndroid Build Coastguard Worker			"tmpJar":      tmpJar.String(),
97*333d2b36SAndroid Build Coastguard Worker		},
98*333d2b36SAndroid Build Coastguard Worker	})
99*333d2b36SAndroid Build Coastguard Worker}
100*333d2b36SAndroid Build Coastguard Worker
101*333d2b36SAndroid Build Coastguard Workerfunc (j *Module) jacocoModuleToZipCommand(ctx android.ModuleContext) string {
102*333d2b36SAndroid Build Coastguard Worker	includes, err := jacocoFiltersToSpecs(j.properties.Jacoco.Include_filter)
103*333d2b36SAndroid Build Coastguard Worker	if err != nil {
104*333d2b36SAndroid Build Coastguard Worker		ctx.PropertyErrorf("jacoco.include_filter", "%s", err.Error())
105*333d2b36SAndroid Build Coastguard Worker	}
106*333d2b36SAndroid Build Coastguard Worker	// Also include the default list of classes to exclude from instrumentation.
107*333d2b36SAndroid Build Coastguard Worker	excludes, err := jacocoFiltersToSpecs(append(j.properties.Jacoco.Exclude_filter, config.DefaultJacocoExcludeFilter...))
108*333d2b36SAndroid Build Coastguard Worker	if err != nil {
109*333d2b36SAndroid Build Coastguard Worker		ctx.PropertyErrorf("jacoco.exclude_filter", "%s", err.Error())
110*333d2b36SAndroid Build Coastguard Worker	}
111*333d2b36SAndroid Build Coastguard Worker
112*333d2b36SAndroid Build Coastguard Worker	return jacocoFiltersToZipCommand(includes, excludes)
113*333d2b36SAndroid Build Coastguard Worker}
114*333d2b36SAndroid Build Coastguard Worker
115*333d2b36SAndroid Build Coastguard Workerfunc jacocoFiltersToZipCommand(includes, excludes []string) string {
116*333d2b36SAndroid Build Coastguard Worker	specs := ""
117*333d2b36SAndroid Build Coastguard Worker	if len(excludes) > 0 {
118*333d2b36SAndroid Build Coastguard Worker		specs += android.JoinWithPrefix(excludes, "-x ") + " "
119*333d2b36SAndroid Build Coastguard Worker	}
120*333d2b36SAndroid Build Coastguard Worker	if len(includes) > 0 {
121*333d2b36SAndroid Build Coastguard Worker		specs += strings.Join(includes, " ")
122*333d2b36SAndroid Build Coastguard Worker	} else {
123*333d2b36SAndroid Build Coastguard Worker		specs += "'**/*.class'"
124*333d2b36SAndroid Build Coastguard Worker	}
125*333d2b36SAndroid Build Coastguard Worker	return specs
126*333d2b36SAndroid Build Coastguard Worker}
127*333d2b36SAndroid Build Coastguard Worker
128*333d2b36SAndroid Build Coastguard Workerfunc jacocoFiltersToSpecs(filters []string) ([]string, error) {
129*333d2b36SAndroid Build Coastguard Worker	specs := make([]string, len(filters))
130*333d2b36SAndroid Build Coastguard Worker	var err error
131*333d2b36SAndroid Build Coastguard Worker	for i, f := range filters {
132*333d2b36SAndroid Build Coastguard Worker		specs[i], err = jacocoFilterToSpec(f)
133*333d2b36SAndroid Build Coastguard Worker		if err != nil {
134*333d2b36SAndroid Build Coastguard Worker			return nil, err
135*333d2b36SAndroid Build Coastguard Worker		}
136*333d2b36SAndroid Build Coastguard Worker	}
137*333d2b36SAndroid Build Coastguard Worker	return proptools.NinjaAndShellEscapeList(specs), nil
138*333d2b36SAndroid Build Coastguard Worker}
139*333d2b36SAndroid Build Coastguard Worker
140*333d2b36SAndroid Build Coastguard Workerfunc jacocoFilterToSpec(filter string) (string, error) {
141*333d2b36SAndroid Build Coastguard Worker	recursiveWildcard := strings.HasSuffix(filter, "**")
142*333d2b36SAndroid Build Coastguard Worker	nonRecursiveWildcard := false
143*333d2b36SAndroid Build Coastguard Worker	if !recursiveWildcard {
144*333d2b36SAndroid Build Coastguard Worker		nonRecursiveWildcard = strings.HasSuffix(filter, "*")
145*333d2b36SAndroid Build Coastguard Worker		filter = strings.TrimSuffix(filter, "*")
146*333d2b36SAndroid Build Coastguard Worker	} else {
147*333d2b36SAndroid Build Coastguard Worker		filter = strings.TrimSuffix(filter, "**")
148*333d2b36SAndroid Build Coastguard Worker	}
149*333d2b36SAndroid Build Coastguard Worker
150*333d2b36SAndroid Build Coastguard Worker	if recursiveWildcard && !(strings.HasSuffix(filter, ".") || filter == "") {
151*333d2b36SAndroid Build Coastguard Worker		return "", fmt.Errorf("only '**' or '.**' is supported as recursive wildcard in a filter")
152*333d2b36SAndroid Build Coastguard Worker	}
153*333d2b36SAndroid Build Coastguard Worker
154*333d2b36SAndroid Build Coastguard Worker	if strings.ContainsRune(filter, '*') {
155*333d2b36SAndroid Build Coastguard Worker		return "", fmt.Errorf("'*' is only supported as the last character in a filter")
156*333d2b36SAndroid Build Coastguard Worker	}
157*333d2b36SAndroid Build Coastguard Worker
158*333d2b36SAndroid Build Coastguard Worker	spec := strings.Replace(filter, ".", "/", -1)
159*333d2b36SAndroid Build Coastguard Worker
160*333d2b36SAndroid Build Coastguard Worker	if recursiveWildcard {
161*333d2b36SAndroid Build Coastguard Worker		spec += "**/*.class"
162*333d2b36SAndroid Build Coastguard Worker	} else if nonRecursiveWildcard {
163*333d2b36SAndroid Build Coastguard Worker		spec += "*.class"
164*333d2b36SAndroid Build Coastguard Worker	} else {
165*333d2b36SAndroid Build Coastguard Worker		spec += ".class"
166*333d2b36SAndroid Build Coastguard Worker	}
167*333d2b36SAndroid Build Coastguard Worker
168*333d2b36SAndroid Build Coastguard Worker	return spec, nil
169*333d2b36SAndroid Build Coastguard Worker}
170