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