org.codehaus.mojo.natives.compiler.AbstractCompiler Maven / Gradle / Ivy
The newest version!
/*
* The MIT License
*
* Copyright (c) 2004, The Codehaus
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.codehaus.mojo.natives.compiler;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.codehaus.mojo.natives.NativeBuildException;
import org.codehaus.mojo.natives.SourceDependencyAnalyzer;
import org.codehaus.mojo.natives.parser.Parser;
import org.codehaus.mojo.natives.util.CommandLineUtil;
import org.codehaus.mojo.natives.util.EnvUtil;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.Os;
import org.codehaus.plexus.util.cli.Commandline;
public abstract class AbstractCompiler
extends AbstractLogEnabled
implements Compiler
{
protected abstract Parser getParser();
protected abstract Commandline getCommandLine( File src, File dest, CompilerConfiguration config )
throws NativeBuildException;
@Override
public List compile( CompilerConfiguration config, File[] sourceFiles )
throws NativeBuildException
{
if ( !config.getOutputDirectory().exists() )
{
config.getOutputDirectory().mkdirs();
}
List compilerOutputFiles = new ArrayList<>( sourceFiles.length );
CompilerThreadPoolExecutor compilerThreadPoolExecutor = null;
if ( config.getNumberOfConcurrentCompilation() > 1 )
{
compilerThreadPoolExecutor = new CompilerThreadPoolExecutor( config.getNumberOfConcurrentCompilation() );
}
for ( File source : sourceFiles )
{
File objectFile = getObjectFile( source, config.getOutputDirectory(), config.getObjectFileExtension() );
compilerOutputFiles.add( objectFile );
Parser parser = this.getParser();
if ( SourceDependencyAnalyzer.isStaled( source, objectFile, parser, config.getIncludePaths() ) )
{
if ( compilerThreadPoolExecutor != null && compilerThreadPoolExecutor.isErrorFound() )
{
break;
}
Commandline cl = getCommandLine( source, objectFile, config );
EnvUtil.setupCommandlineEnv( cl, config.getEnvFactory() );
if ( compilerThreadPoolExecutor != null )
{
try
{
compilerThreadPoolExecutor.execute( new CompilerRunnable( cl, this.getLogger() ) );
}
catch ( RejectedExecutionException e )
{
CommandLineUtil.execute( cl, this.getLogger() );
}
}
else
{
CommandLineUtil.execute( cl, this.getLogger() );
}
}
else
{
this.getLogger().debug( ( objectFile + " is up to date." ) );
}
}
if ( compilerThreadPoolExecutor != null )
{
if ( !compilerThreadPoolExecutor.isErrorFound() )
{
compilerThreadPoolExecutor.shutdown();
}
try
{
compilerThreadPoolExecutor.awaitTermination( Integer.MAX_VALUE, TimeUnit.SECONDS );
}
catch ( InterruptedException e )
{
}
if ( compilerThreadPoolExecutor.isErrorFound() )
{
throw new NativeBuildException( "Compilation failure detected." );
}
}
return compilerOutputFiles;
}
/**
* return "obj" or "o" when file extension is not given based on current platform
*
* @return
*/
protected static String getObjectFileExtension( String fileExtension )
{
if ( fileExtension != null )
{
return fileExtension;
}
else
{
if ( Os.isFamily( "windows" ) )
{
return "obj";
}
else
{
return "o";
}
}
}
/**
* Figure out the object file relative path from a given source file
*
* @param sourceFile
* @param workingDirectory
* @param outputDirectory
* @param config
* @return
*/
protected static File getObjectFile( File sourceFile, File outputDirectory, String objectFileExtension )
throws NativeBuildException
{
String objectFileName;
try
{
objectFileExtension = AbstractCompiler.getObjectFileExtension( objectFileExtension );
// plexus-util requires that we remove all ".." in the the file source, so getCanonicalPath is required
// other filename with .. and no extension will throw StringIndexOutOfBoundsException
objectFileName = FileUtils.basename( sourceFile.getCanonicalPath() );
if ( objectFileName.charAt( objectFileName.length() - 1 ) != '.' )
{
objectFileName += "." + objectFileExtension;
}
else
{
objectFileName += objectFileExtension;
}
}
catch ( IOException e )
{
throw new NativeBuildException(
"Failed to figure out object file name for " + sourceFile + ": " + e.getMessage(), e );
}
File objectFile = new File( outputDirectory, objectFileName );
return objectFile;
}
private class CompilerThreadPoolExecutor
extends ThreadPoolExecutor
{
private boolean errorFound = false;
public synchronized void setErrorFound( boolean errorFound )
{
this.errorFound = errorFound;
}
public synchronized boolean isErrorFound()
{
return errorFound;
}
public CompilerThreadPoolExecutor( int corePoolSize )
{
super( corePoolSize, corePoolSize, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>( corePoolSize * 2 ) );
}
@Override
protected void afterExecute( Runnable r, Throwable t )
{
super.afterExecute( r, t );
if ( t != null )
{
this.setErrorFound( true );
this.shutdown();
}
}
@Override
protected void beforeExecute( Thread t, Runnable r )
{
super.beforeExecute( t, r );
// fail fast
if ( this.isErrorFound() )
{
( (CompilerRunnable) r ).setSkip( true );
}
}
}
public class CompilerRunnable
implements Runnable
{
private Commandline cl;
private Logger logger;
private boolean skip = false;
public void setSkip( boolean skip )
{
this.skip = skip;
}
public CompilerRunnable( Commandline cl, Logger logger )
{
this.cl = cl;
this.logger = logger;
}
@Override
public void run()
throws NativeBuildException
{
if ( skip )
{
return;
}
CommandLineUtil.execute( cl, logger );
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy