Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable more config providers for code generators except those found in the root app module #28538

Merged
merged 1 commit into from
Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@radcortez I tried to keep it as close to the ExtensionLoader one as I could

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

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