# HG changeset patch # User herrick # Date 1571950133 14400 # Node ID fca9cb5f4953d9dee5daab63baa1b956032429b3 # Parent 015949faea55f747bf971e22bda066202dbbd270 8230612: Debian packaging ignores version and release in custom control file Submitted-by: asemenyuk Reviewed-by: aherrick, almatvee diff -r 015949faea55 -r fca9cb5f4953 src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java Wed Oct 23 14:01:17 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java Thu Oct 24 16:48:53 2019 -0400 @@ -257,6 +257,45 @@ }); } + @Override + protected List verifyOutputBundle( + Map params, Path packageBundle) { + List errors = new ArrayList<>(); + + String controlFileName = "control"; + + List 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 cmdline = new ArrayList<>(List.of(TOOL_DPKG_DEB, "-f", + packageBundle.toString())); + properties.forEach(property -> cmdline.add(property.name)); + try { + Map 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" * diff -r 015949faea55 -r fca9cb5f4953 src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java Wed Oct 23 14:01:17 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java Thu Oct 24 16:48:53 2019 -0400 @@ -151,8 +151,17 @@ data.putAll(createReplacementData(params)); - return buildPackageBundle(Collections.unmodifiableMap(data), params, - outputParentDir); + File packageBundle = buildPackageBundle(Collections.unmodifiableMap( + data), params, outputParentDir); + + verifyOutputBundle(params, packageBundle.toPath()).stream() + .filter(Objects::nonNull) + .forEachOrdered(ex -> { + Log.verbose(ex.getLocalizedMessage()); + Log.verbose(ex.getAdvice()); + }); + + return packageBundle; } catch (IOException ex) { Log.verbose(ex); throw new PackagerException(ex); @@ -213,6 +222,9 @@ return data; } + abstract protected List verifyOutputBundle( + Map params, Path packageBundle); + abstract protected void initLibProvidersLookup( Map params, LibProvidersLookup libProvidersLookup); diff -r 015949faea55 -r fca9cb5f4953 src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java Wed Oct 23 14:01:17 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java Thu Oct 24 16:48:53 2019 -0400 @@ -32,6 +32,7 @@ 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.StandardBundlerParam.*; import static jdk.jpackage.internal.LinuxAppBundler.LINUX_INSTALL_DIR; @@ -151,7 +152,7 @@ .setSubstitutionData(replacementData) .saveToFile(specFile); - return buildRPM(params, outputParentDir); + return buildRPM(params, outputParentDir.toPath()).toFile(); } @Override @@ -186,16 +187,62 @@ }); } + @Override + protected List verifyOutputBundle( + Map params, Path packageBundle) { + List errors = new ArrayList<>(); + + String specFileName = specFile(params).getFileName().toString(); + + try { + List properties = List.of( + new PackageProperty("Name", PACKAGE_NAME.fetchFrom(params), + "APPLICATION_PACKAGE", specFileName), + new PackageProperty("Version", VERSION.fetchFrom(params), + "APPLICATION_VERSION", specFileName), + new PackageProperty("Release", RELEASE.fetchFrom(params), + "APPLICATION_RELEASE", specFileName), + new PackageProperty("Arch", rpmArch(), null, specFileName)); + + List actualValues = Executor.of(TOOL_RPM, "-qp", "--queryformat", + properties.stream().map(entry -> String.format("%%{%s}", + entry.name)).collect(Collectors.joining("\\n")), + packageBundle.toString()).saveOutput(true).executeExpectSuccess().getOutput(); + + Iterator actualValuesIt = actualValues.iterator(); + properties.forEach(property -> errors.add(property.verifyValue( + actualValuesIt.next()))); + } catch (IOException ex) { + // Ignore error as it is not critical. Just report it. + Log.verbose(ex); + } + + return errors; + } + + private String rpmArch() throws IOException { + if (rpmArch == null) { + rpmArch = Executor.of(TOOL_RPMBUILD, "--eval=%{_target_cpu}").saveOutput( + true).executeExpectSuccess().getOutput().get(0); + } + return rpmArch; + } + private Path specFile(Map params) { return TEMP_ROOT.fetchFrom(params).toPath().resolve(Path.of("SPECS", PACKAGE_NAME.fetchFrom(params) + ".spec")); } - private File buildRPM(Map params, - File outdir) throws IOException { + private Path buildRPM(Map params, + Path outdir) throws IOException { + + Path rpmFile = outdir.toAbsolutePath().resolve(String.format( + "%s-%s-%s.%s.rpm", PACKAGE_NAME.fetchFrom(params), + VERSION.fetchFrom(params), RELEASE.fetchFrom(params), rpmArch())); + Log.verbose(MessageFormat.format(I18N.getString( "message.outputting-bundle-location"), - outdir.getAbsolutePath())); + rpmFile.getParent())); PlatformPackage thePackage = createMetaPackage(params); @@ -206,33 +253,18 @@ "--define", String.format("%%_sourcedir %s", thePackage.sourceRoot()), // save result to output dir - "--define", String.format("%%_rpmdir %s", - outdir.getAbsolutePath()), + "--define", String.format("%%_rpmdir %s", rpmFile.getParent()), // do not use other system directories to build as current user "--define", String.format("%%_topdir %s", - TEMP_ROOT.fetchFrom(params).toPath().toAbsolutePath()) + TEMP_ROOT.fetchFrom(params).toPath().toAbsolutePath()), + "--define", String.format("%%_rpmfilename %s", rpmFile.getFileName()) ).executeExpectSuccess(); Log.verbose(MessageFormat.format( I18N.getString("message.output-bundle-location"), - outdir.getAbsolutePath())); + rpmFile.getParent())); - // presume the result is the ".rpm" file with the newest modified time - // not the best solution, but it is the most reliable - File result = null; - long lastModified = 0; - File[] list = outdir.listFiles(); - if (list != null) { - for (File f : list) { - if (f.getName().endsWith(".rpm") && - f.lastModified() > lastModified) { - result = f; - lastModified = f.lastModified(); - } - } - } - - return result; + return rpmFile; } @Override @@ -255,4 +287,5 @@ return !LinuxDebBundler.isDebian(); } + private String rpmArch; } diff -r 015949faea55 -r fca9cb5f4953 src/jdk.jpackage/linux/classes/jdk/jpackage/internal/PackageProperty.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/PackageProperty.java Thu Oct 24 16:48:53 2019 -0400 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 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.text.MessageFormat; + +final class PackageProperty { + /** + * Constructor + * + * @param name property name + * @param expectedValue expected property value + * @param substString substitution string to be placed in resource file to + * be replaced with the expected property value by jpackage at package build + * time + * @param customResource name of custom resource from resource directory in + * which this package property can be set + */ + PackageProperty(String name, String expectedValue, String substString, + String customResource) { + this.name = name; + this.expectedValue = expectedValue; + this.substString = substString; + this.customResource = customResource; + } + + ConfigException verifyValue(String actualValue) { + if (expectedValue.equals(actualValue)) { + return null; + } + + final String advice; + if (substString != null) { + advice = MessageFormat.format(I18N.getString( + "error.unexpected-package-property.advice"), substString, + actualValue, name, customResource); + } else { + advice = MessageFormat.format(I18N.getString( + "error.unexpected-default-package-property.advice"), name, + customResource); + } + + return new ConfigException(MessageFormat.format(I18N.getString( + "error.unexpected-package-property"), name, + expectedValue, actualValue, customResource, substString), advice); + } + + final String name; + private final String expectedValue; + private final String substString; + private final String customResource; +} diff -r 015949faea55 -r fca9cb5f4953 src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties Wed Oct 23 14:01:17 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties Thu Oct 24 16:48:53 2019 -0400 @@ -63,3 +63,7 @@ message.ldd-not-available=ldd command not found. Package dependencies will not be generated. message.deb-ldd-not-available.advice=Install "libc-bin" DEB package to get ldd. message.rpm-ldd-not-available.advice=Install "glibc-common" RPM package to get ldd. + +error.unexpected-package-property=Expected value of "{0}" property is [{1}]. Actual value in output package is [{2}]. Looks like custom "{3}" file from resource directory contained hard coded value of "{0}" property +error.unexpected-package-property.advice=Use [{0}] pattern string instead of hard coded value [{1}] of {2} property in custom "{3}" file +error.unexpected-default-package-property.advice=Don't explicitly set value of {0} property in custom "{1}" file diff -r 015949faea55 -r fca9cb5f4953 src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties Wed Oct 23 14:01:17 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties Thu Oct 24 16:48:53 2019 -0400 @@ -63,3 +63,7 @@ message.ldd-not-available=ldd command not found. Package dependencies will not be generated. message.deb-ldd-not-available.advice=Install "libc-bin" DEB package to get ldd. message.rpm-ldd-not-available.advice=Install "glibc-common" RPM package to get ldd. + +error.unexpected-package-property=Expected value of "{0}" property is [{1}]. Actual value in output package is [{2}]. Looks like custom "{3}" file from resource directory contained hard coded value of "{0}" property +error.unexpected-package-property.advice=Use [{0}] pattern string instead of hard coded value [{1}] of {2} property in custom "{3}" file +error.unexpected-default-package-property.advice=Don't explicitly set value of {0} property in custom "{1}" file diff -r 015949faea55 -r fca9cb5f4953 src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties Wed Oct 23 14:01:17 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties Thu Oct 24 16:48:53 2019 -0400 @@ -63,3 +63,7 @@ message.ldd-not-available=ldd command not found. Package dependencies will not be generated. message.deb-ldd-not-available.advice=Install "libc-bin" DEB package to get ldd. message.rpm-ldd-not-available.advice=Install "glibc-common" RPM package to get ldd. + +error.unexpected-package-property=Expected value of "{0}" property is [{1}]. Actual value in output package is [{2}]. Looks like custom "{3}" file from resource directory contained hard coded value of "{0}" property +error.unexpected-package-property.advice=Use [{0}] pattern string instead of hard coded value [{1}] of {2} property in custom "{3}" file +error.unexpected-default-package-property.advice=Don't explicitly set value of {0} property in custom "{1}" file diff -r 015949faea55 -r fca9cb5f4953 src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec Wed Oct 23 14:01:17 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec Thu Oct 24 16:48:53 2019 -0400 @@ -16,9 +16,6 @@ Requires: PACKAGE_DEFAULT_DEPENDENCIES PACKAGE_CUSTOM_DEPENDENCIES %endif -#avoid ARCH subfolder -%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm - #comment line below to enable effective jar compression #it could easily get your package size from 40 to 15Mb but #build time will substantially increase and it may require unpack200/system java to install diff -r 015949faea55 -r fca9cb5f4953 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java Wed Oct 23 14:01:17 2019 -0400 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java Thu Oct 24 16:48:53 2019 -0400 @@ -63,9 +63,8 @@ final String release = getRelease(cmd); final String version = cmd.version(); - return String.format(format, - getPackageName(cmd), version, release, getPackageArch(packageType)) - + packageType.getSuffix(); + return String.format(format, getPackageName(cmd), version, release, + getDefaultPackageArch(packageType)) + packageType.getSuffix(); } public static Stream getPackageFiles(JPackageCommand cmd) { @@ -377,7 +376,7 @@ .executeAndGetFirstLineOfOutput(); } - private static String getPackageArch(PackageType type) { + public static String getDefaultPackageArch(PackageType type) { if (archs == null) { archs = new HashMap<>(); } diff -r 015949faea55 -r fca9cb5f4953 test/jdk/tools/jpackage/linux/LinuxResourceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/linux/LinuxResourceTest.java Thu Oct 24 16:48:53 2019 -0400 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +import java.io.IOException; +import java.nio.file.Path; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.LinuxHelper; +import jdk.jpackage.test.Annotations.Test; +import java.util.List; + +/* + * @test + * @summary jpackage with --resource-dir + * @library ../helpers + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.jpackage/jdk.jpackage.internal + * @compile LinuxResourceTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=LinuxResourceTest + */ + +public class LinuxResourceTest { + @Test + public static void testHardcodedProperties() throws IOException { + new PackageTest() + .forTypes(PackageType.LINUX) + .configureHelloApp() + .addInitializer(cmd -> { + cmd + .setFakeRuntime() + .saveConsoleOutput(true) + .addArguments("--resource-dir", TKit.createTempDirectory("resources")); + }) + .forTypes(PackageType.LINUX_DEB) + .addInitializer(cmd -> { + Path controlFile = Path.of(cmd.getArgumentValue("--resource-dir"), + "control"); + TKit.createTextFile(controlFile, List.of( + "Package: dont-install-me", + "Version: 1.2.3-R2", + "Section: APPLICATION_SECTION", + "Maintainer: APPLICATION_MAINTAINER", + "Priority: optional", + "Architecture: bar", + "Provides: dont-install-me", + "Description: APPLICATION_DESCRIPTION", + "Installed-Size: APPLICATION_INSTALLED_SIZE", + "Depends: PACKAGE_DEFAULT_DEPENDENCIES" + )); + }) + .addBundleVerifier((cmd, result) -> { + TKit.assertTextStream("Using custom package resource [DEB control file]") + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream(String.format( + "Expected value of \"Package\" property is [%s]. Actual value in output package is [dont-install-me]", + LinuxHelper.getPackageName(cmd))) + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream( + "Expected value of \"Version\" property is [1.0-1]. Actual value in output package is [1.2.3-R2]") + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream(String.format( + "Expected value of \"Architecture\" property is [%s]. Actual value in output package is [bar]", + LinuxHelper.getDefaultPackageArch(cmd.packageType()))) + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + }) + .forTypes(PackageType.LINUX_RPM) + .addInitializer(cmd -> { + Path specFile = Path.of(cmd.getArgumentValue("--resource-dir"), + LinuxHelper.getPackageName(cmd) + ".spec"); + TKit.createTextFile(specFile, List.of( + "Name: dont-install-me", + "Version: 1.2.3", + "Release: R2", + "Summary: APPLICATION_SUMMARY", + "License: APPLICATION_LICENSE_TYPE", + "Prefix: %{dirname:APPLICATION_DIRECTORY}", + "Provides: dont-install-me", + "%description", + "APPLICATION_DESCRIPTION", + "%prep", + "%build", + "%install", + "rm -rf %{buildroot}", + "install -d -m 755 %{buildroot}APPLICATION_DIRECTORY", + "cp -r %{_sourcedir}APPLICATION_DIRECTORY/* %{buildroot}APPLICATION_DIRECTORY", + "%files", + "APPLICATION_DIRECTORY" + )); + }) + .addBundleVerifier((cmd, result) -> { + TKit.assertTextStream("Using custom package resource [RPM spec file]") + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream(String.format( + "Expected value of \"Name\" property is [%s]. Actual value in output package is [dont-install-me]", + LinuxHelper.getPackageName(cmd))) + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream( + "Expected value of \"Version\" property is [1.0]. Actual value in output package is [1.2.3]") + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + TKit.assertTextStream( + "Expected value of \"Release\" property is [1]. Actual value in output package is [R2]") + .predicate(String::startsWith) + .apply(result.getOutput().stream()); + }) + .run(); + } +}