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

kr.motd.maven.sphinx.SphinxMojo Maven / Gradle / Ivy

The newest version!
package kr.motd.maven.sphinx;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.reporting.MavenReport;
import org.apache.maven.reporting.MavenReportException;
import org.apache.maven.settings.Proxy;
import org.codehaus.doxia.sink.Sink;

/**
 * Sphinx Mojo
 */
@Mojo(name = "generate", defaultPhase = LifecyclePhase.SITE, requiresReports = true)
public class SphinxMojo extends AbstractMojo implements MavenReport {

    @Parameter( defaultValue = "${session}", readonly = true )
    private MavenSession session;

    /**
     * Boolean to keep default site and make Sphinx doc a project report
     */
    @Parameter(property = "sphinx.asReport", defaultValue = "false", alias = "asReport")
    private boolean asReport;

    /**
     * Name of the report in "Project reports" section (default Maven site)
     */
    @Parameter(property = "sphinx.name", defaultValue = "Sphinx-Docs", alias = "name")
    private String name;

    /**
     * Description of the report in "Project reports" section (default Maven site)
     */
    @Parameter(property = "sphinx.description", defaultValue = "Documentation using Python Sphinx Package", alias = "description")
    private String description;

    /**
     * Sub-directory to store Sphinx output (used only if {@code asReport} is true)
     */
    private static final String sphinxSiteSubDirectory = "sphinx";

    private static final String[] CRUFTS = {
            "css/maven-base.css",
            "css/maven-theme.css",
            "css/print.css",
            "css/site.css",
            "css",
            "images/logos/build-by-maven-black.png",
            "images/logos/build-by-maven-white.png",
            "images/logos/maven-feather.png",
            "images/logos",
            "images/collapsed.gif",
            "images/expanded.gif",
            "images/external.png",
            "images/icon_error_sml.gif",
            "images/icon_info_sml.gif",
            "images/icon_success_sml.gif",
            "images/icon_warning_sml.gif",
            "images/newwindow.png",
            "images"
    };

    /**
     * The directory containing the sphinx doc source.
     */
    @Parameter(property = "sphinx.srcDir", defaultValue = "${basedir}/src/site/sphinx", required = true)
    private File sourceDirectory;

    /**
     * The directory containing the sphinx {@code conf.py} file.
     */
    @Parameter(property = "sphinx.cfgDir")
    private File configDirectory;

    /**
     * Directory where reports will go.
     */
    @Parameter(defaultValue = "${project.reporting.outputDirectory}", required = true)
    private File outputDirectory;

    /**
     * The base URL of the Sphinx binary, which will be used when downloading the Sphinx binary; must start
     * with {@code http://}, {@code https://} or {@code file://}.
     */
    @Parameter(property = "sphinx.binUrl")
    private String binaryUrl = SphinxRunner.DEFAULT_BINARY_URL;

    /**
     * The directory for Sphinx binary cache.
     */
    @Parameter(property = "sphinx.binCacheDir", defaultValue = "${settings.localRepository}/kr/motd/maven/sphinx-binary", required = true)
    private File binaryCacheDir;

    /**
     * The environment variables to set when launching Sphinx.
     */
    @Parameter(property = "sphinx.env")
    private Map environments = Collections.emptyMap();

    /**
     * The path to Graphviz {@code dot} binary.
     */
    @Parameter(property = "sphinx.dotBin")
    private String dotBinary;

    /**
     * The builder to use. See Available builders
     * for a list of supported builders.
     */
    @Parameter(property = "sphinx.builder", required = true, alias = "builder", defaultValue = "html")
    private String builder;

    /**
     * The tags
     * to pass to Sphinx.
     */
    @Parameter(property = "sphinx.tags", alias = "tags")
    private List tags;

    /**
     * Whether Sphinx should generate verbose output.
     */
    @Parameter(property = "sphinx.verbose", defaultValue = "true", required = true, alias = "verbose")
    private boolean verbose;

    /**
     * Whether Sphinx should print full traceback on exception.
     */
    @Parameter(property = "sphinx.traceback", defaultValue = "true", required = true, alias = "traceback")
    private boolean traceback;

    /**
     * Whether Sphinx should treat warnings as errors.
     */
    @Parameter(property = "sphinx.warningAsErrors", defaultValue = "false", required = true, alias = "warningAsErrors")
    private boolean warningsAsErrors;

    /**
     * Whether Sphinx should generate output for all files instead of only the changed ones.
     */
    @Parameter(property = "sphinx.force", defaultValue = "false", required = true, alias = "force")
    private boolean force;

    /**
     * Whether Sphinx execution should be skipped.
     */
    @Parameter(property = "sphinx.skip", defaultValue = "false", required = true, alias = "skip")
    private boolean skip;

    /**
     * Whether Sphinx should use doctree cache.
     */
    @Parameter(property = "sphinx.useDoctreeCache", defaultValue = "false", required = true, alias = "useDocTreeCache")
    private boolean useDoctreeCache;

    /**
     * The directory containing Sphinx doctree cache.
     */
    @Parameter(property = "sphinx.doctreeCacheDir", defaultValue = "${project.reporting.outputDirectory}/.doctrees", required = true, alias = "doctreeCacheDir")
    private File doctreeCacheDir;

    /**
     * Whether Sphinx should use 'make mode' ({@code -M} option) instead of 'build mode' ({@code -b} option).
     */
    @Parameter(property = "sphinx.useMakeMode", defaultValue = "false", required = true, alias = "useMakeMode")
    private boolean useMakeMode;


    @Override
    public void execute() throws MojoExecutionException {
        if (skip) {
            getLog().info("Skipping Sphinx execution.");
            return;
        }

        final File sourceDirectory = canonicalize(this.sourceDirectory);
        final File outputDirectory = getReportOutputDirectory();
        final File binaryCacheDir = canonicalize(this.binaryCacheDir);
        final File doctreeCacheDir = useDoctreeCache ? canonicalize(this.doctreeCacheDir) : null;

        // to avoid Maven overriding resulting index.html, update index.rst to force re-building of index
        if (isHtmlReport()) {
            new File(sourceDirectory.getPath() + "/index.rst").setLastModified(System.currentTimeMillis());
        }

        configureProxy();

        try {
            final SphinxRunner sphinxRunner = new SphinxRunner(
                    binaryUrl, binaryCacheDir, environments,
                    "".equals(dotBinary) ? null : dotBinary,
                    new SphinxRunnerLogger() {
                        @Override
                        public void log(String msg) {
                            getLog().info(msg);
                        }
                    });

            getLog().info("Running Sphinx; output will be placed in " + outputDirectory);
            final List args = getSphinxRunnerCmdLine(sourceDirectory, outputDirectory, doctreeCacheDir);
            if (sphinxRunner.run(sourceDirectory, args) != 0) {
                throw new MavenReportException("Sphinx report generation failed");
            }

            SphinxUtil.convertLineSeparators(outputDirectory);
            // only delete crufts if Maven site is overridden (default behavior)
            if (!asReport) {
                deleteCruft(outputDirectory);
            }
        } catch (Exception e) {
            throw new MojoExecutionException("Failed to run the report", e);
        }
    }

    private static File canonicalize(File directory) throws MojoExecutionException {
        if (directory == null) {
            return null;
        }

        try {
            directory.mkdirs();
            return directory.getCanonicalFile();
        } catch (IOException e) {
            throw new MojoExecutionException("failed to create a directory: " + directory, e);
        }
    }

    private void configureProxy() {
        if (session != null) {
            final Proxy proxy = session.getSettings().getActiveProxy();
            if (proxy != null && proxy.getProtocol() != null &&
                proxy.getProtocol().toLowerCase().startsWith("http")) {
                for (String protocol : Arrays.asList("http", "https")) {
                    System.setProperty(protocol + ".proxyHost", proxy.getHost());
                    System.setProperty(protocol + ".proxyPort", String.valueOf(proxy.getPort()));
                    if (proxy.getUsername() != null && proxy.getPassword() != null) {
                        System.setProperty(protocol + ".proxyUser", proxy.getUsername());
                        System.setProperty(protocol + ".proxyPassword", proxy.getPassword());
                    }
                    if (proxy.getNonProxyHosts() != null) {
                        System.setProperty(protocol + ".nonProxyHosts", proxy.getNonProxyHosts());
                    }
                }
                if (System.getProperty("jdk.http.auth.tunneling.disabledSchemes") == null) {
                    System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
                }
                if (System.getProperty("https.protocols") == null) {
                    System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");
                }
            }
        }
    }

    /**
     * Deletes the crufts generated by maven-site-plugin.
     */
    private static void deleteCruft(final File outputDirectory) {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                for (String c : CRUFTS) {
                    new File(outputDirectory, c.replace('/', File.separatorChar)).delete();
                }
            }
        });
    }

    @Override
    public void generate(
            @SuppressWarnings("deprecation") Sink sink, Locale locale) throws MavenReportException {
        try {
            execute();
        } catch (SphinxException e) {
            final MavenReportException cause = new MavenReportException(e.getMessage());
            if (e.getCause() != null) {
                cause.initCause(e.getCause());
            }
            throw cause;
        } catch (Exception e) {
            throw new MavenReportException("Error generating a Sphinx report:", e);
        }
    }

    private boolean isHtmlReport() {
        return asReport && "html".equals(builder);
    }

    @Override
    public String getOutputName() {
        if (isHtmlReport()) {
            return sphinxSiteSubDirectory + "/index";   // if report, clicking on report will lead to sphinx/index.html
        }
        return "Python-Sphinx";
    }

    @Override
    public String getCategoryName() {
        return MavenReport.CATEGORY_PROJECT_REPORTS;
    }

    @Override
    public String getName(Locale locale) {
        return name;
    }

    @Override
    public String getDescription(Locale locale) {
        return description;
    }

    @Override
    public void setReportOutputDirectory(File outputDirectory) {
        // Unused
    }

    @Override
    public File getReportOutputDirectory() {
        // if documentation is generated as a report
        final File actual;
        if (asReport) {
            // output sphinx doc to outputDirectory/sphinx instead of outputDirectory
            actual = new File(outputDirectory.getPath() + File.separator + sphinxSiteSubDirectory);
        } else {
            actual = outputDirectory;
        }
        try {
            return canonicalize(actual);
        } catch (MojoExecutionException e) {
            getLog().warn("Failed to canonicalize: " + actual, e);
            return actual;
        }
    }

    @Override
    public boolean isExternalReport() {
        return true;
    }

    @Override
    public boolean canGenerateReport() {
        return true;
    }

    /**
     * Build the Sphinx Command line options.
     */
    private List getSphinxRunnerCmdLine(File sourceDirectory, File outputDirectory, File doctreeCacheDir) {
        final List args = new ArrayList<>();

        args.add(useMakeMode ? "-M" : "-b");
        args.add(builder);

        if (verbose) {
            args.add("-v");
        } else {
            args.add("-Q");
        }

        if (traceback) {
            args.add("-T");
        }

        if (warningsAsErrors) {
            args.add("-W");
        }

        if (force) {
            args.add("-a");
            args.add("-E");
        }

        if (configDirectory != null) {
            args.add("-c");
            args.add(configDirectory.getPath());
        }

        if (doctreeCacheDir != null) {
            args.add("-d");
            args.add(doctreeCacheDir.getPath());
        }

        if (tags != null && !tags.isEmpty()) {
            for (String tag : tags) {
                args.add("-t");
                args.add(tag);
            }
        }

        args.add("-n");

        args.add(sourceDirectory.getPath());
        args.add(outputDirectory.getPath());

        return args;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy