View Javadoc
1   package org.apache.maven.plugin.assembly.utils;
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.artifact.Artifact;
23  import org.apache.maven.execution.MavenSession;
24  import org.apache.maven.plugin.assembly.AssemblerConfigurationSource;
25  import org.apache.maven.plugin.assembly.format.AssemblyFormattingException;
26  import org.apache.maven.plugin.assembly.model.Assembly;
27  import org.apache.maven.project.MavenProject;
28  import org.apache.maven.shared.utils.Os;
29  import org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;
30  import org.codehaus.plexus.interpolation.fixed.PrefixedObjectValueSource;
31  import org.codehaus.plexus.interpolation.fixed.PrefixedPropertiesValueSource;
32  import org.codehaus.plexus.interpolation.fixed.PropertiesBasedValueSource;
33  import org.codehaus.plexus.logging.Logger;
34  import org.codehaus.plexus.util.StringUtils;
35  
36  import javax.annotation.Nonnull;
37  import javax.annotation.Nullable;
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.List;
41  import java.util.ListIterator;
42  import java.util.Properties;
43  
44  /**
45   * @version $Id: AssemblyFormatUtils.java 1675144 2015-04-21 16:05:16Z krosenvold $
46   */
47  public final class AssemblyFormatUtils
48  {
49  
50      private AssemblyFormatUtils()
51      {
52      }
53  
54      /**
55       * Get the full name of the distribution artifact
56       *
57       * @param assembly the assembly
58       * @return the distribution name
59       */
60      public static String getDistributionName( final Assembly assembly, final AssemblerConfigurationSource configSource )
61      {
62          final String finalName = configSource.getFinalName();
63          final boolean appendAssemblyId = configSource.isAssemblyIdAppended();
64          final String classifier = configSource.getClassifier();
65  
66          String distributionName = finalName;
67          if ( appendAssemblyId )
68          {
69              if ( !StringUtils.isEmpty( assembly.getId() ) )
70              {
71                  distributionName = finalName + "-" + assembly.getId();
72              }
73          }
74          else if ( classifier != null )
75          {
76              distributionName = finalName + "-" + classifier;
77          }
78  
79          return distributionName;
80      }
81  
82  
83      @Nonnull
84      public static FixedStringSearchInterpolator finalNameInterpolator( String finalName )
85      {
86          final Properties specialExpressionOverrides = new Properties();
87  
88          if ( finalName != null )
89          {
90              specialExpressionOverrides.setProperty( "finalName", finalName );
91              specialExpressionOverrides.setProperty( "build.finalName", finalName );
92          }
93          else
94          {
95              return FixedStringSearchInterpolator.empty();
96          }
97  
98          return FixedStringSearchInterpolator.create( new PropertiesBasedValueSource( specialExpressionOverrides ) );
99      }
100 
101     @Nonnull
102     public static FixedStringSearchInterpolator moduleProjectInterpolator( final MavenProject moduleProject )
103     {
104         if ( moduleProject != null )
105         {
106             return FixedStringSearchInterpolator.createWithPermittedNulls(
107                 new PrefixedObjectValueSource( "module.", moduleProject ),
108                 new PrefixedPropertiesValueSource( "module.properties.", moduleProject.getProperties() ),
109                 moduleProject.getArtifact() != null
110                     ? new PrefixedObjectValueSource( "module.", moduleProject.getArtifact() )
111                     : null );
112         }
113         else
114         {
115             return FixedStringSearchInterpolator.empty();
116         }
117 
118     }
119 
120     public static FixedStringSearchInterpolator moduleArtifactInterpolator( Artifact moduleArtifact )
121     {
122         if ( moduleArtifact != null )
123         {
124             // CHECKSTYLE_OFF: LineLength
125             return FixedStringSearchInterpolator.create( new PrefixedObjectValueSource( "module.", moduleArtifact ),
126                                                          new PrefixedObjectValueSource( "module.",
127                                                                                         moduleArtifact.getArtifactHandler() ),
128                                                          new PrefixedObjectValueSource( "module.handler.",
129                                                                                         moduleArtifact.getArtifactHandler() ) );
130             // CHECKSTYLE_ON: LineLength
131         }
132         else
133         {
134             return FixedStringSearchInterpolator.empty();
135         }
136 
137     }
138 
139     @Nonnull
140     public static FixedStringSearchInterpolator artifactProjectInterpolator( final MavenProject artifactProject )
141     {
142         if ( artifactProject != null )
143         {
144             PrefixedObjectValueSource vs = null;
145             if ( artifactProject.getArtifact() != null )
146             {
147                 vs = new PrefixedObjectValueSource( "artifact.", artifactProject.getArtifact() );
148             }
149 
150             return FixedStringSearchInterpolator.createWithPermittedNulls(
151                 new PrefixedObjectValueSource( "artifact.", artifactProject ),
152                 new PrefixedPropertiesValueSource( "artifact.properties.", artifactProject.getProperties() ), vs );
153 
154 
155         }
156         else
157         {
158             return FixedStringSearchInterpolator.empty();
159         }
160 
161 
162     }
163 
164     @Nonnull
165     public static FixedStringSearchInterpolator artifactInterpolator( @Nonnull final Artifact artifact )
166     {
167         return FixedStringSearchInterpolator.create( new PrefixedObjectValueSource( "artifact.", artifact ),
168                                                      new PrefixedObjectValueSource( "artifact.",
169                                                                                     artifact.getArtifactHandler() ),
170                                                      new PrefixedObjectValueSource( "artifact.handler.",
171                                                                                     artifact.getArtifactHandler() ) );
172     }
173 
174 
175     @Nonnull
176     public static FixedStringSearchInterpolator classifierRules( final Artifact artifact )
177     {
178         final Properties specialRules = new Properties();
179 
180         final String classifier = ProjectUtils.getClassifier( artifact );
181         if ( classifier != null )
182         {
183             specialRules.setProperty( "dashClassifier?", "-" + classifier );
184             specialRules.setProperty( "dashClassifier", "-" + classifier );
185         }
186         else
187         {
188             specialRules.setProperty( "dashClassifier?", "" );
189             specialRules.setProperty( "dashClassifier", "" );
190         }
191 
192         return FixedStringSearchInterpolator.create( new PropertiesBasedValueSource( specialRules ) );
193     }
194 
195 
196     /**
197      * ORDER OF INTERPOLATION PRECEDENCE:
198      * <ol>
199      * <li>Support for special expressions, like ${finalName} (use the assembly plugin configuration not the build
200      * config)</li>
201      * <li>prefixed with "module." if moduleProject is non-null
202      * <ol>
203      * <li>MavenProject instance for module being assembled</li>
204      * </ol>
205      * </li>
206      * <li>prefixed with "artifact." if artifactProject is non-null
207      * <ol>
208      * <li>MavenProject instance for artifact</li>
209      * </ol>
210      * </li>
211      * <li>user-defined properties from the command line</li>
212      * <li>prefixed with "pom." or "project.", or no prefix at all
213      * <ol>
214      * <li>MavenProject instance from current build</li>
215      * </ol>
216      * </li>
217      * <li>properties from main project</li>
218      * <li>system properties, from the MavenSession instance (to support IDEs)</li>
219      * <li>environment variables.</li>
220      * </ol>
221      */
222     public static String getOutputDirectory( final String output, final MavenProject artifactProject,
223                                              final String finalName, final AssemblerConfigurationSource configSource )
224         throws AssemblyFormattingException
225     {
226         return getOutputDirectory( output, finalName, configSource, moduleProjectInterpolator( null ),
227                                    artifactProjectInterpolator( artifactProject ) );
228     }
229 
230 
231     private static FixedStringSearchInterpolator executionPropertiesInterpolator(
232         AssemblerConfigurationSource configSource )
233     {
234         MavenSession session;
235 
236         if ( configSource != null )
237         {
238             session = configSource.getMavenSession();
239 
240             if ( session != null )
241             {
242                 Properties userProperties = session.getExecutionProperties(); // this is added twice....
243 
244                 if ( userProperties != null )
245                 {
246                     return FixedStringSearchInterpolator.create( new PropertiesBasedValueSource( userProperties ) );
247                 }
248             }
249         }
250         return FixedStringSearchInterpolator.empty();
251     }
252 
253     private static FixedStringSearchInterpolator mainProjectOnlyInterpolator( MavenProject mainProject )
254     {
255         if ( mainProject != null )
256         {
257             // 5
258             return FixedStringSearchInterpolator.create(
259                 new org.codehaus.plexus.interpolation.fixed.PrefixedObjectValueSource(
260                     InterpolationConstants.PROJECT_PREFIXES, mainProject, true ) );
261         }
262         else
263         {
264             return FixedStringSearchInterpolator.empty();
265         }
266     }
267 
268 
269     /**
270      * ORDER OF INTERPOLATION PRECEDENCE:
271      * <ol>
272      * <li>prefixed with "module.", if moduleProject != null
273      * <ol>
274      * <li>Artifact instance for module, if moduleArtifact != null</li>
275      * <li>ArtifactHandler instance for module, if moduleArtifact != null</li>
276      * <li>MavenProject instance for module</li>
277      * </ol>
278      * </li>
279      * <li>prefixed with "artifact."
280      * <ol>
281      * <li>Artifact instance</li>
282      * <li>ArtifactHandler instance for artifact</li>
283      * <li>MavenProject instance for artifact</li>
284      * </ol>
285      * </li>
286      * <li>prefixed with "pom." or "project."
287      * <ol>
288      * <li>MavenProject instance from current build</li>
289      * </ol>
290      * </li>
291      * <li>no prefix, using main project instance
292      * <ol>
293      * <li>MavenProject instance from current build</li>
294      * </ol>
295      * </li>
296      * <li>Support for special expressions, like ${dashClassifier?}</li>
297      * <li>user-defined properties from the command line</li>
298      * <li>properties from main project</li>
299      * <li>system properties, from the MavenSession instance (to support IDEs)</li>
300      * <li>environment variables.</li>
301      * </ol>
302      */
303 
304 
305     @Nonnull
306     public static String fixRelativeRefs( @Nonnull String src )
307     {
308         String value = src;
309 
310         String[] separators = { "/", "\\" };
311 
312         String finalSep = null;
313         for ( String sep : separators )
314         {
315             if ( value.endsWith( sep ) )
316             {
317                 finalSep = sep;
318             }
319 
320             if ( value.contains( "." + sep ) )
321             {
322                 List<String> parts = new ArrayList<String>();
323                 parts.addAll( Arrays.asList( value.split( sep.replace( "\\", "\\\\" ) ) ) );
324 
325                 for ( ListIterator<String> it = parts.listIterator(); it.hasNext(); )
326                 {
327                     String part = it.next();
328                     if ( ".".equals( part ) )
329                     {
330                         it.remove();
331                     }
332                     else if ( "..".equals( part ) )
333                     {
334                         it.remove();
335                         if ( it.hasPrevious() )
336                         {
337                             it.previous();
338                             it.remove();
339                         }
340                     }
341                 }
342 
343                 value = StringUtils.join( parts.iterator(), sep );
344             }
345         }
346 
347         if ( finalSep != null && value.length() > 0 && !value.endsWith( finalSep ) )
348         {
349             value += finalSep;
350         }
351 
352         return value;
353     }
354 
355 
356     /**
357      * ORDER OF INTERPOLATION PRECEDENCE:
358      * <ol>
359      * <li>prefixed with "module.", if moduleProject != null
360      * <ol>
361      * <li>Artifact instance for module, if moduleArtifact != null</li>
362      * <li>ArtifactHandler instance for module, if moduleArtifact != null</li>
363      * <li>MavenProject instance for module</li>
364      * </ol>
365      * </li>
366      * <li>prefixed with "artifact."
367      * <ol>
368      * <li>Artifact instance</li>
369      * <li>ArtifactHandler instance for artifact</li>
370      * <li>MavenProject instance for artifact</li>
371      * </ol>
372      * </li>
373      * <li>prefixed with "pom." or "project."
374      * <ol>
375      * <li>MavenProject instance from current build</li>
376      * </ol>
377      * </li>
378      * <li>no prefix, using main project instance
379      * <ol>
380      * <li>MavenProject instance from current build</li>
381      * </ol>
382      * </li>
383      * <li>Support for special expressions, like ${dashClassifier?}</li>
384      * <li>user-defined properties from the command line</li>
385      * <li>properties from main project</li>
386      * <li>system properties, from the MavenSession instance (to support IDEs)</li>
387      * <li>environment variables.</li>
388      * </ol>
389      */
390     public static String evaluateFileNameMapping( final String expression, @Nonnull final Artifact artifact,
391                                                   @Nullable final MavenProject mainProject,
392                                                   @Nullable final Artifact moduleArtifact,
393                                                   @Nonnull final AssemblerConfigurationSource configSource,
394                                                   FixedStringSearchInterpolator moduleProjectInterpolator,
395                                                   FixedStringSearchInterpolator artifactProjectInterpolator )
396         throws AssemblyFormattingException
397     {
398         String value = expression;
399 
400         final FixedStringSearchInterpolator interpolator =
401             FixedStringSearchInterpolator.create( moduleArtifactInterpolator( moduleArtifact ),
402                                                   moduleProjectInterpolator, artifactInterpolator( artifact ),
403                                                   artifactProjectInterpolator,
404                                                   mainProjectOnlyInterpolator( mainProject ),
405                                                   classifierRules( artifact ),
406                                                   executionPropertiesInterpolator( configSource ),
407                                                   configSource.getMainProjectInterpolator(),
408                                                   configSource.getCommandLinePropsInterpolator(),
409                                                   configSource.getEnvInterpolator() );
410 
411         value = interpolator.interpolate( value );
412 
413         value = StringUtils.replace( value, "//", "/" );
414         value = StringUtils.replace( value, "\\\\", "\\" );
415         value = fixRelativeRefs( value );
416 
417         return value;
418     }
419 
420     /**
421      * ORDER OF INTERPOLATION PRECEDENCE:
422      * <ol>
423      * <li>Support for special expressions, like ${finalName} (use the assembly plugin configuration not the build
424      * config)</li>
425      * <li>prefixed with "module." if moduleProject is non-null
426      * <ol>
427      * <li>MavenProject instance for module being assembled</li>
428      * </ol>
429      * </li>
430      * <li>prefixed with "artifact." if artifactProject is non-null
431      * <ol>
432      * <li>MavenProject instance for artifact</li>
433      * </ol>
434      * </li>
435      * <li>user-defined properties from the command line</li>
436      * <li>prefixed with "pom." or "project.", or no prefix at all
437      * <ol>
438      * <li>MavenProject instance from current build</li>
439      * </ol>
440      * </li>
441      * <li>properties from main project</li>
442      * <li>system properties, from the MavenSession instance (to support IDEs)</li>
443      * <li>environment variables.</li>
444      * </ol>
445      */
446     public static String getOutputDirectory( final String output, final String finalName,
447                                              final AssemblerConfigurationSource configSource,
448                                              FixedStringSearchInterpolator moduleProjectIntrpolator,
449                                              FixedStringSearchInterpolator artifactProjectInterpolator )
450         throws AssemblyFormattingException
451     {
452         String value = output;
453         if ( value == null )
454         {
455             value = "";
456         }
457 
458         final FixedStringSearchInterpolator interpolator =
459             FixedStringSearchInterpolator.create( finalNameInterpolator( finalName ), moduleProjectIntrpolator,
460                                                   artifactProjectInterpolator,
461                                                   executionPropertiesInterpolator( configSource ),
462                                                   configSource.getMainProjectInterpolator(),
463                                                   configSource.getCommandLinePropsInterpolator(),
464                                                   configSource.getEnvInterpolator() );
465 
466         value = interpolator.interpolate( value );
467 
468         if ( ( value.length() > 0 ) && !value.endsWith( "/" ) && !value.endsWith( "\\" ) )
469         {
470             value += "/";
471         }
472 
473         if ( ( value.length() > 0 ) && ( value.startsWith( "/" ) || value.startsWith( "\\" ) ) )
474         {
475             value = value.substring( 1 );
476         }
477 
478         value = StringUtils.replace( value, "//", "/" );
479         value = StringUtils.replace( value, "\\\\", "\\" );
480         value = fixRelativeRefs( value );
481 
482         return value;
483     }
484 
485     public static void warnForPlatformSpecifics( Logger logger, String destDirectory )
486     {
487         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
488         {
489             if ( isLinuxRootReference( destDirectory ) )
490             {
491                 logger.error( "OS=Windows and the assembly descriptor contains a *nix-specific root-relative-reference"
492                                   + " (starting with slash) " + destDirectory );
493             }
494             else if ( isWindowsPath( destDirectory ) )
495             {
496                 logger.warn( "The assembly descriptor contains a *nix-specific root-relative-reference"
497                                  + " (starting with slash). This is non-portable and will fail on windows "
498                                  + destDirectory );
499             }
500         }
501         else
502         {
503             if ( isWindowsPath( destDirectory ) )
504             {
505                 logger.error(
506                     "OS=Non-Windows and the assembly descriptor contains a windows-specific directory reference"
507                         + " (with a drive letter) " + destDirectory );
508             }
509             else if ( isLinuxRootReference( destDirectory ) )
510             {
511                 logger.warn( "The assembly descriptor contains a filesystem-root relative reference,"
512                                  + " which is not cross platform compatible " + destDirectory );
513             }
514         }
515     }
516 
517     static boolean isWindowsPath( String destDirectory )
518     {
519         return ( destDirectory != null && destDirectory.length() >= 2 && destDirectory.charAt( 1 ) == ':' );
520     }
521 
522     static boolean isLinuxRootReference( String destDirectory )
523     {
524         return ( destDirectory != null && destDirectory.startsWith( "/" ) );
525     }
526 
527 
528 }