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

net.dongliu.prettypb.maven.Protoc Maven / Gradle / Ivy

package net.dongliu.prettypb.maven;

import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;

import java.io.File;
import java.util.*;

import static com.google.inject.internal.util.Preconditions.checkArgument;
import static com.google.inject.internal.util.Preconditions.checkNotNull;
import static com.google.inject.internal.util.Preconditions.checkState;

/**
 * This class represents an invokable configuration of the {@code protoc}
 * compiler. The actual executable is invoked using the plexus
 * {@link org.codehaus.plexus.util.cli.Commandline}.
 *
 * This class currently only supports generating java source files.
 *
 * @author [email protected] (Gregory Kick)
 */
final class Protoc {
    private final String executable;
    private final Set protoPathElements;
    private final Set protoFiles;
    private final File javaOutputDirectory;
    private final CommandLineUtils.StringStreamConsumer output;
    private final CommandLineUtils.StringStreamConsumer error;

    // protoc build type, java or prettypb
    private final String type;

    /**
     * Constructs a new instance. This should only be used by the {@link Builder}.
     *
     * @param executable          The path to the {@code protoc} executable.
     * @param protoPath           The directories in which to search for imports.
     * @param protoFiles          The proto source files to compile.
     * @param javaOutputDirectory The directory into which the java source files
     *                            will be generated.
     */
    private Protoc(String executable, Set protoPath,
                   Set protoFiles, File javaOutputDirectory, String type) {
        this.executable = checkNotNull(executable, "executable");
        this.protoPathElements = checkNotNull(protoPath, "protoPath");
        this.protoFiles = checkNotNull(protoFiles, "protoFiles");
        this.javaOutputDirectory = checkNotNull(javaOutputDirectory, "javaOutputDirectory");
        this.error = new CommandLineUtils.StringStreamConsumer();
        this.output = new CommandLineUtils.StringStreamConsumer();
        this.type = type;
    }

    /**
     * Invokes the {@code protoc} compiler using the configuration specified at
     * construction.
     *
     * @return The exit status of {@code protoc}.
     * @throws org.codehaus.plexus.util.cli.CommandLineException
     */
    public int compile() throws CommandLineException {
        Commandline cl = new Commandline();
        cl.setExecutable(executable);
        List args = getProtocArgs();
        cl.addArguments(args.toArray(new String[args.size()]));
        return CommandLineUtils.executeCommandLine(cl, null, output, error);
    }

    /**
     * Creates the command line arguments.
     *
     * This method has been made visible for testing only.
     *
     * @return A list consisting of the executable followed by any arguments.
     */
    List getProtocArgs() {
        final List args = new ArrayList<>();
        // add the executable
        for (File protoPathElement : protoPathElements) {
            args.add("--proto_path=" + protoPathElement);
        }
        args.add("--" + type + "_out=" + javaOutputDirectory);
        for (File protoFile : protoFiles) {
            args.add(protoFile.toString());
        }
        return Collections.unmodifiableList(args);
    }

    /**
     * @return the output
     */
    public String getOutput() {
        return output.getOutput();
    }

    /**
     * @return the error
     */
    public String getError() {
        return error.getOutput();
    }

    /**
     * This class builds {@link Protoc} instances.
     *
     * @author [email protected] (Gregory Kick)
     */
    static final class Builder {
        private final String executable;
        private final File javaOutputDirectory;
        private Set protopathElements;
        private Set protoFiles;
        private String type;

        /**
         * Constructs a new builder. The two parameters are present as they are
         * required for all {@link Protoc} instances.
         *
         * @param executable          The path to the {@code protoc} executable.
         * @param javaOutputDirectory The directory into which the java source files
         *                            will be generated.
         * @throws NullPointerException     If either of the arguments are {@code null}.
         * @throws IllegalArgumentException If the {@code javaOutputDirectory} is
         *                                  not a directory.
         */
        public Builder(String executable, File javaOutputDirectory) {
            this.executable = checkNotNull(executable, "executable");
            this.javaOutputDirectory = checkNotNull(javaOutputDirectory);
            checkArgument(javaOutputDirectory.isDirectory());
            this.protoFiles = new HashSet<>();
            this.protopathElements = new HashSet<>();
        }

        /**
         * Adds a proto file to be compiled. Proto files must be on the protopath
         * and this method will fail if a proto file is added without first adding a
         * parent directory to the protopath.
         *
         * @param protoFile
         * @return The builder.
         * @throws IllegalStateException If a proto file is added without first
         *                               adding a parent directory to the protopath.
         * @throws NullPointerException  If {@code protoFile} is {@code null}.
         */
        public Builder addProtoFile(File protoFile) {
            checkNotNull(protoFile);
            checkArgument(protoFile.isFile());
            checkArgument(protoFile.getName().endsWith(".proto"));
            checkProtoFileIsInProtoPath(protoFile);
            protoFiles.add(protoFile);
            return this;
        }

        private void checkProtoFileIsInProtoPath(File protoFile) {
            assert protoFile.isFile();
            checkState(protoFileIsInProtoPath(protoFile.getParentFile()));
        }

        private boolean protoFileIsInProtoPath(File directory) {
            assert directory.isDirectory();
            if (protopathElements.contains(directory)) {
                return true;
            } else {
                final File parentDirectory = directory.getParentFile();
                return (parentDirectory != null) && protoFileIsInProtoPath(parentDirectory);
            }
        }

        /**
         * @see #addProtoFile(java.io.File)
         */
        public Builder addProtoFiles(Iterable protoFiles) {
            for (File protoFile : protoFiles) {
                addProtoFile(protoFile);
            }
            return this;
        }

        public Builder addProtoPathElements(Iterable protoPathElements) {
            for (File protoPathElement : protoPathElements) {
                addProtoPathElement(protoPathElement);
            }
            return this;
        }

        /**
         * Adds the {@code protoPathElement} to the protopath.
         *
         * @param protoPathElement A directory to be searched for imported protocol
         *                         buffer definitions.
         * @return The builder.
         * @throws NullPointerException     If {@code protopathElement} is {@code null}.
         * @throws IllegalArgumentException If {@code protpathElement} is not a
         *                                  directory.
         */
        public Builder addProtoPathElement(File protoPathElement) {
            checkNotNull(protoPathElement);
            checkArgument(protoPathElement.isDirectory());
            protopathElements.add(protoPathElement);
            return this;
        }


        public Builder setType(String type) {
            this.type = type;
            return this;
        }
        /**
         * @return A configured {@link Protoc} instance.
         * @throws IllegalStateException If no proto files have been added.
         */
        public Protoc build() {
            checkState(!protoFiles.isEmpty());
            return new Protoc(executable, Collections.unmodifiableSet(protopathElements),
                    Collections.unmodifiableSet(protoFiles), javaOutputDirectory, type);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy