FELIX-4512 : Add a new Mojo to invoke the BND Baseline tool. Apply patch from Simone Tripodi.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1594524 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/DEPENDENCIES b/bundleplugin/DEPENDENCIES
index b67cfe9..a11e619 100644
--- a/bundleplugin/DEPENDENCIES
+++ b/bundleplugin/DEPENDENCIES
@@ -12,7 +12,7 @@
This product uses software developed by Peter Kriens
(http://www.aqute.biz/Code/Bnd)
-Copyright 2006-2013 aQute, All rights reserved
+Copyright 2006-2014 aQute, All rights reserved
Licensed under the Apache License 2.0.
This product uses software developed at
@@ -23,6 +23,8 @@
The Codehaus (http://www.codehaus.org)
Licensed under the Apache License 2.0.
+This product uses icons developed at
+the Eclipse Project (http://www.eclipse.org/
III. License Summary
- Apache License 2.0
diff --git a/bundleplugin/NOTICE b/bundleplugin/NOTICE
index 57ba9a5..ffd2e64 100644
--- a/bundleplugin/NOTICE
+++ b/bundleplugin/NOTICE
@@ -1,6 +1,6 @@
Apache Felix Bundle Maven Plugin
-Copyright 2006-2013 The Apache Software Foundation
+Copyright 2006-2014 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
-Licensed under the Apache License 2.0.
+Licensed under the Apache License 2.0.)
diff --git a/bundleplugin/pom.xml b/bundleplugin/pom.xml
index e5826d3..22afd41 100644
--- a/bundleplugin/pom.xml
+++ b/bundleplugin/pom.xml
@@ -90,6 +90,16 @@
<version>3.0.10</version>
</dependency>
<dependency>
+ <groupId>org.apache.maven.doxia</groupId>
+ <artifactId>doxia-sink-api</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.doxia</groupId>
+ <artifactId>doxia-site-renderer</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>1.1</version>
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/AbstractBaselinePlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/AbstractBaselinePlugin.java
new file mode 100644
index 0000000..43027be
--- /dev/null
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/AbstractBaselinePlugin.java
@@ -0,0 +1,580 @@
+/*
+ * 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.
+ */
+package org.apache.felix.bundleplugin.baseline;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
+import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
+import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.artifact.resolver.ArtifactResolver;
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
+import org.apache.maven.artifact.versioning.VersionRange;
+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.plexus.util.StringUtils;
+
+import aQute.bnd.differ.Baseline;
+import aQute.bnd.differ.Baseline.Info;
+import aQute.bnd.differ.DiffPluginImpl;
+import aQute.bnd.osgi.Instructions;
+import aQute.bnd.osgi.Jar;
+import aQute.bnd.osgi.Processor;
+import aQute.bnd.service.diff.Delta;
+import aQute.bnd.service.diff.Diff;
+import aQute.bnd.version.Version;
+import aQute.service.reporter.Reporter;
+
+/**
+ * Abstract BND Baseline check between two bundles.
+ */
+abstract class AbstractBaselinePlugin
+ extends AbstractMojo
+{
+
+ /**
+ * Flag to easily skip execution.
+ *
+ * @parameter expression="${baseline.skip}" default-value="false"
+ */
+ protected boolean skip;
+
+ /**
+ * Whether to fail on errors.
+ *
+ * @parameter expression="${baseline.failOnError}" default-value="true"
+ */
+ protected boolean failOnError;
+
+ /**
+ * Whether to fail on warnings.
+ *
+ * @parameter expression="${baseline.failOnWarning}" default-value="false"
+ */
+ protected boolean failOnWarning;
+
+ /**
+ * @parameter expression="${project}"
+ * @required
+ * @readonly
+ */
+ protected MavenProject project;
+
+ /**
+ * @parameter expression="${project.build.directory}"
+ * @required
+ * @readonly
+ */
+ private File buildDirectory;
+
+ /**
+ * @parameter expression="${project.build.finalName}"
+ * @required
+ * @readonly
+ */
+ private String finalName;
+
+ /**
+ * @component
+ */
+ protected ArtifactResolver resolver;
+
+ /**
+ * @component
+ */
+ protected ArtifactFactory factory;
+
+ /**
+ * @parameter default-value="${localRepository}"
+ * @required
+ * @readonly
+ */
+ protected ArtifactRepository localRepository;
+
+ /**
+ * @component
+ */
+ private ArtifactMetadataSource metadataSource;
+
+ /**
+ * Version to compare the current code against.
+ *
+ * @parameter expression="${comparisonVersion}" default-value="(,${project.version})"
+ * @required
+ * @readonly
+ */
+ protected String comparisonVersion;
+
+ /**
+ * A list of packages filter, if empty the whole bundle will be traversed. Values are specified in OSGi package
+ * instructions notation, e.g. <code>!org.apache.felix.bundleplugin</code>.
+ *
+ * @parameter
+ */
+ private String[] filters;
+
+ public final void execute()
+ throws MojoExecutionException, MojoFailureException
+ {
+ if ( skip )
+ {
+ getLog().info( "Skipping Baseline execution" );
+ return;
+ }
+
+ // get the bundles that have to be compared
+
+ final Jar currentBundle = getCurrentBundle();
+ if ( currentBundle == null )
+ {
+ getLog().info( "Not generating Baseline report as there is no bundle generated by the project" );
+ return;
+ }
+
+ final Jar previousBundle = getPreviousBundle();
+ if ( previousBundle == null )
+ {
+ getLog().info( "Not generating Baseline report as there is no previous version of the library to compare against" );
+ return;
+ }
+
+ // preparing the filters
+
+ final Instructions packageFilters;
+ if ( filters == null || filters.length == 0 )
+ {
+ packageFilters = new Instructions();
+ }
+ else
+ {
+ packageFilters = new Instructions( Arrays.asList( filters ) );
+ }
+
+ // go!
+
+ init();
+
+ String generationDate = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm'Z'" ).format( new Date() );
+ Reporter reporter = new Processor();
+
+ try
+ {
+ Set<Info> infoSet = new Baseline( reporter, new DiffPluginImpl() )
+ .baseline( currentBundle, previousBundle, packageFilters );
+
+ startBaseline( generationDate, project.getArtifactId(), project.getVersion(), comparisonVersion );
+
+ final Info[] infos = infoSet.toArray( new Info[infoSet.size()] );
+ Arrays.sort( infos, new InfoComparator() );
+
+ for ( Info info : infos )
+ {
+ DiffMessage diffMessage = null;
+ Version newerVersion = info.newerVersion;
+ Version suggestedVersion = info.suggestedVersion;
+
+ if ( suggestedVersion != null )
+ {
+ if ( newerVersion.compareTo( suggestedVersion ) > 0 )
+ {
+ diffMessage = new DiffMessage( "Excessive version increase", DiffMessage.Type.warning );
+ reporter.warning( "%s: %s; detected %s, suggested %s",
+ info.packageName, diffMessage, info.newerVersion, info.suggestedVersion );
+ }
+ else if ( newerVersion.compareTo( suggestedVersion ) < 0 )
+ {
+ diffMessage = new DiffMessage( "Version increase required", DiffMessage.Type.error );
+ reporter.error( "%s: %s; detected %s, suggested %s",
+ info.packageName, diffMessage, info.newerVersion, info.suggestedVersion );
+ }
+ }
+
+ Diff packageDiff = info.packageDiff;
+
+ Delta delta = packageDiff.getDelta();
+
+ switch ( delta )
+ {
+ case UNCHANGED:
+ if ( ( suggestedVersion.getMajor() != newerVersion.getMajor() )
+ || ( suggestedVersion.getMicro() != newerVersion.getMicro() )
+ || ( suggestedVersion.getMinor() != newerVersion.getMinor() ) )
+ {
+ diffMessage = new DiffMessage( "Version has been increased but analysis detected no changes", DiffMessage.Type.warning );
+ reporter.warning( "%s: %s; detected %s, suggested %s",
+ info.packageName, diffMessage, info.newerVersion, info.suggestedVersion );
+ }
+ break;
+
+ case REMOVED:
+ diffMessage = new DiffMessage( "Package removed", DiffMessage.Type.info );
+ reporter.trace( "%s: %s ", info.packageName, diffMessage );
+ break;
+
+ case CHANGED:
+ case MICRO:
+ case MINOR:
+ case MAJOR:
+ case ADDED:
+ default:
+ // ok
+ break;
+ }
+
+ boolean mismatch = info.mismatch;
+ String packageName = info.packageName;
+ String shortDelta = getShortDelta( info.packageDiff.getDelta() );
+ String deltaString = StringUtils.lowerCase( String.valueOf( info.packageDiff.getDelta() ) );
+ String newerVersionString = String.valueOf( info.newerVersion );
+ String olderVersionString = String.valueOf( info.olderVersion );
+ String suggestedVersionString = String.valueOf( ( info.suggestedVersion == null ) ? "-" : info.suggestedVersion );
+ Map<String,String> attributes = info.attributes;
+
+ startPackage( mismatch,
+ packageName,
+ shortDelta,
+ deltaString,
+ newerVersionString,
+ olderVersionString,
+ suggestedVersionString,
+ diffMessage,
+ attributes );
+
+ if ( Delta.REMOVED != delta )
+ {
+ doPackageDiff( packageDiff );
+ }
+
+ endPackage();
+ }
+
+ endBaseline();
+ }
+ catch ( Exception e )
+ {
+ throw new MojoExecutionException( "Impossible to calculate the baseline", e );
+ }
+ finally
+ {
+ closeJars( currentBundle, previousBundle );
+ }
+
+ // check if it has to fail if some error has been detected
+
+ boolean fail = false;
+
+ if ( !reporter.isOk() )
+ {
+ for ( String errorMessage : reporter.getErrors() )
+ {
+ getLog().error( errorMessage );
+ }
+
+ if ( failOnError )
+ {
+ fail = true;
+ }
+ }
+
+ // check if it has to fail if some warning has been detected
+
+ if ( !reporter.getWarnings().isEmpty() )
+ {
+ for ( String warningMessage : reporter.getWarnings() )
+ {
+ getLog().warn( warningMessage );
+ }
+
+ if ( failOnWarning )
+ {
+ fail = true;
+ }
+ }
+
+ getLog().info( String.format( "Baseline analisys complete, %s error(s), %s warning(s)",
+ reporter.getErrors().size(),
+ reporter.getWarnings().size() ) );
+
+ if ( fail )
+ {
+ throw new MojoFailureException( "Baseline failed, see generated report" );
+ }
+ }
+
+ private void doPackageDiff( Diff diff )
+ {
+ int depth = 1;
+
+ for ( Diff curDiff : diff.getChildren() )
+ {
+ if ( Delta.UNCHANGED != curDiff.getDelta() )
+ {
+ doDiff( curDiff, depth );
+ }
+ }
+ }
+
+ private void doDiff( Diff diff, int depth )
+ {
+ String type = StringUtils.lowerCase( String.valueOf( diff.getType() ) );
+ String shortDelta = getShortDelta( diff.getDelta() );
+ String delta = StringUtils.lowerCase( String.valueOf( diff.getDelta() ) );
+ String name = diff.getName();
+
+ startDiff( depth, type, name, delta, shortDelta );
+
+ for ( Diff curDiff : diff.getChildren() )
+ {
+ if ( Delta.UNCHANGED != curDiff.getDelta() )
+ {
+ doDiff( curDiff, depth + 1 );
+ }
+ }
+
+ endDiff( depth );
+ }
+
+ // extensions APIs
+
+ protected abstract void init();
+
+ protected abstract void startBaseline( String generationDate, String bundleName, String currentVersion, String previousVersion );
+
+ protected abstract void startPackage( boolean mismatch,
+ String name,
+ String shortDelta,
+ String delta,
+ String newerVersion,
+ String olderVersion,
+ String suggestedVersion,
+ DiffMessage diffMessage,
+ Map<String,String> attributes );
+
+ protected abstract void startDiff( int depth,
+ String type,
+ String name,
+ String delta,
+ String shortDelta );
+
+ protected abstract void endDiff( int depth );
+
+ protected abstract void endPackage();
+
+ protected abstract void endBaseline();
+
+ // internals
+
+ private Jar getCurrentBundle()
+ throws MojoExecutionException
+ {
+ /*
+ * Resolving the aQute.bnd.osgi.Jar via the produced artifact rather than what is produced in the target/classes
+ * directory would make the Mojo working also in projects where the bundle-plugin is used just to generate the
+ * manifest file and the final jar is assembled via the jar-plugin
+ */
+ File currentBundle = new File( buildDirectory, getBundleName() );
+ if ( !currentBundle.exists() )
+ {
+ getLog().debug( "Produced bundle not found: " + currentBundle );
+ return null;
+ }
+
+ return openJar( currentBundle );
+ }
+
+ private Jar getPreviousBundle()
+ throws MojoFailureException, MojoExecutionException
+ {
+ // Find the previous version JAR and resolve it, and it's dependencies
+ final VersionRange range;
+ try
+ {
+ range = VersionRange.createFromVersionSpec( comparisonVersion );
+ }
+ catch ( InvalidVersionSpecificationException e )
+ {
+ throw new MojoFailureException( "Invalid comparison version: " + e.getMessage() );
+ }
+
+ final Artifact previousArtifact;
+ try
+ {
+ previousArtifact =
+ factory.createDependencyArtifact( project.getGroupId(),
+ project.getArtifactId(),
+ range,
+ project.getPackaging(),
+ null,
+ Artifact.SCOPE_COMPILE );
+
+ if ( !previousArtifact.getVersionRange().isSelectedVersionKnown( previousArtifact ) )
+ {
+ getLog().debug( "Searching for versions in range: " + previousArtifact.getVersionRange() );
+ @SuppressWarnings( "unchecked" )
+ // type is konwn
+ List<ArtifactVersion> availableVersions =
+ metadataSource.retrieveAvailableVersions( previousArtifact, localRepository,
+ project.getRemoteArtifactRepositories() );
+ filterSnapshots( availableVersions );
+ ArtifactVersion version = range.matchVersion( availableVersions );
+ if ( version != null )
+ {
+ previousArtifact.selectVersion( version.toString() );
+ }
+ }
+ }
+ catch ( OverConstrainedVersionException ocve )
+ {
+ throw new MojoFailureException( "Invalid comparison version: " + ocve.getMessage() );
+ }
+ catch ( ArtifactMetadataRetrievalException amre )
+ {
+ throw new MojoExecutionException( "Error determining previous version: " + amre.getMessage(), amre );
+ }
+
+ if ( previousArtifact.getVersion() == null )
+ {
+ getLog().info( "Unable to find a previous version of the project in the repository" );
+ return null;
+ }
+
+ try
+ {
+ resolver.resolve( previousArtifact, project.getRemoteArtifactRepositories(), localRepository );
+ }
+ catch ( ArtifactResolutionException are )
+ {
+ throw new MojoExecutionException( "Artifact " + previousArtifact + " cannot be resolved", are );
+ }
+ catch ( ArtifactNotFoundException anfe )
+ {
+ throw new MojoExecutionException( "Artifact " + previousArtifact
+ + " does not exist on local/remote repositories", anfe );
+ }
+
+ return openJar( previousArtifact.getFile() );
+ }
+
+ private void filterSnapshots( List<ArtifactVersion> versions )
+ {
+ for ( Iterator<ArtifactVersion> versionIterator = versions.iterator(); versionIterator.hasNext(); )
+ {
+ ArtifactVersion version = versionIterator.next();
+ if ( "SNAPSHOT".equals( version.getQualifier() ) )
+ {
+ versionIterator.remove();
+ }
+ }
+ }
+
+ private static Jar openJar( File file )
+ throws MojoExecutionException
+ {
+ try
+ {
+ return new Jar( file );
+ }
+ catch ( IOException e )
+ {
+ throw new MojoExecutionException( "An error occurred while opening JAR directory: " + file, e );
+ }
+ }
+
+ private static void closeJars( Jar...jars )
+ {
+ for ( Jar jar : jars )
+ {
+ jar.close();
+ }
+ }
+
+ private String getBundleName()
+ {
+ String extension;
+ try
+ {
+ extension = project.getArtifact().getArtifactHandler().getExtension();
+ }
+ catch ( Throwable e )
+ {
+ extension = project.getArtifact().getType();
+ }
+
+ if ( StringUtils.isEmpty( extension ) || "bundle".equals( extension ) || "pom".equals( extension ) )
+ {
+ extension = "jar"; // just in case maven gets confused
+ }
+
+ String classifier = project.getArtifact().getClassifier();
+ if ( null != classifier && classifier.trim().length() > 0 )
+ {
+ return finalName + '-' + classifier + '.' + extension;
+ }
+
+ return finalName + '.' + extension;
+ }
+
+ private static String getShortDelta( Delta delta )
+ {
+ switch ( delta )
+ {
+ case ADDED:
+ return "+";
+
+ case CHANGED:
+ return "~";
+
+ case MAJOR:
+ return ">";
+
+ case MICRO:
+ return "0xB5";
+
+ case MINOR:
+ return "<";
+
+ case REMOVED:
+ return "-";
+
+ case UNCHANGED:
+ return " ";
+
+ default:
+ String deltaString = delta.toString();
+ return String.valueOf( deltaString.charAt( 0 ) );
+ }
+ }
+
+}
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselinePlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselinePlugin.java
new file mode 100644
index 0000000..ba6ec27
--- /dev/null
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselinePlugin.java
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ */
+package org.apache.felix.bundleplugin.baseline;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+
+/**
+ * BND Baseline check between two bundles.
+ *
+ * @goal baseline
+ * @phase verify
+ * @threadSafe true
+ * @since 2.4.1
+ */
+public final class BaselinePlugin
+ extends AbstractBaselinePlugin
+{
+
+ private static final String TABLE_PATTERN = "%s %-50s %-10s %-10s %-10s %-10s %-10s";
+
+ /**
+ * An XML output file to render to <code>${project.build.directory}/baseline.xml</code>.
+ *
+ * @parameter expression="${project.build.directory}/baseline.xml"
+ */
+ private File xmlOutputFile;
+
+ /**
+ * Whether to log the results to the console or not, true by default.
+ *
+ * @parameter expression="${logResults}" default-value="true"
+ */
+ private boolean logResults;
+
+ private FileWriter xmlFileWriter;
+
+ private PrettyPrintXMLWriter xmlWriter;
+
+ protected void init()
+ {
+ if ( xmlOutputFile != null )
+ {
+ xmlOutputFile.getParentFile().mkdirs();
+ try
+ {
+ xmlFileWriter = new FileWriter( xmlOutputFile );
+ xmlWriter = new PrettyPrintXMLWriter( xmlFileWriter );
+ }
+ catch ( IOException e )
+ {
+ getLog().warn( "No XML report will be produced, cannot write data to " + xmlOutputFile, e );
+ }
+ }
+ }
+
+ protected void startBaseline( String generationDate,
+ String bundleName,
+ String currentVersion,
+ String previousVersion )
+ {
+ if ( isLoggingResults() )
+ {
+ log( "Baseline Report - Generated by Apache Felix Maven Bundle Plugin on %s based on Bnd - see http://www.aqute.biz/Bnd/Bnd",
+ generationDate );
+ log( "Comparing bundle %s version %s to version %s", bundleName, currentVersion, previousVersion );
+ log( "" );
+ log( TABLE_PATTERN,
+ " ",
+ "PACKAGE_NAME",
+ "DELTA",
+ "CUR_VER",
+ "BASE_VER",
+ "REC_VER",
+ "WARNINGS",
+ "ATTRIBUTES" );
+ log( TABLE_PATTERN,
+ "=",
+ "==================================================",
+ "==========",
+ "==========",
+ "==========",
+ "==========",
+ "==========",
+ "==========" );
+ }
+
+ if ( isProducingXml() )
+ {
+ xmlWriter.startElement( "baseline" );
+ xmlWriter.addAttribute( "version", "1.0.0" );
+ xmlWriter.addAttribute( "vendor", "The Apache Software Foundation" );
+ xmlWriter.addAttribute( "vendorURL", "http://www.apache.org/" );
+ xmlWriter.addAttribute( "generator", "Apache Felix Maven Bundle Plugin" );
+ xmlWriter.addAttribute( "generatorURL", "http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html" );
+ xmlWriter.addAttribute( "analyzer", "Bnd" );
+ xmlWriter.addAttribute( "analyzerURL", "http://www.aqute.biz/Bnd/Bnd" );
+ xmlWriter.addAttribute( "generatedOn", generationDate );
+ xmlWriter.addAttribute( "bundleName", bundleName );
+ xmlWriter.addAttribute( "currentVersion", currentVersion );
+ xmlWriter.addAttribute( "previousVersion", previousVersion );
+ }
+ }
+
+ protected void startPackage( boolean mismatch,
+ String name,
+ String shortDelta,
+ String delta,
+ String newerVersion,
+ String olderVersion,
+ String suggestedVersion,
+ DiffMessage diffMessage,
+ Map<String,String> attributes )
+ {
+ if ( isLoggingResults() )
+ {
+ log( TABLE_PATTERN,
+ mismatch ? '*' : shortDelta,
+ name,
+ delta,
+ newerVersion,
+ olderVersion,
+ suggestedVersion,
+ diffMessage != null ? diffMessage : '-',
+ attributes );
+ }
+
+ if ( isProducingXml() )
+ {
+ xmlWriter.startElement( "package" );
+ xmlWriter.addAttribute( "name", name );
+ xmlWriter.addAttribute( "delta", delta );
+ simpleElement( "mismatch", String.valueOf( mismatch ) );
+ simpleElement( "newerVersion", newerVersion );
+ simpleElement( "olderVersion", olderVersion );
+ simpleElement( "suggestedVersion", suggestedVersion );
+
+ if ( diffMessage != null )
+ {
+ simpleElement( diffMessage.getType().name(), diffMessage.getMessage() );
+ }
+
+ xmlWriter.startElement( "attributes" );
+ for ( Entry<String, String> attribute : attributes.entrySet() )
+ {
+ String attributeName = attribute.getKey();
+ if ( ':' == attributeName.charAt( attributeName.length() - 1 ) )
+ {
+ attributeName = attributeName.substring( 0, attributeName.length() - 1 );
+ }
+ String attributeValue = attribute.getValue();
+
+ xmlWriter.startElement( attributeName );
+ xmlWriter.writeText( attributeValue );
+ xmlWriter.endElement();
+ }
+ xmlWriter.endElement();
+ }
+ }
+
+ protected void startDiff( int depth, String type, String name, String delta, String shortDelta )
+ {
+ if ( isLoggingResults() )
+ {
+ log( "%-" + (depth * 4) + "s %s %s %s",
+ "",
+ shortDelta,
+ type,
+ name );
+ }
+
+ if ( isProducingXml() )
+ {
+ xmlWriter.startElement( type );
+ xmlWriter.addAttribute( "name", name );
+ xmlWriter.addAttribute( "delta", delta );
+ }
+ }
+
+ protected void endDiff( int depth )
+ {
+ if ( isProducingXml() )
+ {
+ xmlWriter.endElement();
+ }
+ }
+
+ protected void endPackage()
+ {
+ if ( isLoggingResults() )
+ {
+ log( "-----------------------------------------------------------------------------------------------------------" );
+ }
+
+ if ( isProducingXml() )
+ {
+ xmlWriter.endElement();
+ }
+ }
+
+ protected void endBaseline()
+ {
+ if ( xmlWriter != null )
+ {
+ xmlWriter.endElement();
+ IOUtil.close( xmlFileWriter );
+ }
+ }
+
+ private boolean isProducingXml()
+ {
+ return xmlFileWriter!= null && xmlWriter != null;
+ }
+
+ private boolean isLoggingResults()
+ {
+ return logResults && getLog().isInfoEnabled();
+ }
+
+ private void log( String format, Object...args )
+ {
+ getLog().info( String.format( format, args ) );
+ }
+
+ private void simpleElement( String name, String value )
+ {
+ xmlWriter.startElement( name );
+ xmlWriter.writeText( value );
+ xmlWriter.endElement();
+ }
+
+}
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselineReport.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselineReport.java
new file mode 100644
index 0000000..39e9e42
--- /dev/null
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselineReport.java
@@ -0,0 +1,346 @@
+/*
+ * 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.
+ */
+package org.apache.felix.bundleplugin.baseline;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.reporting.MavenReport;
+import org.apache.maven.reporting.MavenReportException;
+import org.codehaus.plexus.util.IOUtil;
+
+/**
+ * BND Baseline report.
+ *
+ * @goal baseline-report
+ * @phase site
+ * @threadSafe true
+ * @since 2.4.1
+ */
+public final class BaselineReport
+ extends AbstractBaselinePlugin
+ implements MavenReport
+{
+
+ /**
+ * Specifies the directory where the report will be generated.
+ *
+ * @parameter default-value="${project.reporting.outputDirectory}"
+ * @required
+ */
+ private File outputDirectory;
+
+ private Sink sink;
+
+ private Locale locale;
+
+ private int currentDepth = 0;
+
+ // AbstractBaselinePlugin events
+
+ @Override
+ protected void init()
+ {
+ failOnError = false;
+ failOnWarning = false;
+
+ final File baselineImagesDirectory = new File( outputDirectory, "images/baseline" );
+ baselineImagesDirectory.mkdirs();
+
+ for ( String resourceName : new String[]{ "access.gif",
+ "annotated.gif",
+ "annotation.gif",
+ "bundle.gif",
+ "class.gif",
+ "constant.gif",
+ "enum.gif",
+ "error.gif",
+ "extends.gif",
+ "field.gif",
+ "implements.gif",
+ "info.gif",
+ "interface.gif",
+ "method.gif",
+ "package.gif",
+ "resource.gif",
+ "return.gif",
+ "version.gif",
+ "warning.gif" } )
+ {
+ InputStream source = getClass().getResourceAsStream( resourceName );
+ OutputStream target = null;
+ File targetFile = new File( baselineImagesDirectory, resourceName );
+
+ try
+ {
+ target = new FileOutputStream( targetFile );
+ IOUtil.copy( source, target );
+ }
+ catch ( IOException e )
+ {
+ getLog().warn( "Impossible to copy " + resourceName + " image, maybe the site won't be properly rendered." );
+ }
+ finally
+ {
+ IOUtil.close( source );
+ IOUtil.close( target );
+ }
+ }
+ }
+
+ protected void startBaseline( String generationDate, String bundleName, String currentVersion, String previousVersion )
+ {
+ sink.head();
+ sink.title();
+
+ String title = getBundle( locale ).getString( "report.baseline.title" );
+ sink.text( title );
+ sink.title_();
+ sink.head_();
+
+ sink.body();
+
+ sink.section1();
+ sink.sectionTitle1();
+ sink.text( title );
+ sink.sectionTitle1_();
+
+ sink.paragraph();
+ sink.text( getBundle( locale ).getString( "report.baseline.bndlink" ) + " " );
+ sink.link( "http://www.aqute.biz/Bnd/Bnd" );
+ sink.text( "Bnd" );
+ sink.link_();
+ sink.text( "." );
+ sink.paragraph_();
+
+ sink.paragraph();
+ sink.text( getBundle( locale ).getString( "report.baseline.bundle" ) + " " );
+ sink.figure();
+ sink.figureGraphics( "images/baseline/bundle.gif" );
+ sink.figure_();
+ sink.text( " " );
+ sink.bold();
+ sink.text( bundleName );
+ sink.bold_();
+ sink.listItem_();
+
+ sink.paragraph();
+ sink.text( getBundle( locale ).getString( "report.baseline.version.current" ) + " " );
+ sink.bold();
+ sink.text( currentVersion );
+ sink.bold_();
+ sink.paragraph_();
+
+ sink.paragraph();
+ sink.text( getBundle( locale ).getString( "report.baseline.version.comparison" ) + " " );
+ sink.bold();
+ sink.text( comparisonVersion );
+ sink.bold_();
+ sink.paragraph_();
+
+ sink.paragraph();
+ sink.text( getBundle( locale ).getString( "report.baseline.generationdate" ) + " " );
+ sink.bold();
+ sink.text( generationDate );
+ sink.bold_();
+ sink.paragraph_();
+
+ sink.section1_();
+ }
+
+ protected void startPackage( boolean mismatch,
+ String packageName,
+ String shortDelta,
+ String delta,
+ String newerVersion,
+ String olderVersion,
+ String suggestedVersion,
+ DiffMessage diffMessage,
+ Map<String,String> attributes )
+ {
+ sink.list();
+
+ sink.listItem();
+
+ sink.figure();
+ sink.figureGraphics( "./images/baseline/package.gif" );
+ sink.figure_();
+ sink.text( " " );
+ sink.monospaced();
+ sink.text( packageName );
+ sink.monospaced_();
+
+ if ( diffMessage != null )
+ {
+ sink.text( " " );
+ sink.figure();
+ sink.figureGraphics( "./images/baseline/" + diffMessage.getType().name() + ".gif" );
+ sink.figure_();
+ sink.text( " " );
+ sink.italic();
+ sink.text( diffMessage.getMessage() );
+ sink.italic_();
+ sink.text( " (newer version: " );
+ sink.monospaced();
+ sink.text( newerVersion );
+ sink.monospaced_();
+ sink.text( ", older version: " );
+ sink.monospaced();
+ sink.text( olderVersion );
+ sink.monospaced_();
+ sink.text( ", suggested version: " );
+ sink.monospaced();
+ sink.text( suggestedVersion );
+ sink.monospaced_();
+ sink.text( ")" );
+ }
+ }
+
+ protected void startDiff( int depth,
+ String type,
+ String name,
+ String delta,
+ String shortDelta )
+ {
+ if ( currentDepth < depth )
+ {
+ sink.list();
+ }
+
+ currentDepth = depth;
+
+ sink.listItem();
+ sink.figure();
+ sink.figureGraphics( "images/baseline/" + type + ".gif" );
+ sink.figure_();
+ sink.text( " " );
+
+ sink.monospaced();
+ sink.text( name );
+ sink.monospaced_();
+
+ sink.text( " " );
+
+ sink.italic();
+ sink.text( delta );
+ sink.italic_();
+ }
+
+ protected void endDiff( int depth )
+ {
+ sink.listItem_();
+
+ if ( currentDepth > depth )
+ {
+ sink.list_();
+ }
+
+ currentDepth = depth;
+ }
+
+ protected void endPackage()
+ {
+ if ( currentDepth > 0 )
+ {
+ sink.list_();
+ currentDepth = 0;
+ }
+
+ sink.listItem_();
+ sink.list_();
+ }
+
+ protected void endBaseline()
+ {
+ sink.body_();
+ sink.flush();
+ sink.close();
+ }
+
+ // MavenReport methods
+
+ public boolean canGenerateReport()
+ {
+ return !skip && outputDirectory != null;
+ }
+
+ public void generate( @SuppressWarnings( "deprecation" ) org.codehaus.doxia.sink.Sink sink, Locale locale )
+ throws MavenReportException
+ {
+ this.sink = sink;
+ this.locale = locale;
+
+ try
+ {
+ execute();
+ }
+ catch ( Exception e )
+ {
+ getLog().warn( "An error occurred while producing the report page, see nested exceptions", e );
+ }
+ }
+
+ public String getCategoryName()
+ {
+ return MavenReport.CATEGORY_PROJECT_REPORTS;
+ }
+
+ public String getDescription( Locale locale )
+ {
+ return getBundle( locale ).getString( "report.baseline.description" );
+ }
+
+ public String getName( Locale locale )
+ {
+ return getBundle( locale ).getString( "report.baseline.name" );
+ }
+
+ private ResourceBundle getBundle( Locale locale )
+ {
+ return ResourceBundle.getBundle( "baseline-report", locale, getClass().getClassLoader() );
+ }
+
+ public String getOutputName()
+ {
+ return "baseline-report";
+ }
+
+ public File getReportOutputDirectory()
+ {
+ return outputDirectory;
+ }
+
+ public boolean isExternalReport()
+ {
+ return false;
+ }
+
+ public void setReportOutputDirectory( File outputDirectory )
+ {
+ this.outputDirectory = outputDirectory;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/DiffMessage.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/DiffMessage.java
new file mode 100644
index 0000000..340601b
--- /dev/null
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/DiffMessage.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package org.apache.felix.bundleplugin.baseline;
+
+final class DiffMessage
+{
+
+ public enum Type { error, warning, info };
+
+ private final String message;
+
+ private final Type type;
+
+ public DiffMessage( String message, Type type )
+ {
+ this.message = message;
+ this.type = type;
+ }
+
+ public String getMessage()
+ {
+ return message;
+ }
+
+ public Type getType()
+ {
+ return type;
+ }
+
+ @Override
+ public String toString()
+ {
+ return message;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/InfoComparator.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/InfoComparator.java
new file mode 100644
index 0000000..9e19851
--- /dev/null
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/InfoComparator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+package org.apache.felix.bundleplugin.baseline;
+
+import java.util.Comparator;
+
+import aQute.bnd.differ.Baseline.Info;
+
+final class InfoComparator
+ implements Comparator<Info>
+{
+
+ public int compare( Info info1, Info info2 )
+ {
+ return info1.packageName.compareTo( info2.packageName );
+ }
+
+}
diff --git a/bundleplugin/src/main/resources/baseline-report.properties b/bundleplugin/src/main/resources/baseline-report.properties
new file mode 100644
index 0000000..5824c34
--- /dev/null
+++ b/bundleplugin/src/main/resources/baseline-report.properties
@@ -0,0 +1,26 @@
+# 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.
+#
+
+report.baseline.name=Baseline
+report.baseline.description=BND Baseline check between two bundles
+report.baseline.title=Baseline Results
+report.baseline.bndlink=The following document contains the results of
+report.baseline.bundle=Bundle Name:
+report.baseline.version.current=Current Version:
+report.baseline.version.comparison=Comparison Version:
+report.baseline.generationdate=Generated on:
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/access.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/access.gif
new file mode 100644
index 0000000..3d4102b
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/access.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/annotated.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/annotated.gif
new file mode 100644
index 0000000..3d4102b
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/annotated.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/annotation.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/annotation.gif
new file mode 100644
index 0000000..3d4102b
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/annotation.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/bundle.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/bundle.gif
new file mode 100644
index 0000000..3d4102b
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/bundle.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/class.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/class.gif
new file mode 100644
index 0000000..9541b53
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/class.gif
@@ -0,0 +1,4 @@
+GIF89a
+
+ ¥ù@
+q
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/constant.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/constant.gif
new file mode 100644
index 0000000..482df23
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/constant.gif
@@ -0,0 +1,3 @@
+GIF89a
+
+o y%¢D*c`x¡1@(#!
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/enum.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/enum.gif
new file mode 100644
index 0000000..341d8ff
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/enum.gif
@@ -0,0 +1,3 @@
+GIF89a
+x f*""B
+f' a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/error.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/error.gif
new file mode 100644
index 0000000..e950051
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/error.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/extends.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/extends.gif
new file mode 100644
index 0000000..83d4b94
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/extends.gif
@@ -0,0 +1,3 @@
+GIF89a
+´·ÿâÿâüïÅ«¬¯f
+*ZS«eÂd,®
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/field.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/field.gif
new file mode 100644
index 0000000..3d4102b
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/field.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/implements.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/implements.gif
new file mode 100644
index 0000000..3d4102b
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/implements.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/info.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/info.gif
new file mode 100644
index 0000000..63048bc
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/info.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/interface.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/interface.gif
new file mode 100644
index 0000000..931235b
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/interface.gif
@@ -0,0 +1,2 @@
+GIF89a
+
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/method.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/method.gif
new file mode 100644
index 0000000..3d4102b
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/method.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/package.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/package.gif
new file mode 100644
index 0000000..63048bc
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/package.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/resource.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/resource.gif
new file mode 100644
index 0000000..3d4102b
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/resource.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/return.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/return.gif
new file mode 100644
index 0000000..3d4102b
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/return.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/version.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/version.gif
new file mode 100644
index 0000000..63048bc
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/version.gif
@@ -0,0 +1 @@
+GIF89a
\ No newline at end of file
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/warning.gif b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/warning.gif
new file mode 100644
index 0000000..37dea09
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/baseline/warning.gif
@@ -0,0 +1,2 @@
+GIF89a
+Ë$(ÑEh
\ No newline at end of file