jacoco.go 5.3 KB

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