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