From 72f8dc26498d03f4e60612fcb1823ddb38518466 Mon Sep 17 00:00:00 2001 From: Mikolaj Izdebski Date: Thu, 29 May 2014 19:58:42 +0200 Subject: [PATCH] Add patch for injecting Javapackages manifests --- ...-post-processing-during-installation.patch | 212 ++++++++++++++++++ build.bash | 2 +- xmvn.spec | 13 +- 3 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 0001-Fix-JAR-post-processing-during-installation.patch diff --git a/0001-Fix-JAR-post-processing-during-installation.patch b/0001-Fix-JAR-post-processing-during-installation.patch new file mode 100644 index 0000000..91a5d8b --- /dev/null +++ b/0001-Fix-JAR-post-processing-during-installation.patch @@ -0,0 +1,212 @@ +From de2dd29f853fdb39ec5916a6c388f81ced4b61a5 Mon Sep 17 00:00:00 2001 +From: Mikolaj Izdebski +Date: Thu, 29 May 2014 18:18:30 +0200 +Subject: [PATCH] Fix JAR post-processing during installation + +This improves native code detection and Javapackages manifest +injection. +--- + .../install/impl/DefaultArtifactInstaller.java | 5 ++ + .../xmvn/tools/install/impl/JarUtils.java | 78 ++++++++++++++++++---- + 2 files changed, 70 insertions(+), 13 deletions(-) + +diff --git a/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/impl/DefaultArtifactInstaller.java b/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/impl/DefaultArtifactInstaller.java +index d973447..38a5eab 100644 +--- a/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/impl/DefaultArtifactInstaller.java ++++ b/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/impl/DefaultArtifactInstaller.java +@@ -16,6 +16,7 @@ + package org.fedoraproject.xmvn.tools.install.impl; + + import static org.fedoraproject.xmvn.tools.install.impl.JarUtils.containsNativeCode; ++import static org.fedoraproject.xmvn.tools.install.impl.JarUtils.injectManifest; + import static org.fedoraproject.xmvn.tools.install.impl.JarUtils.usesNativeCode; + + import java.nio.file.Path; +@@ -65,10 +66,14 @@ public class DefaultArtifactInstaller + new DefaultArtifact( am.getGroupId(), am.getArtifactId(), am.getExtension(), am.getClassifier(), + am.getVersion() ); + ++ // Handle native JARs/WARs etc + Path artifactPath = Paths.get( am.getPath() ); + if ( usesNativeCode( artifactPath ) || containsNativeCode( artifactPath ) ) + am.getProperties().setProperty( "native", "true" ); + ++ // Inject Javapackages manifests ++ injectManifest( artifactPath, artifact ); ++ + Map properties = new LinkedHashMap<>(); + for ( String name : am.getProperties().stringPropertyNames() ) + properties.put( name, am.getProperties().getProperty( name ) ); +diff --git a/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/impl/JarUtils.java b/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/impl/JarUtils.java +index 0ba8920..5dd09ea 100644 +--- a/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/impl/JarUtils.java ++++ b/xmvn-tools/xmvn-install/src/main/java/org/fedoraproject/xmvn/tools/install/impl/JarUtils.java +@@ -30,17 +30,22 @@ import org.objectweb.asm.ClassReader; + import org.objectweb.asm.ClassVisitor; + import org.objectweb.asm.MethodVisitor; + import org.objectweb.asm.Opcodes; ++import org.slf4j.Logger; ++import org.slf4j.LoggerFactory; + + import org.fedoraproject.xmvn.artifact.Artifact; + import org.fedoraproject.xmvn.utils.ArtifactUtils; ++import org.fedoraproject.xmvn.utils.FileUtils; + + /** + * @author Mikolaj Izdebski + */ + class JarUtils + { ++ private static final Logger logger = LoggerFactory.getLogger( JarUtils.class ); ++ + /** +- * Heuristically try to determine whether given JAR (or WAR, EAR, ...) file contains native (architecture-dependant) ++ * Heuristically try to determine whether given JAR (or WAR, EAR, ...) file contains native (architecture-dependent) + * code. + *

+ * Currently this code only checks only for ELF binaries, but that behavior can change in future. +@@ -63,19 +68,43 @@ class JarUtils + if ( ent.isDirectory() ) + continue; + if ( jis.read() == ELFMAG0 && jis.read() == ELFMAG1 && jis.read() == ELFMAG2 && jis.read() == ELFMAG3 ) ++ { ++ logger.debug( "Native code found inside {}: {}", jar, ent.getName() ); + return true; ++ } + } + ++ logger.trace( "Native code not found inside {}", jar ); + return false; + } + catch ( IOException e ) + { ++ logger.debug( "I/O exception caught when trying to determine whether JAR contains native code: {}", jar, e ); + return false; + } + } + ++ static class NativeMethodFound ++ extends RuntimeException ++ { ++ private static final long serialVersionUID = 1; ++ ++ final String className; ++ ++ final String methodName; ++ ++ final String methodSignature; ++ ++ NativeMethodFound( String className, String methodName, String methodSignature ) ++ { ++ this.className = className; ++ this.methodName = methodName; ++ this.methodSignature = methodSignature; ++ } ++ } ++ + /** +- * Heuristically try to determine whether given JAR (or WAR, EAR, ...) file is using native (architecture-dependant) ++ * Heuristically try to determine whether given JAR (or WAR, EAR, ...) file is using native (architecture-dependent) + * code. + *

+ * Currently this code only checks if any class file declares Java native methods, but that behavior can change in +@@ -90,29 +119,33 @@ class JarUtils + ZipEntry ent; + while ( ( ent = jis.getNextEntry() ) != null ) + { +- if ( ent.isDirectory() || !ent.getName().endsWith( ".class" ) ) ++ final String entryName = ent.getName(); ++ if ( ent.isDirectory() || !entryName.endsWith( ".class" ) ) + continue; + +- final boolean[] usesNativeCode = new boolean[1]; +- +- new ClassReader( jis ).accept( new ClassVisitor( Opcodes.ASM4 ) ++ new ClassReader( jis ).accept( new ClassVisitor( Opcodes.ASM5 ) + { + @Override + public MethodVisitor visitMethod( int flags, String name, String desc, String sig, String[] exc ) + { +- usesNativeCode[0] = ( flags & Opcodes.ACC_NATIVE ) != 0; ++ if ( ( flags & Opcodes.ACC_NATIVE ) != 0 ) ++ throw new NativeMethodFound( entryName, name, sig ); ++ + return super.visitMethod( flags, name, desc, sig, exc ); + } + }, ClassReader.SKIP_CODE ); +- +- if ( usesNativeCode[0] ) +- return true; + } + + return false; + } ++ catch ( NativeMethodFound e ) ++ { ++ logger.debug( "Native method {}({}) found in {}: {}", e.methodName, e.methodSignature, jar, e.className ); ++ return true; ++ } + catch ( IOException e ) + { ++ logger.debug( "I/O exception caught when trying to determine whether JAR uses native code: {}", jar, e ); + return false; + } + } +@@ -123,6 +156,11 @@ class JarUtils + { + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue( key, value ); ++ logger.trace( "Injected field {}: {}", key, value ); ++ } ++ else ++ { ++ logger.trace( "Not injecting field {} (it has default value \"{}\")", key, defaultValue ); + } + } + +@@ -132,18 +170,21 @@ class JarUtils + * + * @param targetJar + * @param artifact +- * @throws IOException + */ + public static void injectManifest( Path targetJar, Artifact artifact ) +- throws IOException + { +- targetJar = targetJar.toRealPath(); ++ logger.trace( "Trying to inject manifest to {}", artifact ); ++ ++ targetJar = FileUtils.followSymlink( targetJar ); + + try (JarInputStream jis = new JarInputStream( Files.newInputStream( targetJar ) )) + { + Manifest mf = jis.getManifest(); + if ( mf == null ) ++ { ++ logger.trace( "Manifest injection skipped: no pre-existing manifest found to update" ); + return; ++ } + + putAttribute( mf, ArtifactUtils.MF_KEY_GROUPID, artifact.getGroupId(), null ); + putAttribute( mf, ArtifactUtils.MF_KEY_ARTIFACTID, artifact.getArtifactId(), null ); +@@ -166,6 +207,17 @@ class JarUtils + jos.write( buf, 0, sz ); + } + } ++ catch ( IOException e ) ++ { ++ // 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 ) ++ { ++ logger.debug( "I/O exception caught when trying to read JAR: {}", targetJar ); + } + } + } +-- +1.9.0 + diff --git a/build.bash b/build.bash index a473faf..bed8e81 100644 --- a/build.bash +++ b/build.bash @@ -23,7 +23,7 @@ rpmbuild -bs --clean --define "_topdir `pwd`" --define "_sourcedir `pwd`" xmvn.s rm -Rf ${resultdir}/* # print root.log and build.log in case of failure trap "cat ${resultdir}/root.log | tail -30; cat ${resultdir}/build.log || :" 0 -mock -r fedora-rawhide-x86_64 SRPMS/*.src.rpm +mock -D 'jenkins 1' -r fedora-rawhide-x86_64 SRPMS/*.src.rpm # remove unneeded stuff rm -f xmvn-*.tar.xz diff --git a/xmvn.spec b/xmvn.spec index cc5afc6..9143cd8 100644 --- a/xmvn.spec +++ b/xmvn.spec @@ -1,6 +1,6 @@ Name: xmvn Version: 2.0.0 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Local Extensions for Apache Maven License: ASL 2.0 URL: http://mizdebsk.fedorapeople.org/xmvn @@ -8,6 +8,8 @@ BuildArch: noarch Source0: https://fedorahosted.org/released/%{name}/%{name}-%{version}.tar.xz +Patch0001: 0001-Fix-JAR-post-processing-during-installation.patch + BuildRequires: maven >= 3.2.1-3 BuildRequires: maven-local BuildRequires: beust-jcommander @@ -134,6 +136,12 @@ This package provides %{summary}. %prep %setup -q +# Skip patch application on Jenkins - it is supposed to always use the +# latest vanilla upstream snapshot. +%if !0%{?jenkins} +%patch0001 -p1 +%endif + %mvn_package :xmvn __noinstall # In XMvn 2.x xmvn-connector was renamed to xmvn-connector-aether @@ -280,6 +288,9 @@ end %doc LICENSE NOTICE %changelog +* Thu May 29 2014 Mikolaj Izdebski - 2.0.0-2 +- Add patch for injecting Javapackages manifests + * Thu May 29 2014 Mikolaj Izdebski - 2.0.0-1 - Update to upstream version 2.0.0