All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.lukebemish.taskgraphrunner.runtime.tasks.JstTask Maven / Gradle / Ivy

Go to download

A tool to create and execute acyclic directed graphs of tasks, with locking and up-to-date checking

There is a newer version: 0.1.20
Show newest version
package dev.lukebemish.taskgraphrunner.runtime.tasks;

import dev.lukebemish.taskgraphrunner.model.PathSensitivity;
import dev.lukebemish.taskgraphrunner.model.TaskModel;
import dev.lukebemish.taskgraphrunner.model.WorkItem;
import dev.lukebemish.taskgraphrunner.runtime.Context;
import dev.lukebemish.taskgraphrunner.runtime.Task;
import dev.lukebemish.taskgraphrunner.runtime.TaskInput;
import dev.lukebemish.taskgraphrunner.runtime.execution.ToolDaemonExecutor;
import dev.lukebemish.taskgraphrunner.runtime.mappings.MappingsSourceImpl;
import dev.lukebemish.taskgraphrunner.runtime.mappings.MappingsUtil;
import dev.lukebemish.taskgraphrunner.runtime.mappings.ParchmentMappingWriter;
import dev.lukebemish.taskgraphrunner.runtime.util.Tools;
import org.jspecify.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;

public class JstTask extends Task {
    private final TaskInput.HasFileInput input;
    private final TaskInput.FileListInput classpath;
    private final TaskInput.FileListInput executionClasspath;
    private final TaskInput.@Nullable FileListInput accessTransformers;
    private final TaskInput.@Nullable FileListInput interfaceInjection;
    private final @Nullable MappingsSourceImpl parchmentMappingsSource;
    private final List inputs;
    private final List args;
    private final Map outputExtensions;
    private final boolean classpathScopedJvm;

    public JstTask(TaskModel.Jst model, WorkItem workItem, Context context) {
        super(model);

        this.inputs = new ArrayList<>();

        this.input = TaskInput.file("input", model.input, workItem, context, PathSensitivity.NONE);
        inputs.add(this.input);
        List classpathParts = new ArrayList<>();
        for (int i = 0; i < model.classpath.size(); i++) {
            var part = model.classpath.get(i);
            classpathParts.add(TaskInput.files("classpath" + i, part, workItem, context, PathSensitivity.NONE));
        }
        this.classpath = new TaskInput.RecursiveFileListInput("classpath", classpathParts);
        inputs.add(this.classpath);

        List executionClasspathParts = new ArrayList<>();
        for (int i = 0; i < model.executionClasspath.size(); i++) {
            var part = model.executionClasspath.get(i);
            executionClasspathParts.add(TaskInput.files("executionClasspath" + i, part, workItem, context, PathSensitivity.NONE));
        }
        this.executionClasspath = new TaskInput.RecursiveFileListInput("executionClasspath", executionClasspathParts);
        inputs.add(this.executionClasspath);

        if (model.accessTransformers != null) {
            this.accessTransformers = TaskInput.files("accessTransformers", model.accessTransformers, workItem, context, PathSensitivity.NONE);
            inputs.add(this.accessTransformers);
        } else {
            this.accessTransformers = null;
        }
        if (model.interfaceInjection != null) {
            this.interfaceInjection = TaskInput.files("interfaceInjection", model.interfaceInjection, workItem, context, PathSensitivity.NONE);
            inputs.add(this.interfaceInjection);
        } else {
            this.interfaceInjection = null;
        }
        if (model.parchmentData != null) {
            this.parchmentMappingsSource = MappingsSourceImpl.of(model.parchmentData, workItem, context, new AtomicInteger());
            inputs.addAll(this.parchmentMappingsSource.inputs());
        } else {
            this.parchmentMappingsSource = null;
        }

        this.outputExtensions = new HashMap<>();
        outputExtensions.put("output", "zip");
        outputExtensions.put("stubs", "zip");

        this.args = new ArrayList<>();
        ArgumentProcessor.processArgs("arg", model.args, this.args, workItem, context, outputExtensions);

        this.classpathScopedJvm = model.classpathScopedJvm;
    }

    private void collectArguments(ArrayList command, Context context, Path workingDirectory) {
        command.add(input.path(context).toAbsolutePath().toString());
        command.add(context.taskOutputPath(this, "output").toAbsolutePath().toString());

        command.add("--classpath="+classpath.classpath(context));
        command.add("--in-format=ARCHIVE");
        command.add("--out-format=ARCHIVE");

        if (accessTransformers != null && !accessTransformers.paths(context).isEmpty()) {
            command.add("--enable-accesstransformers");
            for (var path : accessTransformers.paths(context)) {
                command.add("--access-transformer="+path.toAbsolutePath());
            }
        }

        if (interfaceInjection != null && !interfaceInjection.paths(context).isEmpty()) {
            command.add("--enable-interface-injection");
            for (var path : interfaceInjection.paths(context)) {
                command.add("--interface-injection-data="+path.toAbsolutePath());
            }
            command.add("--interface-injection-stubs="+context.taskOutputPath(this, "stubs"));
        }

        if (parchmentMappingsSource != null) {
            command.add("--enable-parchment");
            // Might eventually look at making this configurable
            command.add("--parchment-conflict-prefix=p");
            var parchmentFile = workingDirectory.resolve("parchment.json");
            var mappings = MappingsUtil.fixInnerClasses(parchmentMappingsSource.makeMappings(context));
            try (var writer = Files.newBufferedWriter(parchmentFile, StandardCharsets.UTF_8);
                 var mappingWriter = new ParchmentMappingWriter(writer)) {
                mappingWriter.accept(mappings);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
            command.add("--parchment-mappings="+parchmentFile.toAbsolutePath());
            // Is this still necessary? Unclear, but javadoc don't seem to be working right
            command.add("--parchment-javadoc=true");
        }

        for (int i = 0; i < args.size(); i++) {
            var arg = args.get(i);
            command.addAll(arg.resolve(workingDirectory, name(), context, i));
        }
    }

    @Override
    protected void run(Context context) {
        var workingDirectory = context.taskWorkingDirectory(this);
        try {
            Files.createDirectories(workingDirectory);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
        var logFile = workingDirectory.resolve("log.txt");

        var command = new ArrayList();
        collectArguments(command, context, workingDirectory);

        var mainClass = "net.neoforged.jst.cli.Main";
        var classpath = new ArrayList();
        classpath.add(context.findArtifact(Tools.JST).toAbsolutePath());
        classpath.addAll(this.executionClasspath.paths(context));

        try (var writer = Files.newBufferedWriter(logFile, StandardCharsets.UTF_8)) {
            writer.append("-".repeat(80)).append("\n\n");
            writer.append("Command-Line:\n");
            writer.append(" -cp ").append(classpath.stream().map(p -> p.toAbsolutePath().toString()).collect(Collectors.joining(File.pathSeparator))).append("\n");
            writer.append(" - ").append(mainClass).append("\n");
            for (String s : command) {
                writer.append(" - ").append(s).append("\n");
            }
            writer.append("-".repeat(80)).append("\n\n");
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
        ToolDaemonExecutor.execute(classpath, mainClass, logFile, command.toArray(String[]::new), context, classpathScopedJvm);

        if (this.interfaceInjection == null || this.interfaceInjection.paths(context).isEmpty()) {
            // Make an empty stubs zip
            var path = context.taskOutputPath(this, "stubs");
            try (var os = Files.newOutputStream(path);
                 var zos = new ZipOutputStream(os)) {
                zos.flush();
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }
    }

    @Override
    public List inputs() {
        return inputs;
    }

    @Override
    public Map outputTypes() {
        return outputExtensions;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy