package org.codehaus.mojo.pde;
/*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed 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.File;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.codehaus.mojo.pde.descriptor.Descriptor;
import org.codehaus.mojo.pde.descriptor.DescriptorUtil;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;
import org.codehaus.plexus.util.cli.DefaultConsumer;
/**
* Base class of all Eclispe PDE mojos
*
* @author dtran@gmail.com
* @version $Id:$
*/
public abstract class AbstractEclipsePDEMojo
extends AbstractMojo
{
/**
* A pde build.properties ${configs} must contain this many comma separated
* fields.
*/
private static final int CONFIG_TUPLE = 3;
/**
* The POM
*
* @parameter expression="${project}
* @required
* @readonly
*/
protected MavenProject project;
/**
* Contains the PDE project source
*
* @parameter expression="${pdeDirectory}"
* default-value="${project.basedir}"
* @required
* @readonly
*/
protected File pdeDirectory;
/**
* Build a product instead of a feature.
*
* If defined then a PDE Product will be built (instead of the feature,
* fragment or plug-in). This value defines the product file that will be
* used to build the product.
*
* @parameter expression="${pdeProductFilename}"
* @optional
*/
protected String pdeProductFilename;
/**
* When a product build is run then use this version of the pde build
* scripts. The build scripts are located at
* ${eclipseInstall}/plugins/org.eclipse.pde.build_{pdeBuildVersion}/scripts/productBuild
*
* @parameter expression="${pdeBuildVersion}"
* @optional
*/
protected String pdeBuildVersion;
/**
* When a product build is run, then the location of the PDE build
* configuration directory must be specified. A product build will expect a
* build.properties file to exist in this directory. You can use
* ${eclipseInstall}/plugins/org.eclipse.pde.build_{pdeBuildVersion}/templates/headless-build/build.properties
* as a starter file. See
* http://help.eclipse.org/help32/topic/org.eclipse.pde.doc.user/guide/tasks/pde_product_build.htm
* Building an RCP application from a product configuration file for more
* details.
*
* @parameter expression="${pdeBuildConfigDirectory}"
* default-value="buildConfiguration"
* @optional
*/
protected String pdeBuildConfigDirectory;
/**
* Eclipse Platform SDK Directory
*
* @parameter expression="${eclipseInstall}" default-value="c:/eclipse"
* @required
*/
protected File eclipseInstall;
/**
* Ant debug capability
*
* @parameter expression="${antDebug}" default-value="false"
*/
protected boolean antDebug;
/**
* Ant verbose capability
*
* @parameter expression="${antVerbose}" default-value="false"
*/
protected boolean antVerbose;
// ////////////////////////////////////////////////////////////////////
// Other internal properties
// ////////////////////////////////////////////////////////////////////
/**
* The pde descriptor for the pde project.
*/
protected Descriptor descriptor;
/**
* The eclipse startup.jar file.
*/
private File startupJar;
/**
* startup class in the startup jar
*/
private String startupClass = "org.eclipse.core.launcher.Main";
/**
* The pdeDirectory/buildConfiguraion/build.properties file.
*/
PropertiesConfiguration buildConfigurationProperties = null;
/**
* Execute the Mojo
*
* @throws MojoExecutionException
* build failure
* @throws MojoFailureException
* build failure
*/
public void execute()
throws MojoExecutionException, MojoFailureException
{
initialize();
}
/**
* Initialise the mojo.
*
* @throws MojoExecutionException
* build failure
* @throws MojoFailureException
* build failure
*/
protected void initialize()
throws MojoExecutionException, MojoFailureException
{
if ( this.pdeDirectory == null )
{
this.pdeDirectory = new File( System.getProperty( "user.dir" ) );
}
this.startupJar = this.findStartupJar();
if ( !startupJar.exists() )
{
throw new MojoExecutionException( startupJar.getPath()
+ " not found. Have you set up your -DeclipseInstall?" );
}
this.getLog().info( "Startup jar found at: " + this.startupJar );
if ( pdeProductFilename != null )
{
if ( pdeBuildVersion == null )
{
throw new MojoExecutionException( "pdeBuildVersion must be specified for a product build." );
}
// Fail fast on build.properties missing.
loadBuildConfigurationProperties();
}
this.descriptor = DescriptorUtil.getDescriptor( this.pdeDirectory, pdeProductFilename );
// TODO check for empty id
}
protected File findStartupJar()
{
File startupJar = new File( this.eclipseInstall, "startup.jar" );
if ( !startupJar.exists() )
{
// list all plugins and try to found a plugin that mathches
// org.eclipse.equinox.launcher_*.jar
// @TODO implement a mechanism to select the newer plugin
File[] plugins = new File( eclipseInstall.getAbsolutePath() + "/plugins/" ).listFiles();
for ( int i = 0; i < plugins.length; i++ )
{
if ( plugins[i].isFile() && plugins[i].getName().indexOf( "org.eclipse.equinox.launcher" ) >= 0 )
{
startupJar = plugins[i];
startupClass = "org.eclipse.equinox.launcher.Main";
break;
}
}
}
return startupJar;
}
/**
* Execute the specified Commandline
*
* @param cl
* the Commandline to execute
* @throws MojoExecutionException
* build failures
*/
protected void executeCommandLine( Commandline cl )
throws MojoExecutionException
{
int ok;
try
{
DefaultConsumer stdout = new DefaultConsumer();
DefaultConsumer stderr = stdout;
this.getLog().info( cl.toString() );
ok = CommandLineUtils.executeCommandLine( cl, stdout, stderr );
}
catch ( CommandLineException e )
{
throw new MojoExecutionException( "Error executing PDE build command line.", e );
}
/* return code 13 from PDE means the real error is in the build log file */
if ( ok == 13 )
{
throw new MojoExecutionException( "Error returned by PDE build. The cause for this error should be found within the PDE build logfile.");
}
/* otherwise something else went pear shaped */
if ( ok != 0 )
{
throw new MojoExecutionException( "Error returned by PDE build. Exit code: " + ok );
}
}
/**
* Create a command line to invoke an Ant build file using the Eclipse pde
* environment.
*
* @param buildFile
* the Ant buildfile to use as part of the command
* @param targets
* the targets to invoke on the Ant build file
* @return A command line to invoke the targets on the Ant buildfile
*/
protected Commandline createCommandLine( File buildFile, String targets )
{
Commandline cl = new Commandline();
cl.setWorkingDirectory( this.pdeDirectory.getPath() );
cl.setExecutable( "java" );
cl.createArgument().setValue( "-classpath" );
cl.createArgument().setFile( this.startupJar );
cl.createArgument().setValue( this.startupClass );
cl.createArgument().setValue( "-application" );
cl.createArgument().setValue( "org.eclipse.ant.core.antRunner" );
cl.createArgument().setValue( "-buildfile" );
cl.createArgument().setValue( buildFile.toString() );
if ( targets != null )
{
cl.createArgument().setLine( targets );
}
if ( this.antVerbose )
{
cl.createArgument().setValue( "-verbose" );
}
if ( this.antDebug )
{
cl.createArgument().setValue( "-debug" );
}
return cl;
}
/**
* Create a command line to invoke an Ant build file with the default target
*
* @param buildFile
* the Ant buildfile to use as part of the command
* @return A command line to invoke the targets on the Ant buildfile
*/
protected Commandline createCommandLine( File buildFile )
{
return this.createCommandLine( buildFile, null );
}
/**
* The PDE Build Directory, which is always located two directories higher
* than the pdeDirectory (i.e "../../${pdeDirectory")
*
* @return The PDE Build Directory
*/
protected File getPDEBuildDirectory()
{
return new File( this.pdeDirectory, "../.." );
}
/**
* @return The Eclipse Base Location.
*/
protected File getBaseLocation()
{
return this.eclipseInstall;
}
/**
* Load the properties from pdeBuildConfiguration\build.properties.
*
* @return the properties from pdeBuildConfiguration\build.properties.
* @throws MojoExecutionException
* build failures.
*/
protected PropertiesConfiguration loadBuildConfigurationProperties()
throws MojoExecutionException
{
if ( buildConfigurationProperties != null )
{
return buildConfigurationProperties;
}
File buildPropertiesFile = new File( pdeDirectory, pdeBuildConfigDirectory + "/build.properties" );
try
{
buildConfigurationProperties = new PropertiesConfiguration();
buildConfigurationProperties.setDelimiterParsingDisabled( true );
buildConfigurationProperties.load( buildPropertiesFile );
return buildConfigurationProperties;
}
catch ( ConfigurationException e )
{
throw new MojoExecutionException( "Failed to load pde build.properties from "
+ buildPropertiesFile.getPath(), e );
}
}
/**
* Convert the list of {os, ws, arch} configurations to build as contained
* in the build.properties file ${configs} field into the filename suffix
* for the created artifact. *
*
* <b>Note</b> PDE Build support multiple configs, here only one config is
* supported.
*
* @param configs
* the string form of the configs
* @return the filename suffix for the configs
* @throws MojoExecutionException
* build failures.
*/
protected String convertPdeConfigsToFilenameSuffix( String configs )
throws MojoExecutionException
{
if ( StringUtils.isEmpty( configs ) )
{
throw new MojoExecutionException( "Null pde configs can not be converted to filename suffix" );
}
if ( configs.indexOf( "&" ) > 0 )
{
throw new MojoExecutionException( "Pde build only supports a build with a single config only." );
}
String[] strings = StringUtils.split( configs, "," );
if ( strings.length != CONFIG_TUPLE )
{
throw new MojoExecutionException( "Invalid PDE Config \"" + configs
+ "\": must be of the form: os, ws, arch" );
}
for ( int i = 0; i < strings.length; i++ )
{
strings[i] = strings[i].trim();
}
return StringUtils.join( strings, "." );
}
}