xmvn/0001-Use-apache-commons-compress-for-manifest-injection-a.patch
Mikolaj Izdebski fcf8ed5349 Avoid creating temp files during manifest injection
Resolves: rhbz#1579236
2018-05-18 11:38:24 +02:00

594 lines
24 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 4e1e7377d6318d2bd7dd8620269e172a704650e2 Mon Sep 17 00:00:00 2001
From: Michael Simacek <msimacek@redhat.com>
Date: Mon, 16 Apr 2018 15:29:50 +0200
Subject: [PATCH] Use apache-commons-compress for manifest injection and native
code detection
---
xmvn-parent/pom.xml | 8 +-
xmvn-tools/xmvn-install/pom.xml | 4 +
.../fedoraproject/xmvn/tools/install/JarUtils.java | 176 +++++++++------------
.../xmvn/tools/install/impl/JarUtilsTest.java | 55 +++++++
.../src/test/resources/recompression-size.jar | Bin 0 -> 4376 bytes
xmvn.spec | 3 +-
6 files changed, 140 insertions(+), 106 deletions(-)
create mode 100644 xmvn-tools/xmvn-install/src/test/resources/recompression-size.jar
diff --git a/xmvn-parent/pom.xml b/xmvn-parent/pom.xml
index df6af7fb..f6465d90 100644
--- a/xmvn-parent/pom.xml
+++ b/xmvn-parent/pom.xml
@@ -92,6 +92,7 @@
<plexusUtilsVersion>3.0.24</plexusUtilsVersion>
<pluginToolsVersion>3.5</pluginToolsVersion>
<slf4jVersion>1.7.25</slf4jVersion>
+ <commonsCompressVersion>1.16.1</commonsCompressVersion>
<!-- Build dependencies -->
<apivizVersion>1.3.2.GA</apivizVersion>
@@ -102,7 +103,7 @@
<compilerPluginVersion>3.6.1</compilerPluginVersion>
<dependencyPluginVersion>3.0.0</dependencyPluginVersion>
<deployPluginVersion>2.8.2</deployPluginVersion>
- <easymockVersion>3.4</easymockVersion>
+ <easymockVersion>3.5</easymockVersion>
<gpgPluginVersion>1.6</gpgPluginVersion>
<installPluginVersion>2.5.2</installPluginVersion>
<jacocoVersion>0.7.9</jacocoVersion>
@@ -321,6 +322,11 @@
<artifactId>plexus-container-default</artifactId>
<version>${plexusVersion}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>${commonsCompressVersion}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
<dependencies>
diff --git a/xmvn-tools/xmvn-install/pom.xml b/xmvn-tools/xmvn-install/pom.xml
index 66ac01d7..fbb36a68 100644
--- a/xmvn-tools/xmvn-install/pom.xml
+++ b/xmvn-tools/xmvn-install/pom.xml
@@ -61,5 +61,9 @@
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ </dependency>
</dependencies>
</project>
diff --git a/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/JarUtils.java b/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/JarUtils.java
index 98d3a57e..5cb62b0f 100644
--- a/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/JarUtils.java
+++ b/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/JarUtils.java
@@ -16,19 +16,16 @@
package org.fedoraproject.xmvn.tools.install;
import java.io.IOException;
-import java.lang.reflect.Field;
+import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.Collection;
+import java.util.Enumeration;
import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
-import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
+import org.apache.commons.compress.archivers.zip.ZipFile;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
@@ -43,6 +40,8 @@ import org.fedoraproject.xmvn.artifact.Artifact;
*/
public final class JarUtils
{
+ private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
+
private static final Logger LOGGER = LoggerFactory.getLogger( JarUtils.class );
// From /usr/include/linux/elf.h
@@ -67,28 +66,33 @@ public final class JarUtils
*
* @return {@code true} if native code was found inside given JAR
*/
- public static boolean containsNativeCode( Path jar )
+ public static boolean containsNativeCode( Path jarPath )
{
- try ( ZipInputStream jis = new ZipInputStream( Files.newInputStream( jar ) ) )
+ try ( ZipFile jar = new ZipFile( jarPath.toFile() ) )
{
- ZipEntry ent;
- while ( ( ent = jis.getNextEntry() ) != null )
+ Enumeration<ZipArchiveEntry> entries = jar.getEntries();
+ while ( entries.hasMoreElements() )
{
- if ( ent.isDirectory() )
+ ZipArchiveEntry entry = entries.nextElement();
+ if ( entry.isDirectory() )
continue;
- if ( jis.read() == ELFMAG0 && jis.read() == ELFMAG1 && jis.read() == ELFMAG2 && jis.read() == ELFMAG3 )
+ try ( InputStream jis = jar.getInputStream( entry ) )
{
- LOGGER.debug( "Native code found inside {}: {}", jar, ent.getName() );
- return true;
+ if ( jis.read() == ELFMAG0 && jis.read() == ELFMAG1 && jis.read() == ELFMAG2
+ && jis.read() == ELFMAG3 )
+ {
+ LOGGER.debug( "Native code found inside {}: {}", jarPath, entry.getName() );
+ return true;
+ }
}
}
- LOGGER.trace( "Native code not found inside {}", jar );
+ LOGGER.trace( "Native code not found inside {}", jarPath );
return false;
}
catch ( IOException e )
{
- LOGGER.debug( "I/O exception caught when trying to determine whether JAR contains native code: {}", jar,
+ LOGGER.debug( "I/O exception caught when trying to determine whether JAR contains native code: {}", jarPath,
e );
return false;
}
@@ -122,40 +126,47 @@ public final class JarUtils
*
* @return {@code true} given JAR as found inside to use native code
*/
- public static boolean usesNativeCode( Path jar )
+ public static boolean usesNativeCode( Path jarPath )
{
- try ( ZipInputStream jis = new ZipInputStream( Files.newInputStream( jar ) ) )
+ try ( ZipFile jar = new ZipFile( jarPath.toFile() ) )
{
- ZipEntry ent;
- while ( ( ent = jis.getNextEntry() ) != null )
+ Enumeration<ZipArchiveEntry> entries = jar.getEntries();
+ while ( entries.hasMoreElements() )
{
- final String entryName = ent.getName();
- if ( ent.isDirectory() || !entryName.endsWith( ".class" ) )
+ ZipArchiveEntry entry = entries.nextElement();
+ final String entryName = entry.getName();
+ if ( entry.isDirectory() || !entryName.endsWith( ".class" ) )
continue;
- new ClassReader( jis ).accept( new ClassVisitor( Opcodes.ASM4 )
+ try ( InputStream jis = jar.getInputStream( entry ) )
{
- @Override
- public MethodVisitor visitMethod( int flags, String name, String desc, String sig, String[] exc )
+ new ClassReader( jis ).accept( new ClassVisitor( Opcodes.ASM4 )
{
- if ( ( flags & Opcodes.ACC_NATIVE ) != 0 )
- throw new NativeMethodFound( entryName, name, sig );
+ @Override
+ public MethodVisitor visitMethod( int flags, String name, String desc, String sig,
+ String[] exc )
+ {
+ if ( ( flags & Opcodes.ACC_NATIVE ) != 0 )
+ throw new NativeMethodFound( entryName, name, sig );
- return super.visitMethod( flags, name, desc, sig, exc );
- }
- }, ClassReader.SKIP_CODE );
+ return super.visitMethod( flags, name, desc, sig, exc );
+ }
+ }, ClassReader.SKIP_CODE );
+ }
}
return false;
}
catch ( NativeMethodFound e )
{
- LOGGER.debug( "Native method {}({}) found in {}: {}", e.methodName, e.methodSignature, jar, e.className );
+ LOGGER.debug( "Native method {}({}) found in {}: {}", e.methodName, e.methodSignature, jarPath,
+ e.className );
return true;
}
catch ( IOException e )
{
- LOGGER.debug( "I/O exception caught when trying to determine whether JAR uses native code: {}", jar, e );
+ LOGGER.debug( "I/O exception caught when trying to determine whether JAR uses native code: {}", jarPath,
+ e );
return false;
}
catch ( RuntimeException e )
@@ -178,29 +189,13 @@ public final class JarUtils
}
}
- /**
- * OpenJDK has a sanity check that prevents adding duplicate entries to ZIP streams. The problem is that some of
- * JARs we try to inject manifests to (especially the ones created by Gradle) already contain duplicate entries, so
- * manifest injection would always fail for them with "ZipException: duplicate entry".
- * <p>
- * This function tries to work around this OpenJDK sanity check, effectively allowing creating ZIP files with
- * duplicated entries. It should be called on particular ZIP output stream before adding each duplicate entry.
- *
- * @param zipOutputStream ZIP stream to hack
- */
- private static void openJdkAvoidDuplicateEntryHack( ZipOutputStream zipOutputStream )
+ private static void updateManifest( Artifact artifact, Manifest mf )
{
- try
- {
- Field namesField = ZipOutputStream.class.getDeclaredField( "names" );
- namesField.setAccessible( true );
- Collection<?> names = (Collection<?>) namesField.get( zipOutputStream );
- names.clear();
- }
- catch ( ReflectiveOperationException e )
- {
- // This hack relies on OpenJDK internals and therefore is not guaranteed to work. Ignore failures.
- }
+ putAttribute( mf, Artifact.MF_KEY_GROUPID, artifact.getGroupId(), null );
+ putAttribute( mf, Artifact.MF_KEY_ARTIFACTID, artifact.getArtifactId(), null );
+ putAttribute( mf, Artifact.MF_KEY_EXTENSION, artifact.getExtension(), Artifact.DEFAULT_EXTENSION );
+ putAttribute( mf, Artifact.MF_KEY_CLASSIFIER, artifact.getClassifier(), "" );
+ putAttribute( mf, Artifact.MF_KEY_VERSION, artifact.getVersion(), Artifact.DEFAULT_VERSION );
}
/**
@@ -213,65 +208,38 @@ public final class JarUtils
public static void injectManifest( Path targetJar, Artifact artifact )
{
LOGGER.trace( "Trying to inject manifest to {}", artifact );
- Manifest mf = null;
try
{
- try ( JarInputStream jis = new JarInputStream( Files.newInputStream( targetJar ) ) )
+ try ( ZipFile jar = new ZipFile( targetJar.toFile() ) )
{
- mf = jis.getManifest();
- if ( mf == null )
+ ZipArchiveEntry manifestEntry = jar.getEntry( MANIFEST_PATH );
+ if ( manifestEntry != null )
{
- // getManifest sometimes doesn't find the manifest, try finding it as plain entry
- ZipEntry ent;
- while ( ( ent = jis.getNextEntry() ) != null )
+ Manifest mf = new Manifest( jar.getInputStream( manifestEntry ) );
+ updateManifest( artifact, mf );
+ Files.delete( targetJar );
+ try ( ZipArchiveOutputStream os = new ZipArchiveOutputStream( targetJar.toFile() ) )
{
- if ( ent.getName().equalsIgnoreCase( "META-INF/MANIFEST.MF" ) )
- {
- mf = new Manifest( jis );
- break;
- }
+ // write manifest
+ ZipArchiveEntry newManifestEntry = new ZipArchiveEntry( MANIFEST_PATH );
+ os.putArchiveEntry( newManifestEntry );
+ mf.write( os );
+ os.closeArchiveEntry();
+ // copy the rest of content
+ jar.copyRawEntries( os, entry -> !entry.equals( manifestEntry ) );
}
- }
- }
-
- if ( mf == null )
- {
- LOGGER.trace( "Manifest injection skipped: no pre-existing manifest found to update" );
- return;
- }
-
- putAttribute( mf, Artifact.MF_KEY_GROUPID, artifact.getGroupId(), null );
- putAttribute( mf, Artifact.MF_KEY_ARTIFACTID, artifact.getArtifactId(), null );
- putAttribute( mf, Artifact.MF_KEY_EXTENSION, artifact.getExtension(), Artifact.DEFAULT_EXTENSION );
- putAttribute( mf, Artifact.MF_KEY_CLASSIFIER, artifact.getClassifier(), "" );
- putAttribute( mf, Artifact.MF_KEY_VERSION, artifact.getVersion(), Artifact.DEFAULT_VERSION );
-
- try ( JarInputStream jis = new JarInputStream( Files.newInputStream( targetJar ) ) )
- {
-
- targetJar = targetJar.toRealPath();
- Files.delete( targetJar );
- try ( JarOutputStream jos = new JarOutputStream( Files.newOutputStream( targetJar ), mf ) )
- {
- byte[] buf = new byte[512];
- JarEntry entry;
- while ( ( entry = jis.getNextJarEntry() ) != null )
+ catch ( IOException e )
{
- openJdkAvoidDuplicateEntryHack( jos );
- jos.putNextEntry( entry );
-
- int sz;
- while ( ( sz = jis.read( buf ) ) > 0 )
- jos.write( buf, 0, sz );
+ // Re-throw exceptions that occur when processing JAR file after reading header and manifest.
+ throw new RuntimeException( e );
}
+ LOGGER.trace( "Manifest injected successfully" );
}
- catch ( IOException e )
+ else
{
- // Re-throw exceptions that occur when processing JAR file after reading header and manifest.
- throw new RuntimeException( e );
+ LOGGER.trace( "Manifest injection skipped: no pre-existing manifest found to update" );
+ return;
}
-
- LOGGER.trace( "Manifest injected successfully" );
}
}
catch ( IOException e )
diff --git a/xmvn-tools/xmvn-install/src/test/java/org/fedoraproject/xmvn/tools/install/impl/JarUtilsTest.java b/xmvn-tools/xmvn-install/src/test/java/org/fedoraproject/xmvn/tools/install/impl/JarUtilsTest.java
index 3ec10cfa..98945a64 100644
--- a/xmvn-tools/xmvn-install/src/test/java/org/fedoraproject/xmvn/tools/install/impl/JarUtilsTest.java
+++ b/xmvn-tools/xmvn-install/src/test/java/org/fedoraproject/xmvn/tools/install/impl/JarUtilsTest.java
@@ -19,11 +19,13 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.PosixFilePermission;
import java.util.Arrays;
import java.util.jar.Attributes;
import java.util.jar.JarInputStream;
@@ -116,6 +118,38 @@ public class JarUtilsTest
}
}
+ /**
+ * Regression test for a jar which contains an entry that can recompress with a different size, which caused a
+ * mismatch in sizes.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testManifestInjectionRecompressionCausesSizeMismatch()
+ throws Exception
+ {
+ Path testResource = Paths.get( "src/test/resources/recompression-size.jar" );
+ Path testJar = workDir.resolve( "manifest.jar" );
+ Files.copy( testResource, testJar, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING );
+
+ Artifact artifact =
+ new DefaultArtifact( "org.eclipse.osgi", "osgi.compatibility.state", "1.1.0.v20180409-1212" );
+ JarUtils.injectManifest( testJar, artifact );
+
+ try ( JarInputStream jis = new JarInputStream( Files.newInputStream( testJar ) ) )
+ {
+ Manifest mf = jis.getManifest();
+ assertNotNull( mf );
+
+ Attributes attr = mf.getMainAttributes();
+ assertNotNull( attr );
+
+ assertEquals( "org.eclipse.osgi", attr.getValue( "JavaPackages-GroupId" ) );
+ assertEquals( "osgi.compatibility.state", attr.getValue( "JavaPackages-ArtifactId" ) );
+ assertEquals( "1.1.0.v20180409-1212", attr.getValue( "JavaPackages-Version" ) );
+ }
+ }
+
/**
* Test JAR if manifest injection works as expected when some artifact fields have default values.
*
@@ -148,6 +182,27 @@ public class JarUtilsTest
}
}
+ /**
+ * Test JAR if manifest injection preserves sane file perms.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testManifestInjectionSanePermissions()
+ throws Exception
+ {
+ Path testResource = Paths.get( "src/test/resources/example.jar" );
+ Path testJar = workDir.resolve( "manifest.jar" );
+ Files.copy( testResource, testJar, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING );
+
+ assumeTrue( "sane umask", Files.getPosixFilePermissions( testJar ).contains( PosixFilePermission.OTHERS_READ ) );
+
+ Artifact artifact = new DefaultArtifact( "org.apache.maven", "maven-model", "xsd", "model", "2.2.1" );
+ JarUtils.injectManifest( testJar, artifact );
+
+ assertTrue( Files.getPosixFilePermissions( testJar ).contains( PosixFilePermission.OTHERS_READ ) );
+ }
+
/**
* Test if native code detection works as expected.
*
diff --git a/xmvn-tools/xmvn-install/src/test/resources/recompression-size.jar b/xmvn-tools/xmvn-install/src/test/resources/recompression-size.jar
new file mode 100644
index 00000000..976481ea
--- /dev/null
+++ b/xmvn-tools/xmvn-install/src/test/resources/recompression-size.jar
@@ -0,0 +1,145 @@
+PK
+<00>L<EFBFBD>L<EFBFBD><02>META-INF/MANIFEST.MFUT <03>G<EFBFBD>Z;H<>Zux <04>H<04>HManifest-Version: 1.0
+
+PK<00>L<EFBFBD>L<EFBFBD><4C>^]<5D><00><>
dir/fileUT 7G<37>Z8G<38>Zux <04>H<04>H<00><><EFBFBD><06>0<14><><EFBFBD><EFBFBD><EFBFBD>ӫ<EFBFBD><D3AB><EFBFBD>'
<EFBFBD>QJ %<25>a6<61>m<EFBFBD><6D>u<EFBFBD>,%<25>8<EFBFBD>=<3D><>x}<7D>y<EFBFBD><79><EFBFBD><1B>?<3F><><EFBFBD>
TxP<EFBFBD><EFBFBD><1E>P<>A<EFBFBD><41>TxP{@<40><1E>P<>A<EFBFBD>TxP{@<40><07>TxP<78>A<EFBFBD><1E>Pj<0F>=<3D>ƒ<EFBFBD><03>„<EFBFBD><07>PP<50><50><15>P{@A<><41>Tx{@<40><15>PP<50>A<EFBFBD><41>Tx{@A<><15>PP<50><50>Tx<54>AA<41><07>TxP{<7B><1E>P<>A<EFBFBD><01>TxP{@<40><07><07>P<>A<EFBFBD><1E>P{@<40><07>Tx{@<40><1E>PP<50><50><01>Tx{@A<><41>ԅ<><D485>
+o(<28><><EFBFBD><EFBFBD><EFBFBD>0PP<50><50><1E><1E>AA<41><41>TxP{<7B><15>PP<50>A<EFBFBD>TxP{@<40><07>GxPP<50>A<EFBFBD><1E>P*<<3C>=<3D>ƒ<EFBFBD><03><>@<40><07>TxP{@]<18><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+o<0F> <1E>PP<50><50>ua<75>ƒ<EFBFBD>
+*<2A>=<3D>. Tx{@A<><41>TxP<78><50><1E>P<>A<EFBFBD><41>TxP{@<40><15>PP<50>A<EFBFBD>TxP{@<40><07>TxP<78>A<EFBFBD><1E>Pj<0F>=<3D>ƒ<EFBFBD><03>„<EFBFBD><07>P<>A<EFBFBD><15>P{@A<><41>Tx{@<40><15>PP<50><50><01>Tx{@A<><15>PP<50><50>Tx<54>AA<41><07>TxP{<7B><1E>P<>A<EFBFBD>TxP{@<40><07><07>P<>A<EFBFBD><1E>P{@<40><07>Tx{@<40><1E>PP<50><50><01>TxP{@A<><41>ԅ<><D485>
+o(<28><><EFBFBD><EFBFBD><EFBFBD>0PP<50><50><15>P
+*<2A>=<3D><>ƒ<EFBFBD>#<(<28><><EFBFBD><EFBFBD><EFBFBD>
+j<><6A><EFBFBD><EFBFBD>ƒ<EFBFBD>*<<3C>=ƒ<>
+j<0F><><EFBFBD><EFBFBD><EFBFBD>
+*<<3C>=<3D>ƒ<EFBFBD><03><>@<40><07>TxP{@]<18><><EFBFBD><EFBFBD><EFBFBD>
+j<0F> <1E>PP<50><50>ua<75>ƒ<EFBFBD>
+*<2A>=<3D>. Tx{@A<><41>TxP<78><50><15>PP<50>A<EFBFBD><41>TxP{@<40><15>PP<50>A<EFBFBD>TxP{@<40><07>TxP<78>A<EFBFBD><1E>P<>A<EFBFBD><01>TxP{@]<5D><><EFBFBD><EFBFBD><EFBFBD>*<<3C>=<3D><><EFBFBD><EFBFBD>j<0F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+o<0F>=<3D><><EFBFBD><EFBFBD>
+*<2A>=<3D><><EFBFBD><EFBFBD>
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ
+/<(<28><><EFBFBD><EFBFBD><EFBFBD>
+j<><6A><EFBFBD><EFBFBD>ƒ<EFBFBD>*<<3C>=ƒ<>
+j<0F><><EFBFBD><EFBFBD>
+*<<3C>=<3D>ƒ<EFBFBD>j<0F><><EFBFBD><EFBFBD><EFBFBD>
+j<0F>=<3D>ƒ<EFBFBD>
+*<2A>=<3D><><EFBFBD>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>0P<30>A<EFBFBD><15>P
+*<2A>=<3D><><EFBFBD><EFBFBD><03><>@A<><41>TxP{<7B><15>PP<50>A<EFBFBD>Tx{@A<><07>GxPP<50>A<EFBFBD><1E>P<>A<EFBFBD><07>TxP{@<40><1E>P<>A<EFBFBD>ua<75>ƒ<EFBFBD>*<<3C>=<3D>. TxP{@A<><41>ԅ<>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>0P{@<40><15>PP<50>A<EFBFBD><41>Tx{@A<><15>PP<50>A<EFBFBD>Tx{@A<><07>TxP<78><50><1E>P<>A<EFBFBD><07>TxP{@<40><07><07>P<>A<EFBFBD><1E>P{@<40><07>Tx{@<40><1E>PP<50><50><01>Tx{@A<><41><07>PP<50><50><15>P{@A<><41>TxP<78><50><15>PP<50>A<EFBFBD>Tx{@A<><07>GxPP<50>A<EFBFBD><1E><1E>AA<41><07>TxP{@<40><1E>P<>A<EFBFBD><01>TxP{@<40><07><07>P<>A<EFBFBD><15>P*<<3C>=<3D><><EFBFBD><EFBFBD><03><>@A<><41>Tx{@](<28><><EFBFBD><EFBFBD><EFBFBD>
+o<0F> <15>PP<50>A<EFBFBD>Tx{@A<><07>GxPP<50><50><1E>P<>A<EFBFBD><07>TxP{@<40><1E>P<>A<EFBFBD>ua<75>ƒ<EFBFBD>*<<3C>=<3D>. TxP{@<40><07>ԅ<>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>0P{@<40><15>PP<50>A<EFBFBD><41>Tx{@A<><15>PP<50><50>Tx{@A<><07>TxP<78><50><1E>P<>A<EFBFBD><41>TxP{@<40><07><07>P<>A<EFBFBD><1E>P{@<40><07>Tx{@<40><1E>PP<50><50><01>TxP{@A<><41><07>PP<50><50><15>P{@A<><41>Tx{@](<28><><EFBFBD><EFBFBD><EFBFBD>
+j<><6A><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+*<<3C>=ƒ<>
+j<0F><><EFBFBD><EFBFBD>
+*<<3C>=<3D>ƒ<EFBFBD>#<(<28><><EFBFBD><EFBFBD><EFBFBD>
+j<0F>=<3D>ƒ<EFBFBD>*<<3C>=<3D><><EFBFBD>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>0P<30>A<EFBFBD><15>P*<<3C>=<3D><><EFBFBD><EFBFBD><03><>@A<><41>Tx{@](<28><><EFBFBD><EFBFBD><EFBFBD>
+o<0F> <15>PP<50>A<EFBFBD>Tx{@A<><07>TxP<78>A<EFBFBD><1E>P<>A<EFBFBD><07>TxP{@<40><1E>P<>A<EFBFBD>ua<75>ƒ<EFBFBD>*<<3C>=<3D>. TxP{@<40><07>ԅ<><D485>j(<28><><EFBFBD><EFBFBD><EFBFBD>
+o<0F>=<3D><><EFBFBD><EFBFBD>
+*<<3C><><EFBFBD><EFBFBD><EFBFBD>
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ<EFBFBD>*<<3C>=<3D><><EFBFBD>
+j<0F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*<<3C>=<3D>ƒ<EFBFBD>j<0F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+o<0F>=<3D>ƒ<EFBFBD>
+*<2A>=<3D><><EFBFBD>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><><EFBFBD><EFBFBD><03><>@A<><41>TxP{<7B><15>PP<50>A<EFBFBD>Tx{@A<><07>GxPP<50>A<EFBFBD><1E><1E>AA<41><07>TxP{@<40><1E>P<>A<EFBFBD><01>TxP{@<40><07>ԅ<>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>0P<30>A<EFBFBD><15>P*<<3C>=<3D><><EFBFBD><EFBFBD><03><>@A<><41>Tx{@](<28><><EFBFBD><EFBFBD><EFBFBD>
+j<><6A><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+*<<3C>=<3D>ƒ
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<<3C>=<3D>ƒ<EFBFBD>*<<3C><><EFBFBD><EFBFBD><EFBFBD>
+j<0F><><EFBFBD>ƒ<EFBFBD>*<<3C>=<3D>. TxP{@<40><07>ԅ<><D485>j(<28><><EFBFBD><EFBFBD><EFBFBD>
+o<0F>=<3D><><EFBFBD><EFBFBD>
+*<2A>=<3D><><EFBFBD><EFBFBD>
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ<EFBFBD>*<<3C>=ƒ<>
+j<0F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*<<3C>=<3D>ƒ<EFBFBD>j<0F><><EFBFBD><EFBFBD><EFBFBD>
+j<0F>=<3D>ƒ<EFBFBD>
+*<2A>=<3D><><EFBFBD>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><><EFBFBD><EFBFBD><03><>@A<><41>Tx{@](<28><><EFBFBD><EFBFBD><EFBFBD>
+j<><6A><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+*<<3C>=ƒ<>
+o(<28><><EFBFBD><EFBFBD>
+*<<3C>=<3D>ƒ<EFBFBD>#<(<28><><EFBFBD><EFBFBD><EFBFBD>
+j<0F>=<3D>ƒ<EFBFBD>*<<3C>=<3D>. TxP{@<40><07>ԅ<>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>0P<30>A<EFBFBD><15>P
+*<2A>=<3D><><EFBFBD><EFBFBD><03><>@A<><41>Tx{@](<28><><EFBFBD><EFBFBD><EFBFBD>
+j<0F><><EFBFBD><EFBFBD><EFBFBD>
+*<<3C>=<3D>ƒ
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<<3C>=<3D>ƒ<EFBFBD>*<<3C><><EFBFBD><EFBFBD><EFBFBD>
+j<0F> <1E>P<>A<EFBFBD>ua<75><61><EFBFBD><EFBFBD>*<<3C>=<3D><><EFBFBD><EFBFBD>j(<28><><EFBFBD><EFBFBD><EFBFBD>
+o<0F>=<3D><><EFBFBD><EFBFBD>
+*<<3C><><EFBFBD><EFBFBD><EFBFBD>
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+j<><6A><EFBFBD><EFBFBD>ƒ<EFBFBD>*<<3C>=ƒ<>
+j<0F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*<<3C>=<3D>ƒ<EFBFBD>j<0F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+o<0F>=<3D>ƒ<EFBFBD>
+*<2A>=<3D><><EFBFBD>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>0PP<50><50><15>P
+*<2A>=<3D><><EFBFBD><EFBFBD><03><>@A<><41>TxP{<7B><15>PP<50>A<EFBFBD>TxP{@<40><07>GxPP<50>A<EFBFBD><1E><1E>AA<41><07>TxP{@]<18><><EFBFBD><EFBFBD><EFBFBD>
+j<0F> <1E>PP<50><50>ua<75>ƒ<EFBFBD>
+*<2A>=<3D>. TxP{@A<><41>ԅ<><D485>
+o(<28><><EFBFBD><EFBFBD><EFBFBD>0PP<50><50><15>PP<50>A<EFBFBD><41>TxP{@<40><15>PP<50>A<EFBFBD>TxP{@<40><07>TxP<78>A<EFBFBD><1E>P<>A<EFBFBD><07>TxP{@]<18>=<3D><><EFBFBD>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><><EFBFBD><EFBFBD>j(<28><><EFBFBD><EFBFBD><EFBFBD>
+o<0F>=<3D><><EFBFBD><EFBFBD>
+*<<3C><><EFBFBD><EFBFBD><EFBFBD>
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ<EFBFBD>#<(<28><><EFBFBD><EFBFBD><EFBFBD>
+j<><6A><EFBFBD><EFBFBD>ƒ<EFBFBD>*<<3C>=<3D><><EFBFBD>
+j<0F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*<<3C>=<3D>ƒ<EFBFBD>j<0F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+o<0F>=<3D>ƒ<EFBFBD>
+*<2A>=<3D>. TxP{@A<><41>ԅ<><D485>
+o(<28><><EFBFBD><EFBFBD><EFBFBD>0PP<50><50><1E><1E>AA<41><41>TxP{<7B><15>PP<50>A<EFBFBD>TxP{@<40><07>GxPP<50>A<EFBFBD><1E>P*<<3C>=<3D>ƒ<EFBFBD><03><>@<40><07>TxP{@]<18><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+o<0F> <1E>PP<50><50>ua<75>ƒ<EFBFBD>
+*<2A>=<3D>. Tx{@A<><41>TxP<78><50><1E>P<>A<EFBFBD><41>TxP{@<40><15>PP<50>A<EFBFBD>TxP{@<40><07>TxP<78>A<EFBFBD><1E>P<>A<EFBFBD><01>TxP{@A<><41><07>P<>A<EFBFBD><15>P{@A<><41>Tx{@<40><15>PP<50><50><01>Tx{@A<><15>PP<50><50><1E><1E>AA<41><07>TxP{<7B><1E>P<>A<EFBFBD>TxP{@<40><07><07>P<>A<EFBFBD><1E>P{@<40><07>TxP{@<40><1E>PP<50><50>ua<75>ƒ<EFBFBD>
+*<2A>=<3D>. Tx{@A<><41>ԅ<><D485>
+o(<28><><EFBFBD><EFBFBD><EFBFBD>0PP<50><50><1E><1E>AA<41><41>TxP{<7B><1E>P<>A<EFBFBD>TxP{@<40><07>TxP<78>A<EFBFBD><1E>P*<<3C>=<3D>ƒ<EFBFBD><03><>@<40><07>TxP{@]<18><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+o<0F> <1E>PP<50><50>ua<75><61><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ<EFBFBD>*<<3C><><EFBFBD><EFBFBD><EFBFBD>
+j<0F><><EFBFBD><EFBFBD><EFBFBD>
+*<<3C>=<3D>ƒ
+j<0F><><EFBFBD><EFBFBD><EFBFBD>
+j<0F>=<3D>ƒ<EFBFBD>
+*<2A>=<3D><><EFBFBD>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>*<<3C>=<3D><><EFBFBD><EFBFBD>j(<28><><EFBFBD><EFBFBD><EFBFBD>
+o<0F>=<3D><><EFBFBD><EFBFBD>
+*<<3C><><EFBFBD><EFBFBD><EFBFBD>
+o(<28><><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ<EFBFBD>#<(<28><><EFBFBD><EFBFBD><EFBFBD>
+j<><6A><EFBFBD><EFBFBD>ƒ<EFBFBD>*<<3C>=ƒ<>
+j<0F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*<<3C>=<3D>ƒ<EFBFBD>j<0F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+o<0F> <1E>PP<50><50>ua<75>ƒ<EFBFBD>
+*<2A>=<3D>. Tx{@A<><41>ԅ<><D485>
+o(<28><><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ<EFBFBD>#<(<28><><EFBFBD><EFBFBD><EFBFBD>
+j<><6A><EFBFBD><EFBFBD>ƒ<EFBFBD>*<<3C>=<3D>ƒ
+j<0F><><EFBFBD><EFBFBD><EFBFBD>
+*<<3C>=<3D>ƒ<EFBFBD><03><>@<40><07>TxP{@]<18><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
+o<0F> <1E>PP<50><50>ua<75><61><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ
+o(<28><><EFBFBD><EFBFBD><EFBFBD>
+*<2A>=<3D><><EFBFBD><EFBFBD>
+*<<3C><><EFBFBD><EFBFBD><EFBFBD>
+j<0F><><EFBFBD><EFBFBD><EFBFBD>
+*<<3C>=<3D>ƒ
+j<0F><><EFBFBD><EFBFBD><EFBFBD>
+j<0F>=<3D>ƒ<EFBFBD>*<<3C>=<3D><><EFBFBD>
+j(<28><><EFBFBD><EFBFBD><EFBFBD>*<<3C>=<3D><><EFBFBD><EFBFBD>j(<28><><EFBFBD><EFBFBD><EFBFBD>
+o<0F>=<3D><><EFBFBD><EFBFBD>
+*<2A>=<3D><><EFBFBD><EFBFBD>
+o(<28><><EFBFBD><EFBFBD>
+*<2A>=<3D><>ƒ<EFBFBD>#<(<28><><EFBFBD><EFBFBD><EFBFBD>
+j<><6A><EFBFBD><EFBFBD>ƒ<EFBFBD>*<<3C>=ƒ<>
+j<0F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*<<3C>=<3D>ƒ<EFBFBD>j<0F><><EFBFBD><EFBFBD><EFBFBD>OPK
+<00>L<EFBFBD>L<EFBFBD><02><00><>META-INF/MANIFEST.MFUT<03>G<EFBFBD>Zux <04>H<04>HPK<00>L<EFBFBD>L<EFBFBD><4C>^]<5D><00><>
<00><>gdir/fileUT7G<37>Zux <04>H<04>HPK<00>Z
\ No newline at end of file
diff --git a/xmvn.spec b/xmvn.spec
index 8764b63d..0775d4a2 100644
--- a/xmvn.spec
+++ b/xmvn.spec
@@ -196,6 +196,7 @@ artifact repository.
%package install
Summary: XMvn Install
+Requires: apache-commons-compress
%description install
This package provides XMvn Install, which is a command-line interface
@@ -284,7 +285,7 @@ done
# helper scripts
%jpackage_script org.fedoraproject.xmvn.tools.bisect.BisectCli "" "-Dxmvn.home=%{_datadir}/%{name}" xmvn/xmvn-bisect:beust-jcommander:maven-invoker:plexus/utils xmvn-bisect
-%jpackage_script org.fedoraproject.xmvn.tools.install.cli.InstallerCli "" "" xmvn/xmvn-install:xmvn/xmvn-api:xmvn/xmvn-core:beust-jcommander:slf4j/api:slf4j/simple:objectweb-asm/asm xmvn-install
+%jpackage_script org.fedoraproject.xmvn.tools.install.cli.InstallerCli "" "" xmvn/xmvn-install:xmvn/xmvn-api:xmvn/xmvn-core:beust-jcommander:slf4j/api:slf4j/simple:objectweb-asm/asm:objenesis/objenesis:commons-compress xmvn-install
%jpackage_script org.fedoraproject.xmvn.tools.resolve.ResolverCli "" "" xmvn/xmvn-resolve:xmvn/xmvn-api:xmvn/xmvn-core:beust-jcommander xmvn-resolve
%jpackage_script org.fedoraproject.xmvn.tools.subst.SubstCli "" "" xmvn/xmvn-subst:xmvn/xmvn-api:xmvn/xmvn-core:beust-jcommander xmvn-subst
--
2.14.3