diff --git a/README.rst b/README.rst index 8ab9b27..8247f4b 100644 --- a/README.rst +++ b/README.rst @@ -13,15 +13,15 @@ which is useful for IT shops that develop in both of these languages. Functionality ------------- -* keeps the *setup.py* version in sync with the Maven project version by updating setup.py in the **process-sources** phase -* packages the Python module during the Maven **package** phase -* allows specifying which format should the Python module be distributed as: source, RPM, egg, tar, zip, etc. +* Keeps the *setup.py* version in sync with the Maven project version by updating setup.py in the **process-sources** phase. +* Packages the Python module during the Maven **package** phase. +* Allows specifying which format should the Python module be distributed as: source, RPM, egg, tar, zip, etc. +* Supports uploading packages to PyPI during the **deploy** phase. Configuration ------------- -Add the following to your *pom.xml* build section: -:: +Add the following to your *pom.xml* build section:: maven-python-mojos @@ -43,6 +43,45 @@ Add the following to your *pom.xml* build section: +This defaults to building an egg file. If you would like to use another distribution type, you may specify something else:: + + + package + + package + + + rpm + + + +If you wish to build multiple different distribtion types, you can add multiple ```` blocks with different types. +Supported types are egg, wheel, wininst, rpm, bdist, dumb, source, or docs. + +If you wish to upload your packaged files to PyPI, add the following:: + + + deploy + + deploy + + + +This will default to uploading all of the packages generated by the **package** goal. If you don't want that, you can specify +a distribution type in the same way as you can for packaging. The deploy goal supports all distribution types except for docs. + +You can also optionally specify a repository to upload to:: + + + deploy + + deploy + + + https://pypi.myserver.com + + + setup.py -------- @@ -50,12 +89,13 @@ To make the code runnable outside maven you can have a setup.py. If a setup-temp your source root setup.py will be replaced. setup-template.py --------- +----------------- -setup template allows for using maven controlled variables in your setup.py file. +Setup template allows for using maven controlled variables in your setup.py file. Set the *version* field in your *setup-template.py* to a hardcoded constant of **${VERSION}**, e.g. Set the *name* field in your *setup-template.py* to a hardcoded constant of **${PROJECT_NAME}**, e.g. :: + from setuptools import setup, find_packages setup( @@ -65,13 +105,10 @@ Set the *name* field in your *setup-template.py* to a hardcoded constant of **${ packages = find_packages('.') ) - Maven Repository ---------------- -Add the following plugin repository to your *pom.xml* in order to use this plugin: - -:: +Add the following plugin repository to your *pom.xml* in order to use this plugin:: @@ -79,8 +116,3 @@ Add the following plugin repository to your *pom.xml* in order to use this plugi https://jitpack.io - - - - - diff --git a/src/main/java/com/github/mojos/distribute/AbstractSetupCommandMojo.java b/src/main/java/com/github/mojos/distribute/AbstractSetupCommandMojo.java new file mode 100644 index 0000000..c38669e --- /dev/null +++ b/src/main/java/com/github/mojos/distribute/AbstractSetupCommandMojo.java @@ -0,0 +1,105 @@ +package com.github.mojos.distribute; + +/* + * Copyright 2001-2018 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.project.MavenProject; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Abstract class to run a setup.py command + */ +public abstract class AbstractSetupCommandMojo extends AbstractMojo { + /** + * @parameter default-value="${project}" + * @required + * @readonly + */ + private MavenProject project; + + /** + * @parameter default-value="python" + * @required + */ + private String pythonExecutable; + + /** + * Implementations should add any commands and arguments they wish to pass to setup.py here. + * + * @param args List to add setup.py commands to. + * @throws MojoExecutionException + */ + abstract void addSetupArgs(List args) throws MojoExecutionException; + + /* (non-Javadoc) + * @see org.apache.maven.plugin.AbstractMojo#execute() + */ + @Override + public void execute() throws MojoExecutionException { + final File buildDirectory = Paths.get(project.getBuild().getDirectory(), "maven-python").toFile(); + final String setupOutputCanonicalPath = project.getProperties().getProperty("python.distribute.plugin.setup.path", "src/main/python/setup.py"); + + try { + List args = new ArrayList<>(); + args.add(pythonExecutable); + args.add(setupOutputCanonicalPath); + addSetupArgs(args); + //execute setup script + ProcessBuilder processBuilder = new ProcessBuilder(args.toArray(new String[args.size()])); + processBuilder.directory(buildDirectory); + + Process pr = processBuilder.start(); + BufferedReader stdout = new BufferedReader(new InputStreamReader(pr.getInputStream())); + BufferedReader stderr = new BufferedReader(new InputStreamReader(pr.getErrorStream())); + while (!pr.waitFor(100, TimeUnit.MILLISECONDS)) { + stdout.lines().forEachOrdered(line -> getLog().debug(line)); + stderr.lines().forEachOrdered(line -> getLog().warn(line)); + } + + stdout.lines().forEachOrdered(line -> getLog().debug(line)); + stderr.lines().forEachOrdered(line -> getLog().warn(line)); + + int exitCode = pr.exitValue(); + if (exitCode != 0) { + throw new MojoExecutionException("'" + String.join(" ", processBuilder.command()) + "' returned error code " + exitCode); + } + } catch (FileNotFoundException e) { + throw new MojoExecutionException("Unable to find " + setupOutputCanonicalPath, e); + } catch (IOException e) { + throw new MojoExecutionException("Unable to read " + setupOutputCanonicalPath, e); + } catch (InterruptedException e) { + throw new MojoExecutionException("Unable to execute python " + setupOutputCanonicalPath, e); + } + } + + private void logErrorOrWarning(String line) { + if (line.toLowerCase().contains("error")) + getLog().error(line); + else + getLog().warn(line); + } +} diff --git a/src/main/java/com/github/mojos/distribute/DeployMojo.java b/src/main/java/com/github/mojos/distribute/DeployMojo.java new file mode 100644 index 0000000..d9d5e38 --- /dev/null +++ b/src/main/java/com/github/mojos/distribute/DeployMojo.java @@ -0,0 +1,57 @@ +package com.github.mojos.distribute; + +/* + * Copyright 2001-2018 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.apache.maven.plugin.MojoExecutionException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Uploads a Python module to PyPI using distribute + * + * @goal deploy + * @phase deploy + */ +public class DeployMojo extends AbstractSetupCommandMojo { + /** + * @parameter + */ + private String repository; + + /** + * @parameter + */ + private String distributionType; + + static List builtDistributionTypes = Collections.synchronizedList(new ArrayList<>()); + + @Override + void addSetupArgs(List args) throws MojoExecutionException { + if (distributionType != null) { + args.add(PackageMojo.getDistributionTypeArg(distributionType)); + } else { + args.addAll(builtDistributionTypes); + } + args.add("upload"); + if (repository != null) { + args.add("-r"); + args.add(repository); + } + } +} diff --git a/src/main/java/com/github/mojos/distribute/InstallMojo.java b/src/main/java/com/github/mojos/distribute/InstallMojo.java index b34adac..4b645db 100644 --- a/src/main/java/com/github/mojos/distribute/InstallMojo.java +++ b/src/main/java/com/github/mojos/distribute/InstallMojo.java @@ -1,7 +1,7 @@ package com.github.mojos.distribute; /* - * Copyright 2001-2005 The Apache Software Foundation. + * Copyright 2001-2018 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,55 +16,17 @@ * limitations under the License. */ -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStreamReader; - -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; +import java.util.List; /** * Installs a Python module using distribute - * + * * @goal install * @phase install */ -public class InstallMojo extends AbstractMojo { - - /* (non-Javadoc) - * @see org.apache.maven.plugin.AbstractMojo#execute() - */ - public void execute() throws MojoExecutionException, MojoFailureException { - - File setup = new File("src/main/python/setup.py"); - - try { - //execute setup script - ProcessBuilder t = new ProcessBuilder("python","setup.py","install"); - t.directory(new File("src/test/python")); - t.redirectErrorStream(true); - - Process pr = t.start(); - int exitCode = pr.waitFor(); - BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream())); - String line = ""; - while ((line = buf.readLine()) != null) { - getLog().info(line); - } - - if (exitCode != 0) { - throw new MojoExecutionException("'python setup.py install' returned error code " + exitCode); - } - - } catch (FileNotFoundException e) { - throw new MojoExecutionException("Unable to find " + setup.getPath(),e); - } catch (IOException e) { - throw new MojoExecutionException("Unable to read " + setup.getPath(),e); - } catch (InterruptedException e) { - throw new MojoExecutionException("Unable to execute python " + setup.getPath(),e); - } - } +public class InstallMojo extends AbstractSetupCommandMojo { + @Override + void addSetupArgs(List args) { + args.add("install"); + } } diff --git a/src/main/java/com/github/mojos/distribute/PackageMojo.java b/src/main/java/com/github/mojos/distribute/PackageMojo.java index 61be848..710eb3b 100644 --- a/src/main/java/com/github/mojos/distribute/PackageMojo.java +++ b/src/main/java/com/github/mojos/distribute/PackageMojo.java @@ -1,7 +1,7 @@ package com.github.mojos.distribute; /* - * Copyright 2001-2005 The Apache Software Foundation. + * Copyright 2001-2018 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,9 @@ * limitations under the License. */ -import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.project.MavenProject; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; +import java.util.List; /** * Packages a Python module using distribute @@ -34,72 +26,39 @@ * @goal package * @phase package */ -public class PackageMojo extends AbstractMojo { - /** - * @parameter default-value="${project}" - * @required - * @readonly - */ - private MavenProject project; - - /** - * @parameter default-value="python" - * @required - */ - private String pythonExecutable; - +public class PackageMojo extends AbstractSetupCommandMojo { /** * @parameter default-value="egg" * @required */ private String distributionType; + static String getDistributionTypeArg(String distributionType) throws MojoExecutionException { + switch (distributionType) { + case "egg": + case "wheel": + case "wininst": + case "rpm": + case "dumb": + return "bdist_" + distributionType; + case "bdist": + return "bdist"; + case "source": + return "sdist"; + case "docs": + return "build_sphinx"; + default: + throw new MojoExecutionException("Invalid distributionType (egg, wheel, wininst, rpm, bdist, dumb, source, or docs supported): " + distributionType); + } + } + /* (non-Javadoc) * @see org.apache.maven.plugin.AbstractMojo#execute() */ @Override - public void execute() throws MojoExecutionException { - final File buildDirectory = Paths.get(project.getBuild().getDirectory(), "maven-python").toFile(); - final String setupOutputCanonicalPath = project.getProperties().getProperty("python.distribute.plugin.setup.path"); - - try { - String bdistName; - switch(distributionType) { - case "egg": - bdistName = "bdist_egg"; - break; - case "wheel": - bdistName = "bdist_wheel"; - break; - default: - throw new MojoExecutionException("invalid distributionType (egg or wheel supported): " + distributionType); - } - - //execute setup script - ProcessBuilder processBuilder = new ProcessBuilder(pythonExecutable, setupOutputCanonicalPath, bdistName); - processBuilder.directory(buildDirectory); - - Process pr = processBuilder.start(); - BufferedReader stdout = new BufferedReader(new InputStreamReader(pr.getInputStream())); - BufferedReader stderr = new BufferedReader(new InputStreamReader(pr.getErrorStream())); - while (!pr.waitFor(100, TimeUnit.MILLISECONDS)) { - stdout.lines().forEachOrdered(line->getLog().info(line)); - stderr.lines().forEachOrdered(line->getLog().error(line)); - } - - stdout.lines().forEachOrdered(line->getLog().debug(line)); - stderr.lines().forEachOrdered(line->getLog().warn(line)); - - int exitCode = pr.exitValue(); - if (exitCode != 0) { - throw new MojoExecutionException("python setup.py returned error code " + exitCode); - } - } catch (FileNotFoundException e) { - throw new MojoExecutionException("Unable to find " + setupOutputCanonicalPath, e); - } catch (IOException e) { - throw new MojoExecutionException("Unable to read " + setupOutputCanonicalPath, e); - } catch (InterruptedException e) { - throw new MojoExecutionException("Unable to execute python " + setupOutputCanonicalPath, e); - } + public void addSetupArgs(List args) throws MojoExecutionException { + String distributionTypeArg = getDistributionTypeArg(distributionType); + args.add(distributionTypeArg); + DeployMojo.builtDistributionTypes.add(distributionTypeArg); } } \ No newline at end of file