use of io.quarkus.deployment.builditem.AdditionalApplicationArchiveBuildItem in project quarkus by quarkusio.
the class QuarkusAugmentor method run.
public BuildResult run() throws Exception {
if (!JavaVersionUtil.isJava11OrHigher()) {
throw new IllegalStateException("Quarkus applications require Java 11 or higher to build");
}
long time = System.currentTimeMillis();
log.debug("Beginning Quarkus augmentation");
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
QuarkusBuildCloseablesBuildItem buildCloseables = new QuarkusBuildCloseablesBuildItem();
try {
Thread.currentThread().setContextClassLoader(deploymentClassLoader);
final BuildChainBuilder chainBuilder = BuildChain.builder();
chainBuilder.setClassLoader(deploymentClassLoader);
// provideCapabilities(chainBuilder);
// TODO: we load everything from the deployment class loader
// this allows the deployment config (application.properties) to be loaded, but in theory could result
// in additional stuff from the deployment leaking in, this is unlikely but has a bit of a smell.
ExtensionLoader.loadStepsFrom(deploymentClassLoader, buildSystemProperties == null ? new Properties() : buildSystemProperties, effectiveModel, launchMode, devModeType).accept(chainBuilder);
Thread.currentThread().setContextClassLoader(classLoader);
chainBuilder.loadProviders(classLoader);
chainBuilder.addInitial(QuarkusBuildCloseablesBuildItem.class).addInitial(ArchiveRootBuildItem.class).addInitial(ShutdownContextBuildItem.class).addInitial(RawCommandLineArgumentsBuildItem.class).addInitial(LaunchModeBuildItem.class).addInitial(LiveReloadBuildItem.class).addInitial(AdditionalApplicationArchiveBuildItem.class).addInitial(CuratedApplicationShutdownBuildItem.class).addInitial(BuildSystemTargetBuildItem.class).addInitial(AppModelProviderBuildItem.class);
for (Class<? extends BuildItem> i : finalResults) {
chainBuilder.addFinal(i);
}
for (Consumer<BuildChainBuilder> i : buildChainCustomizers) {
i.accept(chainBuilder);
}
if (launchMode.isDevOrTest()) {
chainBuilder.addFinal(RuntimeApplicationShutdownBuildItem.class);
}
final ArchiveRootBuildItem.Builder rootBuilder = ArchiveRootBuildItem.builder();
if (root != null) {
rootBuilder.addArchiveRoots(root);
}
rootBuilder.setExcludedFromIndexing(excludedFromIndexing);
BuildChain chain = chainBuilder.build();
BuildExecutionBuilder execBuilder = chain.createExecutionBuilder("main").produce(buildCloseables).produce(liveReloadBuildItem).produce(rootBuilder.build(buildCloseables)).produce(new ShutdownContextBuildItem()).produce(new RawCommandLineArgumentsBuildItem()).produce(new CuratedApplicationShutdownBuildItem((QuarkusClassLoader) deploymentClassLoader.getParent(), !liveReloadBuildItem.isLiveReload())).produce(new LaunchModeBuildItem(launchMode, devModeType == null ? Optional.empty() : Optional.of(devModeType), auxiliaryApplication, auxiliaryDevModeType, test)).produce(new BuildSystemTargetBuildItem(targetDir, baseName, rebuild, buildSystemProperties == null ? new Properties() : buildSystemProperties)).produce(new AppModelProviderBuildItem(effectiveModel));
for (PathCollection i : additionalApplicationArchives) {
execBuilder.produce(new AdditionalApplicationArchiveBuildItem(i));
}
BuildResult buildResult = execBuilder.execute();
String message = "Quarkus augmentation completed in " + (System.currentTimeMillis() - time) + "ms";
if (launchMode == LaunchMode.NORMAL) {
log.info(message);
} else {
// test and dev mode already report the total startup time, no need to add noise to the logs
log.debug(message);
}
return buildResult;
} finally {
try {
ConfigProviderResolver.instance().releaseConfig(ConfigProviderResolver.instance().getConfig(deploymentClassLoader));
} catch (Exception ignore) {
}
if (deploymentClassLoader instanceof Closeable) {
((Closeable) deploymentClassLoader).close();
}
Thread.currentThread().setContextClassLoader(originalClassLoader);
buildCloseables.close();
}
}
use of io.quarkus.deployment.builditem.AdditionalApplicationArchiveBuildItem in project quarkus by quarkusio.
the class ApplicationArchiveBuildStep method scanForOtherIndexes.
private List<ApplicationArchive> scanForOtherIndexes(QuarkusBuildCloseablesBuildItem buildCloseables, List<AdditionalApplicationArchiveMarkerBuildItem> appMarkers, ArchiveRootBuildItem root, List<AdditionalApplicationArchiveBuildItem> additionalApplicationArchives, List<IndexDependencyBuildItem> indexDependencyBuildItem, IndexCache indexCache, CurateOutcomeBuildItem curateOutcomeBuildItem, Map<ArtifactKey, Set<String>> removedResources) throws IOException {
List<ApplicationArchive> appArchives = new ArrayList<>();
Set<Path> indexedPaths = new HashSet<>();
// get paths that are included via marker files
final Set<String> markers = new HashSet<>(appMarkers.size() + 1);
for (AdditionalApplicationArchiveMarkerBuildItem i : appMarkers) {
final String marker = i.getFile();
markers.add(marker.endsWith("/") ? marker.substring(0, marker.length() - 1) : marker);
}
markers.add(IndexingUtil.JANDEX_INDEX);
addMarkerFilePaths(markers, root, curateOutcomeBuildItem, indexedPaths, appArchives, buildCloseables, indexCache, removedResources);
// get paths that are included via index-dependencies
addIndexDependencyPaths(indexDependencyBuildItem, root, indexedPaths, appArchives, buildCloseables, indexCache, curateOutcomeBuildItem, removedResources);
for (AdditionalApplicationArchiveBuildItem i : additionalApplicationArchives) {
for (Path apPath : i.getResolvedPaths()) {
if (!root.getResolvedPaths().contains(apPath) && indexedPaths.add(apPath)) {
appArchives.add(createApplicationArchive(buildCloseables, indexCache, apPath, null, removedResources));
}
}
}
return appArchives;
}
use of io.quarkus.deployment.builditem.AdditionalApplicationArchiveBuildItem in project quarkus by quarkusio.
the class JarResultBuildStep method buildThinJar.
private JarBuildItem buildThinJar(CurateOutcomeBuildItem curateOutcomeBuildItem, OutputTargetBuildItem outputTargetBuildItem, TransformedClassesBuildItem transformedClasses, ApplicationArchivesBuildItem applicationArchivesBuildItem, PackageConfig packageConfig, ClassLoadingConfig classLoadingConfig, ApplicationInfoBuildItem applicationInfo, List<GeneratedClassBuildItem> generatedClasses, List<GeneratedResourceBuildItem> generatedResources, List<AdditionalApplicationArchiveBuildItem> additionalApplicationArchiveBuildItems, MainClassBuildItem mainClassBuildItem) throws Exception {
boolean rebuild = outputTargetBuildItem.isRebuild();
Path buildDir;
if (packageConfig.outputDirectory.isPresent()) {
buildDir = outputTargetBuildItem.getOutputDirectory();
} else {
buildDir = outputTargetBuildItem.getOutputDirectory().resolve(DEFAULT_FAST_JAR_DIRECTORY_NAME);
}
// unmodified 3rd party dependencies
Path libDir = buildDir.resolve(LIB);
Path mainLib = libDir.resolve(MAIN);
// parent first entries
Path baseLib = libDir.resolve(BOOT_LIB);
Files.createDirectories(baseLib);
Path appDir = buildDir.resolve(APP);
Path quarkus = buildDir.resolve(QUARKUS);
Path userProviders = null;
if (packageConfig.userProvidersDirectory.isPresent()) {
userProviders = buildDir.resolve(packageConfig.userProvidersDirectory.get());
}
if (!rebuild) {
IoUtils.createOrEmptyDir(buildDir);
Files.createDirectories(mainLib);
Files.createDirectories(baseLib);
Files.createDirectories(appDir);
Files.createDirectories(quarkus);
if (userProviders != null) {
Files.createDirectories(userProviders);
// we add this dir so that it can be copied into container images if required
// and will still be copied even if empty
Files.createFile(userProviders.resolve(".keep"));
}
} else {
IoUtils.createOrEmptyDir(quarkus);
}
Map<ArtifactKey, List<Path>> copiedArtifacts = new HashMap<>();
Path fernflowerJar = null;
Path decompiledOutputDir = null;
boolean wasDecompiledSuccessfully = true;
if (packageConfig.fernflower.enabled) {
Path jarDirectory = Paths.get(packageConfig.fernflower.jarDirectory);
if (!Files.exists(jarDirectory)) {
Files.createDirectory(jarDirectory);
}
fernflowerJar = jarDirectory.resolve(String.format("fernflower-%s.jar", packageConfig.fernflower.hash));
if (!Files.exists(fernflowerJar)) {
boolean downloadComplete = downloadFernflowerJar(packageConfig, fernflowerJar);
if (!downloadComplete) {
// will ensure that no decompilation takes place
fernflowerJar = null;
}
}
decompiledOutputDir = buildDir.getParent().resolve("decompiled");
FileUtil.deleteDirectory(decompiledOutputDir);
Files.createDirectory(decompiledOutputDir);
}
List<Path> jars = new ArrayList<>();
List<Path> parentFirst = new ArrayList<>();
// transformed classes first
if (!transformedClasses.getTransformedClassesByJar().isEmpty()) {
Path transformedZip = quarkus.resolve(TRANSFORMED_BYTECODE_JAR);
jars.add(transformedZip);
try (FileSystem out = ZipUtils.newZip(transformedZip)) {
for (Set<TransformedClassesBuildItem.TransformedClass> transformedSet : transformedClasses.getTransformedClassesByJar().values()) {
for (TransformedClassesBuildItem.TransformedClass transformed : transformedSet) {
Path target = out.getPath(transformed.getFileName());
if (transformed.getData() != null) {
if (target.getParent() != null) {
Files.createDirectories(target.getParent());
}
Files.write(target, transformed.getData());
}
}
}
}
if (fernflowerJar != null) {
wasDecompiledSuccessfully &= decompile(fernflowerJar, decompiledOutputDir, transformedZip);
}
}
// now generated classes and resources
Path generatedZip = quarkus.resolve(GENERATED_BYTECODE_JAR);
jars.add(generatedZip);
try (FileSystem out = ZipUtils.newZip(generatedZip)) {
for (GeneratedClassBuildItem i : generatedClasses) {
String fileName = i.getName().replace('.', '/') + ".class";
Path target = out.getPath(fileName);
if (target.getParent() != null) {
Files.createDirectories(target.getParent());
}
Files.write(target, i.getClassData());
}
for (GeneratedResourceBuildItem i : generatedResources) {
Path target = out.getPath(i.getName());
if (target.getParent() != null) {
Files.createDirectories(target.getParent());
}
Files.write(target, i.getClassData());
}
}
if (fernflowerJar != null) {
wasDecompiledSuccessfully &= decompile(fernflowerJar, decompiledOutputDir, generatedZip);
}
if (wasDecompiledSuccessfully && (decompiledOutputDir != null)) {
log.info("The decompiled output can be found at: " + decompiledOutputDir.toAbsolutePath().toString());
}
// now the application classes
Path runnerJar = appDir.resolve(outputTargetBuildItem.getBaseName() + ".jar");
jars.add(runnerJar);
if (!rebuild) {
Predicate<String> ignoredEntriesPredicate = getThinJarIgnoredEntriesPredicate(packageConfig);
try (FileSystem runnerZipFs = ZipUtils.newZip(runnerJar)) {
copyFiles(applicationArchivesBuildItem.getRootArchive(), runnerZipFs, null, ignoredEntriesPredicate);
}
}
final Set<ArtifactKey> parentFirstKeys = getParentFirstKeys(curateOutcomeBuildItem, classLoadingConfig);
StringBuilder classPath = new StringBuilder();
final Set<ArtifactKey> removed = getRemovedKeys(classLoadingConfig);
for (ResolvedDependency appDep : curateOutcomeBuildItem.getApplicationModel().getRuntimeDependencies()) {
if (rebuild) {
appDep.getResolvedPaths().forEach(jars::add);
} else {
copyDependency(parentFirstKeys, outputTargetBuildItem, copiedArtifacts, mainLib, baseLib, jars, true, classPath, appDep, transformedClasses, removed);
}
if (parentFirstKeys.contains(appDep.getKey())) {
appDep.getResolvedPaths().forEach(parentFirst::add);
}
}
for (AdditionalApplicationArchiveBuildItem i : additionalApplicationArchiveBuildItems) {
for (Path path : i.getResolvedPaths()) {
if (!path.getParent().equals(userProviders)) {
throw new RuntimeException("Additional application archives can only be provided from the user providers directory. " + path + " is not present in " + userProviders);
}
jars.add(path);
}
}
/*
* There are some files like META-INF/microprofile-config.properties that usually don't exist in application
* and yet are always looked up (spec compliance...) and due to the location in the jar,
* the RunnerClassLoader needs to look into every jar to determine whether they exist or not.
* In keeping true to the original design of the RunnerClassLoader which indexes the directory structure,
* we just add a fail-fast path for files we know don't exist.
*
* TODO: if this gets more complex, we'll probably want a build item to carry this information instead of hard
* coding it here
*/
List<String> nonExistentResources = new ArrayList<>(1);
Enumeration<URL> mpConfigURLs = Thread.currentThread().getContextClassLoader().getResources(MP_CONFIG_FILE);
if (!mpConfigURLs.hasMoreElements()) {
nonExistentResources.add(MP_CONFIG_FILE);
}
Path appInfo = buildDir.resolve(QuarkusEntryPoint.QUARKUS_APPLICATION_DAT);
try (OutputStream out = Files.newOutputStream(appInfo)) {
SerializedApplication.write(out, mainClassBuildItem.getClassName(), buildDir, jars, parentFirst, nonExistentResources);
}
runnerJar.toFile().setReadable(true, false);
Path initJar = buildDir.resolve(QUARKUS_RUN_JAR);
boolean mutableJar = packageConfig.type.equalsIgnoreCase(PackageConfig.MUTABLE_JAR);
if (mutableJar) {
// we output the properties in a reproducible manner, so we remove the date comment
// and sort them
// we still use Properties to get the escaping right though, so basically we write out the lines
// to memory, split them, discard comments, sort them, then write them to disk
ByteArrayOutputStream out = new ByteArrayOutputStream();
outputTargetBuildItem.getBuildSystemProperties().store(out, null);
List<String> lines = Arrays.stream(new String(out.toByteArray(), StandardCharsets.UTF_8).split("\n")).filter(s -> !s.startsWith("#")).sorted().collect(Collectors.toList());
Path buildSystemProps = quarkus.resolve(BUILD_SYSTEM_PROPERTIES);
try (OutputStream fileOutput = Files.newOutputStream(buildSystemProps)) {
fileOutput.write(String.join("\n", lines).getBytes(StandardCharsets.UTF_8));
}
}
if (!rebuild) {
try (FileSystem runnerZipFs = ZipUtils.newZip(initJar)) {
ResolvedDependency appArtifact = curateOutcomeBuildItem.getApplicationModel().getAppArtifact();
generateManifest(runnerZipFs, classPath.toString(), packageConfig, appArtifact, QuarkusEntryPoint.class.getName(), applicationInfo);
}
// now copy the deployment artifacts, if required
if (mutableJar) {
Path deploymentLib = libDir.resolve(DEPLOYMENT_LIB);
Files.createDirectories(deploymentLib);
for (ResolvedDependency appDep : curateOutcomeBuildItem.getApplicationModel().getDependencies()) {
copyDependency(parentFirstKeys, outputTargetBuildItem, copiedArtifacts, deploymentLib, baseLib, jars, false, classPath, appDep, new TransformedClassesBuildItem(Collections.emptyMap()), // we don't care about transformation here, so just pass in an empty item
removed);
}
Map<ArtifactKey, List<String>> relativePaths = new HashMap<>();
for (Map.Entry<ArtifactKey, List<Path>> e : copiedArtifacts.entrySet()) {
relativePaths.put(e.getKey(), e.getValue().stream().map(s -> buildDir.relativize(s).toString().replace('\\', '/')).collect(Collectors.toList()));
}
// now we serialize the data needed to build up the reaugmentation class path
// first the app model
MutableJarApplicationModel model = new MutableJarApplicationModel(outputTargetBuildItem.getBaseName(), relativePaths, curateOutcomeBuildItem.getApplicationModel(), packageConfig.userProvidersDirectory.orElse(null), buildDir.relativize(runnerJar).toString());
Path appmodelDat = deploymentLib.resolve(APPMODEL_DAT);
try (OutputStream out = Files.newOutputStream(appmodelDat)) {
ObjectOutputStream obj = new ObjectOutputStream(out);
obj.writeObject(model);
obj.close();
}
// now the bootstrap CP
// we just include all deployment deps, even though we only really need bootstrap
// as we don't really have a resolved bootstrap CP
// once we have the app model it will all be done in QuarkusClassLoader anyway
Path deploymentCp = deploymentLib.resolve(DEPLOYMENT_CLASS_PATH_DAT);
try (OutputStream out = Files.newOutputStream(deploymentCp)) {
ObjectOutputStream obj = new ObjectOutputStream(out);
List<String> paths = new ArrayList<>();
for (ResolvedDependency i : curateOutcomeBuildItem.getApplicationModel().getDependencies()) {
final List<String> list = relativePaths.get(i.getKey());
// some of the dependencies may have been filtered out
if (list != null) {
paths.addAll(list);
}
}
obj.writeObject(paths);
obj.close();
}
}
if (packageConfig.includeDependencyList) {
Path deplist = buildDir.resolve(QUARKUS_APP_DEPS);
List<String> lines = new ArrayList<>();
for (ResolvedDependency i : curateOutcomeBuildItem.getApplicationModel().getRuntimeDependencies()) {
lines.add(i.toGACTVString());
}
lines.sort(Comparator.naturalOrder());
Files.write(deplist, lines);
}
} else {
// if it is a rebuild we might have classes
}
try (Stream<Path> files = Files.walk(buildDir)) {
files.forEach(new Consumer<Path>() {
@Override
public void accept(Path path) {
path.toFile().setReadable(true, false);
}
});
}
return new JarBuildItem(initJar, null, libDir, packageConfig.type, null);
}
Aggregations