| /* | |
| * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package jdk.jpackage.internal; | |
| import static java.util.stream.Collectors.toMap; | |
| import java.io.IOException; | |
| import java.nio.file.LinkOption; | |
| 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.Objects; | |
| import java.util.Optional; | |
| import java.util.concurrent.Callable; | |
| import java.util.function.Function; | |
| import java.util.function.Predicate; | |
| import java.util.function.UnaryOperator; | |
| import jdk.jpackage.internal.model.AppImageLayout; | |
| import jdk.jpackage.internal.model.Application; | |
| import jdk.jpackage.internal.model.ApplicationLayout; | |
| import jdk.jpackage.internal.model.Package; | |
| import jdk.jpackage.internal.model.PackagerException; | |
| import jdk.jpackage.internal.pipeline.DirectedEdge; | |
| import jdk.jpackage.internal.pipeline.FixedDAG; | |
| import jdk.jpackage.internal.pipeline.TaskPipelineBuilder; | |
| import jdk.jpackage.internal.pipeline.TaskSpecBuilder; | |
| import jdk.jpackage.internal.util.function.ExceptionBox; | |
| final class PackagingPipeline { | |
| void execute(BuildEnv env, Application app) throws PackagerException { | |
| execute(appContextMapper.apply(createTaskContext(env, app))); | |
| } | |
| void execute(BuildEnv env, Package pkg, Path outputDir) throws PackagerException { | |
| execute((StartupParameters)createPackagingTaskContext(env, pkg, outputDir, | |
| taskConfig, appImageLayoutForPackaging.apply(pkg))); | |
| } | |
| void execute(StartupParameters startupParameters) throws PackagerException { | |
| execute(pkgContextMapper.apply(createTaskContext((PackagingTaskContext)startupParameters))); | |
| } | |
| interface StartupParameters { | |
| BuildEnv packagingEnv(); | |
| } | |
| interface TaskAction { | |
| } | |
| interface TaskID { | |
| } | |
| enum BuildApplicationTaskID implements TaskID { | |
| RUNTIME, | |
| CONTENT, | |
| LAUNCHERS, | |
| APP_IMAGE_FILE | |
| } | |
| enum CopyAppImageTaskID implements TaskID { | |
| COPY | |
| } | |
| enum PrimaryTaskID implements TaskID { | |
| BUILD_APPLICATION_IMAGE, | |
| COPY_APP_IMAGE, | |
| PACKAGE | |
| } | |
| enum PackageTaskID implements TaskID { | |
| RUN_POST_IMAGE_USER_SCRIPT, | |
| CREATE_CONFIG_FILES, | |
| CREATE_PACKAGE_FILE | |
| } | |
| interface TaskContext extends Predicate<TaskID> { | |
| void execute(TaskAction taskAction) throws IOException, PackagerException; | |
| } | |
| record AppImageBuildEnv<T extends Application, U extends AppImageLayout>(BuildEnv env, T app, U envLayout) { | |
| @SuppressWarnings("unchecked") | |
| U resolvedLayout() { | |
| return (U)envLayout.resolveAt(env.appImageDir()); | |
| } | |
| } | |
| record PackageBuildEnv<T extends Package, U extends AppImageLayout>(BuildEnv env, T pkg, U envLayout, Path outputDir) { | |
| @SuppressWarnings("unchecked") | |
| U resolvedLayout() { | |
| return (U)envLayout.resolveAt(env.appImageDir()); | |
| } | |
| AppImageBuildEnv<Application, U> appImageBuildEnv() { | |
| return new AppImageBuildEnv<>(env, pkg.app(), envLayout); | |
| } | |
| } | |
| @FunctionalInterface | |
| interface ApplicationImageTaskAction<T extends Application, U extends ApplicationLayout> extends TaskAction { | |
| void execute(AppImageBuildEnv<T, U> env) throws IOException, PackagerException; | |
| } | |
| @FunctionalInterface | |
| interface AppImageTaskAction<T extends Application, U extends AppImageLayout> extends TaskAction { | |
| void execute(AppImageBuildEnv<T, U> env) throws IOException, PackagerException; | |
| } | |
| @FunctionalInterface | |
| interface CopyAppImageTaskAction<T extends Package> extends TaskAction { | |
| void execute(T pkg, AppImageDesc srcAppImage, AppImageDesc dstAppImage) throws IOException, PackagerException; | |
| } | |
| @FunctionalInterface | |
| interface PackageTaskAction<T extends Package, U extends AppImageLayout> extends TaskAction { | |
| void execute(PackageBuildEnv<T, U> env) throws IOException, PackagerException; | |
| } | |
| @FunctionalInterface | |
| interface NoArgTaskAction extends TaskAction { | |
| void execute() throws IOException, PackagerException; | |
| } | |
| record TaskConfig(Optional<TaskAction> action) { | |
| TaskConfig { | |
| Objects.requireNonNull(action); | |
| } | |
| } | |
| static final class Builder { | |
| private Builder() { | |
| } | |
| final class TaskBuilder extends TaskSpecBuilder<TaskID> { | |
| private TaskBuilder(TaskID id) { | |
| super(id); | |
| } | |
| private TaskBuilder setAction(TaskAction v) { | |
| action = v; | |
| return this; | |
| } | |
| TaskBuilder noaction() { | |
| action = null; | |
| return this; | |
| } | |
| <T extends Application, U extends ApplicationLayout> TaskBuilder applicationAction(ApplicationImageTaskAction<T, U> action) { | |
| return setAction(action); | |
| } | |
| <T extends Application, U extends AppImageLayout> TaskBuilder appImageAction(AppImageTaskAction<T, U> action) { | |
| return setAction(action); | |
| } | |
| <T extends Package> TaskBuilder copyAction(CopyAppImageTaskAction<T> action) { | |
| return setAction(action); | |
| } | |
| <T extends Package, U extends AppImageLayout> TaskBuilder packageAction(PackageTaskAction<T, U> action) { | |
| return setAction(action); | |
| } | |
| TaskBuilder action(NoArgTaskAction action) { | |
| return setAction(action); | |
| } | |
| @Override | |
| public TaskBuilder addDependent(TaskID v) { | |
| super.addDependent(v); | |
| return this; | |
| } | |
| @Override | |
| public TaskBuilder addDependency(TaskID v) { | |
| super.addDependency(v); | |
| return this; | |
| } | |
| @Override | |
| public TaskBuilder addDependencies(Collection<? extends TaskID> tasks) { | |
| super.addDependencies(tasks); | |
| return this; | |
| } | |
| @Override | |
| public TaskBuilder addDependents(Collection<? extends TaskID> tasks) { | |
| super.addDependents(tasks); | |
| return this; | |
| } | |
| public TaskBuilder addDependencies(TaskID ... tasks) { | |
| return addDependencies(List.of(tasks)); | |
| } | |
| public TaskBuilder addDependents(TaskID ... tasks) { | |
| return addDependents(List.of(tasks)); | |
| } | |
| Builder add() { | |
| final var config = new TaskConfig(Optional.ofNullable(action)); | |
| taskConfig.put(task(), config); | |
| createLinks().forEach(Builder.this::linkTasks); | |
| return Builder.this; | |
| } | |
| private TaskAction action; | |
| } | |
| Builder linkTasks(DirectedEdge<TaskID> edge) { | |
| taskGraphBuilder.addEdge(edge); | |
| if (taskGraphSnapshot != null) { | |
| taskGraphSnapshot = null; | |
| } | |
| return this; | |
| } | |
| Builder linkTasks(TaskID tail, TaskID head) { | |
| return linkTasks(DirectedEdge.create(tail, head)); | |
| } | |
| TaskBuilder task(TaskID id) { | |
| return new TaskBuilder(id); | |
| } | |
| Builder excludeDirFromCopying(Path path) { | |
| Objects.requireNonNull(path); | |
| excludeCopyDirs.add(path); | |
| return this; | |
| } | |
| Builder contextMapper(UnaryOperator<TaskContext> v) { | |
| appContextMapper(v); | |
| pkgContextMapper(v); | |
| return this; | |
| } | |
| Builder appContextMapper(UnaryOperator<TaskContext> v) { | |
| appContextMapper = v; | |
| return this; | |
| } | |
| Builder pkgContextMapper(UnaryOperator<TaskContext> v) { | |
| pkgContextMapper = v; | |
| return this; | |
| } | |
| Builder appImageLayoutForPackaging(Function<Package, AppImageLayout> v) { | |
| appImageLayoutForPackaging = v; | |
| return this; | |
| } | |
| FixedDAG<TaskID> taskGraphSnapshot() { | |
| if (taskGraphSnapshot == null) { | |
| taskGraphSnapshot = taskGraphBuilder.create(); | |
| } | |
| return taskGraphSnapshot; | |
| } | |
| StartupParameters createStartupParameters(BuildEnv env, Package pkg, Path outputDir) { | |
| return createPackagingTaskContext(env, pkg, outputDir, taskConfig, | |
| validatedAppImageLayoutForPackaging().apply(pkg)); | |
| } | |
| private Function<Package, AppImageLayout> validatedAppImageLayoutForPackaging() { | |
| return Optional.ofNullable(appImageLayoutForPackaging).orElse(Package::packageLayout); | |
| } | |
| PackagingPipeline create() { | |
| return new PackagingPipeline(taskGraphSnapshot(), taskConfig, | |
| Optional.ofNullable(appContextMapper).orElse(UnaryOperator.identity()), | |
| Optional.ofNullable(pkgContextMapper).orElse(UnaryOperator.identity()), | |
| validatedAppImageLayoutForPackaging()); | |
| } | |
| private final FixedDAG.Builder<TaskID> taskGraphBuilder = FixedDAG.build(); | |
| private final List<Path> excludeCopyDirs = new ArrayList<>(); | |
| private final Map<TaskID, TaskConfig> taskConfig = new HashMap<>(); | |
| private UnaryOperator<TaskContext> appContextMapper; | |
| private UnaryOperator<TaskContext> pkgContextMapper; | |
| private Function<Package, AppImageLayout> appImageLayoutForPackaging; | |
| private FixedDAG<TaskID> taskGraphSnapshot; | |
| } | |
| static Builder build() { | |
| return new Builder(); | |
| } | |
| static Builder buildStandard() { | |
| final var builder = build(); | |
| configureApplicationTasks(builder); | |
| configurePackageTasks(builder); | |
| return builder; | |
| } | |
| static Builder configureApplicationTasks(Builder builder) { | |
| builder.task(BuildApplicationTaskID.RUNTIME) | |
| .addDependent(BuildApplicationTaskID.CONTENT) | |
| .applicationAction(ApplicationImageUtils.createWriteRuntimeAction()).add(); | |
| builder.task(BuildApplicationTaskID.LAUNCHERS) | |
| .addDependent(BuildApplicationTaskID.CONTENT) | |
| .applicationAction(ApplicationImageUtils.createWriteLaunchersAction()).add(); | |
| builder.task(BuildApplicationTaskID.APP_IMAGE_FILE) | |
| .addDependent(PrimaryTaskID.BUILD_APPLICATION_IMAGE) | |
| .applicationAction(ApplicationImageUtils.createWriteAppImageFileAction()).add(); | |
| builder.task(BuildApplicationTaskID.CONTENT) | |
| .addDependent(BuildApplicationTaskID.APP_IMAGE_FILE) | |
| .applicationAction(ApplicationImageUtils.createCopyContentAction(() -> builder.excludeCopyDirs)).add(); | |
| return builder; | |
| } | |
| static Builder configurePackageTasks(Builder builder) { | |
| builder.task(CopyAppImageTaskID.COPY) | |
| .copyAction(PackagingPipeline::copyAppImage) | |
| .addDependent(PrimaryTaskID.COPY_APP_IMAGE) | |
| .add(); | |
| builder.task(PrimaryTaskID.COPY_APP_IMAGE).add(); | |
| builder.task(PrimaryTaskID.BUILD_APPLICATION_IMAGE).add(); | |
| builder.task(PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT) | |
| .addDependencies(PrimaryTaskID.BUILD_APPLICATION_IMAGE, PrimaryTaskID.COPY_APP_IMAGE) | |
| .addDependency(PackageTaskID.CREATE_CONFIG_FILES) | |
| .addDependent(PackageTaskID.CREATE_PACKAGE_FILE) | |
| .packageAction(PackagingPipeline::runPostAppImageUserScript).add(); | |
| builder.task(PackageTaskID.CREATE_CONFIG_FILES) | |
| .addDependent(PackageTaskID.CREATE_PACKAGE_FILE) | |
| .add(); | |
| builder.task(PackageTaskID.CREATE_PACKAGE_FILE) | |
| .addDependent(PrimaryTaskID.PACKAGE) | |
| .add(); | |
| builder.task(PrimaryTaskID.PACKAGE).add(); | |
| return builder; | |
| } | |
| static void copyAppImage(Package pkg, AppImageDesc srcAppImage, AppImageDesc dstAppImage) throws IOException { | |
| copyAppImage(srcAppImage, dstAppImage, true); | |
| } | |
| static void copyAppImage(AppImageDesc srcAppImage, AppImageDesc dstAppImage, | |
| boolean removeAppImageFile) throws IOException { | |
| final var srcLayout = srcAppImage.resolvedAppImagelayout(); | |
| final var srcLayoutPathGroup = AppImageLayout.toPathGroup(srcLayout); | |
| if (removeAppImageFile && srcLayout instanceof ApplicationLayout appLayout) { | |
| // Copy app layout omitting application image info file. | |
| srcLayoutPathGroup.ghostPath(AppImageFile.getPathInAppImage(appLayout)); | |
| } | |
| srcLayoutPathGroup.copy(AppImageLayout.toPathGroup(dstAppImage.resolvedAppImagelayout()), LinkOption.NOFOLLOW_LINKS); | |
| } | |
| static void runPostAppImageUserScript(PackageBuildEnv<Package, AppImageLayout> env) throws IOException { | |
| final var appImageDir = env.env().appImageDir(); | |
| new ScriptRunner() | |
| .setDirectory(appImageDir) | |
| .setResourceCategoryId("resource.post-app-image-script") | |
| .setScriptNameSuffix("post-image") | |
| .setEnvironmentVariable("JpAppImageDir", appImageDir.toAbsolutePath().toString()) | |
| .run(env.env(), env.pkg().app().name()); | |
| } | |
| private PackagingPipeline(FixedDAG<TaskID> taskGraph, Map<TaskID, TaskConfig> taskConfig, | |
| UnaryOperator<TaskContext> appContextMapper, UnaryOperator<TaskContext> pkgContextMapper, | |
| Function<Package, AppImageLayout> appImageLayoutForPackaging) { | |
| this.taskGraph = Objects.requireNonNull(taskGraph); | |
| this.taskConfig = Objects.requireNonNull(taskConfig); | |
| this.appContextMapper = Objects.requireNonNull(appContextMapper); | |
| this.pkgContextMapper = Objects.requireNonNull(pkgContextMapper); | |
| this.appImageLayoutForPackaging = Objects.requireNonNull(appImageLayoutForPackaging); | |
| } | |
| private TaskContext createTaskContext(BuildEnv env, Application app) { | |
| return new DefaultTaskContext(taskGraph, env, app, app.asApplicationLayout(), Optional.empty()); | |
| } | |
| private TaskContext createTaskContext(PackagingTaskContext packagingContext) { | |
| final var pkgEnv = BuildEnv.withAppImageDir(packagingContext.env.env(), packagingContext.srcAppImage.path()); | |
| return new DefaultTaskContext(taskGraph, pkgEnv, packagingContext.env.pkg.app(), | |
| packagingContext.srcAppImage.asApplicationLayout(), Optional.of(packagingContext)); | |
| } | |
| private static PackagingTaskContext createPackagingTaskContext(BuildEnv env, Package pkg, | |
| Path outputDir, Map<TaskID, TaskConfig> taskConfig, AppImageLayout appImageLayoutForPackaging) { | |
| Objects.requireNonNull(env); | |
| Objects.requireNonNull(outputDir); | |
| Objects.requireNonNull(taskConfig); | |
| Objects.requireNonNull(appImageLayoutForPackaging); | |
| final AppImageDesc srcAppImageDesc; | |
| final AppImageDesc dstAppImageDesc; | |
| if (pkg.app().runtimeBuilder().isPresent()) { | |
| // Runtime builder is present, will build application image. | |
| // appImageDir() should point to a directory where the application image will be created. | |
| srcAppImageDesc = new AppImageDesc(appImageLayoutForPackaging, env.appImageDir()); | |
| dstAppImageDesc = srcAppImageDesc; | |
| } else { | |
| srcAppImageDesc = new AppImageDesc(pkg.app().imageLayout(), pkg.predefinedAppImage().orElseGet(() -> { | |
| // No predefined app image and no runtime builder. | |
| // This should be runtime packaging. | |
| if (pkg.isRuntimeInstaller()) { | |
| return env.appImageDir(); | |
| } else { | |
| // Can't create app image without runtime builder. | |
| throw new UnsupportedOperationException(); | |
| } | |
| })); | |
| if (taskConfig.get(CopyAppImageTaskID.COPY).action().isEmpty()) { | |
| // "copy app image" task action is undefined indicating | |
| // the package will use provided app image as-is. | |
| dstAppImageDesc = srcAppImageDesc; | |
| } else { | |
| dstAppImageDesc = new AppImageDesc(appImageLayoutForPackaging, env.buildRoot().resolve("image")); | |
| } | |
| } | |
| final var pkgEnv = new PackageBuildEnv<>( | |
| BuildEnv.withAppImageDir(env, dstAppImageDesc.path()), pkg, dstAppImageDesc.appImageLayout(), outputDir); | |
| return new PackagingTaskContext(pkgEnv, srcAppImageDesc); | |
| } | |
| private void execute(TaskContext context) throws PackagerException { | |
| final Map<TaskID, Callable<Void>> tasks = taskConfig.entrySet().stream().collect(toMap(Map.Entry::getKey, task -> { | |
| return createTask(context, task.getKey(), task.getValue()); | |
| })); | |
| final var builder = new TaskPipelineBuilder(); | |
| for (final var tail : taskGraph.nodes()) { | |
| for (final var head : taskGraph.getHeadsOf(tail)) { | |
| builder.linkTasks(tasks.get(tail), tasks.get(head)); | |
| } | |
| } | |
| try { | |
| builder.create().call(); | |
| } catch (Exception ex) { | |
| if (ex instanceof PackagerException pex) { | |
| throw pex; | |
| } else if (ex instanceof ExceptionBox bex) { | |
| throw new PackagerException(bex.getCause()); | |
| } else { | |
| throw new PackagerException(ex); | |
| } | |
| } | |
| } | |
| private record PackagingTaskContext(PackageBuildEnv<Package, AppImageLayout> env, | |
| AppImageDesc srcAppImage) implements TaskContext, StartupParameters { | |
| PackagingTaskContext { | |
| Objects.requireNonNull(env); | |
| Objects.requireNonNull(srcAppImage); | |
| } | |
| @Override | |
| public BuildEnv packagingEnv() { | |
| return env.env; | |
| } | |
| @Override | |
| public boolean test(TaskID taskID) { | |
| if (taskID == BuildApplicationTaskID.APP_IMAGE_FILE) { | |
| // Application image for packaging, skip adding application image info file. | |
| return false; | |
| } | |
| return true; | |
| } | |
| @SuppressWarnings("unchecked") | |
| @Override | |
| public void execute(TaskAction taskAction) throws IOException, PackagerException { | |
| if (taskAction instanceof PackageTaskAction<?, ?>) { | |
| ((PackageTaskAction<Package, AppImageLayout>)taskAction).execute(env); | |
| } else if (taskAction instanceof CopyAppImageTaskAction<?>) { | |
| ((CopyAppImageTaskAction<Package>)taskAction).execute(env.pkg(), | |
| srcAppImage, new AppImageDesc(env.envLayout(), env.env().appImageDir())); | |
| } else { | |
| throw new IllegalArgumentException(); | |
| } | |
| } | |
| AppImageBuildEnv<Application, AppImageLayout> appImageBuildEnv() { | |
| return env.appImageBuildEnv(); | |
| } | |
| } | |
| private record DefaultTaskContext(FixedDAG<TaskID> taskGraph, BuildEnv env, Application app, | |
| Optional<ApplicationLayout> appLayout, Optional<PackagingTaskContext> pkg) implements TaskContext { | |
| DefaultTaskContext { | |
| Objects.requireNonNull(taskGraph); | |
| Objects.requireNonNull(env); | |
| Objects.requireNonNull(app); | |
| Objects.requireNonNull(appLayout); | |
| Objects.requireNonNull(pkg); | |
| } | |
| @Override | |
| public boolean test(TaskID taskID) { | |
| final var isBuildApplicationImageTask = isBuildApplicationImageTask(taskID); | |
| final var isCopyAppImageTask = isCopyAppImageTask(taskID); | |
| final var isPackageTask = !isBuildApplicationImageTask && !isCopyAppImageTask; | |
| if (pkg.isPresent() && !pkg.orElseThrow().test(taskID)) { | |
| return false; | |
| } else if (pkg.isEmpty() && isPackageTask) { | |
| // Building application image, skip packaging tasks. | |
| return false; | |
| } else if (app.runtimeBuilder().isEmpty() && isBuildApplicationImageTask && !isCopyAppImageTask) { | |
| // Runtime builder is not present, skip building application image tasks. | |
| return false; | |
| } else if (app.runtimeBuilder().isPresent() && isCopyAppImageTask && !isBuildApplicationImageTask) { | |
| // Runtime builder is present, skip copying app image tasks. | |
| return false; | |
| } else { | |
| return true; | |
| } | |
| } | |
| @SuppressWarnings("unchecked") | |
| @Override | |
| public void execute(TaskAction taskAction) throws IOException, PackagerException { | |
| if (taskAction instanceof AppImageTaskAction<?, ?>) { | |
| final var taskEnv = pkg.map(PackagingTaskContext::appImageBuildEnv).orElseGet(this::appBuildEnv); | |
| ((AppImageTaskAction<Application, AppImageLayout>)taskAction).execute(taskEnv); | |
| } else if (taskAction instanceof ApplicationImageTaskAction<?, ?>) { | |
| ((ApplicationImageTaskAction<Application, ApplicationLayout>)taskAction).execute(appBuildEnv()); | |
| } else if (taskAction instanceof NoArgTaskAction noArgAction) { | |
| noArgAction.execute(); | |
| } else { | |
| pkg.orElseThrow().execute(taskAction); | |
| } | |
| } | |
| private boolean isBuildApplicationImageTask(TaskID taskID) { | |
| return (taskID == PrimaryTaskID.BUILD_APPLICATION_IMAGE | |
| || taskGraph.getAllHeadsOf(taskID).contains(PrimaryTaskID.BUILD_APPLICATION_IMAGE)); | |
| } | |
| private boolean isCopyAppImageTask(TaskID taskID) { | |
| return (taskID == PrimaryTaskID.COPY_APP_IMAGE | |
| || taskGraph.getAllHeadsOf(taskID).contains(PrimaryTaskID.COPY_APP_IMAGE)); | |
| } | |
| @SuppressWarnings("unchecked") | |
| private <T extends AppImageLayout> AppImageBuildEnv<Application, T> appBuildEnv() { | |
| return new AppImageBuildEnv<>(env, app, (T)appLayout.orElseThrow()); | |
| } | |
| } | |
| private static Callable<Void> createTask(TaskContext context, TaskID id, TaskConfig config) { | |
| Objects.requireNonNull(context); | |
| Objects.requireNonNull(id); | |
| Objects.requireNonNull(config); | |
| return () -> { | |
| if (config.action.isPresent() && context.test(id)) { | |
| try { | |
| context.execute(config.action.orElseThrow()); | |
| } catch (ExceptionBox ex) { | |
| throw ExceptionBox.rethrowUnchecked(ex); | |
| } | |
| } | |
| return null; | |
| }; | |
| } | |
| private final FixedDAG<TaskID> taskGraph; | |
| private final Map<TaskID, TaskConfig> taskConfig; | |
| private final Function<Package, AppImageLayout> appImageLayoutForPackaging; | |
| private final UnaryOperator<TaskContext> appContextMapper; | |
| private final UnaryOperator<TaskContext> pkgContextMapper; | |
| } |