View Javadoc

1   package org.apache.maven.plugin.source;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.archiver.MavenArchiveConfiguration;
23  import org.apache.maven.archiver.MavenArchiver;
24  import org.apache.maven.artifact.DependencyResolutionRequiredException;
25  import org.apache.maven.model.Resource;
26  import org.apache.maven.plugin.AbstractMojo;
27  import org.apache.maven.plugin.MojoExecutionException;
28  import org.apache.maven.plugins.annotations.Component;
29  import org.apache.maven.plugins.annotations.Parameter;
30  import org.apache.maven.project.MavenProject;
31  import org.apache.maven.project.MavenProjectHelper;
32  import org.codehaus.plexus.archiver.Archiver;
33  import org.codehaus.plexus.archiver.ArchiverException;
34  import org.codehaus.plexus.archiver.jar.JarArchiver;
35  import org.codehaus.plexus.archiver.jar.ManifestException;
36  import org.codehaus.plexus.util.FileUtils;
37  
38  import java.io.File;
39  import java.io.IOException;
40  import java.util.ArrayList;
41  import java.util.Arrays;
42  import java.util.List;
43  
44  /**
45   * Base class for bundling sources into a jar archive.
46   *
47   * @version $Id: AbstractSourceJarMojo.java 1356885 2012-07-03 19:39:07Z olamy $
48   * @since 2.0.3
49   */
50  public abstract class AbstractSourceJarMojo
51      extends AbstractMojo
52  {
53      private static final String[] DEFAULT_INCLUDES = new String[]{ "**/*" };
54  
55      private static final String[] DEFAULT_EXCLUDES = new String[]{ };
56  
57      /**
58       * List of files to include. Specified as fileset patterns which are relative to the input directory whose contents
59       * is being packaged into the JAR.
60       *
61       * @since 2.1
62       */
63      @Parameter
64      private String[] includes;
65  
66      /**
67       * List of files to exclude. Specified as fileset patterns which are relative to the input directory whose contents
68       * is being packaged into the JAR.
69       *
70       * @since 2.1
71       */
72      @Parameter
73      private String[] excludes;
74  
75      /**
76       * Exclude commonly excluded files such as SCM configuration. These are defined in the plexus
77       * FileUtils.getDefaultExcludes()
78       *
79       * @since 2.1
80       */
81      @Parameter( defaultValue = "true" )
82      private boolean useDefaultExcludes;
83  
84      /**
85       * The Maven Project Object
86       *
87       */
88      @Parameter(defaultValue = "${project}", readonly = true, required = true)
89      protected MavenProject project;
90  
91      /**
92       * The Jar archiver.
93       */
94      @Component( role = Archiver.class, hint = "jar" )
95      private JarArchiver jarArchiver;
96  
97      /**
98       * The archive configuration to use. See <a href="http://maven.apache.org/shared/maven-archiver/index.html">Maven
99       * Archiver Reference</a>.
100      *
101      * @since 2.1
102      */
103     @Parameter
104     private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
105 
106     /**
107      * Path to the default MANIFEST file to use. It will be used if <code>useDefaultManifestFile</code> is set to
108      * <code>true</code>.
109      *
110      * @since 2.1
111      */
112     @Parameter( defaultValue = "${project.build.outputDirectory}/META-INF/MANIFEST.MF", readonly = false,
113                 required = true )
114     private File defaultManifestFile;
115 
116     /**
117      * Set this to <code>true</code> to enable the use of the <code>defaultManifestFile</code>. <br/>
118      *
119      * @since 2.1
120      */
121     @Parameter( defaultValue = "false" )
122     private boolean useDefaultManifestFile;
123 
124     /**
125      * Specifies whether or not to attach the artifact to the project
126      */
127     @Parameter( property = "attach", defaultValue = "true" )
128     private boolean attach;
129 
130     /**
131      * Specifies whether or not to exclude resources from the sources-jar. This
132      * can be convenient if your project includes large resources, such as
133      * images, and you don't want to include them in the sources-jar.
134      *
135      * @since 2.0.4
136      */
137     @Parameter( property = "source.excludeResources", defaultValue = "false" )
138     protected boolean excludeResources;
139 
140     /**
141      * Specifies whether or not to include the POM file in the sources-jar.
142      *
143      * @since 2.1
144      */
145     @Parameter( property = "source.includePom", defaultValue = "false" )
146     protected boolean includePom;
147 
148     /**
149      * Used for attaching the source jar to the project.
150      */
151     @Component
152     private MavenProjectHelper projectHelper;
153 
154     /**
155      * The directory where the generated archive file will be put.
156      */
157     @Parameter( defaultValue = "${project.build.directory}" )
158     protected File outputDirectory;
159 
160     /**
161      * The filename to be used for the generated archive file.
162      * For the source:jar goal, "-sources" is appended to this filename.
163      * For the source:test-jar goal, "-test-sources" is appended.
164      */
165     @Parameter( defaultValue = "${project.build.finalName}" )
166     protected String finalName;
167 
168     /**
169      * Contains the full list of projects in the reactor.
170      */
171     @Parameter( defaultValue = "${reactorProjects}", readonly = true )
172     protected List reactorProjects;
173 
174     /**
175      * Whether creating the archive should be forced.  If set to true, the jar will
176      * always be created.  If set to false, the jar will only be created when the
177      * sources are newer than the jar.
178      *
179      * @since 2.1
180      */
181     @Parameter( property = "source.forceCreation", defaultValue = "false" )
182     private boolean forceCreation;
183 
184     /**
185      * A flag used to disable the source procedure. This is primarily intended for usage from the command line to
186      * occasionally adjust the build.
187      *
188      * @since 2.2
189      */
190     @Parameter( property = "source.skip", defaultValue = "false" )
191     private boolean skipSource;
192 
193     // ----------------------------------------------------------------------
194     // Public methods
195     // ----------------------------------------------------------------------
196 
197     /**
198      * {@inheritDoc}
199      */
200     public void execute()
201         throws MojoExecutionException
202     {
203         if ( skipSource )
204         {
205             getLog().info( "Skipping source per configuration." );
206             return;
207         }
208 
209         packageSources( project );
210     }
211 
212     // ----------------------------------------------------------------------
213     // Protected methods
214     // ----------------------------------------------------------------------
215 
216     /**
217      * @return the wanted classifier, ie <code>sources</code> or <code>test-sources</code>
218      */
219     protected abstract String getClassifier();
220 
221     /**
222      * @param p not null
223      * @return the compile or test sources
224      */
225     protected abstract List<String> getSources( MavenProject p )
226         throws MojoExecutionException;
227 
228     /**
229      * @param p not null
230      * @return the compile or test resources
231      */
232     protected abstract List<Resource> getResources( MavenProject p )
233         throws MojoExecutionException;
234 
235     protected void packageSources( MavenProject p )
236         throws MojoExecutionException
237     {
238         if ( !"pom".equals( p.getPackaging() ) )
239         {
240             packageSources( Arrays.asList( p ) );
241         }
242     }
243 
244     protected void packageSources( List<MavenProject> projects )
245         throws MojoExecutionException
246     {
247         if ( project.getArtifact().getClassifier() != null )
248         {
249             getLog().warn( "NOT adding sources to artifacts with classifier as Maven only supports one classifier "
250                                + "per artifact. Current artifact [" + project.getArtifact().getId() + "] has a ["
251                                + project.getArtifact().getClassifier() + "] classifier." );
252 
253             return;
254         }
255 
256         MavenArchiver archiver = createArchiver();
257 
258         for ( MavenProject project : projects )
259         {
260             MavenProject subProject = getProject( project );
261 
262             if ( "pom".equals( subProject.getPackaging() ) )
263             {
264                 continue;
265             }
266 
267             archiveProjectContent( subProject, archiver.getArchiver() );
268         }
269 
270         if ( !archiver.getArchiver().getFiles().isEmpty() )
271         {
272 
273             if ( useDefaultManifestFile && defaultManifestFile.exists() && archive.getManifestFile() == null )
274             {
275                 getLog().info( "Adding existing MANIFEST to archive. Found under: " + defaultManifestFile.getPath() );
276                 archive.setManifestFile( defaultManifestFile );
277             }
278 
279             File outputFile = new File( outputDirectory, finalName + "-" + getClassifier() + getExtension() );
280 
281             try
282             {
283                 archiver.setOutputFile( outputFile );
284 
285                 archive.setAddMavenDescriptor( false );
286                 archive.setForced( forceCreation );
287 
288                 archiver.createArchive( project, archive );
289             }
290             catch ( IOException e )
291             {
292                 throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
293             }
294             catch ( ArchiverException e )
295             {
296                 throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
297             }
298             catch ( DependencyResolutionRequiredException e )
299             {
300                 throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
301             }
302             catch ( ManifestException e )
303             {
304                 throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
305             }
306 
307             if ( attach )
308             {
309                 projectHelper.attachArtifact( project, getType(), getClassifier(), outputFile );
310             }
311             else
312             {
313                 getLog().info( "NOT adding java-sources to attached artifacts list." );
314             }
315         }
316         else
317         {
318             getLog().info( "No sources in project. Archive not created." );
319         }
320     }
321 
322     protected void archiveProjectContent( MavenProject p, Archiver archiver )
323         throws MojoExecutionException
324     {
325         if ( includePom )
326         {
327             try
328             {
329                 archiver.addFile( p.getFile(), p.getFile().getName() );
330             }
331             catch ( ArchiverException e )
332             {
333                 throw new MojoExecutionException( "Error adding POM file to target jar file.", e );
334             }
335         }
336 
337         for ( String s : getSources( p ) )
338         {
339 
340             File sourceDirectory = new File( s );
341 
342             if ( sourceDirectory.exists() )
343             {
344                 addDirectory( archiver, sourceDirectory, getCombinedIncludes( null ), getCombinedExcludes( null ) );
345             }
346         }
347 
348         //MAPI: this should be taken from the resources plugin
349         for ( Resource resource : getResources( p ) )
350         {
351 
352             File sourceDirectory = new File( resource.getDirectory() );
353 
354             if ( !sourceDirectory.exists() )
355             {
356                 continue;
357             }
358 
359             List resourceIncludes = resource.getIncludes();
360 
361             String[] combinedIncludes = getCombinedIncludes( resourceIncludes );
362 
363             List resourceExcludes = resource.getExcludes();
364 
365             String[] combinedExcludes = getCombinedExcludes( resourceExcludes );
366 
367             String targetPath = resource.getTargetPath();
368             if ( targetPath != null )
369             {
370                 if ( !targetPath.trim().endsWith( "/" ) )
371                 {
372                     targetPath += "/";
373                 }
374                 addDirectory( archiver, sourceDirectory, targetPath, combinedIncludes, combinedExcludes );
375             }
376             else
377             {
378                 addDirectory( archiver, sourceDirectory, combinedIncludes, combinedExcludes );
379             }
380         }
381     }
382 
383     protected MavenArchiver createArchiver()
384         throws MojoExecutionException
385     {
386         MavenArchiver archiver = new MavenArchiver();
387         archiver.setArchiver( jarArchiver );
388 
389         if ( project.getBuild() != null )
390         {
391             List<Resource> resources = project.getBuild().getResources();
392 
393             for ( Resource r : resources )
394             {
395 
396                 if ( r.getDirectory().endsWith( "maven-shared-archive-resources" ) )
397                 {
398                     addDirectory( archiver.getArchiver(), new File( r.getDirectory() ), getCombinedIncludes( null ),
399                                   getCombinedExcludes( null ) );
400                 }
401             }
402         }
403 
404         return archiver;
405     }
406 
407     protected void addDirectory( Archiver archiver, File sourceDirectory, String[] includes, String[] excludes )
408         throws MojoExecutionException
409     {
410         try
411         {
412             archiver.addDirectory( sourceDirectory, includes, excludes );
413         }
414         catch ( ArchiverException e )
415         {
416             throw new MojoExecutionException( "Error adding directory to source archive.", e );
417         }
418     }
419 
420     protected void addDirectory( Archiver archiver, File sourceDirectory, String prefix, String[] includes,
421                                  String[] excludes )
422         throws MojoExecutionException
423     {
424         try
425         {
426             archiver.addDirectory( sourceDirectory, prefix, includes, excludes );
427         }
428         catch ( ArchiverException e )
429         {
430             throw new MojoExecutionException( "Error adding directory to source archive.", e );
431         }
432     }
433 
434     protected String getExtension()
435     {
436         return ".jar";
437     }
438 
439     protected MavenProject getProject( MavenProject p )
440     {
441         if ( p.getExecutionProject() != null )
442         {
443             return p.getExecutionProject();
444         }
445 
446         return p;
447     }
448 
449     protected String getType()
450     {
451         return "java-source";
452     }
453 
454     /**
455      * Combines the includes parameter and additional includes. Defaults to {@link #DEFAULT_INCLUDES} If the
456      * additionalIncludes parameter is null, it is not added to the combined includes.
457      *
458      * @param additionalIncludes The includes specified in the pom resources section
459      * @return The combined array of includes.
460      */
461     private String[] getCombinedIncludes( List additionalIncludes )
462     {
463         List<String> combinedIncludes = new ArrayList<String>();
464 
465         if ( includes != null && includes.length > 0 )
466         {
467             combinedIncludes.addAll( Arrays.asList( includes ) );
468         }
469 
470         if ( additionalIncludes != null && additionalIncludes.size() > 0 )
471         {
472             combinedIncludes.addAll( additionalIncludes );
473         }
474 
475         // If there are no other includes, use the default.
476         if ( combinedIncludes.size() == 0 )
477         {
478             combinedIncludes.addAll( Arrays.asList( DEFAULT_INCLUDES ) );
479         }
480 
481         return combinedIncludes.toArray( new String[combinedIncludes.size()] );
482     }
483 
484     /**
485      * Combines the user parameter {@link #excludes}, the default excludes from plexus FileUtils,
486      * and the contents of the parameter addionalExcludes.
487      *
488      * @param additionalExcludes Additional excludes to add to the array
489      * @return The combined list of excludes.
490      */
491 
492     private String[] getCombinedExcludes( List additionalExcludes )
493     {
494         List<String> combinedExcludes = new ArrayList<String>();
495 
496         if ( useDefaultExcludes )
497         {
498             combinedExcludes.addAll( FileUtils.getDefaultExcludesAsList() );
499         }
500 
501         if ( excludes != null && excludes.length > 0 )
502         {
503             combinedExcludes.addAll( Arrays.asList( excludes ) );
504         }
505 
506         if ( additionalExcludes != null && additionalExcludes.size() > 0 )
507         {
508             combinedExcludes.addAll( additionalExcludes );
509         }
510 
511         if ( combinedExcludes.size() == 0 )
512         {
513             combinedExcludes.addAll( Arrays.asList( DEFAULT_EXCLUDES ) );
514         }
515 
516         return combinedExcludes.toArray( new String[combinedExcludes.size()] );
517     }
518 }