package org.apache.maven.plugin.nar; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.regex.Pattern; import org.apache.bcel.classfile.ClassParser; import org.apache.bcel.classfile.JavaClass; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.PropertyUtils; import org.codehaus.plexus.util.cli.Commandline; /** * @author Mark Donszelmann */ public final class NarUtil { private NarUtil() { // never instantiate } private static Properties defaults; public static Properties getDefaults() throws MojoFailureException { // read properties file with defaults if ( defaults == null ) { defaults = PropertyUtils.loadProperties( NarUtil.class.getResourceAsStream( "aol.properties" ) ); } if ( defaults == null ) { throw new MojoFailureException( "NAR: Could not load default properties file: 'aol.properties'." ); } return defaults; } public static String getOS( String defaultOs ) { String os = defaultOs; // adjust OS if not given if ( os == null ) { os = System.getProperty( "os.name" ); String name = os.toLowerCase(); if ( name.startsWith( "windows" ) ) { os = OS.WINDOWS; } if ( name.startsWith( "linux" ) ) { os = OS.LINUX; } if ( name.equals( "mac os x" ) ) { os = OS.MACOSX; } } return os; } public static String getArchitecture( String architecture ) { return architecture; } public static Linker getLinker( Linker linker ) { Linker link = linker; if ( link == null ) { link = new Linker(); } return link; } public static String getLinkerName( String architecture, String os, Linker linker ) throws MojoFailureException, MojoExecutionException { return getLinker( linker ).getName( getDefaults(), getArchitecture( architecture ) + "." + getOS( os ) + "." ); } public static AOL getAOL( String architecture, String os, Linker linker, String aol ) throws MojoFailureException, MojoExecutionException { // adjust aol return aol == null ? new AOL( getArchitecture( architecture ), getOS( os ), getLinkerName( architecture, os, linker ) ) : new AOL( aol ); } // FIXME, should go to AOL. public static String getAOLKey( String architecture, String os, Linker linker ) throws MojoFailureException, MojoExecutionException { // construct AOL key prefix return getArchitecture( architecture ) + "." + getOS( os ) + "." + getLinkerName( architecture, os, linker ) + "."; } public static String getAOLKey( String aol ) { // FIXME, this may not always work correctly return replace( "-", ".", aol ); } public static File getJavaHome( File javaHome, String os ) { File home = javaHome; // adjust JavaHome if ( home == null ) { home = new File( System.getProperty( "java.home" ) ); if ( !getOS( os ).equals( OS.MACOSX ) ) { home = new File( home, ".." ); } } return home; } public static void makeExecutable( File file, final Log log ) throws MojoExecutionException, MojoFailureException { if ( !file.exists() ) { return; } if ( file.isDirectory() ) { File[] files = file.listFiles(); for ( int i = 0; i < files.length; i++ ) { makeExecutable( files[i], log ); } } if ( file.isFile() && file.canRead() && file.canWrite() && !file.isHidden() ) { // chmod +x file int result = runCommand( "chmod", new String[] { "+x", file.getPath() }, null, null, log ); if ( result != 0 ) { throw new MojoExecutionException( "Failed to execute 'chmod +x " + file.getPath() + "'" + " return code: \'" + result + "\'." ); } } } public static void runRanlib( File file, final Log log ) throws MojoExecutionException, MojoFailureException { if ( !file.exists() ) { return; } if ( file.isDirectory() ) { File[] files = file.listFiles(); for ( int i = 0; i < files.length; i++ ) { runRanlib( files[i], log ); } } if ( file.isFile() && file.canWrite() && !file.isHidden() && file.getName().endsWith( ".a" ) ) { // ranlib file int result = runCommand( "ranlib", new String[] { file.getPath() }, null, null, log ); if ( result != 0 ) { throw new MojoExecutionException( "Failed to execute 'ranlib " + file.getPath() + "'" + " return code: \'" + result + "\'." ); } } } static void runInstallNameTool( File[] files, final Log log ) throws MojoExecutionException, MojoFailureException { Set libs = findInstallNameToolCandidates( files, log ); for ( Iterator i = libs.iterator(); i.hasNext(); ) { File subjectFile = ( File )i.next(); String subjectName = subjectFile.getName(); String subjectPath = subjectFile.getPath(); int idResult = runCommand( "install_name_tool", new String[] { "-id", subjectPath, subjectPath }, null, null, log ); if ( idResult != 0 ) { throw new MojoExecutionException( "Failed to execute 'install_name_tool -id " + subjectPath + " " + subjectPath + "'" + " return code: \'" + idResult + "\'." ); } for ( Iterator j = libs.iterator(); j.hasNext(); ) { File dependentFile = ( File )j.next(); String dependentPath = dependentFile.getPath(); if (dependentPath == subjectPath) continue; int changeResult = runCommand( "install_name_tool", new String[] { "-change", subjectName, subjectPath, dependentPath }, null, null, log ); if ( changeResult != 0 ) { throw new MojoExecutionException( "Failed to execute 'install_name_tool -change " + subjectName + " " + subjectPath + " " + dependentPath + "'" + " return code: \'" + changeResult + "\'." ); } } } } static Set findInstallNameToolCandidates( File[] files, final Log log ) throws MojoExecutionException, MojoFailureException { HashSet candidates = new HashSet(); for (int i = 0; i < files.length; i++) { File file = files[i]; if ( !file.exists() ) { continue; } if ( file.isDirectory() ) { candidates.addAll(findInstallNameToolCandidates( file.listFiles(), log )); } String fileName = file.getName(); if ( file.isFile() && file.canWrite() && ( fileName.endsWith( ".so" ) || fileName.endsWith( ".dylib" ) || (fileName.endsWith( ".jnilib" )))) { candidates.add(file); } } return candidates; } public static void makeLink( File file, final Log log ) throws MojoExecutionException, MojoFailureException { if ( !file.exists() ) { return; } if ( file.isDirectory() ) { File[] files = file.listFiles(); for ( int i = 0; i < files.length; i++ ) { makeLink( files[i], log ); } } if ( file.isFile() && file.canRead() && file.canWrite() && !file.isHidden() && file.getName().matches( ".*\\.so(\\.\\d+)+$" ) ) { File sofile = new File( file.getParent(), file.getName().substring( 0, file.getName().indexOf( ".so" ) + 3 ) ); if ( !sofile.exists() ) { // ln -s lib.so.xx lib.so int result = runCommand( "ln", new String[] { "-s", file.getName(), sofile.getPath() }, null, null, log ); if ( result != 0 ) { throw new MojoExecutionException( "Failed to execute 'ln -s " + file.getName() + " " + sofile.getPath() + "'" + " return code: \'" + result + "\'." ); } } } } /** * Returns the Bcel Class corresponding to the given class filename * * @param filename the absolute file name of the class * @return the Bcel Class. * @throws IOException */ public static JavaClass getBcelClass( String filename ) throws IOException { ClassParser parser = new ClassParser( filename ); return parser.parse(); } /** * Returns the header file name (javah) corresponding to the given class file name * * @param filename the absolute file name of the class * @return the header file name. */ public static String getHeaderName( String basename, String filename ) { String base = basename.replaceAll( "\\\\", "/" ); String file = filename.replaceAll( "\\\\", "/" ); if ( !file.startsWith( base ) ) { throw new IllegalArgumentException( "Error " + file + " does not start with " + base ); } String header = file.substring( base.length() + 1 ); header = header.replaceAll( "/", "_" ); header = header.replaceAll( "\\.class", ".h" ); return header; } /** * Replaces target with replacement in string. For jdk 1.4 compatiblity. * * @param target * @param replacement * @param string * @return */ public static String replace( CharSequence target, CharSequence replacement, String string ) { return Pattern.compile( quote( target.toString() )/* * , Pattern.LITERAL jdk 1.4 */).matcher( string ).replaceAll( /* Matcher. jdk 1.4 */quoteReplacement( replacement.toString() ) ); } /* for jdk 1.4 */ private static String quote( String s ) { final String escQ = "\\Q"; final String escE = "\\E"; int slashEIndex = s.indexOf( escE ); if ( slashEIndex == -1 ) { return escQ + s + escE; } StringBuffer sb = new StringBuffer( s.length() * 2 ); sb.append( escQ ); slashEIndex = 0; int current = 0; while ( ( slashEIndex = s.indexOf( escE, current ) ) != -1 ) { sb.append( s.substring( current, slashEIndex ) ); current = slashEIndex + 2; sb.append( escE ); sb.append( "\\" ); sb.append( escE ); sb.append( escQ ); } sb.append( s.substring( current, s.length() ) ); sb.append( escE ); return sb.toString(); } /* for jdk 1.4 */ private static String quoteReplacement( String s ) { if ( ( s.indexOf( '\\' ) == -1 ) && ( s.indexOf( '$' ) == -1 ) ) { return s; } StringBuffer sb = new StringBuffer(); for ( int i = 0; i < s.length(); i++ ) { char c = s.charAt( i ); if ( c == '\\' ) { sb.append( '\\' ); sb.append( '\\' ); } else if ( c == '$' ) { sb.append( '\\' ); sb.append( '$' ); } else { sb.append( c ); } } return sb.toString(); } public static final String DEFAULT_EXCLUDES = "**/*~,**/#*#,**/.#*,**/%*%,**/._*," + "**/CVS,**/CVS/**,**/.cvsignore," + "**/SCCS,**/SCCS/**,**/vssver.scc," + "**/.svn,**/.svn/**,**/.DS_Store"; public static int copyDirectoryStructure( File sourceDirectory, File destinationDirectory, String includes, String excludes ) throws IOException { if ( !sourceDirectory.exists() ) { throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." ); } List files = FileUtils.getFiles( sourceDirectory, includes, excludes ); String sourcePath = sourceDirectory.getAbsolutePath(); int copied = 0; for ( Iterator i = files.iterator(); i.hasNext(); ) { File file = (File) i.next(); String dest = file.getAbsolutePath(); dest = dest.substring( sourcePath.length() + 1 ); File destination = new File( destinationDirectory, dest ); if ( file.isFile() ) { // destination = destination.getParentFile(); FileUtils.copyFile( file, destination ); copied++; // copy executable bit try { // 1.6 only so coded using introspection // destination.setExecutable( file.canExecute(), false ); Method canExecute = file.getClass().getDeclaredMethod( "canExecute", new Class[] {} ); Method setExecutable = destination.getClass().getDeclaredMethod( "setExecutable", new Class[] { boolean.class, boolean.class } ); setExecutable.invoke( destination, new Object[] { (Boolean) canExecute.invoke( file, new Object[] {} ), Boolean.FALSE } ); } catch ( SecurityException e ) { // ignored } catch ( NoSuchMethodException e ) { // ignored } catch ( IllegalArgumentException e ) { // ignored } catch ( IllegalAccessException e ) { // ignored } catch ( InvocationTargetException e ) { // ignored } } else if ( file.isDirectory() ) { if ( !destination.exists() && !destination.mkdirs() ) { throw new IOException( "Could not create destination directory '" + destination.getAbsolutePath() + "'." ); } copied += copyDirectoryStructure( file, destination, includes, excludes ); } else { throw new IOException( "Unknown file type: " + file.getAbsolutePath() ); } } return copied; } public static String getEnv( String envKey, String alternateSystemProperty, String defaultValue ) { String envValue = null; try { envValue = System.getenv( envKey ); if ( envValue == null && alternateSystemProperty != null ) { envValue = System.getProperty( alternateSystemProperty ); } } catch ( Error e ) { // JDK 1.4? if ( alternateSystemProperty != null ) { envValue = System.getProperty( alternateSystemProperty ); } } if ( envValue == null ) { envValue = defaultValue; } return envValue; } public static String addLibraryPathToEnv( String path, Map environment, String os ) { String pathName = null; char separator = ' '; if ( os.equals( OS.WINDOWS ) ) { pathName = "Path"; separator = ';'; } else if ( os.equals( OS.MACOSX ) ) { pathName = "DYLD_LIBRARY_PATH"; separator = ':'; } else { pathName = "LD_LIBRARY_PATH"; separator = ':'; } String value = environment != null ? (String) environment.get( pathName ) : null; if ( value == null ) { value = NarUtil.getEnv( pathName, pathName, null ); } String libPath = path; libPath = libPath.replace( File.pathSeparatorChar, separator ); if ( value != null ) { value += separator + libPath; } else { value = libPath; } if ( environment != null ) { environment.put( pathName, value ); } return pathName + "=" + value; } public static int runCommand( String cmd, String[] args, File workingDirectory, String[] env, final Log log ) throws MojoExecutionException, MojoFailureException { return runCommand( cmd, args, workingDirectory, env, new TextStream() { public void println( String text ) { log.info( text ); } }, new TextStream() { public void println( String text ) { log.error( text ); } }, new TextStream() { public void println( String text ) { log.debug( text ); } } ); } public static int runCommand( String cmd, String[] args, File workingDirectory, String[] env, TextStream out, TextStream err, TextStream dbg ) throws MojoExecutionException, MojoFailureException { Commandline cmdLine = new Commandline(); try { dbg.println( "RunCommand: " + cmd ); cmdLine.setExecutable( cmd ); if ( args != null ) { for ( int i = 0; i < args.length; i++ ) { dbg.println( " '" + args[i] + "'" ); } cmdLine.addArguments( args ); } if ( workingDirectory != null ) { dbg.println( "in: " + workingDirectory.getPath() ); cmdLine.setWorkingDirectory( workingDirectory ); } if ( env != null ) { dbg.println( "with Env:" ); for ( int i = 0; i < env.length; i++ ) { String[] nameValue = env[i].split( "=", 2 ); if ( nameValue.length < 2 ) { throw new MojoFailureException( " Misformed env: '" + env[i] + "'" ); } dbg.println( " '" + nameValue[0] + "=" + nameValue[1] + "'" ); cmdLine.addEnvironment( nameValue[0], nameValue[1] ); } } Process process = cmdLine.execute(); StreamGobbler errorGobbler = new StreamGobbler( process.getErrorStream(), err ); StreamGobbler outputGobbler = new StreamGobbler( process.getInputStream(), out ); errorGobbler.start(); outputGobbler.start(); process.waitFor(); dbg.println( "ExitValue: " + process.exitValue() ); final int timeout = 5000; errorGobbler.join( timeout ); outputGobbler.join( timeout ); return process.exitValue(); } catch ( Exception e ) { throw new MojoExecutionException( "Could not launch " + cmdLine, e ); } } private static final class StreamGobbler extends Thread { private InputStream is; private TextStream ts; private StreamGobbler( InputStream is, TextStream ts ) { this.is = is; this.ts = ts; } public void run() { try { BufferedReader reader = new BufferedReader( new InputStreamReader( is ) ); String line = null; while ( ( line = reader.readLine() ) != null ) { ts.println( line ); } reader.close(); } catch ( IOException e ) { // e.printStackTrace() StackTraceElement[] stackTrace = e.getStackTrace(); for ( int i = 0; i < stackTrace.length; i++ ) { ts.println( stackTrace[i].toString() ); } } } } }