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

io.github.mike10004.debianmaven.FilesLinkGenerator 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.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.MojoExecutionException;

import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static java.util.Objects.requireNonNull;

/**
 * Link generator implementation that uses {@link java.nio.file.Files}.
 */
public class FilesLinkGenerator implements LinkGenerator {

    private final FilesSymlinker symlinker;
    private final LinksLineParser linksLineParser;
    private final Charset encoding;

    public FilesLinkGenerator() {
        this(new DefaultLinksLineParser(), FilesSymlinker.createDefault(), StandardCharsets.UTF_8);
    }

    public FilesLinkGenerator(LinksLineParser linksLineParser,
                              FilesSymlinker symlinker,
                              Charset linksFileEncoding) {
        this.linksLineParser = requireNonNull(linksLineParser);
        this.symlinker = requireNonNull(symlinker);
        this.encoding = requireNonNull(linksFileEncoding);
    }

    @Override
    public void generateLinks(@Nullable File[] linksFiles, Path stageDir) throws IOException, MojoExecutionException {
        if (linksFiles == null || linksFiles.length == 0) {
            return;
        }
        List linksFileTexts = new ArrayList<>();
        for (File linksFile : linksFiles) {
            linksFileTexts.add(readLinksFileText(linksFile));
        }
        List specifications = new ArrayList<>();
        for (String linksFileText : linksFileTexts) {
            List lines = linksFileText.lines().collect(Collectors.toUnmodifiableList());
            for (String line : lines) {
                @Nullable LinkSpecification specification = linksLineParser.parseSpecification(line);
                if (specification != null) {
                    specifications.add(specification);
                }
            }
        }
        for (LinkSpecification specification : specifications) {
            createLink(specification.sourcePath(), specification.linkPath(), stageDir);
        }
    }

    private String readLinksFileText(File linksFile) throws IOException {
        return java.nio.file.Files.readString(linksFile.toPath(), encoding);
    }

    public interface FilesSymlinker {

        void createSymlink(Path source, Path link, FileAttribute...attrs) throws IOException;

        static FilesSymlinker createDefault() {
            return new DefaultFilesSymlinker();
        }
    }

    private static class DefaultFilesSymlinker implements  FilesSymlinker {

        @Override
        public void createSymlink(Path source, Path link, FileAttribute... attrs) throws IOException {
            try {
                java.nio.file.Files.createSymbolicLink(link, source, attrs);
            } catch (FileAlreadyExistsException ignore) {
                if (java.nio.file.Files.isSymbolicLink(link) && source.equals(java.nio.file.Files.readSymbolicLink(link))) {
                    // then we have no work to do
                    return;
                }
                // try deleting and relinking; if that fails, let the build fail
                //noinspection ResultOfMethodCallIgnored
                link.toFile().delete();
                java.nio.file.Files.createSymbolicLink(link, source, attrs);
            }
        }
    }

    /**
     * Source paths are transformed to be relative to the link path.
     * @param absoluteSourcePath absolute source path
     * @param absoluteLinkPath absolute link path
     * @param stageDir deb filesystem root
     * @throws IOException
     */
    public void createLink(String absoluteSourcePath, String absoluteLinkPath, Path stageDir) throws IOException, MojoExecutionException {
        Path stagedSourcePath = relativizeSourcePath(absoluteSourcePath, absoluteLinkPath);
        Path stagedLinkPath = stageDir.resolve(removeRoot(absoluteLinkPath));
        ensureParentExists(stagedLinkPath);
        symlinker.createSymlink(stagedSourcePath, stagedLinkPath);
    }

    private static Path removeRoot(String absolutePathStr) {
        return Path.of("/").relativize(Path.of(absolutePathStr));
    }

    private void ensureParentExists(Path linkPath) throws IOException {
        Path parent = linkPath.getParent();
        File parentFile = parent.toFile();
        if (!parentFile.isDirectory()) {
            //noinspection ResultOfMethodCallIgnored
            parentFile.mkdirs();
        }
    }

    private static class InvalidLinkException extends MojoExecutionException {
        public InvalidLinkException(String link) {
            super("links must be absolute: " + StringUtils.abbreviate(link, 512));
        }
    }

    /**
     * Relativizes a source path against the link path.
     * The exception is where they share no common ancestor (except /),
     * in which case the return value is an absolute path.
     * @param absoluteSourcePathStr
     * @param absoluteLinkPathStr
     * @return
     * @throws IOException
     * @throws MojoExecutionException
     */
    public Path relativizeSourcePath(String absoluteSourcePathStr, String absoluteLinkPathStr) throws IOException, MojoExecutionException {
        absoluteSourcePathStr = FilenameUtils.normalizeNoEndSeparator(absoluteSourcePathStr, true);
        absoluteLinkPathStr = FilenameUtils.normalizeNoEndSeparator(absoluteLinkPathStr, true);
        Path absoluteSourcePath = Path.of(absoluteSourcePathStr);
        if (!absoluteSourcePath.isAbsolute()) {
            throw new InvalidLinkException(absoluteSourcePathStr);
        }
        Path absoluteLinkPath = Path.of(absoluteLinkPathStr);
        if (!absoluteLinkPath.isAbsolute()) {
            throw new InvalidLinkException(absoluteLinkPathStr);
        }
        if (!haveCommonAncestor(absoluteSourcePath, absoluteLinkPath)) {
            return absoluteSourcePath;
        }
        Path relativized = absoluteLinkPath.relativize(absoluteSourcePath);
        /*
         * This doesn't quite do the job we want because its semantics
         * are different from symlinks
         */
        relativized = stripFirstDotDot(relativized);
        return relativized;
    }

    private static Path stripFirstDotDot(Path p) {
        String pathStr = p.toString();
        pathStr = StringUtils.removeStart(pathStr, "../");
        return Path.of(pathStr);
    }

    private static boolean haveCommonAncestor(Path a, Path b) {
        Path a1 = a.iterator().next();
        Path b1 = b.iterator().next();
        return a1.equals(b1);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy