4
4
import java .io .IOException ;
5
5
import java .nio .file .Files ;
6
6
import java .nio .file .Path ;
7
- import java .nio .file .Paths ;
8
7
import java .util .Collections ;
8
+ import java .util .HashMap ;
9
9
import java .util .HashSet ;
10
+ import java .util .Map ;
10
11
import java .util .Optional ;
11
12
import java .util .Set ;
12
13
import java .util .function .BiFunction ;
@@ -44,6 +45,8 @@ FeatureBuildItem feature() {
44
45
return new FeatureBuildItem ("jacoco" );
45
46
}
46
47
48
+ private static final Map <String , BytecodeTransformerBuildItem > transformedClasses = new HashMap <>();
49
+
47
50
@ BuildStep (onlyIf = IsTest .class )
48
51
void transformerBuildItem (BuildProducer <BytecodeTransformerBuildItem > transformers ,
49
52
OutputTargetBuildItem outputTargetBuildItem ,
@@ -61,58 +64,74 @@ void transformerBuildItem(BuildProducer<BytecodeTransformerBuildItem> transforme
61
64
return ;
62
65
}
63
66
64
- String dataFile = getFilePath (config .dataFile (), outputTargetBuildItem .getOutputDirectory (),
65
- JacocoConfig .JACOCO_QUARKUS_EXEC );
67
+ Path outputDir = outputTargetBuildItem .getOutputDirectory ().toAbsolutePath ();
68
+ Files .createDirectories (outputDir );
69
+
70
+ Path dataFilePath = outputDir .resolve (config .dataFile ().orElse (JacocoConfig .JACOCO_QUARKUS_EXEC ));
71
+ String dataFile = dataFilePath .toString ();
72
+
66
73
System .setProperty ("jacoco-agent.destfile" , dataFile );
67
- if (!config .reuseDataFile ()) {
68
- Files .deleteIfExists (Paths .get (dataFile ));
69
- }
74
+ System .setProperty ("jacoco-agent.jmx" , "true" );
70
75
71
76
Instrumenter instrumenter = new Instrumenter (new OfflineInstrumentationAccessGenerator ());
72
- Set <String > seen = new HashSet <>();
73
77
for (ApplicationArchive archive : applicationArchivesBuildItem .getAllApplicationArchives ()) {
74
78
for (ClassInfo i : archive .getIndex ().getKnownClasses ()) {
75
79
String className = i .name ().toString ();
76
- if (seen .contains (className )) {
77
- continue ;
78
- }
79
- seen .add (className );
80
- transformers .produce (
81
- new BytecodeTransformerBuildItem .Builder ().setClassToTransform (className )
82
- .setCacheable (true )
83
- .setInputTransformer (new BiFunction <String , byte [], byte []>() {
84
- @ Override
85
- public byte [] apply (String className , byte [] bytes ) {
86
- try {
87
- byte [] enhanced = instrumenter .instrument (bytes , className );
88
- if (enhanced == null ) {
89
- return bytes ;
90
- }
91
- return enhanced ;
92
- } catch (IOException e ) {
93
- if (!log .isDebugEnabled ()) {
94
- log .warnf (
95
- "Unable to instrument class %s with JaCoCo: %s, keeping the original class" ,
96
- className , e .getMessage ());
97
- } else {
98
- log .warnf (e ,
99
- "Unable to instrument class %s with JaCoCo, keeping the original class" ,
100
- className );
101
- }
80
+ BytecodeTransformerBuildItem bytecodeTransformerBuildItem = transformedClasses .get (className );
81
+ if (bytecodeTransformerBuildItem == null ) {
82
+ bytecodeTransformerBuildItem = new BytecodeTransformerBuildItem .Builder ().setClassToTransform (className )
83
+ .setCacheable (true )
84
+ .setInputTransformer (new BiFunction <>() {
85
+ @ Override
86
+ public byte [] apply (String className , byte [] bytes ) {
87
+ try {
88
+ byte [] enhanced = instrumenter .instrument (bytes , className );
89
+ if (enhanced == null ) {
102
90
return bytes ;
103
91
}
92
+ return enhanced ;
93
+ } catch (IOException e ) {
94
+ if (!log .isDebugEnabled ()) {
95
+ log .warnf (
96
+ "Unable to instrument class %s with JaCoCo: %s, keeping the original class" ,
97
+ className , e .getMessage ());
98
+ } else {
99
+ log .warnf (e ,
100
+ "Unable to instrument class %s with JaCoCo, keeping the original class" ,
101
+ className );
102
+ }
103
+ return bytes ;
104
104
}
105
- }).build ());
105
+ }
106
+ }).build ();
107
+ transformedClasses .put (className , bytecodeTransformerBuildItem );
108
+ }
109
+ transformers .produce (bytecodeTransformerBuildItem );
106
110
}
107
111
}
112
+
113
+ String sysPropName = "io.quarkus.internal.jacoco.report-data-file" ;
114
+ String currentDataFile = System .setProperty (sysPropName , dataFile );
115
+ if (currentDataFile != null ) {
116
+ if (!currentDataFile .equals (dataFile )) {
117
+ System .err .println ("Quarkus will use the Jacoco data file " + currentDataFile
118
+ + ", not the configured data file " + dataFile + ", because another build item triggered a report." );
119
+ }
120
+ return ;
121
+ }
122
+
123
+ if (!config .reuseDataFile ()) {
124
+ Files .deleteIfExists (dataFilePath );
125
+ }
126
+
108
127
if (config .report ()) {
109
128
ReportInfo info = new ReportInfo ();
110
- info .dataFile = dataFile ;
129
+ info .dataFile = dataFilePath ;
111
130
112
- File targetdir = new File (
113
- getFilePath ( config . reportLocation (), outputTargetBuildItem . getOutputDirectory (),
114
- JacocoConfig . JACOCO_REPORT ) );
115
- info . reportDir = targetdir . getAbsolutePath ( );
131
+ Path targetPath = outputDir . resolve ( config . reportLocation (). orElse ( JacocoConfig . JACOCO_REPORT ));
132
+ info . reportDir = targetPath . toString ();
133
+ info . errorFile = targetPath . resolve ( "error.txt" );
134
+ Files . deleteIfExists ( info . errorFile );
116
135
String includes = String .join ("," , config .includes ());
117
136
String excludes = String .join ("," , config .excludes ().orElse (Collections .emptyList ()));
118
137
Set <String > classes = new HashSet <>();
@@ -159,7 +178,7 @@ private void addProjectModule(ResolvedDependency module, JacocoConfig config, Re
159
178
}
160
179
}
161
180
162
- private String getFilePath (Optional <String > path , Path outputDirectory , String defaultRelativePath ) {
181
+ private static String getFilePath (Optional <String > path , Path outputDirectory , String defaultRelativePath ) {
163
182
return path .orElse (outputDirectory .toAbsolutePath () + File .separator + defaultRelativePath );
164
183
}
165
184
}
0 commit comments