Skip to content

Commit

Permalink
Enable more config sources for code generators except those found in the
Browse files Browse the repository at this point in the history
root app module
  • Loading branch information
aloubyansky committed Oct 12, 2022
1 parent 4342fa4 commit 6253b9f
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.function.Consumer;

import org.eclipse.microprofile.config.Config;

import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.FilteredClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.bootstrap.prebuild.CodeGenException;
import io.quarkus.deployment.codegen.CodeGenData;
Expand All @@ -19,25 +25,35 @@
import io.quarkus.paths.PathCollection;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.configuration.ConfigUtils;
import io.smallrye.config.KeyMap;
import io.smallrye.config.KeyMapBackedConfigSource;
import io.smallrye.config.NameIterator;
import io.smallrye.config.PropertiesConfigSource;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.SmallRyeConfigBuilder;
import io.smallrye.config.SysPropConfigSource;

/**
* A set of methods to initialize and execute {@link CodeGenProvider}s.
*/
public class CodeGenerator {

private static final String MP_CONFIG_SPI_CONFIG_SOURCE_PROVIDER = "META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider";

// used by Gradle and Maven
public static void initAndRun(ClassLoader classLoader,
public static void initAndRun(QuarkusClassLoader 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);
if (generators.isEmpty()) {
return;
}
final LaunchMode mode = LaunchMode.valueOf(launchMode);
final Config config = getConfig(appModel, mode, properties, classLoader);
for (CodeGenData generator : generators) {
generator.setRedirectIO(true);
trigger(classLoader, generator, appModel, properties, LaunchMode.valueOf(launchMode), test);
trigger(classLoader, generator, appModel, config, test);
}
}

Expand All @@ -51,7 +67,7 @@ private static List<CodeGenData> init(ClassLoader deploymentClassLoader,
if (codeGenProviders.isEmpty()) {
return List.of();
}
List<CodeGenData> result = new ArrayList<>();
final List<CodeGenData> result = new ArrayList<>(codeGenProviders.size());
for (CodeGenProvider provider : codeGenProviders) {
Path outputDir = codeGenOutDir(generatedSourcesDir, provider, sourceRegistrar);
for (Path sourceParentDir : sourceParentDirs) {
Expand Down Expand Up @@ -144,23 +160,9 @@ private static <T> T callWithClassloader(ClassLoader deploymentClassLoader, Code
public static boolean trigger(ClassLoader deploymentClassLoader,
CodeGenData data,
ApplicationModel appModel,
Properties properties,
LaunchMode launchMode,
Config config,
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(
Expand All @@ -169,6 +171,47 @@ public static boolean trigger(ClassLoader deploymentClassLoader,
});
}

public static Config getConfig(ApplicationModel appModel, LaunchMode launchMode, Properties buildSystemProps,
QuarkusClassLoader deploymentClassLoader) throws CodeGenException {
// Config instance that is returned by this method should be as close to the one built in the ExtensionLoader as possible
if (appModel.getAppArtifact().getContentTree()
.contains(MP_CONFIG_SPI_CONFIG_SOURCE_PROVIDER)) {
final List<ClassPathElement> allElements = ((QuarkusClassLoader) deploymentClassLoader).getAllElements(false);
// we don't want to load config sources from the current module because they haven't been compiled yet
final QuarkusClassLoader.Builder configClBuilder = QuarkusClassLoader
.builder("CodeGenerator Config ClassLoader", QuarkusClassLoader.getSystemClassLoader(), false);
final Collection<Path> appRoots = appModel.getAppArtifact().getContentTree().getRoots();
for (ClassPathElement e : allElements) {
if (appRoots.contains(e.getRoot())) {
configClBuilder.addElement(new FilteredClassPathElement(e, List.of(MP_CONFIG_SPI_CONFIG_SOURCE_PROVIDER)));
} else {
configClBuilder.addElement(e);
}
}
deploymentClassLoader = configClBuilder.build();
}
final SmallRyeConfigBuilder builder = ConfigUtils.configBuilder(false, launchMode)
.forClassLoader(deploymentClassLoader);
final PropertiesConfigSource pcs = new PropertiesConfigSource(buildSystemProps, "Build system");
final SysPropConfigSource spcs = new SysPropConfigSource();

final Map<String, String> platformProperties = appModel.getPlatformProperties();
if (platformProperties.isEmpty()) {
builder.withSources(pcs, spcs);
} else {
final KeyMap<String> props = new KeyMap<>(platformProperties.size());
for (Map.Entry<String, String> prop : platformProperties.entrySet()) {
props.findOrAdd(new NameIterator(prop.getKey())).putRootValue(prop.getValue());
}
final KeyMapBackedConfigSource platformConfigSource = new KeyMapBackedConfigSource("Quarkus platform",
// Our default value configuration source is using an ordinal of Integer.MIN_VALUE
// (see io.quarkus.deployment.configuration.DefaultValuesConfigurationSource)
Integer.MIN_VALUE + 1000, props);
builder.withSources(platformConfigSource, pcs, spcs);
}
return builder.build();
}

private static Path codeGenOutDir(Path generatedSourcesDir,
CodeGenProvider provider,
Consumer<Path> sourceRegistrar) throws CodeGenException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.List;
import java.util.Properties;

import org.eclipse.microprofile.config.Config;
import org.jboss.logging.Logger;

import io.quarkus.bootstrap.app.CuratedApplication;
Expand Down Expand Up @@ -33,13 +34,15 @@ class CodeGenWatcher {
final Collection<FSWatchUtil.Watcher> watchers = new ArrayList<>(codeGens.size());
final Properties properties = new Properties();
properties.putAll(context.getBuildSystemProperties());
final Config config = CodeGenerator.getConfig(curatedApplication.getApplicationModel(), LaunchMode.DEVELOPMENT,
properties, deploymentClassLoader);
for (CodeGenData codeGen : codeGens) {
watchers.add(new FSWatchUtil.Watcher(codeGen.sourceDir, codeGen.provider.inputExtension(),
modifiedPaths -> {
try {
CodeGenerator.trigger(deploymentClassLoader,
codeGen,
curatedApplication.getApplicationModel(), properties, LaunchMode.DEVELOPMENT, false);
curatedApplication.getApplicationModel(), config, false);
} catch (Exception any) {
log.warn("Code generation failed", any);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.function.Consumer;
Expand All @@ -29,6 +27,7 @@
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.deployment.CodeGenerator;
import io.quarkus.paths.PathCollection;
import io.quarkus.paths.PathList;
import io.quarkus.runtime.LaunchMode;

Expand Down Expand Up @@ -125,14 +124,17 @@ public void prepareQuarkus() {
QuarkusClassLoader deploymentClassLoader = appCreationContext.createDeploymentClassLoader();
Class<?> codeGenerator = deploymentClassLoader.loadClass(CodeGenerator.class.getName());

Optional<Method> initAndRun = Arrays.stream(codeGenerator.getMethods())
.filter(m -> m.getName().equals(INIT_AND_RUN))
.findAny();
if (initAndRun.isEmpty()) {
throw new GradleException("Failed to find " + INIT_AND_RUN + " method in " + CodeGenerator.class.getName());
Method initAndRun;
try {
initAndRun = codeGenerator.getMethod(INIT_AND_RUN, QuarkusClassLoader.class, PathCollection.class,
Path.class, Path.class,
Consumer.class, ApplicationModel.class, Properties.class, String.class,
boolean.class);
} catch (Exception e) {
throw new GradleException("Quarkus code generation phase has failed", e);
}

initAndRun.get().invoke(null, deploymentClassLoader,
initAndRun.invoke(null, deploymentClassLoader,
PathList.from(sourcesDirectories),
paths.get(0),
buildDir,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void generateCode(PathCollection sourceParents,
Thread.currentThread().setContextClassLoader(deploymentClassLoader);

final Class<?> codeGenerator = deploymentClassLoader.loadClass("io.quarkus.deployment.CodeGenerator");
final Method initAndRun = codeGenerator.getMethod("initAndRun", ClassLoader.class, PathCollection.class,
final Method initAndRun = codeGenerator.getMethod("initAndRun", QuarkusClassLoader.class, PathCollection.class,
Path.class, Path.class,
Consumer.class, ApplicationModel.class, Properties.class, String.class,
boolean.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ public static List<ClassPathElement> getElements(String resourceName, boolean lo
return ((QuarkusClassLoader) ccl).getElementsWithResource(resourceName, localOnly);
}

public List<ClassPathElement> getAllElements(boolean localOnly) {
List<ClassPathElement> ret = new ArrayList<>();
if (parent instanceof QuarkusClassLoader && !localOnly) {
ret.addAll(((QuarkusClassLoader) parent).getAllElements(localOnly));
}
ret.addAll(elements);
return ret;
}

/**
* Indicates if a given class is present at runtime.
*
Expand Down

0 comments on commit 6253b9f

Please sign in to comment.