sourceParents = new HashSet<>();
+ for (File srcDir : module.getSourceSourceSet().getSourceDirectories()) {
+ sourceDirectories.add(srcDir.getPath());
+ sourceParents.add(srcDir.getParent());
+ }
+
+ return new DevModeContext.ModuleInfo(key,
+ module.getArtifactCoords().getArtifactId(),
+ module.getProjectRoot().getPath(),
+ sourceDirectories,
+ QuarkusModelHelper.getClassPath(module).toAbsolutePath().toString(),
+ module.getSourceSourceSet().getResourceDirectory().toString(),
+ module.getSourceSet().getResourceDirectory().getPath(),
+ sourceParents,
+ module.getBuildDir().toPath().resolve("generated-sources").toAbsolutePath().toString(),
+ module.getBuildDir().toString());
+ }
+
private DevModeContext.ModuleInfo toModule(LocalProject project) {
return new DevModeContext.ModuleInfo(project.getKey(), project.getArtifactId(),
project.getDir().toAbsolutePath().toString(),
Collections.singleton(project.getSourcesSourcesDir().toAbsolutePath().toString()),
project.getClassesDir().toAbsolutePath().toString(),
project.getResourcesSourcesDir().toAbsolutePath().toString(),
- project.getSourcesDir().toString(), project.getCodeGenOutputDir().toString(),
+ project.getSourcesDir().toString(),
+ project.getCodeGenOutputDir().toString(),
project.getOutputDir().toString());
}
}
diff --git a/core/launcher/src/main/java/io/quarkus/launcher/QuarkusLauncher.java b/core/launcher/src/main/java/io/quarkus/launcher/QuarkusLauncher.java
index 53157f63633a9..d62c8154b75c8 100644
--- a/core/launcher/src/main/java/io/quarkus/launcher/QuarkusLauncher.java
+++ b/core/launcher/src/main/java/io/quarkus/launcher/QuarkusLauncher.java
@@ -11,6 +11,8 @@
import java.util.Map;
import java.util.function.Consumer;
+import io.quarkus.bootstrap.BootstrapConstants;
+
/**
* IDE entry point.
*
@@ -24,7 +26,6 @@
public class QuarkusLauncher {
public static void launch(String callingClass, String quarkusApplication, Consumer exitHandler, String... args) {
-
try {
String classResource = callingClass.replace(".", "/") + ".class";
URL resource = Thread.currentThread().getContextClassLoader().getResource(classResource);
@@ -43,11 +44,14 @@ public static void launch(String callingClass, String quarkusApplication, Consum
IDEClassLoader loader = new IDEClassLoader(QuarkusLauncher.class.getClassLoader());
Thread.currentThread().setContextClassLoader(loader);
+
Class> launcher = loader.loadClass("io.quarkus.bootstrap.IDELauncherImpl");
launcher.getDeclaredMethod("launch", Path.class, Map.class).invoke(null, appClasses, context);
} catch (Exception e) {
throw new RuntimeException(e);
+ } finally {
+ System.clearProperty(BootstrapConstants.SERIALIZED_APP_MODEL);
}
}
diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/AppModelGradleResolver.java b/devtools/gradle/src/main/java/io/quarkus/gradle/AppModelGradleResolver.java
index 2ca0c1dada2bd..501932d1fd348 100644
--- a/devtools/gradle/src/main/java/io/quarkus/gradle/AppModelGradleResolver.java
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/AppModelGradleResolver.java
@@ -1,66 +1,35 @@
package io.quarkus.gradle;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
-import java.util.Map;
-import java.util.Properties;
import java.util.Set;
-import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.ResolvedConfiguration;
-import org.gradle.api.artifacts.ResolvedDependency;
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.attributes.Category;
-import org.gradle.api.file.RegularFile;
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
import org.gradle.api.internal.artifacts.dependencies.DefaultDependencyArtifact;
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
-import org.gradle.api.plugins.Convention;
-import org.gradle.api.plugins.JavaPlugin;
-import org.gradle.api.plugins.JavaPluginConvention;
-import org.gradle.api.provider.Provider;
-import org.gradle.api.tasks.SourceSet;
-import org.gradle.jvm.tasks.Jar;
-import io.quarkus.bootstrap.BootstrapConstants;
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.model.AppArtifactKey;
import io.quarkus.bootstrap.model.AppDependency;
import io.quarkus.bootstrap.model.AppModel;
-import io.quarkus.bootstrap.model.PathsCollection;
import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.bootstrap.resolver.AppModelResolverException;
-import io.quarkus.gradle.tasks.QuarkusGradleUtils;
-import io.quarkus.runtime.LaunchMode;
+import io.quarkus.bootstrap.resolver.model.QuarkusModel;
+import io.quarkus.bootstrap.util.QuarkusModelHelper;
public class AppModelGradleResolver implements AppModelResolver {
private AppModel appModel;
-
private final Project project;
- private final LaunchMode launchMode;
+ private final QuarkusModel model;
- public AppModelGradleResolver(Project project, LaunchMode mode) {
+ public AppModelGradleResolver(Project project, QuarkusModel model) {
+ this.model = model;
this.project = project;
- this.launchMode = mode;
}
@Override
@@ -95,7 +64,6 @@ public void relink(AppArtifact appArtifact, Path localPath) throws AppModelResol
@Override
public Path resolve(AppArtifact appArtifact) throws AppModelResolverException {
if (!appArtifact.isResolved()) {
-
final DefaultDependencyArtifact dep = new DefaultDependencyArtifact();
dep.setExtension(appArtifact.getType());
dep.setType(appArtifact.getType());
@@ -137,224 +105,16 @@ public List resolveUserDependencies(AppArtifact appArtifact, List
@Override
public AppModel resolveModel(AppArtifact appArtifact) throws AppModelResolverException {
- AppModel.Builder appBuilder = new AppModel.Builder();
- if (appModel != null && appModel.getAppArtifact().equals(appArtifact)) {
- return appModel;
- }
- final List directExtensionDeps = new ArrayList<>();
-
- // collect enforced platforms
- final Configuration impl = project.getConfigurations().getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME);
- for (Dependency d : impl.getAllDependencies()) {
- if (!(d instanceof ModuleDependency)) {
- continue;
- }
- final ModuleDependency module = (ModuleDependency) d;
- final Category category = module.getAttributes().getAttribute(Category.CATEGORY_ATTRIBUTE);
- if (category != null && Category.ENFORCED_PLATFORM.equals(category.getName())) {
- directExtensionDeps.add(d);
- }
- }
-
- final List userDeps = new ArrayList<>();
- Map versionMap = new HashMap<>();
- Map userModules = new HashMap<>();
-
- final String classpathConfigName = launchMode == LaunchMode.TEST ? JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME
- : JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME;
-
- collectDependencies(project.getConfigurations().getByName(classpathConfigName),
- appBuilder, directExtensionDeps, userDeps,
- versionMap, userModules);
-
- if (launchMode == LaunchMode.DEVELOPMENT) {
- collectDependencies(project.getConfigurations().getByName(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME),
- appBuilder, directExtensionDeps, userDeps,
- versionMap, userModules);
- }
-
- final List deploymentDeps = new ArrayList<>();
- final List fullDeploymentDeps = new ArrayList<>(userDeps);
- if (!directExtensionDeps.isEmpty()) {
- final Configuration deploymentConfig = project.getConfigurations()
- .detachedConfiguration(directExtensionDeps.toArray(new Dependency[0]));
- final ResolvedConfiguration rc = deploymentConfig.getResolvedConfiguration();
- for (ResolvedArtifact a : rc.getResolvedArtifacts()) {
- final ModuleVersionIdentifier userVersion = userModules.get(getModuleId(a));
- if (userVersion != null || !isDependency(a)) {
- continue;
- }
- final AppDependency dependency = toAppDependency(a);
- fullDeploymentDeps.add(dependency);
- if (!userDeps.contains(dependency)) {
- AppDependency deploymentDep = alignVersion(dependency, versionMap);
- deploymentDeps.add(deploymentDep);
- }
- }
- }
-
- if (!appArtifact.isResolved()) {
- final Jar jarTask = (Jar) project.getTasks().findByName(JavaPlugin.JAR_TASK_NAME);
- if (jarTask == null) {
- throw new AppModelResolverException("Failed to locate task 'jar' in the project.");
- }
- if (jarTask.getDidWork()) {
- final Provider jarProvider = jarTask.getArchiveFile();
- Path classesDir = null;
- if (jarProvider.isPresent()) {
- final File f = jarProvider.get().getAsFile();
- if (f.exists()) {
- classesDir = f.toPath();
- }
- }
- if (classesDir == null) {
- throw new AppModelResolverException("Failed to locate classes directory for " + appArtifact);
- }
- appArtifact.setPaths(PathsCollection.of(classesDir));
+ if (appModel != null) {
+ if (appModel.getAppArtifact().equals(appArtifact)) {
+ return appModel;
} else {
- final Convention convention = project.getConvention();
- JavaPluginConvention javaConvention = convention.findPlugin(JavaPluginConvention.class);
- if (javaConvention != null) {
- final SourceSet mainSourceSet = javaConvention.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
- PathsCollection.Builder paths = PathsCollection.builder();
- mainSourceSet.getOutput().filter(s -> s.exists()).forEach(f -> {
- paths.add(f.toPath());
- });
- for (File resourcesDir : mainSourceSet.getResources().getSourceDirectories()) {
- if (resourcesDir.exists()) {
- paths.add(resourcesDir.toPath());
- }
- }
- appArtifact.setPaths(paths.build());
- }
+ throw new AppModelResolverException(
+ "Requested artifact : " + appArtifact + ", does not match loaded model " + appModel.getAppArtifact());
}
}
-
- appBuilder.addRuntimeDeps(userDeps)
- .addFullDeploymentDeps(fullDeploymentDeps)
- .addDeploymentDeps(deploymentDeps)
- .setAppArtifact(appArtifact);
- return this.appModel = appBuilder.build();
- }
-
- private void collectDependencies(Configuration config, AppModel.Builder appBuilder,
- final List directExtensionDeps,
- final List userDeps, Map versionMap,
- Map userModules) {
-
- final ResolvedConfiguration resolvedConfig = config.getResolvedConfiguration();
- for (ResolvedArtifact a : resolvedConfig.getResolvedArtifacts()) {
- if (!isDependency(a)) {
- continue;
- }
- userModules.put(getModuleId(a), a.getModuleVersion().getId());
-
- final AppDependency dependency = toAppDependency(a);
- final AppArtifactKey artifactGa = new AppArtifactKey(dependency.getArtifact().getGroupId(),
- dependency.getArtifact().getArtifactId());
-
- // If we are running in dev mode we prefer directories of classes and resources over the JARs
- // for local project dependencies
- if (LaunchMode.DEVELOPMENT.equals(launchMode)
- && (a.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier)) {
- final Project depProject = project.getRootProject()
- .findProject(((ProjectComponentIdentifier) a.getId().getComponentIdentifier()).getProjectPath());
- final JavaPluginConvention javaConvention = depProject.getConvention().findPlugin(JavaPluginConvention.class);
- if (javaConvention != null) {
- SourceSet mainSourceSet = javaConvention.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
- final PathsCollection.Builder paths = PathsCollection.builder();
- final Path classesDir = Paths
- .get(QuarkusGradleUtils.getClassesDir(mainSourceSet, depProject.getBuildDir(), false));
- if (Files.exists(classesDir)) {
- paths.add(classesDir);
- }
- for (File resourcesDir : mainSourceSet.getResources().getSourceDirectories()) {
- if (resourcesDir.exists()) {
- paths.add(resourcesDir.toPath());
- }
- }
- dependency.getArtifact().setPaths(paths.build());
- }
- }
-
- if (!dependency.getArtifact().isResolved()) {
- throw new IllegalStateException("Failed to resolve " + a.getId());
- }
-
- userDeps.add(dependency);
- versionMap.put(artifactGa, dependency);
- }
-
- collectExtensionDeps(resolvedConfig.getFirstLevelModuleDependencies(), versionMap, appBuilder, directExtensionDeps,
- true, new HashSet<>());
- }
-
- private void collectExtensionDeps(Set resolvedDeps,
- Map versionMap,
- AppModel.Builder appBuilder,
- List firstLevelExtensions,
- boolean firstLevelExt,
- Set visited) {
- for (ResolvedDependency dep : resolvedDeps) {
- final AppArtifactKey key = new AppArtifactKey(dep.getModuleGroup(), dep.getModuleName());
- if (!visited.add(key)) {
- continue;
- }
- final AppDependency appDep = versionMap.get(key);
- if (appDep == null) {
- // not a jar
- continue;
- }
-
- Dependency extDep = null;
- for (Path artifactPath : appDep.getArtifact().getPaths()) {
- if (!Files.exists(artifactPath)) {
- continue;
- }
- if (Files.isDirectory(artifactPath)) {
- extDep = processQuarkusDir(appDep.getArtifact(), artifactPath.resolve(BootstrapConstants.META_INF),
- appBuilder);
- } else {
- try (FileSystem artifactFs = FileSystems.newFileSystem(artifactPath, null)) {
- extDep = processQuarkusDir(appDep.getArtifact(), artifactFs.getPath(BootstrapConstants.META_INF),
- appBuilder);
- } catch (IOException e) {
- throw new GradleException("Failed to process " + artifactPath, e);
- }
- }
- if (extDep != null) {
- break;
- }
- }
-
- boolean addChildExtensions = firstLevelExt;
- if (extDep != null && firstLevelExt) {
- firstLevelExtensions.add(extDep);
- addChildExtensions = false;
- }
- final Set resolvedChildren = dep.getChildren();
- if (!resolvedChildren.isEmpty()) {
- collectExtensionDeps(resolvedChildren, versionMap, appBuilder, firstLevelExtensions, addChildExtensions,
- visited);
- }
- }
- }
-
- /**
- * A {@link ResolvedArtifact} is valid if it's a JAR or a directory
- */
- private static boolean isDependency(ResolvedArtifact a) {
- return BootstrapConstants.JAR.equalsIgnoreCase(a.getExtension()) || "exe".equalsIgnoreCase(a.getExtension()) ||
- a.getFile().isDirectory();
- }
-
- private AppDependency alignVersion(AppDependency dependency, Map versionMap) {
- AppArtifactKey appKey = new AppArtifactKey(dependency.getArtifact().getGroupId(),
- dependency.getArtifact().getArtifactId());
- if (versionMap.containsKey(appKey)) {
- return versionMap.get(appKey);
- }
- return dependency;
+ appModel = QuarkusModelHelper.convert(model, appArtifact);
+ return appModel;
}
@Override
@@ -369,56 +129,4 @@ public AppModel resolveManagedModel(AppArtifact appArtifact, List
return resolveModel(appArtifact);
}
- private static ModuleIdentifier getModuleId(ResolvedArtifact a) {
- final String[] split = a.getModuleVersion().toString().split(":");
- return DefaultModuleIdentifier.newId(split[0], split[1]);
- }
-
- static AppDependency toAppDependency(ResolvedArtifact a) {
- return new AppDependency(toAppArtifact(a), "runtime");
- }
-
- public static AppArtifact toAppArtifact(ResolvedArtifact a) {
- final String[] split = a.getModuleVersion().toString().split(":");
- final AppArtifact appArtifact = new AppArtifact(split[0], split[1], a.getClassifier(), a.getType(),
- split.length > 2 ? split[2] : null);
- if (a.getFile().exists()) {
- appArtifact.setPath(a.getFile().toPath());
- }
- return appArtifact;
- }
-
- private Dependency processQuarkusDir(AppArtifact a, Path quarkusDir, AppModel.Builder appBuilder) {
- if (!Files.exists(quarkusDir)) {
- return null;
- }
- final Path quarkusDescr = quarkusDir.resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME);
- if (!Files.exists(quarkusDescr)) {
- return null;
- }
- final Properties extProps = resolveDescriptor(quarkusDescr);
- if (extProps == null) {
- return null;
- }
- appBuilder.handleExtensionProperties(extProps, a.toString());
- String value = extProps.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT);
- final String[] split = value.split(":");
-
- return new DefaultExternalModuleDependency(split[0], split[1], split[2], null);
- }
-
- private Properties resolveDescriptor(final Path path) {
- final Properties rtProps;
- if (!Files.exists(path)) {
- // not a platform artifact
- return null;
- }
- rtProps = new Properties();
- try (BufferedReader reader = Files.newBufferedReader(path)) {
- rtProps.load(reader);
- } catch (IOException e) {
- throw new GradleException("Failed to load extension description " + path, e);
- }
- return rtProps;
- }
}
diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java
index beab9a593b18e..c4568f8c394df 100644
--- a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java
@@ -8,6 +8,8 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;
+import javax.inject.Inject;
+
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
@@ -26,8 +28,10 @@
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.api.tasks.testing.Test;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
import org.gradle.util.GradleVersion;
+import io.quarkus.gradle.builder.QuarkusModelBuilder;
import io.quarkus.gradle.tasks.QuarkusAddExtension;
import io.quarkus.gradle.tasks.QuarkusBuild;
import io.quarkus.gradle.tasks.QuarkusDev;
@@ -67,9 +71,17 @@ public class QuarkusPlugin implements Plugin {
public static final String NATIVE_TEST_IMPLEMENTATION_CONFIGURATION_NAME = "nativeTestImplementation";
public static final String NATIVE_TEST_RUNTIME_ONLY_CONFIGURATION_NAME = "nativeTestRuntimeOnly";
+ private final ToolingModelBuilderRegistry registry;
+
+ @Inject
+ public QuarkusPlugin(ToolingModelBuilderRegistry registry) {
+ this.registry = registry;
+ }
+
@Override
public void apply(Project project) {
verifyGradleVersion();
+ registerModel();
// register extension
final QuarkusPluginExtension quarkusExt = project.getExtensions().create(EXTENSION_NAME, QuarkusPluginExtension.class,
project);
@@ -185,6 +197,10 @@ private Set getSourcesParents(SourceSet mainSourceSet) {
.collect(Collectors.toSet());
}
+ private void registerModel() {
+ registry.register(new QuarkusModelBuilder());
+ }
+
private void verifyGradleVersion() {
if (GradleVersion.current().compareTo(GradleVersion.version("5.0")) < 0) {
throw new GradleException("Quarkus plugin requires Gradle 5.0 or later. Current version is: " +
diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPluginExtension.java b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPluginExtension.java
index 8b711ad3865a7..900a2bffe3395 100644
--- a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPluginExtension.java
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPluginExtension.java
@@ -21,6 +21,10 @@
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.bootstrap.resolver.AppModelResolver;
+import io.quarkus.bootstrap.resolver.model.ModelParameter;
+import io.quarkus.bootstrap.resolver.model.QuarkusModel;
+import io.quarkus.bootstrap.resolver.model.impl.ModelParameterImpl;
+import io.quarkus.gradle.builder.QuarkusModelBuilder;
import io.quarkus.gradle.tasks.QuarkusGradleUtils;
import io.quarkus.runtime.LaunchMode;
@@ -163,7 +167,22 @@ public AppModelResolver getAppModelResolver() {
}
public AppModelResolver getAppModelResolver(LaunchMode mode) {
- return new AppModelGradleResolver(project, mode);
+ return new AppModelGradleResolver(project, getQuarkusModel(mode));
+ }
+
+ public QuarkusModel getQuarkusModel() {
+ return getQuarkusModel(LaunchMode.NORMAL);
+ }
+
+ public QuarkusModel getQuarkusModel(LaunchMode mode) {
+ return create(project, mode);
+ }
+
+ private QuarkusModel create(Project project, LaunchMode mode) {
+ QuarkusModelBuilder builder = new QuarkusModelBuilder();
+ ModelParameter params = new ModelParameterImpl();
+ params.setMode(mode.toString());
+ return (QuarkusModel) builder.buildAll(QuarkusModel.class.getName(), params, project);
}
/**
@@ -179,4 +198,5 @@ private File getLastFile(FileCollection fileCollection) {
}
return result;
}
+
}
diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/builder/QuarkusModelBuilder.java b/devtools/gradle/src/main/java/io/quarkus/gradle/builder/QuarkusModelBuilder.java
new file mode 100644
index 0000000000000..60f202b688aec
--- /dev/null
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/builder/QuarkusModelBuilder.java
@@ -0,0 +1,299 @@
+package io.quarkus.gradle.builder;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ResolvedArtifact;
+import org.gradle.api.artifacts.ResolvedConfiguration;
+import org.gradle.api.artifacts.ResolvedDependency;
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+import org.gradle.api.attributes.Category;
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
+import org.gradle.api.plugins.Convention;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.tooling.provider.model.ParameterizedToolingModelBuilder;
+
+import io.quarkus.bootstrap.BootstrapConstants;
+import io.quarkus.bootstrap.resolver.model.ArtifactCoords;
+import io.quarkus.bootstrap.resolver.model.Dependency;
+import io.quarkus.bootstrap.resolver.model.ModelParameter;
+import io.quarkus.bootstrap.resolver.model.QuarkusModel;
+import io.quarkus.bootstrap.resolver.model.WorkspaceModule;
+import io.quarkus.bootstrap.resolver.model.impl.ArtifactCoordsImpl;
+import io.quarkus.bootstrap.resolver.model.impl.DependencyImpl;
+import io.quarkus.bootstrap.resolver.model.impl.ModelParameterImpl;
+import io.quarkus.bootstrap.resolver.model.impl.QuarkusModelImpl;
+import io.quarkus.bootstrap.resolver.model.impl.SourceSetImpl;
+import io.quarkus.bootstrap.resolver.model.impl.WorkspaceImpl;
+import io.quarkus.bootstrap.resolver.model.impl.WorkspaceModuleImpl;
+import io.quarkus.bootstrap.util.QuarkusModelHelper;
+import io.quarkus.gradle.tasks.QuarkusGradleUtils;
+import io.quarkus.runtime.LaunchMode;
+
+public class QuarkusModelBuilder implements ParameterizedToolingModelBuilder {
+
+ private static final List scannedConfigurations = new LinkedList();
+
+ @Override
+ public boolean canBuild(String modelName) {
+ return modelName.equals(QuarkusModel.class.getName());
+ }
+
+ @Override
+ public Class getParameterType() {
+ return ModelParameter.class;
+ }
+
+ @Override
+ public Object buildAll(String modelName, Project project) {
+ final ModelParameterImpl modelParameter = new ModelParameterImpl();
+ modelParameter.setMode(LaunchMode.DEVELOPMENT.toString());
+ return buildAll(modelName, modelParameter, project);
+ }
+
+ @Override
+ public Object buildAll(String modelName, Object parameter, Project project) {
+ LaunchMode mode = LaunchMode.valueOf(((ModelParameter) parameter).getMode());
+
+ if (LaunchMode.TEST.equals(mode)) {
+ scannedConfigurations.add(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
+ } else {
+ scannedConfigurations.add(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
+ }
+
+ if (LaunchMode.DEVELOPMENT.equals(mode)) {
+ scannedConfigurations.add(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME);
+ }
+
+ final Collection directExtensionDependencies = getEnforcedPlatforms(project);
+
+ final Map appDependencies = new HashMap<>();
+ for (String configurationName : scannedConfigurations) {
+ final ResolvedConfiguration configuration = project.getConfigurations().getByName(configurationName)
+ .getResolvedConfiguration();
+ appDependencies.putAll(collectDependencies(configuration, configurationName, mode, project));
+ directExtensionDependencies
+ .addAll(getDirectExtensionDependencies(configuration.getFirstLevelModuleDependencies(), appDependencies,
+ new HashSet<>()));
+ }
+
+ final Set extensionDependencies = collectExtensionDependencies(project, directExtensionDependencies);
+
+ ArtifactCoords appArtifactCoords = new ArtifactCoordsImpl(project.getGroup().toString(), project.getName(),
+ project.getVersion().toString());
+
+ return new QuarkusModelImpl(new WorkspaceImpl(appArtifactCoords, getWorkspace(project.getRootProject())),
+ new HashSet<>(appDependencies.values()),
+ extensionDependencies);
+ }
+
+ public Set getWorkspace(Project project) {
+ Set modules = new HashSet<>();
+ for (Project subproject : project.getAllprojects()) {
+ final Convention convention = subproject.getConvention();
+ JavaPluginConvention javaConvention = convention.findPlugin(JavaPluginConvention.class);
+ if (javaConvention == null) {
+ continue;
+ }
+ modules.add(getWorkspaceModule(subproject));
+ }
+ return modules;
+ }
+
+ private WorkspaceModule getWorkspaceModule(Project project) {
+ ArtifactCoords appArtifactCoords = new ArtifactCoordsImpl(project.getGroup().toString(), project.getName(),
+ project.getVersion().toString());
+ final SourceSet mainSourceSet = QuarkusGradleUtils.getSourceSet(project, SourceSet.MAIN_SOURCE_SET_NAME);
+
+ return new WorkspaceModuleImpl(appArtifactCoords, project.getProjectDir().getAbsoluteFile(),
+ project.getBuildDir().getAbsoluteFile(), getSourceSourceSet(mainSourceSet), convert(mainSourceSet));
+ }
+
+ private Set getEnforcedPlatforms(Project project) {
+ final Set directExtension = new HashSet<>();
+ // collect enforced platforms
+ final Configuration impl = project.getConfigurations()
+ .getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME);
+ for (org.gradle.api.artifacts.Dependency d : impl.getAllDependencies()) {
+ if (!(d instanceof ModuleDependency)) {
+ continue;
+ }
+ final ModuleDependency module = (ModuleDependency) d;
+ final Category category = module.getAttributes().getAttribute(Category.CATEGORY_ATTRIBUTE);
+ if (category != null && Category.ENFORCED_PLATFORM.equals(category.getName())) {
+ directExtension.add(d);
+ }
+ }
+ return directExtension;
+ }
+
+ private Set getDirectExtensionDependencies(Set dependencies,
+ Map appDependencies, Set visited) {
+ Set extensions = new HashSet<>();
+ for (ResolvedDependency d : dependencies) {
+ ArtifactCoords key = new ArtifactCoordsImpl(d.getModuleGroup(), d.getModuleName(), "");
+ if (!visited.add(key)) {
+ continue;
+ }
+
+ Dependency appDep = appDependencies.get(key);
+ if (appDep == null) {
+ continue;
+ }
+ final org.gradle.api.artifacts.Dependency deploymentArtifact = getDeploymentArtifact(appDep);
+
+ boolean addChildExtension = true;
+ if (deploymentArtifact != null && addChildExtension) {
+ extensions.add(deploymentArtifact);
+ addChildExtension = false;
+ }
+
+ final Set resolvedChildren = d.getChildren();
+ if (addChildExtension && !resolvedChildren.isEmpty()) {
+ extensions
+ .addAll(getDirectExtensionDependencies(resolvedChildren, appDependencies, visited));
+ }
+ }
+ return extensions;
+ }
+
+ private org.gradle.api.artifacts.Dependency getDeploymentArtifact(Dependency dependency) {
+ for (File file : dependency.getPaths()) {
+ if (!file.exists()) {
+ continue;
+ }
+ Properties depsProperties;
+ if (file.isDirectory()) {
+ Path quarkusDescr = file.toPath()
+ .resolve(BootstrapConstants.META_INF)
+ .resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME);
+ if (!Files.exists(quarkusDescr)) {
+ continue;
+ }
+ depsProperties = QuarkusModelHelper.resolveDescriptor(quarkusDescr);
+ } else {
+ try (FileSystem artifactFs = FileSystems.newFileSystem(file.toPath(), getClass().getClassLoader())) {
+ Path quarkusDescr = artifactFs.getPath(BootstrapConstants.META_INF)
+ .resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME);
+ if (!Files.exists(quarkusDescr)) {
+ continue;
+ }
+ depsProperties = QuarkusModelHelper.resolveDescriptor(quarkusDescr);
+ } catch (IOException e) {
+ throw new GradleException("Failed to process " + file, e);
+ }
+ }
+ String value = depsProperties.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT);
+ String[] split = value.split(":");
+ return new DefaultExternalModuleDependency(split[0], split[1], split[2], null);
+ }
+ return null;
+ }
+
+ private Set collectExtensionDependencies(Project project,
+ Collection extensions) {
+ final Set platformDependencies = new HashSet<>();
+
+ final Configuration deploymentConfig = project.getConfigurations()
+ .detachedConfiguration(extensions.toArray(new org.gradle.api.artifacts.Dependency[0]));
+ final ResolvedConfiguration rc = deploymentConfig.getResolvedConfiguration();
+ for (ResolvedArtifact a : rc.getResolvedArtifacts()) {
+ if (!isDependency(a)) {
+ continue;
+ }
+
+ final Dependency dependency = toDependency(a, deploymentConfig.getName());
+ platformDependencies.add(dependency);
+ }
+
+ return platformDependencies;
+ }
+
+ private Map collectDependencies(ResolvedConfiguration configuration, String configurationName,
+ LaunchMode mode, Project project) {
+ Map modelDependencies = new HashMap<>();
+ for (ResolvedArtifact a : configuration.getResolvedArtifacts()) {
+ if (!isDependency(a)) {
+ continue;
+ }
+ Dependency dep;
+ if (LaunchMode.DEVELOPMENT.equals(mode) &&
+ a.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier) {
+ Project projectDep = project.getRootProject()
+ .findProject(((ProjectComponentIdentifier) a.getId().getComponentIdentifier()).getProjectPath());
+
+ dep = toDependency(a, configurationName, projectDep);
+ } else {
+ dep = toDependency(a, configurationName);
+ }
+ ArtifactCoords gaKey = new ArtifactCoordsImpl(dep.getGroupId(), dep.getName(), "");
+ modelDependencies.put(gaKey, dep);
+ }
+
+ return modelDependencies;
+ }
+
+ private SourceSetImpl convert(SourceSet sourceSet) {
+ return new SourceSetImpl(
+ sourceSet.getOutput().getClassesDirs().getFiles(),
+ sourceSet.getOutput().getResourcesDir());
+ }
+
+ private io.quarkus.bootstrap.resolver.model.SourceSet getSourceSourceSet(SourceSet sourceSet) {
+ return new SourceSetImpl(sourceSet.getAllJava().getSrcDirs(),
+ sourceSet.getResources().getSourceDirectories().getSingleFile());
+ }
+
+ private static boolean isDependency(ResolvedArtifact a) {
+ return BootstrapConstants.JAR.equalsIgnoreCase(a.getExtension()) || "exe".equalsIgnoreCase(a.getExtension()) ||
+ a.getFile().isDirectory();
+ }
+
+ private DependencyImpl toDependency(ResolvedArtifact a, String configuration, Project project) {
+ final String[] split = a.getModuleVersion().toString().split(":");
+ final DependencyImpl dependency = new DependencyImpl(split[1], split[0], split.length > 2 ? split[2] : null,
+ configuration, a.getType(), a.getClassifier());
+ final JavaPluginConvention javaConvention = project.getConvention().findPlugin(JavaPluginConvention.class);
+ if (javaConvention != null) {
+ SourceSet mainSourceSet = javaConvention.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
+ final File classesDir = new File(QuarkusGradleUtils.getClassesDir(mainSourceSet, project.getBuildDir(), false));
+ if (classesDir.exists()) {
+ dependency.addPath(classesDir);
+ }
+ for (File resourcesDir : mainSourceSet.getResources().getSourceDirectories()) {
+ if (resourcesDir.exists()) {
+ dependency.addPath(resourcesDir);
+ }
+ }
+ }
+ return dependency;
+ }
+
+ static DependencyImpl toDependency(ResolvedArtifact a, String configuration) {
+ final String[] split = a.getModuleVersion().toString().split(":");
+
+ final DependencyImpl dependency = new DependencyImpl(split[1], split[0], split.length > 2 ? split[2] : null,
+ configuration, a.getType(), a.getClassifier());
+ dependency.addPath(a.getFile());
+ return dependency;
+ }
+
+}
diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java
index 2e3d91aa40d97..a878eb3d76519 100644
--- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java
@@ -8,10 +8,8 @@
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
-import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URI;
-import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -463,6 +461,7 @@ private void addLocalProject(Project project, DevModeContext context, Set paths = new ArrayList<>();
generatedSources.getOutput()
.filter(f -> f.getName().equals(generateSourcesDir))
@@ -75,22 +74,23 @@ public void prepareQuarkus() {
getLogger().debug("Will trigger preparing sources for source directory: {} buildDir: {}",
sourcesDirectories, getProject().getBuildDir().getAbsolutePath());
- QuarkusClassLoader deploymentClassLoader = appCreationContext.createDeploymentClassLoader();
+ QuarkusClassLoader deploymentClassLoader = appCreationContext.createDeploymentClassLoader();
Class> codeGenerator = deploymentClassLoader.loadClass(CodeGenerator.class.getName());
+
Optional initAndRun = Arrays.stream(codeGenerator.getMethods())
.filter(m -> m.getName().equals(INIT_AND_RUN))
.findAny();
if (!initAndRun.isPresent()) {
throw new GradleException("Failed to find " + INIT_AND_RUN + " method in " + CodeGenerator.class.getName());
}
-
initAndRun.get().invoke(null, deploymentClassLoader,
sourcesDirectories,
paths.iterator().next(),
buildDir,
sourceRegistrar,
appCreationContext.getAppModel());
+
}
} catch (BootstrapException | IllegalAccessException | InvocationTargetException | ClassNotFoundException e) {
throw new GradleException("Failed to generate sources in the QuarkusPrepare task", e);
diff --git a/devtools/gradle/src/test/java/io/quarkus/gradle/AppModelGradleResolverTest.java b/devtools/gradle/src/test/java/io/quarkus/gradle/AppModelGradleResolverTest.java
deleted file mode 100644
index a426fd90396c4..0000000000000
--- a/devtools/gradle/src/test/java/io/quarkus/gradle/AppModelGradleResolverTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package io.quarkus.gradle;
-
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import org.assertj.core.util.Files;
-import org.gradle.api.artifacts.ResolvedArtifact;
-import org.gradle.api.artifacts.ResolvedModuleVersion;
-import org.junit.jupiter.api.Test;
-
-class AppModelGradleResolverTest {
-
- @Test
- void testToAppDependency() {
- ResolvedArtifact artifact = mock(ResolvedArtifact.class);
- ResolvedModuleVersion version = mock(ResolvedModuleVersion.class);
- when(version.toString()).thenReturn(":commons-lang3-3.9:");
- when(artifact.getModuleVersion()).thenReturn(version);
- when(artifact.getFile()).thenReturn(Files.currentFolder());
- assertThatCode(() -> AppModelGradleResolver.toAppDependency(artifact)).doesNotThrowAnyException();
- }
-}
diff --git a/devtools/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java b/devtools/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java
new file mode 100644
index 0000000000000..df9bf9b9f3d6b
--- /dev/null
+++ b/devtools/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java
@@ -0,0 +1,112 @@
+package io.quarkus.gradle.builder;
+
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Properties;
+
+import org.assertj.core.util.Files;
+import org.gradle.api.artifacts.ResolvedArtifact;
+import org.gradle.api.artifacts.ResolvedModuleVersion;
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.bootstrap.resolver.QuarkusGradleModelFactory;
+import io.quarkus.bootstrap.resolver.model.QuarkusModel;
+import io.quarkus.bootstrap.resolver.model.SourceSet;
+import io.quarkus.bootstrap.resolver.model.Workspace;
+import io.quarkus.bootstrap.resolver.model.WorkspaceModule;
+
+class QuarkusModelBuilderTest {
+
+ @Test
+ void testToAppDependency() {
+ ResolvedArtifact artifact = mock(ResolvedArtifact.class);
+ ResolvedModuleVersion version = mock(ResolvedModuleVersion.class);
+ when(version.toString()).thenReturn(":commons-lang3-3.9:");
+ when(artifact.getModuleVersion()).thenReturn(version);
+ when(artifact.getFile()).thenReturn(Files.currentFolder());
+ assertThatCode(() -> QuarkusModelBuilder.toDependency(artifact, "implementation")).doesNotThrowAnyException();
+ }
+
+ @Test
+ public void shouldLoadSimpleModuleModel() throws URISyntaxException, IOException {
+ File projectDir = getResourcesProject("simple-module-project");
+ final QuarkusModel quarkusModel = QuarkusGradleModelFactory.create(projectDir, "TEST");
+
+ assertNotNull(quarkusModel);
+ Workspace workspace = quarkusModel.getWorkspace();
+ assertWorkspace(workspace.getMainModule(), projectDir);
+ assertEquals(1, quarkusModel.getWorkspace().getAllModules().size());
+ }
+
+ @Test
+ public void shouldLoadMultiModuleModel() throws URISyntaxException, IOException {
+ File projectDir = getResourcesProject("multi-module-project");
+ final QuarkusModel quarkusModel = QuarkusGradleModelFactory.create(new File(projectDir, "application"), "TEST");
+
+ assertNotNull(quarkusModel);
+ assertEquals(2, quarkusModel.getWorkspace().getAllModules().size());
+
+ for (WorkspaceModule module : quarkusModel.getWorkspace().getAllModules()) {
+ assertWorkspace(module, new File(projectDir, module.getArtifactCoords().getArtifactId()));
+ }
+ }
+
+ private void assertWorkspace(WorkspaceModule workspaceModule, File projectDir) {
+ assertNotNull(workspaceModule);
+ assertEquals(projectDir, workspaceModule.getProjectRoot());
+ assertEquals(new File(projectDir, "build"), workspaceModule.getBuildDir());
+ final SourceSet sourceSet = workspaceModule.getSourceSet();
+ assertNotNull(sourceSet);
+ assertEquals(new File(projectDir, "build/resources/main"), sourceSet.getResourceDirectory());
+ assertEquals(1, sourceSet.getSourceDirectories().size());
+ assertEquals(new File(projectDir, "build/classes/java/main"), sourceSet.getSourceDirectories().iterator().next());
+ final SourceSet sourceSourceSet = workspaceModule.getSourceSourceSet();
+ assertEquals(new File(projectDir, "src/main/resources"), sourceSourceSet.getResourceDirectory());
+ assertEquals(1, sourceSourceSet.getSourceDirectories().size());
+ assertEquals(new File(projectDir, "src/main/java"), sourceSourceSet.getSourceDirectories().iterator().next());
+ }
+
+ private File getResourcesProject(String projectName) throws URISyntaxException, IOException {
+ final URL basedirUrl = Thread.currentThread().getContextClassLoader().getResource(projectName);
+ assertNotNull(basedirUrl);
+
+ final File projectDir = new File(basedirUrl.toURI());
+
+ final File projectProps = new File(projectDir, "gradle.properties");
+ final Properties props = new Properties();
+ final String quarkusVersion = getQuarkusVersion();
+ props.setProperty("quarkusPlatformVersion", quarkusVersion);
+ props.setProperty("quarkusPluginVersion", quarkusVersion);
+ try (OutputStream os = new FileOutputStream(projectProps)) {
+ props.store(os, "Quarkus Gradle TS");
+ }
+ return projectDir;
+ }
+
+ protected String getQuarkusVersion() throws IOException {
+ final Path curDir = Paths.get("").toAbsolutePath().normalize();
+ final Path gradlePropsFile = curDir.resolve("gradle.properties");
+ Properties props = new Properties();
+ try (InputStream is = java.nio.file.Files.newInputStream(gradlePropsFile)) {
+ props.load(is);
+ }
+ final String quarkusVersion = props.getProperty("version");
+ if (quarkusVersion == null) {
+ throw new IllegalStateException("Failed to locate Quarkus version in " + gradlePropsFile);
+ }
+ return quarkusVersion;
+ }
+
+}
diff --git a/devtools/gradle/src/test/resources/multi-module-project/application/build.gradle b/devtools/gradle/src/test/resources/multi-module-project/application/build.gradle
new file mode 100644
index 0000000000000..9a9126404f827
--- /dev/null
+++ b/devtools/gradle/src/test/resources/multi-module-project/application/build.gradle
@@ -0,0 +1,7 @@
+plugins {
+ id 'io.quarkus'
+}
+
+dependencies {
+ implementation project(":common")
+}
\ No newline at end of file
diff --git a/devtools/gradle/src/test/resources/multi-module-project/build.gradle b/devtools/gradle/src/test/resources/multi-module-project/build.gradle
new file mode 100644
index 0000000000000..c154c33071814
--- /dev/null
+++ b/devtools/gradle/src/test/resources/multi-module-project/build.gradle
@@ -0,0 +1,19 @@
+
+subprojects {
+ apply plugin: 'java'
+
+ repositories {
+ mavenLocal()
+ mavenCentral()
+ }
+
+ dependencies {
+ implementation enforcedPlatform("io.quarkus:quarkus-bom:999-SNAPSHOT")
+ implementation 'io.quarkus:quarkus-resteasy'
+ testImplementation 'io.quarkus:quarkus-junit5'
+ }
+
+}
+
+group 'org.acme'
+version '1.0-SNAPSHOT'
diff --git a/devtools/gradle/src/test/resources/multi-module-project/common/build.gradle b/devtools/gradle/src/test/resources/multi-module-project/common/build.gradle
new file mode 100644
index 0000000000000..2bb603cfb457a
--- /dev/null
+++ b/devtools/gradle/src/test/resources/multi-module-project/common/build.gradle
@@ -0,0 +1,8 @@
+plugins {
+ id 'io.quarkus'
+ id 'java-library'
+}
+
+dependencies {
+
+}
\ No newline at end of file
diff --git a/devtools/gradle/src/test/resources/multi-module-project/settings.gradle b/devtools/gradle/src/test/resources/multi-module-project/settings.gradle
new file mode 100644
index 0000000000000..60c19d80c56b0
--- /dev/null
+++ b/devtools/gradle/src/test/resources/multi-module-project/settings.gradle
@@ -0,0 +1,13 @@
+pluginManagement {
+ repositories {
+ mavenLocal()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+ plugins {
+ id 'io.quarkus' version "${quarkusPluginVersion}"
+ }
+}
+rootProject.name='mutli-module-project'
+
+include 'common', 'application'
\ No newline at end of file
diff --git a/devtools/gradle/src/test/resources/simple-module-project/build.gradle b/devtools/gradle/src/test/resources/simple-module-project/build.gradle
new file mode 100644
index 0000000000000..3aed0a675a9b6
--- /dev/null
+++ b/devtools/gradle/src/test/resources/simple-module-project/build.gradle
@@ -0,0 +1,22 @@
+plugins {
+ id 'java'
+ id 'io.quarkus'
+}
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+}
+
+dependencies {
+ implementation enforcedPlatform("io.quarkus:quarkus-bom:999-SNAPSHOT")
+ implementation 'io.quarkus:quarkus-resteasy'
+}
+
+group 'org.acme'
+version '1.0-SNAPSHOT'
+
+
+compileTestJava {
+ options.encoding = 'UTF-8'
+}
diff --git a/devtools/gradle/src/test/resources/simple-module-project/settings.gradle b/devtools/gradle/src/test/resources/simple-module-project/settings.gradle
new file mode 100644
index 0000000000000..775892b4374e6
--- /dev/null
+++ b/devtools/gradle/src/test/resources/simple-module-project/settings.gradle
@@ -0,0 +1,11 @@
+pluginManagement {
+ repositories {
+ mavenLocal()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+ plugins {
+ id 'io.quarkus' version "${quarkusPluginVersion}"
+ }
+}
+rootProject.name='simple-module-project'
\ No newline at end of file
diff --git a/independent-projects/bootstrap/app-model/pom.xml b/independent-projects/bootstrap/app-model/pom.xml
index 0dc319ac188ea..425c62d6b7565 100644
--- a/independent-projects/bootstrap/app-model/pom.xml
+++ b/independent-projects/bootstrap/app-model/pom.xml
@@ -24,5 +24,4 @@
runtime
-
diff --git a/independent-projects/bootstrap/core/pom.xml b/independent-projects/bootstrap/core/pom.xml
index 90c07519d839f..9c6639a993f6a 100644
--- a/independent-projects/bootstrap/core/pom.xml
+++ b/independent-projects/bootstrap/core/pom.xml
@@ -22,10 +22,14 @@
io.quarkus
quarkus-bootstrap-maven-resolver
+
+ io.quarkus
+ quarkus-bootstrap-gradle-resolver
+
io.smallrye.common
smallrye-common-io
-
+
org.junit.jupiter
junit-jupiter
diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java
index c55cd008b51fa..5dcd7803c8237 100644
--- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java
+++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/IDELauncherImpl.java
@@ -1,7 +1,12 @@
package io.quarkus.bootstrap;
+import io.quarkus.bootstrap.app.AdditionalDependency;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
+import io.quarkus.bootstrap.resolver.model.QuarkusModel;
+import io.quarkus.bootstrap.resolver.model.WorkspaceModule;
+import io.quarkus.bootstrap.util.QuarkusModelHelper;
+import io.quarkus.bootstrap.utils.BuildToolHelper;
import java.nio.file.Path;
import java.util.Map;
@@ -19,12 +24,36 @@ public static void launch(Path projectRoot, Map context) {
try {
//todo : proper support for everything
- CuratedApplication app = QuarkusBootstrap.builder(projectRoot)
+ final QuarkusBootstrap.Builder builder = QuarkusBootstrap.builder()
+ .setApplicationRoot(projectRoot)
.setBaseClassLoader(IDELauncherImpl.class.getClassLoader())
.setProjectRoot(projectRoot)
.setIsolateDeployment(true)
- .setMode(QuarkusBootstrap.Mode.DEV)
+ .setMode(QuarkusBootstrap.Mode.DEV);
+
+ if (!BuildToolHelper.isMavenProject(projectRoot)) {
+ final QuarkusModel quarkusModel = BuildToolHelper.enableGradleAppModelForDevMode(projectRoot);
+ // Gradle uses a different output directory for classes, we override the one used by the IDE
+ final WorkspaceModule launchingModule = quarkusModel.getWorkspace().getMainModule();
+ Path launchingModulePath = QuarkusModelHelper.getClassPath(launchingModule);
+
+ builder.setProjectRoot(launchingModulePath)
+ .setApplicationRoot(launchingModulePath);
+
+ for (WorkspaceModule additionalModule : quarkusModel.getWorkspace().getAllModules()) {
+ if (!additionalModule.getArtifactCoords().equals(launchingModule.getArtifactCoords())) {
+ builder.addAdditionalApplicationArchive(new AdditionalDependency(
+ QuarkusModelHelper.toPathsCollection(additionalModule.getSourceSet().getSourceDirectories()),
+ true, false));
+ builder.addAdditionalApplicationArchive(new AdditionalDependency(
+ additionalModule.getSourceSet().getResourceDirectory().toPath(), true, false));
+ }
+ }
+ }
+
+ CuratedApplication app = builder
.build().bootstrap();
+
app.runInAugmentClassLoader("io.quarkus.deployment.dev.IDEDevModeMain", context);
} catch (Exception e) {
throw new RuntimeException(e);
diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/utils/BuildToolHelper.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/utils/BuildToolHelper.java
new file mode 100644
index 0000000000000..babc69bd05341
--- /dev/null
+++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/utils/BuildToolHelper.java
@@ -0,0 +1,83 @@
+package io.quarkus.bootstrap.utils;
+
+import static io.quarkus.bootstrap.util.QuarkusModelHelper.DEVMODE_REQUIRED_TASKS;
+
+import io.quarkus.bootstrap.resolver.AppModelResolverException;
+import io.quarkus.bootstrap.resolver.QuarkusGradleModelFactory;
+import io.quarkus.bootstrap.resolver.model.QuarkusModel;
+import io.quarkus.bootstrap.util.QuarkusModelHelper;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * Helper class used to expose build tool used by the project
+ */
+public class BuildToolHelper {
+
+ public enum BuildTool {
+ MAVEN("pom.xml"),
+ GRADLE("build.gradle");
+
+ private final String buildFile;
+
+ BuildTool(String buildFile) {
+ this.buildFile = buildFile;
+ }
+
+ public String getBuildFile() {
+ return buildFile;
+ }
+ }
+
+ private BuildToolHelper() {
+
+ }
+
+ public static boolean isMavenProject(Path project) {
+ Path currentPath = project;
+ while (currentPath != null) {
+ if (Files.exists(currentPath.resolve(BuildTool.MAVEN.getBuildFile()))) {
+ return true;
+ }
+ if (Files.exists(currentPath.resolve(BuildTool.GRADLE.getBuildFile()))) {
+ return false;
+ }
+ currentPath = currentPath.getParent();
+ }
+ return false;
+ }
+
+ public static Path getBuildFile(Path project, BuildTool tool) {
+ Path currentPath = project;
+ while (currentPath != null) {
+ if (Files.exists(currentPath.resolve(tool.getBuildFile()))) {
+ return currentPath;
+ }
+ currentPath = currentPath.getParent();
+ }
+ return null;
+ }
+
+ public static QuarkusModel enableGradleAppModel(Path projectRoot, String mode)
+ throws IOException, AppModelResolverException {
+ if (isMavenProject(projectRoot)) {
+ return null;
+ }
+ final QuarkusModel model = QuarkusGradleModelFactory.create(getBuildFile(projectRoot, BuildTool.GRADLE).toFile(),
+ mode);
+ QuarkusModelHelper.exportModel(model);
+ return model;
+ }
+
+ public static QuarkusModel enableGradleAppModelForDevMode(Path projectRoot) throws IOException, AppModelResolverException {
+ if (isMavenProject(projectRoot)) {
+ return null;
+ }
+ final QuarkusModel model = QuarkusGradleModelFactory
+ .createForTasks(getBuildFile(projectRoot, BuildTool.GRADLE).toFile(), DEVMODE_REQUIRED_TASKS);
+ QuarkusModelHelper.exportModel(model);
+ return model;
+ }
+
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/pom.xml b/independent-projects/bootstrap/gradle-resolver/pom.xml
new file mode 100644
index 0000000000000..288d1ffe8bad0
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/pom.xml
@@ -0,0 +1,35 @@
+
+
+
+ quarkus-bootstrap-parent
+ io.quarkus
+ 999-SNAPSHOT
+ ../pom.xml
+
+ 4.0.0
+
+ quarkus-bootstrap-gradle-resolver
+ Quarkus - Bootstrap - Gradle Resolver
+
+
+
+ io.quarkus
+ quarkus-bootstrap-app-model
+
+
+ org.gradle
+ gradle-tooling-api
+
+
+ org.jboss.slf4j
+ slf4j-jboss-logging
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
\ No newline at end of file
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/BootstrapGradleException.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/BootstrapGradleException.java
new file mode 100644
index 0000000000000..967f445ad8248
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/BootstrapGradleException.java
@@ -0,0 +1,14 @@
+package io.quarkus.bootstrap;
+
+import io.quarkus.bootstrap.resolver.AppModelResolverException;
+
+public class BootstrapGradleException extends AppModelResolverException {
+
+ public BootstrapGradleException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public BootstrapGradleException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusGradleModelFactory.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusGradleModelFactory.java
new file mode 100644
index 0000000000000..8f025f98f159f
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusGradleModelFactory.java
@@ -0,0 +1,29 @@
+package io.quarkus.bootstrap.resolver;
+
+import io.quarkus.bootstrap.resolver.model.QuarkusModel;
+import java.io.File;
+import org.gradle.tooling.GradleConnector;
+import org.gradle.tooling.ModelBuilder;
+import org.gradle.tooling.ProjectConnection;
+
+public class QuarkusGradleModelFactory {
+
+ public static QuarkusModel create(File projectDir, String mode) {
+ try (ProjectConnection connection = GradleConnector.newConnector()
+ .forProjectDirectory(projectDir)
+ .connect()) {
+ return connection.action(new QuarkusModelBuildAction(mode)).run();
+ }
+ }
+
+ public static QuarkusModel createForTasks(File projectDir, String... tasks) {
+ try (ProjectConnection connection = GradleConnector.newConnector()
+ .forProjectDirectory(projectDir)
+ .connect()) {
+ final ModelBuilder modelBuilder = connection.model(QuarkusModel.class);
+ modelBuilder.forTasks(tasks);
+ return modelBuilder.get();
+ }
+ }
+
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusModelBuildAction.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusModelBuildAction.java
new file mode 100644
index 0000000000000..b021b1b1c5230
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusModelBuildAction.java
@@ -0,0 +1,20 @@
+package io.quarkus.bootstrap.resolver;
+
+import io.quarkus.bootstrap.resolver.model.ModelParameter;
+import io.quarkus.bootstrap.resolver.model.QuarkusModel;
+import java.io.Serializable;
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.BuildController;
+
+public class QuarkusModelBuildAction implements BuildAction, Serializable {
+ private final String mode;
+
+ public QuarkusModelBuildAction(String mode) {
+ this.mode = mode;
+ }
+
+ @Override
+ public QuarkusModel execute(BuildController controller) {
+ return controller.getModel(QuarkusModel.class, ModelParameter.class, p -> p.setMode(mode));
+ }
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ArtifactCoords.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ArtifactCoords.java
new file mode 100644
index 0000000000000..2435025b8e94f
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ArtifactCoords.java
@@ -0,0 +1,14 @@
+package io.quarkus.bootstrap.resolver.model;
+
+public interface ArtifactCoords {
+
+ String getGroupId();
+
+ String getArtifactId();
+
+ String getClassifier();
+
+ String getVersion();
+
+ String getType();
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Dependency.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Dependency.java
new file mode 100644
index 0000000000000..ac58deec4eefa
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Dependency.java
@@ -0,0 +1,22 @@
+package io.quarkus.bootstrap.resolver.model;
+
+import java.io.File;
+import java.util.Set;
+
+public interface Dependency {
+
+ String getName();
+
+ String getGroupId();
+
+ String getVersion();
+
+ String getClassifier();
+
+ Set getPaths();
+
+ String getType();
+
+ String getScope();
+
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ModelParameter.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ModelParameter.java
new file mode 100644
index 0000000000000..8d6dc75e8284d
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/ModelParameter.java
@@ -0,0 +1,8 @@
+package io.quarkus.bootstrap.resolver.model;
+
+public interface ModelParameter {
+
+ String getMode();
+
+ void setMode(String mode);
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/QuarkusModel.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/QuarkusModel.java
new file mode 100644
index 0000000000000..735a74cec8a2e
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/QuarkusModel.java
@@ -0,0 +1,13 @@
+package io.quarkus.bootstrap.resolver.model;
+
+import java.util.Set;
+
+public interface QuarkusModel {
+
+ Workspace getWorkspace();
+
+ Set getAppDependencies();
+
+ Set getExtensionDependencies();
+
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/SourceSet.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/SourceSet.java
new file mode 100644
index 0000000000000..c90d35a06f23e
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/SourceSet.java
@@ -0,0 +1,11 @@
+package io.quarkus.bootstrap.resolver.model;
+
+import java.io.File;
+import java.util.Set;
+
+public interface SourceSet {
+
+ Set getSourceDirectories();
+
+ File getResourceDirectory();
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Workspace.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Workspace.java
new file mode 100644
index 0000000000000..89888c31aadb4
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/Workspace.java
@@ -0,0 +1,13 @@
+package io.quarkus.bootstrap.resolver.model;
+
+import java.util.Collection;
+
+public interface Workspace {
+
+ WorkspaceModule getMainModule();
+
+ Collection getAllModules();
+
+ WorkspaceModule getModule(ArtifactCoords key);
+
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/WorkspaceModule.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/WorkspaceModule.java
new file mode 100644
index 0000000000000..4b27f8c24d6e6
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/WorkspaceModule.java
@@ -0,0 +1,16 @@
+package io.quarkus.bootstrap.resolver.model;
+
+import java.io.File;
+
+public interface WorkspaceModule {
+
+ ArtifactCoords getArtifactCoords();
+
+ File getProjectRoot();
+
+ File getBuildDir();
+
+ SourceSet getSourceSet();
+
+ SourceSet getSourceSourceSet();
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ArtifactCoordsImpl.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ArtifactCoordsImpl.java
new file mode 100644
index 0000000000000..f18feb15d744a
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ArtifactCoordsImpl.java
@@ -0,0 +1,72 @@
+package io.quarkus.bootstrap.resolver.model.impl;
+
+import io.quarkus.bootstrap.resolver.model.ArtifactCoords;
+import java.io.Serializable;
+import java.util.Objects;
+
+public class ArtifactCoordsImpl implements ArtifactCoords, Serializable {
+
+ public static final String TYPE_JAR = "jar";
+
+ private final String groupId;
+ private final String artifactId;
+ private final String classifier;
+ private final String version;
+ private final String type;
+
+ public ArtifactCoordsImpl(String groupId, String artifactId, String version) {
+ this(groupId, artifactId, "", version, TYPE_JAR);
+ }
+
+ public ArtifactCoordsImpl(String groupId, String artifactId, String classifier, String version, String type) {
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ this.classifier = classifier;
+ this.version = version;
+ this.type = type;
+ }
+
+ @Override
+ public String getGroupId() {
+ return groupId;
+ }
+
+ @Override
+ public String getArtifactId() {
+ return artifactId;
+ }
+
+ @Override
+ public String getClassifier() {
+ return classifier;
+ }
+
+ @Override
+ public String getVersion() {
+ return version;
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ ArtifactCoordsImpl that = (ArtifactCoordsImpl) o;
+ return Objects.equals(groupId, that.groupId) &&
+ Objects.equals(artifactId, that.artifactId) &&
+ Objects.equals(classifier, that.classifier) &&
+ Objects.equals(version, that.version) &&
+ Objects.equals(type, that.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(groupId, artifactId, classifier, version, type);
+ }
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/DependencyImpl.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/DependencyImpl.java
new file mode 100644
index 0000000000000..3b75e0a14bcf5
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/DependencyImpl.java
@@ -0,0 +1,106 @@
+package io.quarkus.bootstrap.resolver.model.impl;
+
+import io.quarkus.bootstrap.resolver.model.Dependency;
+import java.io.File;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+public class DependencyImpl implements Dependency, Serializable {
+
+ private final String name;
+ private final String groupId;
+ private final String version;
+ private final String classifier;
+ private final Set paths = new HashSet<>();
+ private final String scope;
+ private final String type;
+
+ public DependencyImpl(String name, String groupId, String version, File path, String scope, String type,
+ String classifier) {
+ this(name, groupId, version, scope, type, classifier);
+ this.paths.add(path);
+ }
+
+ public DependencyImpl(String name, String groupId, String version, String scope, String type, String classifier) {
+ this.name = name;
+ this.groupId = groupId;
+ this.version = version;
+ this.scope = scope;
+ this.type = type;
+ this.classifier = classifier;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getGroupId() {
+ return groupId;
+ }
+
+ @Override
+ public String getVersion() {
+ return version;
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public String getClassifier() {
+ return classifier;
+ }
+
+ @Override
+ public Set getPaths() {
+ return paths;
+ }
+
+ public void addPath(File path) {
+ this.paths.add(path);
+ }
+
+ @Override
+ public String getScope() {
+ return scope;
+ }
+
+ @Override
+ public String toString() {
+ return "DependencyImpl{" +
+ "name='" + name + '\'' +
+ ", groupId='" + groupId + '\'' +
+ ", version='" + version + '\'' +
+ ", type='" + type + '\'' +
+ ", path=" + paths +
+ ", classifier= " + classifier +
+ ", scope='" + scope + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ DependencyImpl that = (DependencyImpl) o;
+ return name.equals(that.name) &&
+ groupId.equals(that.groupId) &&
+ version.equals(that.version) &&
+ paths.equals(that.paths) &&
+ scope.equals(that.scope) &&
+ type.equals(that.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, groupId, version, paths, scope, type);
+ }
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ModelParameterImpl.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ModelParameterImpl.java
new file mode 100644
index 0000000000000..085680bcfb618
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/ModelParameterImpl.java
@@ -0,0 +1,19 @@
+package io.quarkus.bootstrap.resolver.model.impl;
+
+import io.quarkus.bootstrap.resolver.model.ModelParameter;
+import java.io.Serializable;
+
+public class ModelParameterImpl implements ModelParameter, Serializable {
+
+ private String mode;
+
+ @Override
+ public String getMode() {
+ return mode;
+ }
+
+ @Override
+ public void setMode(String mode) {
+ this.mode = mode;
+ }
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/QuarkusModelImpl.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/QuarkusModelImpl.java
new file mode 100644
index 0000000000000..3dd21461a4da0
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/QuarkusModelImpl.java
@@ -0,0 +1,37 @@
+package io.quarkus.bootstrap.resolver.model.impl;
+
+import io.quarkus.bootstrap.resolver.model.Dependency;
+import io.quarkus.bootstrap.resolver.model.QuarkusModel;
+import io.quarkus.bootstrap.resolver.model.Workspace;
+import java.io.Serializable;
+import java.util.Set;
+
+public class QuarkusModelImpl implements QuarkusModel, Serializable {
+
+ private final Workspace workspace;
+ private final Set appDependencies;
+ private final Set extensionDependencies;
+
+ public QuarkusModelImpl(Workspace workspace,
+ Set appDependencies,
+ Set extensionDependencies) {
+ this.workspace = workspace;
+ this.appDependencies = appDependencies;
+ this.extensionDependencies = extensionDependencies;
+ }
+
+ @Override
+ public Workspace getWorkspace() {
+ return workspace;
+ }
+
+ @Override
+ public Set getAppDependencies() {
+ return appDependencies;
+ }
+
+ @Override
+ public Set getExtensionDependencies() {
+ return extensionDependencies;
+ }
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/SourceSetImpl.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/SourceSetImpl.java
new file mode 100644
index 0000000000000..d8a052accb667
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/SourceSetImpl.java
@@ -0,0 +1,36 @@
+package io.quarkus.bootstrap.resolver.model.impl;
+
+import io.quarkus.bootstrap.resolver.model.SourceSet;
+import java.io.File;
+import java.io.Serializable;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class SourceSetImpl implements SourceSet, Serializable {
+
+ private final Set sourceDirectories;
+ private final File resourceDirectory;
+
+ public SourceSetImpl(Set sourceDirectories, File resourceDirectory) {
+ this.sourceDirectories = sourceDirectories;
+ this.resourceDirectory = resourceDirectory;
+ }
+
+ @Override
+ public Set getSourceDirectories() {
+ return sourceDirectories;
+ }
+
+ @Override
+ public File getResourceDirectory() {
+ return resourceDirectory;
+ }
+
+ @Override
+ public String toString() {
+ return "SourceSetImpl{" +
+ "sourceDirectories=" + sourceDirectories.stream().map(File::getPath).collect(Collectors.joining(":")) +
+ ", resourceDirectory=" + resourceDirectory +
+ '}';
+ }
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceImpl.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceImpl.java
new file mode 100644
index 0000000000000..bba576057fc4a
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceImpl.java
@@ -0,0 +1,38 @@
+package io.quarkus.bootstrap.resolver.model.impl;
+
+import io.quarkus.bootstrap.resolver.model.ArtifactCoords;
+import io.quarkus.bootstrap.resolver.model.Workspace;
+import io.quarkus.bootstrap.resolver.model.WorkspaceModule;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class WorkspaceImpl implements Workspace, Serializable {
+
+ public ArtifactCoords mainModuleKey;
+ public Map modules = new HashMap<>();
+
+ public WorkspaceImpl(ArtifactCoords mainModuleKey, Set workspaceModules) {
+ this.mainModuleKey = mainModuleKey;
+ for (WorkspaceModule module : workspaceModules) {
+ modules.put(module.getArtifactCoords(), module);
+ }
+ }
+
+ @Override
+ public WorkspaceModule getMainModule() {
+ return modules.get(mainModuleKey);
+ }
+
+ @Override
+ public Collection getAllModules() {
+ return modules.values();
+ }
+
+ @Override
+ public WorkspaceModule getModule(ArtifactCoords key) {
+ return modules.get(key);
+ }
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceModuleImpl.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceModuleImpl.java
new file mode 100644
index 0000000000000..8827a6fd56687
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/model/impl/WorkspaceModuleImpl.java
@@ -0,0 +1,70 @@
+package io.quarkus.bootstrap.resolver.model.impl;
+
+import io.quarkus.bootstrap.resolver.model.ArtifactCoords;
+import io.quarkus.bootstrap.resolver.model.SourceSet;
+import io.quarkus.bootstrap.resolver.model.WorkspaceModule;
+import java.io.File;
+import java.io.Serializable;
+import java.util.Objects;
+
+public class WorkspaceModuleImpl implements WorkspaceModule, Serializable {
+
+ private final ArtifactCoords artifactCoords;
+ private final File projectRoot;
+ private final File buildDir;
+ private final SourceSet sourceSourceSet;
+ private final SourceSet sourceSet;
+
+ public WorkspaceModuleImpl(ArtifactCoords artifactCoords, File projectRoot, File buildDir, SourceSet sourceSourceSet,
+ SourceSet sourceSet) {
+ this.artifactCoords = artifactCoords;
+ this.projectRoot = projectRoot;
+ this.buildDir = buildDir;
+ this.sourceSourceSet = sourceSourceSet;
+ this.sourceSet = sourceSet;
+ }
+
+ @Override
+ public ArtifactCoords getArtifactCoords() {
+ return artifactCoords;
+ }
+
+ @Override
+ public File getProjectRoot() {
+ return projectRoot;
+ }
+
+ @Override
+ public File getBuildDir() {
+ return buildDir;
+ }
+
+ @Override
+ public SourceSet getSourceSet() {
+ return sourceSet;
+ }
+
+ @Override
+ public SourceSet getSourceSourceSet() {
+ return sourceSourceSet;
+ }
+
+ public boolean equals(Object object) {
+ if (this == object)
+ return true;
+ if (object == null || getClass() != object.getClass())
+ return false;
+ if (!super.equals(object))
+ return false;
+ WorkspaceModuleImpl that = (WorkspaceModuleImpl) object;
+ return java.util.Objects.equals(artifactCoords, that.artifactCoords) &&
+ java.util.Objects.equals(projectRoot, that.projectRoot) &&
+ java.util.Objects.equals(buildDir, that.buildDir) &&
+ java.util.Objects.equals(sourceSourceSet, that.sourceSourceSet) &&
+ java.util.Objects.equals(sourceSet, that.sourceSet);
+ }
+
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), artifactCoords, projectRoot, buildDir, sourceSourceSet, sourceSet);
+ }
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/util/QuarkusModelHelper.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/util/QuarkusModelHelper.java
new file mode 100644
index 0000000000000..c90a7d07ef4c8
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/util/QuarkusModelHelper.java
@@ -0,0 +1,179 @@
+package io.quarkus.bootstrap.util;
+
+import io.quarkus.bootstrap.BootstrapConstants;
+import io.quarkus.bootstrap.BootstrapGradleException;
+import io.quarkus.bootstrap.model.AppArtifact;
+import io.quarkus.bootstrap.model.AppArtifactKey;
+import io.quarkus.bootstrap.model.AppDependency;
+import io.quarkus.bootstrap.model.AppModel;
+import io.quarkus.bootstrap.model.PathsCollection;
+import io.quarkus.bootstrap.resolver.AppModelResolverException;
+import io.quarkus.bootstrap.resolver.model.ArtifactCoords;
+import io.quarkus.bootstrap.resolver.model.Dependency;
+import io.quarkus.bootstrap.resolver.model.QuarkusModel;
+import io.quarkus.bootstrap.resolver.model.WorkspaceModule;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import org.gradle.api.GradleException;
+
+public class QuarkusModelHelper {
+
+ private QuarkusModelHelper() {
+
+ }
+
+ public final static String[] DEVMODE_REQUIRED_TASKS = new String[] { "classes" };
+
+ public static void exportModel(QuarkusModel model) throws AppModelResolverException, IOException {
+ Path serializedModel = QuarkusModelHelper
+ .serializeAppModel(model);
+ System.setProperty(BootstrapConstants.SERIALIZED_APP_MODEL, serializedModel.toString());
+ }
+
+ public static Path serializeAppModel(QuarkusModel model) throws AppModelResolverException, IOException {
+ final Path serializedModel = File.createTempFile("quarkus-app-model", ".dat").toPath();
+ final ArtifactCoords artifactCoords = model.getWorkspace().getMainModule().getArtifactCoords();
+ AppArtifact appArtifact = new AppArtifact(artifactCoords.getGroupId(),
+ artifactCoords.getArtifactId(),
+ artifactCoords.getVersion());
+ try (ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(serializedModel))) {
+ out.writeObject(QuarkusModelHelper.convert(model, appArtifact));
+ }
+ return serializedModel;
+ }
+
+ public static Path getClassPath(WorkspaceModule model) throws BootstrapGradleException {
+ // TODO handle multiple class directory
+ final Optional classDir = model.getSourceSet().getSourceDirectories().stream().filter(File::exists)
+ .map(File::toPath).findFirst();
+ if (!classDir.isPresent()) {
+ throw new BootstrapGradleException("Failed to locate class directory");
+ }
+ return classDir.get();
+ }
+
+ public static AppModel convert(QuarkusModel model, AppArtifact appArtifact) throws AppModelResolverException {
+ AppModel.Builder appBuilder = new AppModel.Builder();
+
+ final List userDeps = new ArrayList<>();
+ Map versionMap = new HashMap<>();
+ model.getAppDependencies().stream().map(QuarkusModelHelper::toAppDependency).forEach(appDependency -> {
+ userDeps.add(appDependency);
+ versionMap.put(appDependency.getArtifact().getKey(), appDependency);
+ });
+
+ final List deploymentDeps = new ArrayList<>();
+ for (Dependency extensionDependency : model.getExtensionDependencies()) {
+ AppDependency appDep = toAppDependency(extensionDependency);
+ for (Path artifactPath : appDep.getArtifact().getPaths()) {
+ if (!Files.exists(artifactPath) || !extensionDependency.getType().equals("jar")) {
+ continue;
+ }
+ if (Files.isDirectory(artifactPath)) {
+ processQuarkusDir(appDep.getArtifact(), artifactPath.resolve(BootstrapConstants.META_INF),
+ appBuilder);
+ } else {
+ try (FileSystem artifactFs = FileSystems.newFileSystem(artifactPath,
+ QuarkusModelHelper.class.getClassLoader())) {
+ processQuarkusDir(appDep.getArtifact(), artifactFs.getPath(BootstrapConstants.META_INF),
+ appBuilder);
+ } catch (IOException e) {
+ throw new AppModelResolverException("Failed to process " + artifactPath, e);
+ }
+ }
+ }
+ if (!userDeps.contains(appDep)) {
+ AppDependency deploymentDep = alignVersion(appDep, versionMap);
+ deploymentDeps.add(deploymentDep);
+ }
+ }
+
+ final List fullDeploymentDeps = new ArrayList<>(userDeps);
+ fullDeploymentDeps.addAll(deploymentDeps);
+
+ if (!appArtifact.isResolved()) {
+ PathsCollection.Builder paths = PathsCollection.builder();
+ WorkspaceModule module = model.getWorkspace().getMainModule();
+ module.getSourceSet().getSourceDirectories().stream().filter(File::exists).map(File::toPath)
+ .forEach(paths::add);
+ if (module.getSourceSet().getResourceDirectory().exists()) {
+ paths.add(module.getSourceSet().getResourceDirectory().toPath());
+ }
+ appArtifact.setPaths(paths.build());
+ }
+
+ appBuilder.addRuntimeDeps(userDeps)
+ .addFullDeploymentDeps(fullDeploymentDeps)
+ .addDeploymentDeps(deploymentDeps)
+ .setAppArtifact(appArtifact);
+ return appBuilder.build();
+ }
+
+ public static AppDependency toAppDependency(Dependency dependency) {
+ AppArtifact artifact = new AppArtifact(dependency.getGroupId(), dependency.getName(), dependency.getClassifier(),
+ dependency.getType(), dependency.getVersion());
+ artifact.setPaths(QuarkusModelHelper.toPathsCollection(dependency.getPaths()));
+ return new AppDependency(artifact, "runtime");
+ }
+
+ public static PathsCollection toPathsCollection(Collection files) {
+ PathsCollection.Builder paths = PathsCollection.builder();
+ for (File f : files) {
+ paths.add(f.toPath());
+ }
+ return paths.build();
+ }
+
+ public static Properties resolveDescriptor(final Path path) {
+ final Properties rtProps;
+ if (!Files.exists(path)) {
+ // not a platform artifact
+ return null;
+ }
+ rtProps = new Properties();
+ try (BufferedReader reader = Files.newBufferedReader(path)) {
+ rtProps.load(reader);
+ } catch (IOException e) {
+ throw new GradleException("Failed to load extension description " + path, e);
+ }
+ return rtProps;
+ }
+
+ private static void processQuarkusDir(AppArtifact a, Path quarkusDir, AppModel.Builder appBuilder) {
+ if (!Files.exists(quarkusDir)) {
+ return;
+ }
+ final Path quarkusDescr = quarkusDir.resolve(BootstrapConstants.DESCRIPTOR_FILE_NAME);
+ if (!Files.exists(quarkusDescr)) {
+ return;
+ }
+ final Properties extProps = QuarkusModelHelper.resolveDescriptor(quarkusDescr);
+ if (extProps == null) {
+ return;
+ }
+ appBuilder.handleExtensionProperties(extProps, a.toString());
+ }
+
+ static AppDependency alignVersion(AppDependency dependency, Map versionMap) {
+ AppArtifactKey appKey = new AppArtifactKey(dependency.getArtifact().getGroupId(),
+ dependency.getArtifact().getArtifactId());
+ if (versionMap.containsKey(appKey)) {
+ return versionMap.get(appKey);
+ }
+ return dependency;
+ }
+
+}
diff --git a/independent-projects/bootstrap/gradle-resolver/src/test/java/io/quarkus/bootstrap/util/QuarkusModelHelperTest.java b/independent-projects/bootstrap/gradle-resolver/src/test/java/io/quarkus/bootstrap/util/QuarkusModelHelperTest.java
new file mode 100644
index 0000000000000..7626776874559
--- /dev/null
+++ b/independent-projects/bootstrap/gradle-resolver/src/test/java/io/quarkus/bootstrap/util/QuarkusModelHelperTest.java
@@ -0,0 +1,39 @@
+package io.quarkus.bootstrap.util;
+
+import io.quarkus.bootstrap.model.AppArtifact;
+import io.quarkus.bootstrap.model.AppArtifactKey;
+import io.quarkus.bootstrap.model.AppDependency;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class QuarkusModelHelperTest {
+
+ @Test
+ public void shouldKeepExtensionDependencyVersion() {
+ Map userDependencies = new HashMap<>();
+
+ final AppArtifact appArtifact = new AppArtifact("org.acme", "common", "0.0.1-SNAPSHOT");
+ AppDependency extensionDependency = new AppDependency(appArtifact, "runtime", false);
+
+ final AppDependency dependency = QuarkusModelHelper.alignVersion(extensionDependency, userDependencies);
+
+ Assertions.assertEquals(extensionDependency, dependency);
+ }
+
+ @Test
+ public void shouldUseUserDependencyVersion() {
+ Map userDependencies = new HashMap<>();
+ final AppArtifact userAppArtifact = new AppArtifact("org.acme", "common", "1.0.0-SNAPSHOT");
+ final AppDependency userDependency = new AppDependency(userAppArtifact, "runtime", false);
+ userDependencies.put(new AppArtifactKey("org.acme", "common"), userDependency);
+
+ final AppArtifact appArtifact = new AppArtifact("org.acme", "common", "0.0.1-SNAPSHOT");
+ AppDependency extensionDependency = new AppDependency(appArtifact, "runtime", false);
+
+ final AppDependency dependency = QuarkusModelHelper.alignVersion(extensionDependency, userDependencies);
+
+ Assertions.assertEquals(userDependency, dependency);
+ }
+}
diff --git a/independent-projects/bootstrap/maven-resolver/pom.xml b/independent-projects/bootstrap/maven-resolver/pom.xml
index fe2b7c931c8ef..52e96826928a5 100644
--- a/independent-projects/bootstrap/maven-resolver/pom.xml
+++ b/independent-projects/bootstrap/maven-resolver/pom.xml
@@ -18,6 +18,10 @@
io.quarkus
quarkus-bootstrap-app-model
+
+ org.jboss.slf4j
+ slf4j-jboss-logging
+
org.ow2.asm
asm
diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml
index cf0a68049ef45..49fc1342851ed 100644
--- a/independent-projects/bootstrap/pom.xml
+++ b/independent-projects/bootstrap/pom.xml
@@ -44,10 +44,12 @@
8.0.1
1.2.6
1.0.4
+ 1.2.0.Final
20.1.0
2.6.0
3.0.0-M5
1.1.0
+ 6.5
app-model
@@ -55,6 +57,7 @@
core
maven-plugin
runner
+ gradle-resolver
@@ -100,6 +103,11 @@
quarkus-bootstrap-maven-plugin
${project.version}
+
+ io.quarkus
+ quarkus-bootstrap-gradle-resolver
+ ${project.version}
+
org.apache.maven
maven-plugin-api
@@ -164,9 +172,23 @@
org.checkerframework
checker-qual
+
+ org.slf4j
+ slf4j-api
+
+
+
+
+ org.gradle
+ gradle-tooling-api
+ ${gradle-tooling.version}
+
+
+ org.slf4j
+ slf4j-api
+
-
jakarta.annotation
jakarta.annotation-api
@@ -306,6 +328,11 @@
commons-logging-jboss-logging
${commons-logging-jboss-logging.version}
+
+ org.jboss.slf4j
+ slf4j-jboss-logging
+ ${slf4j-jboss-logging.version}
+
org.codehaus.plexus
plexus-classworlds
@@ -490,6 +517,14 @@
+
+
+ gradle-dependencies
+ Gradle releases repository
+ https://repo.gradle.org/gradle/libs-releases
+
+
+
quick-build
diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusGradleWrapperTestBase.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusGradleWrapperTestBase.java
index feb7e3cd9a3c7..eeaca45d3c0d3 100644
--- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusGradleWrapperTestBase.java
+++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusGradleWrapperTestBase.java
@@ -1,7 +1,9 @@
package io.quarkus.gradle;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.LinkedList;
@@ -19,13 +21,23 @@ public BuildResult runGradleWrapper(File projectDir, String... args) throws IOEx
List command = new LinkedList<>();
command.add(getGradleWrapperCommand());
command.add(GRADLE_NO_DAEMON);
+ command.add("--stacktrace");
command.addAll(Arrays.asList(args));
+
+ File logOutput = new File(projectDir, "command-output.log");
+
Process p = new ProcessBuilder()
.directory(projectDir)
.command(command)
+ .redirectInput(ProcessBuilder.Redirect.INHERIT)
+ .redirectOutput(logOutput)
.start();
+
p.waitFor(5, TimeUnit.MINUTES);
- return BuildResult.of(p.getInputStream());
+
+ try (InputStream is = new FileInputStream(logOutput)) {
+ return BuildResult.of(is);
+ }
}
private String getGradleWrapperCommand() {
diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java
index 7702f9e1bca6d..4a7f71eb10c1a 100644
--- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java
+++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java
@@ -134,6 +134,15 @@ public void canDetectOutputChangeWhenBuilding() throws IOException, InterruptedE
assertThat(runnerJar).exists();
}
+ @Test
+ public void canRunTest() throws IOException, InterruptedException {
+ createProject(SourceType.JAVA);
+
+ BuildResult buildResult = runGradleWrapper(projectRoot, "test", "--stacktrace");
+
+ assertThat(buildResult.getTasks().get(":test")).isEqualTo(BuildResult.SUCCESS_OUTCOME);
+ }
+
private void createProject(SourceType sourceType) throws IOException {
Map context = new HashMap<>();
context.put("path", "/greeting");
diff --git a/integration-tests/gradle/src/test/resources/multi-module-kotlin-project/build.gradle b/integration-tests/gradle/src/test/resources/multi-module-kotlin-project/build.gradle
index b6d57ccb49aba..4152ab9cf293c 100644
--- a/integration-tests/gradle/src/test/resources/multi-module-kotlin-project/build.gradle
+++ b/integration-tests/gradle/src/test/resources/multi-module-kotlin-project/build.gradle
@@ -4,7 +4,6 @@ plugins {
id 'io.quarkus'
}
-
dependencies {
implementation(project(":port"))
implementation(project(":domain"))
diff --git a/pom.xml b/pom.xml
index 3ebd96e594c75..aa8a3bb3b491e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,6 +80,11 @@
Maven Repository Switchboard
https://repo.maven.apache.org/maven2
+
+ gradle-dependencies
+ Gradle releases repository
+ https://repo.gradle.org/gradle/libs-releases
+
diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/PathTestHelper.java b/test-framework/common/src/main/java/io/quarkus/test/common/PathTestHelper.java
index 228684cdaf1c5..ded07182a3925 100644
--- a/test-framework/common/src/main/java/io/quarkus/test/common/PathTestHelper.java
+++ b/test-framework/common/src/main/java/io/quarkus/test/common/PathTestHelper.java
@@ -22,6 +22,10 @@ public final class PathTestHelper {
TEST_TO_MAIN_DIR_FRAGMENTS.put(
"bin" + File.separator + "test",
"bin" + File.separator + "main");
+ // idea
+ TEST_TO_MAIN_DIR_FRAGMENTS.put(
+ "out" + File.separator + "test",
+ "out" + File.separator + "production");
// gradle
TEST_TO_MAIN_DIR_FRAGMENTS.put(
"classes" + File.separator + "java" + File.separator + "native-test",
diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java
index 13ec67201e3d6..8d1c2a2110fd0 100644
--- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java
+++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java
@@ -48,6 +48,7 @@
import org.junit.jupiter.api.extension.TestInstantiationException;
import org.opentest4j.TestAbortedException;
+import io.quarkus.bootstrap.BootstrapConstants;
import io.quarkus.bootstrap.app.AugmentAction;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
@@ -55,6 +56,7 @@
import io.quarkus.bootstrap.app.StartupAction;
import io.quarkus.bootstrap.model.PathsCollection;
import io.quarkus.bootstrap.runner.Timing;
+import io.quarkus.bootstrap.utils.BuildToolHelper;
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildContext;
import io.quarkus.builder.BuildStep;
@@ -168,6 +170,12 @@ private ExtensionState doJavaStart(ExtensionContext context, Class extends Qua
rootBuilder.add(appResourcesLocation);
}
+ Path root = Paths.get("").normalize().toAbsolutePath();
+ // If gradle project running directly with IDE
+ if (System.getProperty(BootstrapConstants.SERIALIZED_APP_MODEL) == null) {
+ BuildToolHelper.enableGradleAppModel(root, "TEST");
+ }
+
runnerBuilder.setApplicationRoot(rootBuilder.build());
CuratedApplication curatedApplication = runnerBuilder