forked from quarkusio/quarkus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding support for jbang integration
Done by implementing jbang experimental SPI to let Quarkus participate in the build of a jar + passing //Q:CONFIG in as system properties. Initial version supports building jar as well as native image via docker. - various fixes for handling default packages in arc and metrics. - fixes a few places of class.forname not honoring TCCL - slightly open up appmodel to enable this but still reliant on pom.xml being bundled (future versions shouldn't) Co-Authored: Alexey Loubyansky <olubyans@redhat.com> Co-Authored: Stuart Douglas <stuart.w.douglas@gmail.com>
- Loading branch information
1 parent
f585d9d
commit 545eff5
Showing
20 changed files
with
488 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
156 changes: 156 additions & 0 deletions
156
core/deployment/src/main/java/io/quarkus/deployment/jbang/JBangAugmentorImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package io.quarkus.deployment.jbang; | ||
|
||
import java.io.File; | ||
import java.nio.file.Path; | ||
import java.util.*; | ||
import java.util.function.BiConsumer; | ||
import java.util.function.Consumer; | ||
|
||
import org.jboss.logging.Logger; | ||
|
||
import io.quarkus.bootstrap.BootstrapGradleException; | ||
import io.quarkus.bootstrap.app.AdditionalDependency; | ||
import io.quarkus.bootstrap.app.CuratedApplication; | ||
import io.quarkus.bootstrap.app.QuarkusBootstrap; | ||
import io.quarkus.bootstrap.classloading.QuarkusClassLoader; | ||
import io.quarkus.bootstrap.model.AppArtifactKey; | ||
import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; | ||
import io.quarkus.bootstrap.resolver.model.WorkspaceModule; | ||
import io.quarkus.bootstrap.util.QuarkusModelHelper; | ||
import io.quarkus.builder.BuildChainBuilder; | ||
import io.quarkus.builder.BuildResult; | ||
import io.quarkus.builder.BuildStepBuilder; | ||
import io.quarkus.deployment.QuarkusAugmentor; | ||
import io.quarkus.deployment.builditem.ApplicationClassNameBuildItem; | ||
import io.quarkus.deployment.builditem.GeneratedClassBuildItem; | ||
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem; | ||
import io.quarkus.deployment.builditem.LiveReloadBuildItem; | ||
import io.quarkus.deployment.builditem.TransformedClassesBuildItem; | ||
import io.quarkus.deployment.dev.DevModeContext; | ||
import io.quarkus.deployment.dev.IDEDevModeMain; | ||
import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem; | ||
import io.quarkus.deployment.pkg.builditem.NativeImageBuildItem; | ||
import io.quarkus.deployment.pkg.builditem.ProcessInheritIODisabled; | ||
import io.quarkus.runtime.LaunchMode; | ||
|
||
public class JBangAugmentorImpl implements BiConsumer<CuratedApplication, Map<String, Object>> { | ||
|
||
private static final Logger log = Logger.getLogger(IDEDevModeMain.class.getName()); | ||
|
||
@Override | ||
public void accept(CuratedApplication curatedApplication, Map<String, Object> resultMap) { | ||
|
||
QuarkusClassLoader classLoader = curatedApplication.getAugmentClassLoader(); | ||
|
||
QuarkusBootstrap quarkusBootstrap = curatedApplication.getQuarkusBootstrap(); | ||
QuarkusAugmentor.Builder builder = QuarkusAugmentor.builder() | ||
.setRoot(quarkusBootstrap.getApplicationRoot()) | ||
.setClassLoader(classLoader) | ||
.addFinal(ApplicationClassNameBuildItem.class) | ||
.setTargetDir(quarkusBootstrap.getTargetDirectory()) | ||
.setDeploymentClassLoader(curatedApplication.createDeploymentClassLoader()) | ||
.setBuildSystemProperties(quarkusBootstrap.getBuildSystemProperties()) | ||
.setEffectiveModel(curatedApplication.getAppModel()); | ||
if (quarkusBootstrap.getBaseName() != null) { | ||
builder.setBaseName(quarkusBootstrap.getBaseName()); | ||
} | ||
|
||
builder.setLaunchMode(LaunchMode.NORMAL); | ||
builder.setRebuild(quarkusBootstrap.isRebuild()); | ||
builder.setLiveReloadState(new LiveReloadBuildItem(false, Collections.emptySet(), new HashMap<>())); | ||
for (AdditionalDependency i : quarkusBootstrap.getAdditionalApplicationArchives()) { | ||
//this gets added to the class path either way | ||
//but we only need to add it to the additional app archives | ||
//if it is forced as an app archive | ||
if (i.isForceApplicationArchive()) { | ||
builder.addAdditionalApplicationArchive(i.getArchivePath()); | ||
} | ||
} | ||
builder.addBuildChainCustomizer(new Consumer<BuildChainBuilder>() { | ||
@Override | ||
public void accept(BuildChainBuilder builder) { | ||
final BuildStepBuilder stepBuilder = builder.addBuildStep((ctx) -> { | ||
ctx.produce(new ProcessInheritIODisabled()); | ||
}); | ||
stepBuilder.produces(ProcessInheritIODisabled.class).build(); | ||
} | ||
}); | ||
builder.excludeFromIndexing(quarkusBootstrap.getExcludeFromClassPath()); | ||
builder.addFinal(GeneratedClassBuildItem.class); | ||
builder.addFinal(GeneratedResourceBuildItem.class); | ||
builder.addFinal(TransformedClassesBuildItem.class); | ||
boolean nativeRequested = "native".equals(System.getProperty("quarkus.package.type")); | ||
boolean containerBuildRequested = Boolean.getBoolean("quarkus.container-image.build"); | ||
if (nativeRequested) { | ||
builder.addFinal(NativeImageBuildItem.class); | ||
} | ||
if (containerBuildRequested) { | ||
//TODO: this is a bit ugly | ||
//we don't nessesarily need these artifacts | ||
//but if we include them it does mean that you can auto create docker images | ||
//and deploy to kube etc | ||
//for an ordinary build with no native and no docker this is a waste | ||
builder.addFinal(ArtifactResultBuildItem.class); | ||
} | ||
|
||
try { | ||
BuildResult buildResult = builder.build().run(); | ||
Map<String, byte[]> result = new HashMap<>(); | ||
for (GeneratedClassBuildItem i : buildResult.consumeMulti(GeneratedClassBuildItem.class)) { | ||
result.put(i.getName().replace(".", "/") + ".class", i.getClassData()); | ||
} | ||
for (GeneratedResourceBuildItem i : buildResult.consumeMulti(GeneratedResourceBuildItem.class)) { | ||
result.put(i.getName(), i.getClassData()); | ||
} | ||
for (Map.Entry<Path, Set<TransformedClassesBuildItem.TransformedClass>> entry : buildResult | ||
.consume(TransformedClassesBuildItem.class).getTransformedClassesByJar().entrySet()) { | ||
for (TransformedClassesBuildItem.TransformedClass transformed : entry.getValue()) { | ||
result.put(transformed.getFileName(), transformed.getData()); | ||
} | ||
} | ||
resultMap.put("files", result); | ||
List javaargs = new ArrayList<String>(); | ||
javaargs.add("-Djava.util.logging.manager=org.jboss.logmanager.LogManager"); | ||
resultMap.put("java-args", javaargs); | ||
if (nativeRequested) { | ||
resultMap.put("native-image", buildResult.consume(NativeImageBuildItem.class).getPath()); | ||
} | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
private DevModeContext.ModuleInfo toModule(WorkspaceModule module) throws BootstrapGradleException { | ||
AppArtifactKey key = new AppArtifactKey(module.getArtifactCoords().getGroupId(), | ||
module.getArtifactCoords().getArtifactId(), module.getArtifactCoords().getClassifier()); | ||
|
||
Set<String> sourceDirectories = new HashSet<>(); | ||
Set<String> 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.getOutputDir().toString()); | ||
} | ||
} |
92 changes: 92 additions & 0 deletions
92
core/launcher/src/main/java/io/quarkus/launcher/JBangIntegration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package io.quarkus.launcher; | ||
|
||
import java.io.IOException; | ||
import java.net.URL; | ||
import java.nio.file.Path; | ||
import java.util.Collections; | ||
import java.util.Enumeration; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import io.quarkus.bootstrap.BootstrapConstants; | ||
|
||
public class JBangIntegration { | ||
|
||
public static final String CONFIG = "//Q:CONFIG"; | ||
|
||
public static Map<String, Object> postBuild(Path appClasses, Path pomFile, List<Map.Entry<String, String>> repositories, | ||
List<Map.Entry<String, Path>> dependencies, | ||
List<String> comments, boolean nativeImage) { | ||
for (String comment : comments) { | ||
//we allow config to be provided via //Q:CONFIG name=value | ||
if (comment.startsWith(CONFIG)) { | ||
String conf = comment.substring(CONFIG.length()).trim(); | ||
int equals = conf.indexOf("="); | ||
if (equals == -1) { | ||
throw new RuntimeException("invalid config " + comment); | ||
} | ||
System.setProperty(conf.substring(0, equals), conf.substring(equals + 1)); | ||
} | ||
} | ||
|
||
ClassLoader old = Thread.currentThread().getContextClassLoader(); | ||
try { | ||
RuntimeLaunchClassLoader loader = new RuntimeLaunchClassLoader( | ||
new ClassLoader(JBangIntegration.class.getClassLoader()) { | ||
@Override | ||
public Class<?> loadClass(String name) throws ClassNotFoundException { | ||
return loadClass(name, false); | ||
} | ||
|
||
@Override | ||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { | ||
if (name.startsWith("org.")) { | ||
//jbang has some but not all of the maven resolver classes we need on its | ||
//class path. These all start with org. so we filter them out to make sure | ||
//we get a complete class path | ||
throw new ClassNotFoundException(); | ||
} | ||
return super.loadClass(name, resolve); | ||
} | ||
|
||
@Override | ||
public URL getResource(String name) { | ||
if (name.startsWith("org/")) { | ||
//jbang has some but not all of the maven resolver classes we need on its | ||
//class path. These all start with org. so we filter them out to make sure | ||
//we get a complete class path | ||
return null; | ||
} | ||
return super.getResource(name); | ||
} | ||
|
||
@Override | ||
public Enumeration<URL> getResources(String name) throws IOException { | ||
if (name.startsWith("org/")) { | ||
//jbang has some but not all of the maven resolver classes we need on its | ||
//class path. These all start with org. so we filter them out to make sure | ||
//we get a complete class path | ||
return Collections.emptyEnumeration(); | ||
} | ||
return super.getResources(name); | ||
} | ||
}); | ||
Thread.currentThread().setContextClassLoader(loader); | ||
Class<?> launcher = loader.loadClass("io.quarkus.bootstrap.JBangBuilderImpl"); | ||
return (Map<String, Object>) launcher | ||
.getDeclaredMethod("postBuild", Path.class, Path.class, List.class, List.class, boolean.class).invoke( | ||
null, | ||
appClasses, | ||
pomFile, | ||
repositories, | ||
dependencies, | ||
nativeImage); | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} finally { | ||
System.clearProperty(BootstrapConstants.SERIALIZED_APP_MODEL); | ||
Thread.currentThread().setContextClassLoader(old); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.