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

io.github.mike10004.debianmaven.PackageMojo Maven / Gradle / Ivy

Go to download

This plugin helps building DEB packages from Maven projects. The packages can be used in DEB-based operating systems such as Debian and Ubuntu. The plugin uses external Debian tools to do the actual packaging.

The newest version!
package io.github.mike10004.debianmaven;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;

import javax.annotation.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.zip.GZIPOutputStream;

import static java.util.Objects.requireNonNull;

/**
 * Generates a Debian package.
 *
 * Uses Debian utilities: dpkg-deb and fakeroot.
 *
 * @goal package
 * @phase package
 * @requiresDependencyResolution
 */
public class PackageMojo extends AbstractDebianMojo
{

	/**
	 * Package priority.
	 * @required
	 * @parameter property="deb.package.priority" default-value="optional"
	 */
	protected String packagePriority;

	/**
	 * Package section.
	 * @required
	 * @parameter property="deb.package.section" default-value="contrib/utils"
	 */
	protected String packageSection;

	/**
	 * Package title.
	 * @required
	 * @parameter property="deb.package.title" default-value="${project.name}"
	 */
	protected String packageTitle;

	/**
	 * Package description.
	 * @required
	 * @parameter property="deb.package.description" default-value="${project.description}"
	 */
	protected String packageDescription;

	/**
	 * Package dependencies.
	 * @parameter property="deb.package.dependencies"
	 */
	protected String[] packageDependencies;

	/**
	 * Package conflicts.
	 * @parameter
	 */
	protected String[] packageConflicts;

	/**
	 * Project URL.
	 * @parameter property="${deb.project.url}" default-value="${project.organization.url}"
	 */
	protected String projectUrl;

	/**
	 * Project organization.
	 * @parameter property="deb.project.organization" default-value="${project.organization.name}"
	 */
	protected String projectOrganization;

	/**
	 * Package filename.
	 * @parameter property="deb.package.filename"
	 * @since 1.0.9
	 */
	protected String packageFilename;

	/**
	 * Additional lines in control files. Each element must have
	 * children {@code } and {@code }, and you may include
	 * {@code } to specify placement within the control file.
	 * @parameter
	 * @since 3.0
	 */
	protected ControlFileLine[] control;

	/**
	 * Other packaging files, such as {@code rules}, if needed.
	 * The file at each pathname will be copied to the {@code DEBIAN/}
	 * directory of the package. The filename will remain the same.
	 * See documentation on adding the Debian packaging files.
	 * for examples of other packaging files.
	 * @parameter
	 * @since 3.0
	 */
	protected File[] packagingFiles;

	/**
	 * Files that declare symbolic links. Each line of each file
	 * should be of the form {@code /path/to/source /path/to/link},
	 * where each path is absolute.
	 *
	 * 

* This mimics the functionality of the links files in a {@code debbuild} * source directory. * * @parameter * @since 3.3 */ protected File[] linksFiles; /** * List of options to pass to the {@code dpkg-deb --build} command. * These options are inserted between {@code dpkg-deb} and {@code --build package_file.deb}. * @parameter * @since 3.1 */ protected String[] dpkgDebBuildOptions; /** * List of environment variables pairs that will be provided to the {@code dpkg-deb --build} process. * Example: *

	 *     <dpkgDebBuildEnvironment>
	 *         <variable>
	 *             <name>FOO</name>
	 *             <value>bar</value>
	 *         </variable>
	 *     </dpkgDebBuildEnvironment>
	 * 
* @parameter * @since 3.1 */ protected NameValuePair[] dpkgDebBuildEnvironment; /** * Maven project object. * * @parameter property="project" */ @SuppressWarnings("unused") // injected private MavenProject project; // services private final LinkGenerator linkGenerator; public PackageMojo(LinkGenerator linkGenerator) { this.linkGenerator = requireNonNull(linkGenerator, "linkGenerator"); } public PackageMojo() { this(new FilesLinkGenerator()); } private void generateCopyright() throws IOException { File targetDocDir = new File(stageDir, "usr/share/doc/" + packageName); //noinspection ResultOfMethodCallIgnored targetDocDir.mkdirs(); File copyrightFile = new File(targetDocDir, "copyright"); if (!copyrightFile.exists()) { PrintWriter out = new PrintWriter(new FileWriter(copyrightFile)); out.println(packageName); out.println(projectUrl); out.println(); out.printf("Copyright %d %s\n", Calendar.getInstance().get(Calendar.YEAR), projectOrganization); out.println(); out.println("The entire code base may be distributed under the terms of the GNU General"); out.println("Public License (GPL)."); out.println(); out.println("See /usr/share/common-licenses/GPL"); out.close(); } } @Override protected File getPackageFile() { String filename = this.packageFilename; String packageArchitecture = this.packageArchitecture; if (packageArchitecture == null) { packageArchitecture = "all"; } if (filename == null) { filename = String.format("%s_%s-%s_%s.deb", packageName, getPackageVersion(), packageRevision, packageArchitecture); } return new File(targetDir, filename); } private void generateControl(File target) throws IOException { getLog().info("Generating control file: " + target); List lines = new ArrayList<>(generateKnownControlLines()); if (control != null) { lines.addAll(Arrays.asList(control)); } lines = ControlFileLine.sorted(lines); try (PrintWriter out = new PrintWriter(new FileWriter(target))) { for (ControlFileLine line : lines) { out.println(String.format("%s: %s", line.getField(), line.getValue())); } } } private static void addIfValueNotNull(String field, @Nullable String value, Collection lines) { requireNonNull(field, "field"); if (value != null) { lines.add(new ControlFileLine(field, value, null)); } } private List generateKnownControlLines() throws IOException { List lines = new ArrayList<>(); addIfValueNotNull("Package", requireNonNull(packageName, "packageName"), lines); addIfValueNotNull("Version", requireNonNull(getPackageVersion()), lines); addIfValueNotNull("Section", packageSection, lines); addIfValueNotNull("Priority", packagePriority, lines); addIfValueNotNull("Architecture", requireNonNull(packageArchitecture), lines); if (packageDependencies != null && packageDependencies.length > 0) { addIfValueNotNull("Depends", StringUtils.join(processVersion(packageDependencies), ", "), lines); } if (packageConflicts != null && packageConflicts.length > 0) { addIfValueNotNull("Conflicts", StringUtils.join(processVersion(packageConflicts), ", "), lines); } long installedSizeKb = 1 + FileUtils.sizeOfDirectory(stageDir) / 1024; addIfValueNotNull("Installed-Size", String.valueOf(installedSizeKb), lines); String value = null; if (maintainerName != null && maintainerEmail == null) { value = maintainerName; } else if (maintainerName == null && maintainerEmail != null) { value = String.format("<%s>", maintainerEmail); } else //noinspection ConstantConditions if (maintainerName != null && maintainerEmail != null) { value = String.format("%s <%s>", maintainerName, maintainerEmail); } addIfValueNotNull("Maintainer", value, lines); addIfValueNotNull("Homepage", projectUrl, lines); addIfValueNotNull("Description", formatDescription(packageTitle, packageDescription), lines); return lines; } private String formatDescription(String packageTitle, String description) { if (packageTitle == null) { packageTitle = "Package Title"; } if (packageTitle.length() > 60) { getLog().warn("Package title will be truncated to the upper limit of 60 characters."); packageTitle = packageTitle.substring(0, 60); } StringBuilder sb = new StringBuilder(); sb.append(packageTitle); if (description != null) { sb.append(System.lineSeparator()); String descFormatted = packageDescription.trim(); descFormatted = descFormatted.replaceAll("\\s+", " "); sb.append(' ').append(descFormatted.stripTrailing()); } return sb.toString(); } private void generateConffiles(File target) throws IOException { List conffiles = new ArrayList<>(); File configDir = new File(stageDir, "etc"); if (configDir.exists()) { Collection files = FileUtils.listFiles(configDir, null, true); for (File f : files) { if (f.isFile()) conffiles.add(f.toString().substring(stageDir.toString().length())); } } if (conffiles.size() > 0) { PrintWriter out = new PrintWriter(new FileWriter(target)); for (String fname : conffiles) out.println(fname); out.close(); } } private void generateMd5Sums(File target) throws IOException { try (PrintWriter out = new PrintWriter(new FileWriter(target))) { Collection files = FileUtils.listFiles(stageDir, null, true); for (File f : files) { // check whether the file is a non-regular file if (!f.isFile()) continue; // check whether the file is a possible link if (!f.getAbsolutePath().equals(f.getCanonicalPath())) continue; String fname = f.toString().substring(stageDir.toString().length() + 1); if (!fname.startsWith("DEBIAN")) { FileInputStream fis = new FileInputStream(f); String md5 = DigestUtils.md5Hex(fis); fis.close(); out.printf("%s %s\n", md5, fname); } } } } private void generateManPages() throws MojoExecutionException, IOException { File source = new File(sourceDir, "man"); if (!source.exists()) { getLog().info("No manual page directory found: "+source); return; } int npages = 0; Collection files = FileUtils.listFiles(source, null, true); for (File f : files) { if (f.isFile() && f.getName().matches(".*[.][1-9]$")) { char section = f.getName().charAt(f.getName().length()-1); File target = new File(stageDir, String.format("usr/share/man/man%c/%s.gz", section, f.getName())); //noinspection ResultOfMethodCallIgnored target.getParentFile().mkdirs(); String[] cmd = {"groff", "-man", "-Tascii", f.getPath()}; byte[] processOutput = createProcessRunner().runProcessWithOutput(cmd, NonzeroProcessExitAction.throwMojoExecutionException()); try (GZIPOutputStream os = new GZIPOutputStream(new FileOutputStream(target))) { os.write(processOutput); } npages++; } } if (npages == 0) { getLog().debug("No manual pages found in directory: " + source); } } @SuppressWarnings("CollectionAddAllCanBeReplacedWithConstructor") private void generatePackage() throws IOException, MojoExecutionException { List cmd = new ArrayList<>(); cmd.addAll(Arrays.asList("fakeroot", "--", "dpkg-deb")); if (dpkgDebBuildOptions != null) { getLog().info("using dpkg-deb options " + Arrays.toString(dpkgDebBuildOptions)); cmd.addAll(Arrays.asList(dpkgDebBuildOptions)); } cmd.addAll(Arrays.asList("--build", stageDir.toString(), getPackageFile().toString())); runProcess(cmd.toArray(new String[0]), buildDpkgDebBuildEnvironmentMap()); } private Map buildDpkgDebBuildEnvironmentMap() throws MojoExecutionException { Map env = new LinkedHashMap<>(); Set nameUsed = new TreeSet<>(); if (dpkgDebBuildEnvironment != null) { for (NameValuePair variable : dpkgDebBuildEnvironment) { String name = variable.getName(); if (nameUsed.contains(name)) { throw new MojoExecutionException("names of variables in must be unique; duplicate: " + StringUtils.abbreviate(name, 256)); } env.put(name, variable.getValue()); nameUsed.add(name); } } return env; } protected void executeDebMojo() throws MojoExecutionException { File targetDebDir = new File(stageDir, "DEBIAN"); try { FileUtils.deleteDirectory(targetDebDir); //noinspection ResultOfMethodCallIgnored targetDebDir.mkdirs(); if (!targetDebDir.isDirectory()) { throw new MojoExecutionException("Unable to create directory: " + targetDebDir); } generateManPages(); generateCopyright(); generateConffiles(new File(targetDebDir, "conffiles")); generateControl(new File(targetDebDir, "control")); generateMd5Sums(new File(targetDebDir, "md5sums")); copyOtherPackagingFiles(targetDebDir.toPath()); linkGenerator.generateLinks(linksFiles, stageDir.toPath()); generatePackage(); } catch (IOException e) { getLog().error(e.toString()); throw new MojoExecutionException(e.toString()); } } private void copyOtherPackagingFiles(Path destinationDir) throws IOException, MojoExecutionException { File[] files = packagingFiles; if (files == null) { return; } files = Arrays.copyOf(files, files.length); Set filenameSet = new HashSet<>(); for (File file : files) { String filename = file.getName(); if (filenameSet.contains(filename)) { throw new MojoExecutionException("packaging files must have unique filenames (because they all get copied to the same directory); found duplicate " + StringUtils.abbreviate(filename, 128)); } filenameSet.add(filename); Path destinationFile = destinationDir.resolve(filename); java.nio.file.Files.copy(file.toPath(), destinationFile, StandardCopyOption.COPY_ATTRIBUTES); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy