8230612: Debian packaging ignores version and release in custom control file
Submitted-by: asemenyuk
Reviewed-by: aherrick, almatvee
--- 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<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"
*
--- 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<ConfigException> verifyOutputBundle(
+ Map<String, ? super Object> params, Path packageBundle);
+
abstract protected void initLibProvidersLookup(
Map<String, ? super Object> params,
LibProvidersLookup libProvidersLookup);
--- 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<ConfigException> verifyOutputBundle(
+ Map<String, ? super Object> params, Path packageBundle) {
+ List<ConfigException> errors = new ArrayList<>();
+
+ String specFileName = specFile(params).getFileName().toString();
+
+ try {
+ List<PackageProperty> 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<String> 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<String> 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<String, ? super Object> params) {
return TEMP_ROOT.fetchFrom(params).toPath().resolve(Path.of("SPECS",
PACKAGE_NAME.fetchFrom(params) + ".spec"));
}
- private File buildRPM(Map<String, ? super Object> params,
- File outdir) throws IOException {
+ private Path buildRPM(Map<String, ? super Object> 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;
}
--- /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;
+}
--- 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
--- 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
--- 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
--- 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
--- 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<Path> 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<>();
}
--- /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();
+ }
+}