forked from quarkusio/quarkus
-
Notifications
You must be signed in to change notification settings - Fork 1
/
CodeGenerator.java
190 lines (173 loc) · 8.16 KB
/
CodeGenerator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package io.quarkus.deployment;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.function.Consumer;
import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.bootstrap.prebuild.CodeGenException;
import io.quarkus.deployment.codegen.CodeGenData;
import io.quarkus.deployment.dev.DevModeContext;
import io.quarkus.deployment.dev.DevModeContext.ModuleInfo;
import io.quarkus.paths.PathCollection;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.configuration.ConfigUtils;
import io.smallrye.config.PropertiesConfigSource;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.SysPropConfigSource;
/**
* A set of methods to initialize and execute {@link CodeGenProvider}s.
*/
public class CodeGenerator {
// used by Gradle and Maven
public static void initAndRun(ClassLoader classLoader,
PathCollection sourceParentDirs, Path generatedSourcesDir, Path buildDir,
Consumer<Path> sourceRegistrar, ApplicationModel appModel, Properties properties,
String launchMode, boolean test) throws CodeGenException {
final List<CodeGenData> generators = init(classLoader, sourceParentDirs, generatedSourcesDir, buildDir,
sourceRegistrar);
for (CodeGenData generator : generators) {
generator.setRedirectIO(true);
trigger(classLoader, generator, appModel, properties, LaunchMode.valueOf(launchMode), test);
}
}
private static List<CodeGenData> init(ClassLoader deploymentClassLoader,
PathCollection sourceParentDirs,
Path generatedSourcesDir,
Path buildDir,
Consumer<Path> sourceRegistrar) throws CodeGenException {
return callWithClassloader(deploymentClassLoader, () -> {
final List<CodeGenProvider> codeGenProviders = loadCodeGenProviders(deploymentClassLoader);
if (codeGenProviders.isEmpty()) {
return List.of();
}
List<CodeGenData> result = new ArrayList<>();
for (CodeGenProvider provider : codeGenProviders) {
Path outputDir = codeGenOutDir(generatedSourcesDir, provider, sourceRegistrar);
for (Path sourceParentDir : sourceParentDirs) {
result.add(
new CodeGenData(provider, outputDir, sourceParentDir.resolve(provider.inputDirectory()), buildDir));
}
}
return result;
});
}
public static List<CodeGenData> init(ClassLoader deploymentClassLoader, Collection<ModuleInfo> modules)
throws CodeGenException {
return callWithClassloader(deploymentClassLoader, () -> {
List<CodeGenProvider> codeGenProviders = null;
List<CodeGenData> codeGens = List.of();
for (DevModeContext.ModuleInfo module : modules) {
if (!module.getSourceParents().isEmpty() && module.getPreBuildOutputDir() != null) { // it's null for remote dev
if (codeGenProviders == null) {
codeGenProviders = loadCodeGenProviders(deploymentClassLoader);
if (codeGenProviders.isEmpty()) {
return List.of();
}
}
for (CodeGenProvider provider : codeGenProviders) {
Path outputDir = codeGenOutDir(Path.of(module.getPreBuildOutputDir()), provider,
sourcePath -> module.addSourcePathFirst(sourcePath.toAbsolutePath().toString()));
for (Path sourceParentDir : module.getSourceParents()) {
if (codeGens.isEmpty()) {
codeGens = new ArrayList<>();
}
codeGens.add(
new CodeGenData(provider, outputDir, sourceParentDir.resolve(provider.inputDirectory()),
Path.of(module.getTargetDir())));
}
}
}
}
return codeGens;
});
}
private static List<CodeGenProvider> loadCodeGenProviders(ClassLoader deploymentClassLoader)
throws CodeGenException {
Class<? extends CodeGenProvider> codeGenProviderClass;
try {
//noinspection unchecked
codeGenProviderClass = (Class<? extends CodeGenProvider>) deploymentClassLoader
.loadClass(CodeGenProvider.class.getName());
} catch (ClassNotFoundException e) {
throw new CodeGenException("Failed to load CodeGenProvider class from deployment classloader", e);
}
final Iterator<? extends CodeGenProvider> i = ServiceLoader.load(codeGenProviderClass, deploymentClassLoader)
.iterator();
if (!i.hasNext()) {
return List.of();
}
final List<CodeGenProvider> codeGenProviders = new ArrayList<>();
while (i.hasNext()) {
codeGenProviders.add(i.next());
}
return codeGenProviders;
}
private static <T> T callWithClassloader(ClassLoader deploymentClassLoader, CodeGenAction<T> supplier)
throws CodeGenException {
ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(deploymentClassLoader);
return supplier.fire();
} finally {
Thread.currentThread().setContextClassLoader(originalClassloader);
}
}
/**
* generate sources for given code gen
*
* @param deploymentClassLoader deployment classloader
* @param data code gen
* @param appModel app model
* @param properties custom code generation properties
* @param test whether the sources are generated for production code or tests
* @return true if sources have been created
* @throws CodeGenException on failure
*/
public static boolean trigger(ClassLoader deploymentClassLoader,
CodeGenData data,
ApplicationModel appModel,
Properties properties,
LaunchMode launchMode,
boolean test) throws CodeGenException {
return callWithClassloader(deploymentClassLoader, () -> {
final PropertiesConfigSource pcs = new PropertiesConfigSource(properties, "Build system");
final SysPropConfigSource spcs = new SysPropConfigSource();
// Discovered Config classes may cause issues here, because this goal runs before compile
final SmallRyeConfig config = ConfigUtils.configBuilder(false, false, launchMode)
.setAddDiscoveredSources(false)
.setAddDiscoveredInterceptors(false)
.setAddDiscoveredConverters(false)
.withProfile(launchMode.getDefaultProfile())
.withSources(pcs, spcs)
.build();
CodeGenProvider provider = data.provider;
return provider.shouldRun(data.sourceDir, config)
&& provider.trigger(
new CodeGenContext(appModel, data.outPath, data.buildDir, data.sourceDir, data.redirectIO, config,
test));
});
}
private static Path codeGenOutDir(Path generatedSourcesDir,
CodeGenProvider provider,
Consumer<Path> sourceRegistrar) throws CodeGenException {
Path outputDir = generatedSourcesDir.resolve(provider.providerId());
try {
Files.createDirectories(outputDir);
sourceRegistrar.accept(outputDir);
return outputDir;
} catch (IOException e) {
throw new CodeGenException(
"Failed to create output directory for generated sources: " + outputDir.toAbsolutePath(), e);
}
}
@FunctionalInterface
private interface CodeGenAction<T> {
T fire() throws CodeGenException;
}
}