src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java
branchJDK-8200758-branch
changeset 58994 b09ba68c6a19
parent 58993 b5e1baa9d2c3
child 58995 de1413ae214c
--- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java	Wed Nov 06 07:20:07 2019 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,487 +0,0 @@
-/*
- * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.jpackage.internal;
-
-import java.io.*;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-
-import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.PosixFilePermissions;
-import java.text.MessageFormat;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import static jdk.jpackage.internal.LinuxAppBundler.LINUX_INSTALL_DIR;
-import static jdk.jpackage.internal.OverridableResource.createResource;
-
-import static jdk.jpackage.internal.StandardBundlerParam.*;
-
-
-public class LinuxDebBundler extends LinuxPackageBundler {
-
-    // Debian rules for package naming are used here
-    // https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Source
-    //
-    // Package names must consist only of lower case letters (a-z),
-    // digits (0-9), plus (+) and minus (-) signs, and periods (.).
-    // They must be at least two characters long and
-    // must start with an alphanumeric character.
-    //
-    private static final Pattern DEB_PACKAGE_NAME_PATTERN =
-            Pattern.compile("^[a-z][a-z\\d\\+\\-\\.]+");
-
-    private static final BundlerParamInfo<String> PACKAGE_NAME =
-            new StandardBundlerParam<> (
-            Arguments.CLIOptions.LINUX_BUNDLE_NAME.getId(),
-            String.class,
-            params -> {
-                String nm = APP_NAME.fetchFrom(params);
-
-                if (nm == null) return null;
-
-                // make sure to lower case and spaces/underscores become dashes
-                nm = nm.toLowerCase().replaceAll("[ _]", "-");
-                return nm;
-            },
-            (s, p) -> {
-                if (!DEB_PACKAGE_NAME_PATTERN.matcher(s).matches()) {
-                    throw new IllegalArgumentException(new ConfigException(
-                            MessageFormat.format(I18N.getString(
-                            "error.invalid-value-for-package-name"), s),
-                            I18N.getString(
-                            "error.invalid-value-for-package-name.advice")));
-                }
-
-                return s;
-            });
-
-    private final static String TOOL_DPKG_DEB = "dpkg-deb";
-    private final static String TOOL_DPKG = "dpkg";
-    private final static String TOOL_FAKEROOT = "fakeroot";
-
-    private final static String DEB_ARCH;
-    static {
-        String debArch;
-        try {
-            debArch = Executor.of(TOOL_DPKG, "--print-architecture").saveOutput(
-                    true).executeExpectSuccess().getOutput().get(0);
-        } catch (IOException ex) {
-            debArch = null;
-        }
-        DEB_ARCH = debArch;
-    }
-
-    private static final BundlerParamInfo<String> FULL_PACKAGE_NAME =
-            new StandardBundlerParam<>(
-                    "linux.deb.fullPackageName", String.class, params -> {
-                        return PACKAGE_NAME.fetchFrom(params)
-                            + "_" + VERSION.fetchFrom(params)
-                            + "-" + RELEASE.fetchFrom(params)
-                            + "_" + DEB_ARCH;
-                    }, (s, p) -> s);
-
-    private static final BundlerParamInfo<String> EMAIL =
-            new StandardBundlerParam<> (
-            Arguments.CLIOptions.LINUX_DEB_MAINTAINER.getId(),
-            String.class,
-            params -> "Unknown",
-            (s, p) -> s);
-
-    private static final BundlerParamInfo<String> MAINTAINER =
-            new StandardBundlerParam<> (
-            BundleParams.PARAM_MAINTAINER,
-            String.class,
-            params -> VENDOR.fetchFrom(params) + " <"
-                    + EMAIL.fetchFrom(params) + ">",
-            (s, p) -> s);
-
-    private static final BundlerParamInfo<String> SECTION =
-            new StandardBundlerParam<>(
-            Arguments.CLIOptions.LINUX_CATEGORY.getId(),
-            String.class,
-            params -> "misc",
-            (s, p) -> s);
-
-    private static final BundlerParamInfo<String> LICENSE_TEXT =
-            new StandardBundlerParam<> (
-            "linux.deb.licenseText",
-            String.class,
-            params -> {
-                try {
-                    String licenseFile = LICENSE_FILE.fetchFrom(params);
-                    if (licenseFile != null) {
-                        return Files.readString(Path.of(licenseFile));
-                    }
-                } catch (IOException e) {
-                    Log.verbose(e);
-                }
-                return "Unknown";
-            },
-            (s, p) -> s);
-
-    public LinuxDebBundler() {
-        super(PACKAGE_NAME);
-    }
-
-    @Override
-    public void doValidate(Map<String, ? super Object> params)
-            throws ConfigException {
-
-        // Show warning if license file is missing
-        if (LICENSE_FILE.fetchFrom(params) == null) {
-            Log.verbose(I18N.getString("message.debs-like-licenses"));
-        }
-    }
-
-    @Override
-    protected List<ToolValidator> getToolValidators(
-            Map<String, ? super Object> params) {
-        return Stream.of(TOOL_DPKG_DEB, TOOL_DPKG, TOOL_FAKEROOT).map(
-                ToolValidator::new).collect(Collectors.toList());
-    }
-
-    @Override
-    protected File buildPackageBundle(
-            Map<String, String> replacementData,
-            Map<String, ? super Object> params, File outputParentDir) throws
-            PackagerException, IOException {
-
-        prepareProjectConfig(replacementData, params);
-        adjustPermissionsRecursive(createMetaPackage(params).sourceRoot().toFile());
-        return buildDeb(params, outputParentDir);
-    }
-
-    private static final Pattern PACKAGE_NAME_REGEX = Pattern.compile("^(^\\S+):");
-
-    @Override
-    protected void initLibProvidersLookup(
-            Map<String, ? super Object> params,
-            LibProvidersLookup libProvidersLookup) {
-
-        //
-        // `dpkg -S` command does glob pattern lookup. If not the absolute path
-        // to the file is specified it might return mltiple package names.
-        // Even for full paths multiple package names can be returned as
-        // it is OK for multiple packages to provide the same file. `/opt`
-        // directory is such an example. So we have to deal with multiple
-        // packages per file situation.
-        //
-        // E.g.: `dpkg -S libc.so.6` command reports three packages:
-        // libc6-x32: /libx32/libc.so.6
-        // libc6:amd64: /lib/x86_64-linux-gnu/libc.so.6
-        // libc6-i386: /lib32/libc.so.6
-        // `:amd64` is architecture suffix and can (should) be dropped.
-        // Still need to decide what package to choose from three.
-        // libc6-x32 and libc6-i386 both depend on libc6:
-        // $ dpkg -s libc6-x32
-        // Package: libc6-x32
-        // Status: install ok installed
-        // Priority: optional
-        // Section: libs
-        // Installed-Size: 10840
-        // Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
-        // Architecture: amd64
-        // Source: glibc
-        // Version: 2.23-0ubuntu10
-        // Depends: libc6 (= 2.23-0ubuntu10)
-        //
-        // We can dive into tracking dependencies, but this would be overly
-        // complicated.
-        //
-        // For simplicity lets consider the following rules:
-        // 1. If there is one item in `dpkg -S` output, accept it.
-        // 2. If there are multiple items in `dpkg -S` output and there is at
-        //  least one item with the default arch suffix (DEB_ARCH),
-        //  accept only these items.
-        // 3. If there are multiple items in `dpkg -S` output and there are
-        //  no with the default arch suffix (DEB_ARCH), accept all items.
-        // So lets use this heuristics: don't accept packages for whom
-        //  `dpkg -p` command fails.
-        // 4. Arch suffix should be stripped from accepted package names.
-        //
-
-        libProvidersLookup.setPackageLookup(file -> {
-            Set<String> archPackages = new HashSet<>();
-            Set<String> otherPackages = new HashSet<>();
-
-            Executor.of(TOOL_DPKG, "-S", file.toString())
-                    .saveOutput(true).executeExpectSuccess()
-                    .getOutput().forEach(line -> {
-                        Matcher matcher = PACKAGE_NAME_REGEX.matcher(line);
-                        if (matcher.find()) {
-                            String name = matcher.group(1);
-                            if (name.endsWith(":" + DEB_ARCH)) {
-                                // Strip arch suffix
-                                name = name.substring(0,
-                                        name.length() - (DEB_ARCH.length() + 1));
-                                archPackages.add(name);
-                            } else {
-                                otherPackages.add(name);
-                            }
-                        }
-                    });
-
-            if (!archPackages.isEmpty()) {
-                return archPackages.stream();
-            }
-            return otherPackages.stream();
-        });
-    }
-
-    @Override
-    protected List<ConfigException> verifyOutputBundle(
-            Map<String, ? super Object> params, Path packageBundle) {
-        List<ConfigException> errors = new ArrayList<>();
-
-        String controlFileName = "control";
-
-        List<PackageProperty> properties = List.of(
-                new PackageProperty("Package", PACKAGE_NAME.fetchFrom(params),
-                        "APPLICATION_PACKAGE", controlFileName),
-                new PackageProperty("Version", String.format("%s-%s",
-                        VERSION.fetchFrom(params), RELEASE.fetchFrom(params)),
-                        "APPLICATION_VERSION-APPLICATION_RELEASE",
-                        controlFileName),
-                new PackageProperty("Architecture", DEB_ARCH, "APPLICATION_ARCH",
-                        controlFileName));
-
-        List<String> cmdline = new ArrayList<>(List.of(TOOL_DPKG_DEB, "-f",
-                packageBundle.toString()));
-        properties.forEach(property -> cmdline.add(property.name));
-        try {
-            Map<String, String> actualValues = Executor.of(cmdline.toArray(String[]::new))
-                    .saveOutput(true)
-                    .executeExpectSuccess()
-                    .getOutput().stream()
-                            .map(line -> line.split(":\\s+", 2))
-                            .collect(Collectors.toMap(
-                                    components -> components[0],
-                                    components -> components[1]));
-            properties.forEach(property -> errors.add(property.verifyValue(
-                    actualValues.get(property.name))));
-        } catch (IOException ex) {
-            // Ignore error as it is not critical. Just report it.
-            Log.verbose(ex);
-        }
-
-        return errors;
-    }
-
-    /*
-     * set permissions with a string like "rwxr-xr-x"
-     *
-     * This cannot be directly backport to 22u which is built with 1.6
-     */
-    private void setPermissions(File file, String permissions) {
-        Set<PosixFilePermission> filePermissions =
-                PosixFilePermissions.fromString(permissions);
-        try {
-            if (file.exists()) {
-                Files.setPosixFilePermissions(file.toPath(), filePermissions);
-            }
-        } catch (IOException ex) {
-            Log.error(ex.getMessage());
-            Log.verbose(ex);
-        }
-
-    }
-
-    public static boolean isDebian() {
-        // we are just going to run "dpkg -s coreutils" and assume Debian
-        // or deritive if no error is returned.
-        try {
-            Executor.of(TOOL_DPKG, "-s", "coreutils").executeExpectSuccess();
-            return true;
-        } catch (IOException e) {
-            // just fall thru
-        }
-        return false;
-    }
-
-    private void adjustPermissionsRecursive(File dir) throws IOException {
-        Files.walkFileTree(dir.toPath(), new SimpleFileVisitor<Path>() {
-            @Override
-            public FileVisitResult visitFile(Path file,
-                    BasicFileAttributes attrs)
-                    throws IOException {
-                if (file.endsWith(".so") || !Files.isExecutable(file)) {
-                    setPermissions(file.toFile(), "rw-r--r--");
-                } else if (Files.isExecutable(file)) {
-                    setPermissions(file.toFile(), "rwxr-xr-x");
-                }
-                return FileVisitResult.CONTINUE;
-            }
-
-            @Override
-            public FileVisitResult postVisitDirectory(Path dir, IOException e)
-                    throws IOException {
-                if (e == null) {
-                    setPermissions(dir.toFile(), "rwxr-xr-x");
-                    return FileVisitResult.CONTINUE;
-                } else {
-                    // directory iteration failed
-                    throw e;
-                }
-            }
-        });
-    }
-
-    private class DebianFile {
-
-        DebianFile(Path dstFilePath, String comment) {
-            this.dstFilePath = dstFilePath;
-            this.comment = comment;
-        }
-
-        DebianFile setExecutable() {
-            permissions = "rwxr-xr-x";
-            return this;
-        }
-
-        void create(Map<String, String> data, Map<String, ? super Object> params)
-                throws IOException {
-            createResource("template." + dstFilePath.getFileName().toString(),
-                    params)
-                    .setCategory(I18N.getString(comment))
-                    .setSubstitutionData(data)
-                    .saveToFile(dstFilePath);
-            if (permissions != null) {
-                setPermissions(dstFilePath.toFile(), permissions);
-            }
-        }
-
-        private final Path dstFilePath;
-        private final String comment;
-        private String permissions;
-    }
-
-    private void prepareProjectConfig(Map<String, String> data,
-            Map<String, ? super Object> params) throws IOException {
-
-        Path configDir = createMetaPackage(params).sourceRoot().resolve("DEBIAN");
-        List<DebianFile> debianFiles = new ArrayList<>();
-        debianFiles.add(new DebianFile(
-                configDir.resolve("control"),
-                "resource.deb-control-file"));
-        debianFiles.add(new DebianFile(
-                configDir.resolve("preinst"),
-                "resource.deb-preinstall-script").setExecutable());
-        debianFiles.add(new DebianFile(
-                configDir.resolve("prerm"),
-                "resource.deb-prerm-script").setExecutable());
-        debianFiles.add(new DebianFile(
-                configDir.resolve("postinst"),
-                "resource.deb-postinstall-script").setExecutable());
-        debianFiles.add(new DebianFile(
-                configDir.resolve("postrm"),
-                "resource.deb-postrm-script").setExecutable());
-
-        if (!StandardBundlerParam.isRuntimeInstaller(params)) {
-            debianFiles.add(new DebianFile(
-                    getConfig_CopyrightFile(params).toPath(),
-                    "resource.copyright-file"));
-        }
-
-        for (DebianFile debianFile : debianFiles) {
-            debianFile.create(data, params);
-        }
-    }
-
-    @Override
-    protected Map<String, String> createReplacementData(
-            Map<String, ? super Object> params) throws IOException {
-        Map<String, String> data = new HashMap<>();
-
-        data.put("APPLICATION_MAINTAINER", MAINTAINER.fetchFrom(params));
-        data.put("APPLICATION_SECTION", SECTION.fetchFrom(params));
-        data.put("APPLICATION_COPYRIGHT", COPYRIGHT.fetchFrom(params));
-        data.put("APPLICATION_LICENSE_TEXT", LICENSE_TEXT.fetchFrom(params));
-        data.put("APPLICATION_ARCH", DEB_ARCH);
-        data.put("APPLICATION_INSTALLED_SIZE", Long.toString(
-                createMetaPackage(params).sourceApplicationLayout().sizeInBytes() >> 10));
-
-        return data;
-    }
-
-    private File getConfig_CopyrightFile(Map<String, ? super Object> params) {
-        PlatformPackage thePackage = createMetaPackage(params);
-        return thePackage.sourceRoot().resolve(Path.of(".",
-                LINUX_INSTALL_DIR.fetchFrom(params), PACKAGE_NAME.fetchFrom(
-                params), "share/doc/copyright")).toFile();
-    }
-
-    private File buildDeb(Map<String, ? super Object> params,
-            File outdir) throws IOException {
-        File outFile = new File(outdir,
-                FULL_PACKAGE_NAME.fetchFrom(params)+".deb");
-        Log.verbose(MessageFormat.format(I18N.getString(
-                "message.outputting-to-location"), outFile.getAbsolutePath()));
-
-        PlatformPackage thePackage = createMetaPackage(params);
-
-        List<String> cmdline = new ArrayList<>();
-        cmdline.addAll(List.of(TOOL_FAKEROOT, TOOL_DPKG_DEB));
-        if (Log.isVerbose()) {
-            cmdline.add("--verbose");
-        }
-        cmdline.addAll(List.of("-b", thePackage.sourceRoot().toString(),
-                outFile.getAbsolutePath()));
-
-        // run dpkg
-        Executor.of(cmdline.toArray(String[]::new)).executeExpectSuccess();
-
-        Log.verbose(MessageFormat.format(I18N.getString(
-                "message.output-to-location"), outFile.getAbsolutePath()));
-
-        return outFile;
-    }
-
-    @Override
-    public String getName() {
-        return I18N.getString("deb.bundler.name");
-    }
-
-    @Override
-    public String getID() {
-        return "deb";
-    }
-
-    @Override
-    public boolean supported(boolean runtimeInstaller) {
-        return Platform.isLinux() && (new ToolValidator(TOOL_DPKG_DEB).validate() == null);
-    }
-
-    @Override
-    public boolean isDefault() {
-        return isDebian();
-    }
-}