diff --git a/src/it/test19-intermediate-directories-collect/pom.xml b/src/it/test19-intermediate-directories-collect/pom.xml new file mode 100644 index 0000000..ffebda0 --- /dev/null +++ b/src/it/test19-intermediate-directories-collect/pom.xml @@ -0,0 +1,136 @@ + + + 4.0.0 + + de.dentrassi.maven.rpm.test + test19 + 1.0.0-SNAPSHOT + jar + + Test Package #19 + + Test explicitly adding and applying rules for implicitly created intermediate directories with collect entries. + + + 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 + + 2009-01-01T12:00:00+01:00 + + + + + + + de.dentrassi.maven + rpm + @project.version@ + + + + rpm + + + false + Application/Misc + + + ${keyId} + ${user.home}/.gnupg/secring.gpg + ${passphrase} + SHA1 + ${skipSigning} + + + + /opt/mycompany/myapp + + + + + my-default + + + + /opt/mycompany + + mygeneraluser + mygeneralgroup + 0111 + + + + /opt/mycompany/myapp + + myuser + mygroup + 0555 + + + + /opt/mycompany/myapp + directory + + 0777 + + + + + + + + /opt/mycompany/myapp/a/b + + ${project.basedir}/src/main/data + false + + my-default + + + /opt/mycompany/myapp/c/d + + ${project.basedir}/src/main/data + true + + my-default + + + + + + + + + + + + + sign + + false + + + false + + + + + \ No newline at end of file diff --git a/src/it/test19-intermediate-directories-collect/src/main/data/x/y/foobar b/src/it/test19-intermediate-directories-collect/src/main/data/x/y/foobar new file mode 100644 index 0000000..e69de29 diff --git a/src/it/test19-intermediate-directories-collect/verify.groovy b/src/it/test19-intermediate-directories-collect/verify.groovy new file mode 100644 index 0000000..7fd6bb6 --- /dev/null +++ b/src/it/test19-intermediate-directories-collect/verify.groovy @@ -0,0 +1,25 @@ +def dump() { + Process proc = ('rpm -q --dump -p ' + basedir + "/target/*.rpm").execute() + return proc.in.getText().trim() +} + +def actual = dump() +println "Dump:\n" + actual + +def expected = """\ + /opt/mycompany/myapp 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040777 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/a 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040777 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/a/b 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040777 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/a/b/x/y/foobar 0 1230807600 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0100555 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/c 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040777 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/c/d 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040777 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/c/d/x 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040777 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/c/d/x/y 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040777 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/c/d/x/y/foobar 0 1230807600 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0100555 myuser mygroup 0 0 0 X""".stripIndent() + +if (actual != expected) { + System.out.format("RPM dump doesn't match - actual:%n%s%nexpected:%n%s%n", actual, expected); + return false; +} + +return true; diff --git a/src/it/test19-intermediate-directories/pom.xml b/src/it/test19-intermediate-directories/pom.xml new file mode 100644 index 0000000..b2fcb50 --- /dev/null +++ b/src/it/test19-intermediate-directories/pom.xml @@ -0,0 +1,141 @@ + + + 4.0.0 + + de.dentrassi.maven.rpm.test + test19 + 1.0.0-SNAPSHOT + jar + + Test Package #19 + + Test explicitly adding and applying rules for implicitly created intermediate directories. + + + 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 + + 2009-01-01T12:00:00+01:00 + + + + + + + de.dentrassi.maven + rpm + @project.version@ + + + + rpm + + + false + Application/Misc + + + ${keyId} + ${user.home}/.gnupg/secring.gpg + ${passphrase} + SHA1 + ${skipSigning} + + + + /etc/mycompany/myapp + /opt/mycompany/myapp + + + + + my-default + + + + /opt/mycompany + + mygeneraluser + mygeneralgroup + 0111 + + + + /opt/mycompany/myapp + + myuser + mygroup + 0555 + + + + /opt/mycompany/myapp + directory + + 0777 + + + + + + + + /opt/mycompany/myapp/a/b/c/foobar + ${project.basedir}/src/main/data/foobar + my-default + + + /opt/mycompany/myapp + true + my-default + + + /etc/mycompany/myapp/defaults + true + my-default + + + /opt/mycompany/otherapp/a/b/c/foobar + ${project.basedir}/src/main/data/foobar + my-default + + + + + + + + + + + + + sign + + false + + + false + + + + + \ No newline at end of file diff --git a/src/it/test19-intermediate-directories/src/main/data/foobar b/src/it/test19-intermediate-directories/src/main/data/foobar new file mode 100644 index 0000000..e69de29 diff --git a/src/it/test19-intermediate-directories/verify.groovy b/src/it/test19-intermediate-directories/verify.groovy new file mode 100644 index 0000000..37f0515 --- /dev/null +++ b/src/it/test19-intermediate-directories/verify.groovy @@ -0,0 +1,24 @@ +def dump() { + Process proc = ('rpm -q --dump -p ' + basedir + "/target/*.rpm").execute() + return proc.in.getText().trim() +} + +def actual = dump() +println "Dump:\n" + actual + +def expected = """\ + /etc/mycompany/myapp 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040755 root root 0 0 0 X + /etc/mycompany/myapp/defaults 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040755 root root 0 0 0 X + /opt/mycompany/myapp 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040777 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/a 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040777 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/a/b 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040777 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/a/b/c 0 1230807600 0000000000000000000000000000000000000000000000000000000000000000 040777 myuser mygroup 0 0 0 X + /opt/mycompany/myapp/a/b/c/foobar 0 1230807600 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0100555 myuser mygroup 0 0 0 X + /opt/mycompany/otherapp/a/b/c/foobar 0 1230807600 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0100111 mygeneraluser mygeneralgroup 0 0 0 X""".stripIndent() + +if (actual != expected) { + System.out.format("RPM dump doesn'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/BuilderContextListener.java b/src/main/java/de/dentrassi/rpm/builder/BuilderContextListener.java new file mode 100644 index 0000000..5227a27 --- /dev/null +++ b/src/main/java/de/dentrassi/rpm/builder/BuilderContextListener.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2023 Fraunhofer-Institut fuer Optronik, Systemtechnik und Bildauswertung IOSB and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * Contributors: + * Fraunhofer-Institut fuer Optronik, Systemtechnik und Bildauswertung IOSB - initial API and implementation + *******************************************************************************/ +package de.dentrassi.rpm.builder; + +import org.eclipse.packager.rpm.build.FileInformationProvider; + +public interface BuilderContextListener { + + void notifyFileAdded(String name, FileInformationProvider provider); + + void notifyDirectoryAdded(String name, FileInformationProvider provider); + + void notifySymbolicLinkAdded(String name, FileInformationProvider provider); +} diff --git a/src/main/java/de/dentrassi/rpm/builder/ListenableBuilderContext.java b/src/main/java/de/dentrassi/rpm/builder/ListenableBuilderContext.java new file mode 100644 index 0000000..47aeb77 --- /dev/null +++ b/src/main/java/de/dentrassi/rpm/builder/ListenableBuilderContext.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2023 Fraunhofer-Institut fuer Optronik, Systemtechnik und Bildauswertung IOSB and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * Contributors: + * Fraunhofer-Institut fuer Optronik, Systemtechnik und Bildauswertung IOSB - initial API and implementation + *******************************************************************************/ +package de.dentrassi.rpm.builder; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.packager.rpm.build.BuilderContext; +import org.eclipse.packager.rpm.build.FileInformationProvider; + +public class ListenableBuilderContext implements BuilderContext { + + private final BuilderContext builderContext; + private final Set listeners; + + public ListenableBuilderContext(BuilderContext builderContext) { + this.builderContext = builderContext; + this.listeners = new HashSet<>(); + } + + @Override + public void setDefaultInformationProvider(FileInformationProvider provider) { + builderContext.setDefaultInformationProvider(provider); + } + + @Override + public FileInformationProvider getDefaultInformationProvider() { + return builderContext.getDefaultInformationProvider(); + } + + @Override + public void addFile(String targetName, Path source, FileInformationProvider provider) throws IOException { + builderContext.addFile(targetName, source, provider); + listeners.forEach(listener -> listener.notifyFileAdded(targetName, (MojoFileInformationProvider) provider)); + } + + @Override + public void addFile(String targetName, InputStream source, FileInformationProvider provider) throws IOException { + builderContext.addFile(targetName, source, provider); + listeners.forEach(listener -> listener.notifyFileAdded(targetName, provider)); + } + + @Override + public void addFile(String targetName, ByteBuffer source, FileInformationProvider provider) throws IOException { + builderContext.addFile(targetName, source, provider); + listeners.forEach(listener -> listener.notifyFileAdded(targetName, provider)); + } + + @Override + public void addDirectory(String targetName, FileInformationProvider provider) throws IOException { + builderContext.addDirectory(targetName, provider); + listeners.forEach(listener -> listener.notifyDirectoryAdded(targetName, (MojoFileInformationProvider) provider)); + } + + @Override + public void addSymbolicLink(String targetName, String linkTo, FileInformationProvider provider) throws IOException { + builderContext.addSymbolicLink(targetName, linkTo, provider); + listeners.forEach(listener -> listener.notifySymbolicLinkAdded(targetName, (MojoFileInformationProvider) provider)); + } + + public void registerListener(BuilderContextListener listener) { + if (listener != null) { + listeners.add(listener); + } + } + + public void removeListener(BuilderContextListener listener) { + listeners.remove(listener); + } + +} diff --git a/src/main/java/de/dentrassi/rpm/builder/MissingDirectoryTracker.java b/src/main/java/de/dentrassi/rpm/builder/MissingDirectoryTracker.java new file mode 100644 index 0000000..c3d5789 --- /dev/null +++ b/src/main/java/de/dentrassi/rpm/builder/MissingDirectoryTracker.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2023 Fraunhofer-Institut fuer Optronik, Systemtechnik und Bildauswertung IOSB and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * Contributors: + * Fraunhofer-Institut fuer Optronik, Systemtechnik und Bildauswertung IOSB - initial API and implementation + *******************************************************************************/ +package de.dentrassi.rpm.builder; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.packager.rpm.build.BuilderContext; +import org.eclipse.packager.rpm.build.FileInformationProvider; + +public class MissingDirectoryTracker implements BuilderContextListener { + + // keep track of explicit added directories + private final Set explicitAddedDirectories; + // keep track of all missing directories which should be added after processing (without explicit added directories) + private final Map> missingDirectories; + // base directories (prefixes) for which the intermediate directories should be generated + private final List baseDirectories; + + public MissingDirectoryTracker(List baseDirectories) { + this.explicitAddedDirectories = new HashSet<>(); + this.missingDirectories = new HashMap<>(); + this.baseDirectories = baseDirectories; + } + + @Override + public void notifyFileAdded(String targetName, FileInformationProvider provider) { + addMissingDirectoriesFromPath(targetName, provider); + } + + @Override + public void notifyDirectoryAdded(String targetName, FileInformationProvider provider) { + explicitAddedDirectories.add(targetName); + missingDirectories.remove(targetName); + addMissingDirectoriesFromPath(targetName, provider); + } + + @Override + public void notifySymbolicLinkAdded(String targetName, FileInformationProvider provider) { + addMissingDirectoriesFromPath(targetName, provider); + } + + private void addMissingDirectoriesFromPath(String targetName, FileInformationProvider provider) { + if (provider instanceof MojoFileInformationProvider) { + MojoFileInformationProvider mojoProvider = (MojoFileInformationProvider) provider; + + List intermediateDirectories; + if (containsCollectEntry(mojoProvider)) { + // collect handles intermediate directories by themselves via flag 'directories' + // -> only use base directory (entry name of collect) as target path to calculate + // intermediate directories + targetName = mojoProvider.getEntry().getName(); + intermediateDirectories = getIntermediateDirectoriesIncludingTarget(targetName); + } else { + intermediateDirectories = getIntermediateDirectories(targetName); + } + + for (String intermediateDirectory : intermediateDirectories) { + addIfIsMissingDirectory(intermediateDirectory, mojoProvider); + } + } + } + + private void addIfIsMissingDirectory(String intermediateDirectory, MojoFileInformationProvider mojoProvider) { + if (startsPathWithPrefix(intermediateDirectory) && !explicitAddedDirectories.contains(intermediateDirectory)) { + missingDirectories.computeIfAbsent(intermediateDirectory, + (String directory) -> new MojoFileInformationProvider( + mojoProvider.getRulesetEval(), + mojoProvider.getRuleId(), + null, + mojoProvider.getLogger(), + mojoProvider.getTimestamp())); + } + } + + private boolean containsCollectEntry(MojoFileInformationProvider mojoProvider) { + return mojoProvider.getEntry().getCollect() != null; + } + + private boolean startsPathWithPrefix(String directory) { + return baseDirectories.stream().anyMatch(directory::startsWith); + } + + public void addMissingIntermediateDirectoriesToContext(BuilderContext ctx) throws IOException { + for (Map.Entry> missingEntry : missingDirectories.entrySet()) { + ctx.addDirectory(missingEntry.getKey(), missingEntry.getValue()); + } + } + + private List getIntermediateDirectories(String targetName) { + ArrayList intermediateDirectories = new ArrayList<>(); + Path path = Paths.get(targetName); + + for (int i = 1; i < path.getNameCount(); i++) { + Path subPath = path.subpath(0, i); + intermediateDirectories.add("/" + subPath); + } + + return intermediateDirectories; + } + + private List getIntermediateDirectoriesIncludingTarget(String targetName) { + List intermediateDirectories = getIntermediateDirectories(targetName); + intermediateDirectories.add(targetName); + + return intermediateDirectories; + } + +} diff --git a/src/main/java/de/dentrassi/rpm/builder/MojoFileInformationProvider.java b/src/main/java/de/dentrassi/rpm/builder/MojoFileInformationProvider.java index 5ed5bce..c4db8f6 100644 --- a/src/main/java/de/dentrassi/rpm/builder/MojoFileInformationProvider.java +++ b/src/main/java/de/dentrassi/rpm/builder/MojoFileInformationProvider.java @@ -74,4 +74,24 @@ private FileInformation provideByRule(final String targetName, final Object obje return result; } + + public RulesetEvaluator getRulesetEval() { + return rulesetEval; + } + + public Consumer getLogger() { + return logger; + } + + public String getRuleId() { + return ruleId; + } + + public PackageEntry getEntry() { + return entry; + } + + public Instant getTimestamp() { + return timestamp; + } } diff --git a/src/main/java/de/dentrassi/rpm/builder/RpmMojo.java b/src/main/java/de/dentrassi/rpm/builder/RpmMojo.java index 5be010a..aaf19f2 100644 --- a/src/main/java/de/dentrassi/rpm/builder/RpmMojo.java +++ b/src/main/java/de/dentrassi/rpm/builder/RpmMojo.java @@ -14,6 +14,7 @@ * Peter Wilkinson - add skip entry flag * Daniel Singhal - Added primary artifact support * Jarkko Sonninen - Added outputFileNameProperty + * Fraunhofer-Institut fuer Optronik, Systemtechnik und Bildauswertung IOSB - apply rules to implicit created intermediate directories *******************************************************************************/ package de.dentrassi.rpm.builder; @@ -365,6 +366,33 @@ public void setGenerateDefaultSourcePackage(final boolean generateDefaultSourceP @Parameter(property = "rpm.prefixes") List prefixes; + /** + * Base paths for which implicitly created intermediate directories should + * explicitly add to package. + * + *
+     *       <generateIntermediateDirectories>
+     *           <baseDirectory>/opt/mycompany/myapp</baseDirectory>
+     *           <baseDirectory>/etc/mycompany</baseDirectory>
+     *       </generateIntermediateDirectories>
+     * 
+ * + *

+ * For these given base directories, any intermediate directories created + * implicitly from {@code } elements are added to the package + * as explicit entries. + * The rules with file information are applied to these added directories as well. + *

+ *

+ * Note: In case of {@code } entries this feature is only applied to + * directories outside of the collection. + *

+ * + * @since 1.11.0 + */ + @Parameter(property = "rpm.generateIntermediateDirectories") + List generateIntermediateDirectories; + /** * The actual payload/file entries *

@@ -1076,7 +1104,9 @@ protected void fillPayload(final RpmBuilder builder) throws MojoFailureException return; } - final BuilderContext ctx = builder.newContext(); + final ListenableBuilderContext ctx = new ListenableBuilderContext(builder.newContext()); + final MissingDirectoryTracker missingDirectoryTracker = new MissingDirectoryTracker(this.generateIntermediateDirectories); + ctx.registerListener(missingDirectoryTracker); this.logger.debug("Building payload:"); @@ -1091,6 +1121,11 @@ protected void fillPayload(final RpmBuilder builder) throws MojoFailureException fillFromEntry(ctx, entry); } } + + ctx.removeListener(missingDirectoryTracker); + if (!generateIntermediateDirectories.isEmpty()) { + missingDirectoryTracker.addMissingIntermediateDirectoriesToContext(ctx); + } } private void customizeHeader(final RpmBuilder builder) { @@ -1133,7 +1168,6 @@ private void fillFromEntryFile(final BuilderContext ctx, final PackageEntry entr this.logger.debug(" as file:"); final Path source = entry.getFile().toPath().toAbsolutePath(); this.logger.debug(" - source: %s", source); - ctx.addFile(entry.getName(), source, makeProvider(entry, " - ")); } diff --git a/src/site/markdown/entry.md b/src/site/markdown/entry.md index 5c8d64b..9b90de8 100644 --- a/src/site/markdown/entry.md +++ b/src/site/markdown/entry.md @@ -49,13 +49,16 @@ symbolic link points to. If this link to path is relative, then it is relative o In order to walk through a directory tree and add all files use: ``. -The collect elements requires one additional element: `` which defines the base path. In addition +The collect elements requires one additional element: `` which defines the base path. In addition, there is the optional element ``, which can be set to `false` in order to not record -directories explicitly. **Note:** the `` directory itself will never be added as explicit directory. This can be done using an additional `` element. +directories explicitly. **Note:** the `` directory itself will never be added as explicit directory. +This can be done using an additional `` element. +Alternatively the general configuration for [generating intermediate directories](#generate-intermediate-directories) +can be used to create intermediate directories when the package structure is built. **Note:** By default symbolic links in the file system will be ignored. Since not all platforms support -symbolic links in the same way. It is recommended to create the manually using a `` style -entry. This behavior can be changed by changed by adding `true` to the +symbolic links in the same way. It is recommended to create them manually using a `` style +entry. This behavior can be changed by adding `true` to the collector configuration. The target file names will be constructed out the entry name, as base prefix, and the relative @@ -87,7 +90,7 @@ Will result in the following payload entries: /usr/lib/foo/dir2/foo3.txt (file) /usr/lib/foo/dir2/foo4.txt (file) -As of version 1.0.0 it is also possible to use the standard Maven `includes`/`excludes` elements +As of version `1.0.0` it is also possible to use the standard Maven `includes`/`excludes` elements which follow the standard Maven include/exclude pattern. For example: @@ -144,3 +147,72 @@ In order to use explicit information use the following elements: Also see [payload entry information](payload_information.html) for a full list. + +## Generate intermediate directories + +Since version `1.11.0` there is an option to automatically generate and add intermediate directories +explicitly for all paths added by `` elements. +Therefore, it is not necessary to manually configure each sub path to have it added explicitly to the package. + +To configure this optional function, a list of base paths must be specified by the configuration (TODO, ggf configuration streichen) property +``. +For these given base directories, any intermediate directories created implicitly from `` elements are added to the package +as explicit entries. +Sub paths of the base directories themselfs are not explicitly added to the package. + +**NOTE:** The [entry information](#entry-information) (explicit and ruleset) is applied to these intermediate directories as well. + +Example: + + + /usr/lib/foo + /some/other/basedirectory + + + + + /usr/lib/foo/bar/ + path/to/file/foobar.txt + + + +Result: + + /usr/lib/foo (dir - added by generateIntermediateDirectories) + /usr/lib/foo/bar (dir - added by generateIntermediateDirectories) + /usr/lib/foo/bar/foobar.txt (file - added by file entry) + +**Note**: In case of `` entries the feature of generating intermediate directories is only +applied to directories outside of the collection. +The behaviour of adding directories inside of `` is controlled independently by setting the +`` flag (see [file system collector](#file-system-collector)). + +Collection Example: + + + /usr/lib/foo + + + + + /usr/lib/foo/bar + + ${project.basedir}/src/main/data + ... + + + + +Result with `true`: + + /usr/lib/foo (dir - added by generateIntermediateDirectories) + /usr/lib/foo/bar (dir - added by generateIntermediateDirectories) + /usr/lib/foo/bar/x (dir - added by collect entry) + /usr/lib/foo/bar/x/y (dir - added by collect entry) + /usr/lib/foo/bar/x/y/foobar.txt (file - added by collect entry) + +Result with `false`: + + /usr/lib/foo (dir - added by generateIntermediateDirectories) + /usr/lib/foo/bar (dir - added by generateIntermediateDirectories) + /usr/lib/foo/bar/x/y/foobar.txt (file - added by collect entry)