8
diff --git a/src/it/test17-reproducible-date/verify.groovy b/src/it/test17-reproducible-date/verify.groovy
index b696d06..6cd221e 100644
--- a/src/it/test17-reproducible-date/verify.groovy
+++ b/src/it/test17-reproducible-date/verify.groovy
@@ -16,7 +16,7 @@ def verify() {
def result = verify()
println "Verify: " + result
-def expectedMd5Sum = "cd831b22be9f30db30eb69ab35ddb3c5";
+def expectedMd5Sum = "d5295483d055f6fe9d19b2c36490a997";
def md5sum = generateMD5(new File(basedir, "target/test17-1.0.0-0.200901011100.noarch.rpm"))
if (md5sum != expectedMd5Sum) {
System.out.format("RPM MD5 doesn't match - actual: %s, expected: %s%n", md5sum, expectedMd5Sum);
diff --git a/src/it/test17-reproducible-epoch/verify.groovy b/src/it/test17-reproducible-epoch/verify.groovy
index facd9af..32ff0cc 100644
--- a/src/it/test17-reproducible-epoch/verify.groovy
+++ b/src/it/test17-reproducible-epoch/verify.groovy
@@ -16,7 +16,7 @@ def verify() {
def result = verify()
println "Verify: " + result
-def expectedMd5Sum = "6fc07068db044f8f73a8e6ecb01256ce";
+def expectedMd5Sum = "d5e4e3cfd001f5d7a71ac6ab10744be8";
def md5sum = generateMD5(new File(basedir, "target/test17-1.0.0-0.197001010000.noarch.rpm"))
if (md5sum != expectedMd5Sum) {
System.out.format("RPM MD5 doesn't match - actual: %s, expected: %s%n", md5sum, expectedMd5Sum);
diff --git a/src/it/test20-payload/pom.xml b/src/it/test20-payload/pom.xml
new file mode 100644
index 0000000..b82f348
--- /dev/null
+++ b/src/it/test20-payload/pom.xml
@@ -0,0 +1,89 @@
+
+
+ 4.0.0
+
+ de.dentrassi.maven.rpm.test
+ test20
+ 1.0.0-SNAPSHOT
+ jar
+
+ Test Package #20
+
+ Test explicitly setting payload coding and payload flags
+
+
+ http://dentrassi.de
+
+
+ Jens Reimann
+ http://dentrassi.de
+
+
+
+
+ Eclipse Public License - v 1.0
+ repo
+ https://www.eclipse.org/legal/epl-v10.html
+
+
+
+
+ UTF-8
+ UTF-8
+ true
+
+ 2025-04-16T16:25:26.470Z
+
+
+
+
+
+
+ de.dentrassi.maven
+ rpm
+ @project.version@
+
+
+
+ rpm
+
+
+ test20.rpm
+ false
+ Application/Misc
+
+
+ ${keyId}
+ ${user.home}/.gnupg/secring.gpg
+ ${passphrase}
+ SHA1
+ ${skipSigning}
+
+
+
+ Zstd
+ 19
+ 0
+ 23
+
+
+
+
+
+
+
+
+
+
+ sign
+
+ false
+
+
+ false
+
+
+
+
+
diff --git a/src/it/test20-payload/verify.groovy b/src/it/test20-payload/verify.groovy
new file mode 100644
index 0000000..13d0e35
--- /dev/null
+++ b/src/it/test20-payload/verify.groovy
@@ -0,0 +1,16 @@
+def verifyPayload() {
+ Process proc = ['rpm', '-q', '--queryformat', '%{PAYLOADCOMPRESSOR} %{PAYLOADFLAGS}\n', basedir.toString().replace(File.separator, "/") + '/target/test20.rpm'].execute()
+ proc.waitFor()
+ return proc.in.getText().trim()
+}
+
+def actual = verifyPayload()
+println "Verify payload:\n" + actual
+def expected = "zstd 19T0L23"
+
+if (actual != expected) {
+ System.out.format("RPM payloads don't match - actual:%n%s%nexpected:%n%s%n", actual, expected);
+ return false;
+}
+
+return true;
diff --git a/src/main/java/de/dentrassi/rpm/builder/RpmMojo.java b/src/main/java/de/dentrassi/rpm/builder/RpmMojo.java
index 6ec0dff..f18fc1b 100644
--- a/src/main/java/de/dentrassi/rpm/builder/RpmMojo.java
+++ b/src/main/java/de/dentrassi/rpm/builder/RpmMojo.java
@@ -66,6 +66,8 @@
import org.eclipse.packager.rpm.build.*;
import org.eclipse.packager.rpm.build.RpmBuilder.PackageInformation;
import org.eclipse.packager.rpm.build.RpmBuilder.Version;
+import org.eclipse.packager.rpm.coding.PayloadCoding;
+import org.eclipse.packager.rpm.coding.PayloadFlags;
import org.eclipse.packager.rpm.deps.RpmDependencyFlags;
import org.eclipse.packager.rpm.signature.RsaHeaderSignatureProcessor;
import org.eclipse.packager.rpm.signature.RsaSignatureProcessor;
@@ -85,6 +87,10 @@
*/
@Mojo(name = "rpm", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true)
public class RpmMojo extends AbstractMojo {
+ private static final PayloadCoding DEFAULT_PAYLOAD_CODING = PayloadCoding.gzip;
+
+ private static final String DEFAULT_PAYLOAD_FLAGS = "9";
+
private static final String SNAPSHOT_SUFFIX = "-SNAPSHOT";
/**
@@ -601,6 +607,19 @@ public void setGenerateDefaultSourcePackage(final boolean generateDefaultSourceP
@Parameter(property = "rpm.signature")
Signature signature;
+ /**
+ * Optional payload flags to use for compressing the payload of the final RPM.
+ *
+ * The coding must be one of the names returned by {@link PayloadCoding#values()}.
+ * The default coding is {@link PayloadCoding#gzip}.
+ * The level must be a number.
+ * The default level is {@code 9}.
+ *
+ * Also see Payload compression
+ */
+ @Parameter(property = "rpm.payloadFlags")
+ PayloadFlags payloadFlags;
+
/**
* Disable the mojo altogether.
*
@@ -842,10 +861,30 @@ public void execute() throws MojoExecutionException, MojoFailureException {
testLeadFlags();
final BuilderOptions options = new BuilderOptions();
- DigestAlgorithm fileDigestAlgorithm = evalDigestAlgorithm(this.fileDigestAlgorithm);
+ final DigestAlgorithm fileDigestAlgorithm = evalDigestAlgorithm(this.fileDigestAlgorithm);
this.logger.info("File Digest Algorithm: %s", fileDigestAlgorithm.getAlgorithm());
options.setFileDigestAlgorithm(fileDigestAlgorithm);
+ final PayloadCoding payloadCoding;
+ if (this.payloadFlags.getCoding() != null) {
+ payloadCoding = PayloadCoding.valueOf(this.payloadFlags.getCoding());
+ this.logger.info("Payload Coding: %s", payloadCoding);
+ } else {
+ payloadCoding = DEFAULT_PAYLOAD_CODING;
+ this.logger.info("Using default Payload Coding: %s", payloadCoding);
+ }
+
+ options.setPayloadCoding(payloadCoding);
+
+ if (this.payloadFlags != null && !this.payloadFlags.toString().isEmpty()) {
+ this.logger.info("Payload Flags: %s", this.payloadFlags);
+ } else {
+ this.payloadFlags = new PayloadFlags(payloadCoding, DEFAULT_PAYLOAD_FLAGS);
+ this.logger.info("Using default Payload Flags: %s", this.payloadFlags);
+ }
+
+ options.setPayloadFlags(this.payloadFlags);
+
// setup basic signature processors
final SignatureConfiguration provider;
diff --git a/src/main/java/de/dentrassi/rpm/builder/RpmUnpackMojo.java b/src/main/java/de/dentrassi/rpm/builder/RpmUnpackMojo.java
index 009a775..6a1bdf0 100644
--- a/src/main/java/de/dentrassi/rpm/builder/RpmUnpackMojo.java
+++ b/src/main/java/de/dentrassi/rpm/builder/RpmUnpackMojo.java
@@ -28,6 +28,7 @@
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.attribute.UserPrincipalNotFoundException;
import java.util.EnumSet;
+import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
@@ -383,41 +384,25 @@ private void setFileOwnership(final InputHeader payloadHeader,
}
private static String getName(final InputHeader payloadHeader, final RpmTag tag, final long id) {
- final Object values =
- payloadHeader.getEntry(tag)
- .orElseThrow(() -> new IllegalStateException("RPM lacks " + tag + " lookup table"))
- .getValue();
-
- if (!(values instanceof String[])) {
- throw new IllegalStateException("RPM " + tag + " header is not a list of Strings, got " +
- values.getClass());
- }
+ final List names = payloadHeader.getStringList(tag);
+ final int size = names.size();
- final String[] names = (String[]) values;
- if (id < 0 || names.length <= id) {
- throw new IllegalArgumentException("id out of range [0," + names.length + ']');
+ if (id < 0 || size <= id) {
+ throw new IllegalArgumentException("id out of range [0," + size + ']');
}
- return names[(int) id];
+ return names.get((int) id);
}
private static String getLinkTarget(final InputHeader payloadHeader, final long inode) {
- final Object values =
- payloadHeader.getEntry(RpmTag.FILE_LINKTO)
- .orElseThrow(() ->
- new IllegalStateException("RPM contains symbolic link, but lacks linkTo header"))
- .getValue();
-
- if (!(values instanceof String[])) {
- throw new IllegalStateException("RPM linkTo header is not a list of Strings, got " + values.getClass());
- }
+ final List linkTo = payloadHeader.getStringList(RpmTag.FILE_LINKTO);
+ final int size = linkTo.size();
- final String[] linkTo = (String[]) values;
- if (inode < 0 || linkTo.length <= inode) {
- throw new IllegalArgumentException("Symbolic link inode out of range [0," + linkTo.length + ']');
+ if (inode < 0 || size <= inode) {
+ throw new IllegalArgumentException("Symbolic link inode out of range [0," + size + ']');
}
- return linkTo[(int) inode];
+ return linkTo.get((int) inode);
}
public void setRpmFile(final File rpmFile) {
diff --git a/src/site/markdown/payload_compression.md b/src/site/markdown/payload_compression.md
new file mode 100644
index 0000000..84d6e0d
--- /dev/null
+++ b/src/site/markdown/payload_compression.md
@@ -0,0 +1,84 @@
+# Payload compression
+
+RPM packages can be compressed using one of several methods. All of these methods allow setting a custom
+compression level as well as additional options.
+
+## Configuration
+
+Payload compression is configured using the `` element. For example:
+
+```xml
+
+
+ zstd
+ 19
+
+
+```
+
+### Payload coding
+
+Payload `coding` must be one of the following compressors:
+
+* none (uncompressed)
+* gzip (default compressor, with a compression level of 9)
+* bzip2
+* lzma
+* xz
+* zstd
+
+### Payload flags
+
+| coding | level (number) | threads (number) | strategy (number) | windowLog (number) | smallMode (boolean) |
+|--------|----------------|------------------|------------------------------------------------|--------------------|---------------------|
+| none | ✗ | ✗ | ✗ | ✗ | ✗ |
+| gzip | ✓ 0-9 | ✗ | ✓ 0 (default), 1 (filtered), 2 (huffman) | ✗ | ✗ |
+| bzip2 | ✓ 0-9 | ✗ | ✗ | ✗ | ✓ false, true |
+| lzma | ✓ 0-9 | ✓ | ✗ | ✗ | ✗ |
+| xz | ✓ 0-9 | ✓ | ✗ | ✗ | ✗ |
+| zstd | ✓ 0-22 | ✓ | ✗ | ✓ | ✗ |
+
+#### level
+
+The compressors `gzip`, `lzma`, `bzip2`,and `xz` support a range of compression `level`s between 0 (no compression) and
+9\. Additionally, `gzip` supports the value -1 for the default compression level. The compressor `zstd` supports a range
+of compression levels between 0 (use default compression level) and 22.
+
+#### threads
+
+The compressors `lzma`, `xz`, and `zstd` support a number of `threads`. A value of 0 means that the compressor will use
+`Runtime.getRuntime().availableProcessors()` threads. The compressors `gzip` and `bzip2` do not support this option.
+The Java implementations of `lzma` and `xz` currently do not support multithreaded operation, so this value has no
+effect.
+
+#### strategy
+
+The compressor `gzip` supports a `strategy` value. The default value is 0, which means that the compressor will
+use the default strategy. A value of 1 means that the compressor will use a filtered strategy. A value of 2 means that
+the compressor will use a huffman-only strategy.
+
+#### windowLog
+
+The compressor `zstd` supports a numeric `windowLog` value. The special value 0 will cause zstd to use the default
+`windowLog` value.
+
+#### smallMode
+
+The compressor `bzip2` supports a `smallMode` boolean value. The Java implementation of `bzip2` does not support this,
+so this value has no effect.
+
+### Payload flags strings
+
+ The payload flags are converted to a string representation in the RPM header.
+
+| payload flags | description | valid for codings |
+|---------------|--------------------------------------------------------------------|-----------------------------|
+| "9" | level 9 | gzip, bzip2, lzma, xz, zstd |
+| "9h" | level 9 huffman-only strategy | gzip |
+| "9f" | level 9 filtered strategy | gzip |
+| "9s" | level 9 small mode | bzip2 |
+| "7T16" | level 7 using 16 threads | lzma, xz, zstd |
+| "7T0" | level 7 using `Runtime.getRuntime().availableProcessors()` threads | lzma, xz, zstd |
+| "7T" | level 7 using `Runtime.getRuntime().availableProcessors()` threads | lzma, xz, zstd |
+| "7L" | level 7 using the default window log value | zstd |
+| "7L0" | level 7 using the default window log value | zstd |
diff --git a/src/site/site.xml b/src/site/site.xml
index c22f2d1..6595d04 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -16,6 +16,8 @@
+