View Javadoc
1   package org.apache.maven.plugin.ant;
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.model.Dependency;
24  import org.apache.maven.model.Profile;
25  import org.apache.maven.model.Repository;
26  import org.apache.maven.model.Resource;
27  import org.apache.maven.project.MavenProject;
28  import org.apache.maven.settings.Settings;
29  import org.apache.tools.ant.Main;
30  import org.codehaus.plexus.util.FileUtils;
31  import org.codehaus.plexus.util.IOUtil;
32  import org.codehaus.plexus.util.StringUtils;
33  import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
34  import org.codehaus.plexus.util.xml.XMLWriter;
35  import org.codehaus.plexus.util.xml.XmlWriterUtil;
36  
37  import java.io.File;
38  import java.io.FileOutputStream;
39  import java.io.IOException;
40  import java.io.OutputStreamWriter;
41  import java.net.URL;
42  import java.util.ArrayList;
43  import java.util.Arrays;
44  import java.util.List;
45  import java.util.Map;
46  import java.util.Properties;
47  import java.util.SortedMap;
48  import java.util.TreeMap;
49  
50  /**
51   * Write Ant build files from <code>Maven Project</code> for <a href="http://ant.apache.org">Ant</a> 1.6.2 or above:
52   * <ul>
53   * <li>build.xml</li>
54   * <li>maven-build.xml</li>
55   * <li>maven-build.properties</li>
56   * </ul>
57   *
58   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
59   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
60   * @version $Id: AntBuildWriter.java 1645084 2014-12-12 22:28:31Z khmarbaise $
61   */
62  public class AntBuildWriter
63  {
64      /**
65       * The default line indenter
66       */
67      protected static final int DEFAULT_INDENTATION_SIZE = XmlWriterUtil.DEFAULT_INDENTATION_SIZE;
68  
69      /**
70       * The default build file name (build.xml)
71       */
72      protected static final String DEFAULT_BUILD_FILENAME = Main.DEFAULT_BUILD_FILENAME;
73  
74      /**
75       * The default generated build file name
76       */
77      protected static final String DEFAULT_MAVEN_BUILD_FILENAME = "maven-build.xml";
78  
79      /**
80       * The default build properties file name
81       */
82      protected static final String DEFAULT_MAVEN_PROPERTIES_FILENAME = "maven-build.properties";
83  
84      private final MavenProject project;
85  
86      private final ArtifactResolverWrapper artifactResolverWrapper;
87  
88      private final File localRepository;
89  
90      private final Settings settings;
91  
92      private final boolean overwrite;
93  
94      private final Properties executionProperties;
95  
96      /**
97       * @param project {@link MavenProject}
98       * @param artifactResolverWrapper {@link ArtifactResolverWrapper}
99       * @param settings {@link Settings}
100      * @param overwrite true/false to overwrite or not.
101      * @param executionProperties {@link Properties}
102      */
103     public AntBuildWriter( MavenProject project, ArtifactResolverWrapper artifactResolverWrapper, Settings settings,
104                            boolean overwrite, Properties executionProperties )
105     {
106         this.project = project;
107         this.artifactResolverWrapper = artifactResolverWrapper;
108         this.localRepository = new File( artifactResolverWrapper.getLocalRepository().getBasedir() );
109         this.settings = settings;
110         this.overwrite = overwrite;
111         this.executionProperties = ( executionProperties != null ) ? executionProperties : new Properties();
112     }
113 
114     /**
115      * Generate Ant build XML files
116      *
117      * @throws IOException In case of an error.
118      */
119     protected void writeBuildXmls()
120         throws IOException
121     {
122         writeGeneratedBuildXml();
123         writeBuildXml();
124     }
125 
126     /**
127      * Generate <code>maven-build.properties</code> only for a non-POM project
128      *
129      * @throws IOException In case of an failure {@link IOException}
130      * @see #DEFAULT_MAVEN_PROPERTIES_FILENAME
131      */
132     protected void writeBuildProperties()
133         throws IOException
134     {
135         if ( AntBuildWriterUtil.isPomPackaging( project ) )
136         {
137             return;
138         }
139 
140         Properties properties = new Properties();
141 
142         // ----------------------------------------------------------------------
143         // Build properties
144         // ----------------------------------------------------------------------
145 
146         addProperty( properties, "maven.build.finalName",
147                      AntBuildWriterUtil.toRelative( project.getBasedir(), project.getBuild().getFinalName() ) );
148 
149         // target
150         addProperty( properties, "maven.build.dir",
151                      AntBuildWriterUtil.toRelative( project.getBasedir(), project.getBuild().getDirectory() ) );
152         addProperty( properties, "project.build.directory", "${maven.build.dir}" );
153 
154         // ${maven.build.dir}/classes
155         addProperty( properties,
156                      "maven.build.outputDir",
157                      "${maven.build.dir}/"
158                          + AntBuildWriterUtil.toRelative( new File( project.getBasedir(),
159                                                                     properties.getProperty( "maven.build.dir" ) ),
160                                                           project.getBuild().getOutputDirectory() ) );
161         addProperty( properties, "project.build.outputDirectory", "${maven.build.outputDir}" );
162 
163         // src/main/java
164         if ( !project.getCompileSourceRoots().isEmpty() )
165         {
166             List var = project.getCompileSourceRoots();
167             String[] compileSourceRoots = (String[]) var.toArray( new String[var.size()] );
168             for ( int i = 0; i < compileSourceRoots.length; i++ )
169             {
170                 addProperty( properties, "maven.build.srcDir." + i,
171                              AntBuildWriterUtil.toRelative( project.getBasedir(), compileSourceRoots[i] ) );
172             }
173         }
174         // src/main/resources
175         if ( project.getBuild().getResources() != null )
176         {
177             List<Resource> var = project.getBuild().getResources();
178             Resource[] array = var.toArray( new Resource[var.size()] );
179             for ( int i = 0; i < array.length; i++ )
180             {
181                 addProperty( properties, "maven.build.resourceDir." + i,
182                              AntBuildWriterUtil.toRelative( project.getBasedir(), array[i].getDirectory() ) );
183             }
184         }
185 
186         // ${maven.build.dir}/test-classes
187         addProperty( properties,
188                      "maven.build.testOutputDir",
189                      "${maven.build.dir}/"
190                          + AntBuildWriterUtil.toRelative( new File( project.getBasedir(),
191                                                                     properties.getProperty( "maven.build.dir" ) ),
192                                                           project.getBuild().getTestOutputDirectory() ) );
193         // src/test/java
194         if ( !project.getTestCompileSourceRoots().isEmpty() )
195         {
196             List var = project.getTestCompileSourceRoots();
197             String[] compileSourceRoots = (String[]) var.toArray( new String[var.size()] );
198             for ( int i = 0; i < compileSourceRoots.length; i++ )
199             {
200                 addProperty( properties, "maven.build.testDir." + i,
201                              AntBuildWriterUtil.toRelative( project.getBasedir(), compileSourceRoots[i] ) );
202             }
203         }
204         // src/test/resources
205         if ( project.getBuild().getTestResources() != null )
206         {
207             List<Resource> var = project.getBuild().getTestResources();
208             Resource[] array = var.toArray( new Resource[var.size()] );
209             for ( int i = 0; i < array.length; i++ )
210             {
211                 addProperty( properties, "maven.build.testResourceDir." + i,
212                              AntBuildWriterUtil.toRelative( project.getBasedir(), array[i].getDirectory() ) );
213             }
214         }
215 
216         addProperty( properties, "maven.test.reports", "${maven.build.dir}/test-reports" );
217 
218         addProperty( properties, "maven.reporting.outputDirectory", "${maven.build.dir}/site" );
219 
220         // ----------------------------------------------------------------------
221         // Settings properties
222         // ----------------------------------------------------------------------
223 
224         addProperty( properties, "maven.settings.offline", String.valueOf( settings.isOffline() ) );
225         addProperty( properties, "maven.settings.interactiveMode", String.valueOf( settings.isInteractiveMode() ) );
226         addProperty( properties, "maven.repo.local", getLocalRepositoryPath() );
227 
228         // ----------------------------------------------------------------------
229         // Project properties
230         // ----------------------------------------------------------------------
231 
232         if ( project.getProperties() != null )
233         {
234             for ( Map.Entry<Object, Object> objectObjectEntry : project.getProperties().entrySet() )
235             {
236                 Map.Entry property = (Map.Entry) objectObjectEntry;
237                 addProperty( properties, property.getKey().toString(), property.getValue().toString() );
238             }
239         }
240 
241         FileOutputStream os =
242             new FileOutputStream( new File( project.getBasedir(), DEFAULT_MAVEN_PROPERTIES_FILENAME ) );
243         try
244         {
245             properties.store( os, "Generated by Maven Ant Plugin - DO NOT EDIT THIS FILE!" );
246         }
247         finally
248         {
249             IOUtil.close( os );
250         }
251     }
252 
253     /**
254      * Generate an <code>maven-build.xml</code>
255      *
256      * @throws IOException
257      * @see #DEFAULT_MAVEN_BUILD_FILENAME
258      */
259     private void writeGeneratedBuildXml()
260         throws IOException
261     {
262         // TODO: parameter
263         File outputFile = new File( project.getBasedir(), DEFAULT_MAVEN_BUILD_FILENAME );
264 
265         String encoding = "UTF-8";
266 
267         OutputStreamWriter w = new OutputStreamWriter( new FileOutputStream( outputFile ), encoding );
268 
269         XMLWriter writer =
270             new PrettyPrintXMLWriter( w, StringUtils.repeat( " ", DEFAULT_INDENTATION_SIZE ), encoding, null );
271 
272         // ----------------------------------------------------------------------
273         // <!-- comments -->
274         // ----------------------------------------------------------------------
275 
276         AntBuildWriterUtil.writeHeader( writer );
277 
278         // ----------------------------------------------------------------------
279         // <project/>
280         // ----------------------------------------------------------------------
281 
282         writer.startElement( "project" );
283         writer.addAttribute( "name", project.getArtifactId() + "-from-maven" );
284         writer.addAttribute( "default", "package" );
285         writer.addAttribute( "basedir", "." );
286 
287         XmlWriterUtil.writeLineBreak( writer );
288 
289         // ----------------------------------------------------------------------
290         // <property/>
291         // ----------------------------------------------------------------------
292 
293         writeProperties( writer );
294 
295         // ----------------------------------------------------------------------
296         // <path/>
297         // ----------------------------------------------------------------------
298 
299         writeBuildPathDefinition( writer );
300 
301         // ----------------------------------------------------------------------
302         // <target name="clean" />
303         // ----------------------------------------------------------------------
304 
305         writeCleanTarget( writer );
306 
307         // ----------------------------------------------------------------------
308         // <target name="compile" />
309         // ----------------------------------------------------------------------
310 
311         List compileSourceRoots = AntBuildWriterUtil.removeEmptyCompileSourceRoots( project.getCompileSourceRoots() );
312         writeCompileTarget( writer, compileSourceRoots );
313 
314         // ----------------------------------------------------------------------
315         // <target name="compile-tests" />
316         // ----------------------------------------------------------------------
317 
318         List testCompileSourceRoots =
319             AntBuildWriterUtil.removeEmptyCompileSourceRoots( project.getTestCompileSourceRoots() );
320         writeCompileTestsTarget( writer, testCompileSourceRoots );
321 
322         // ----------------------------------------------------------------------
323         // <target name="test" />
324         // ----------------------------------------------------------------------
325 
326         writeTestTargets( writer, testCompileSourceRoots );
327 
328         // ----------------------------------------------------------------------
329         // <target name="javadoc" />
330         // ----------------------------------------------------------------------
331         writeJavadocTarget( writer );
332 
333         // ----------------------------------------------------------------------
334         // <target name="package" />
335         // ----------------------------------------------------------------------
336         writePackageTarget( writer );
337 
338         // ----------------------------------------------------------------------
339         // <target name="get-deps" />
340         // ----------------------------------------------------------------------
341         writeGetDepsTarget( writer );
342 
343         XmlWriterUtil.writeLineBreak( writer );
344 
345         writer.endElement(); // project
346 
347         XmlWriterUtil.writeLineBreak( writer );
348 
349         IOUtil.close( w );
350     }
351 
352     /**
353      * Generate an generic <code>build.xml</code> if not already exist
354      *
355      * @throws IOException
356      * @see #DEFAULT_BUILD_FILENAME
357      */
358     private void writeBuildXml()
359         throws IOException
360     {
361         File outputFile = new File( project.getBasedir(), DEFAULT_BUILD_FILENAME );
362 
363         if ( outputFile.exists() && !overwrite )
364         {
365             return;
366         }
367 
368         String encoding = "UTF-8";
369 
370         OutputStreamWriter w = new OutputStreamWriter( new FileOutputStream( outputFile ), encoding );
371 
372         XMLWriter writer =
373             new PrettyPrintXMLWriter( w, StringUtils.repeat( " ", DEFAULT_INDENTATION_SIZE ), encoding, null );
374 
375         // ----------------------------------------------------------------------
376         // <!-- comments -->
377         // ----------------------------------------------------------------------
378 
379         AntBuildWriterUtil.writeAntVersionHeader( writer );
380 
381         // ----------------------------------------------------------------------
382         // <project/>
383         // ----------------------------------------------------------------------
384 
385         writer.startElement( "project" );
386         writer.addAttribute( "name", project.getArtifactId() );
387         writer.addAttribute( "default", "package" );
388         writer.addAttribute( "basedir", "." );
389 
390         XmlWriterUtil.writeLineBreak( writer );
391 
392         XmlWriterUtil.writeCommentText( writer, "Import " + DEFAULT_MAVEN_BUILD_FILENAME + " into the current project",
393                                         1 );
394 
395         writer.startElement( "import" );
396         writer.addAttribute( "file", DEFAULT_MAVEN_BUILD_FILENAME );
397         writer.endElement(); // import
398 
399         XmlWriterUtil.writeLineBreak( writer, 1, 1 );
400 
401         XmlWriterUtil.writeCommentText( writer, "Help target", 1 );
402 
403         writer.startElement( "target" );
404         writer.addAttribute( "name", "help" );
405 
406         writer.startElement( "echo" );
407         writer.addAttribute( "message", "Please run: $ant -projecthelp" );
408         writer.endElement(); // echo
409 
410         writer.endElement(); // target
411 
412         XmlWriterUtil.writeLineBreak( writer, 2 );
413 
414         writer.endElement(); // project
415 
416         XmlWriterUtil.writeLineBreak( writer );
417 
418         IOUtil.close( w );
419     }
420 
421     /**
422      * Write properties in the writer only for a non-POM project.
423      *
424      * @param writer
425      */
426     private void writeProperties( XMLWriter writer )
427     {
428         if ( AntBuildWriterUtil.isPomPackaging( project ) )
429         {
430             return;
431         }
432 
433         // TODO: optional in m1
434         // TODO: USD properties
435         XmlWriterUtil.writeCommentText( writer, "Build environment properties", 1 );
436 
437         // ----------------------------------------------------------------------
438         // File properties to override local properties
439         // ----------------------------------------------------------------------
440 
441         writer.startElement( "property" );
442         writer.addAttribute( "file", "${user.home}/.m2/maven.properties" );
443         writer.endElement(); // property
444 
445         writer.startElement( "property" );
446         writer.addAttribute( "file", DEFAULT_MAVEN_PROPERTIES_FILENAME );
447         writer.endElement(); // property
448 
449         // ----------------------------------------------------------------------
450         // Build properties
451         // ----------------------------------------------------------------------
452 
453         XmlWriterUtil.writeLineBreak( writer, 2, 1 );
454 
455         writer.startElement( "property" );
456         writer.addAttribute( "name", "maven.build.finalName" );
457         writer.addAttribute( "value", project.getBuild().getFinalName() );
458         writer.endElement(); // property
459 
460         writer.startElement( "property" );
461         writer.addAttribute( "name", "maven.build.dir" );
462         writer.addAttribute( "value",
463                              AntBuildWriterUtil.toRelative( project.getBasedir(), project.getBuild().getDirectory() ) );
464         writer.endElement(); // property
465 
466         writer.startElement( "property" );
467         writer.addAttribute( "name", "maven.build.outputDir" );
468         writer.addAttribute( "value",
469                              "${maven.build.dir}/"
470                                  + AntBuildWriterUtil.toRelative( new File( project.getBuild().getDirectory() ),
471                                                                   project.getBuild().getOutputDirectory() ) );
472         writer.endElement(); // property
473 
474         if ( !project.getCompileSourceRoots().isEmpty() )
475         {
476             List var = project.getCompileSourceRoots();
477             String[] compileSourceRoots = (String[]) var.toArray( new String[var.size()] );
478             for ( int i = 0; i < compileSourceRoots.length; i++ )
479             {
480                 writer.startElement( "property" );
481                 writer.addAttribute( "name", "maven.build.srcDir." + i );
482                 writer.addAttribute( "value",
483                                      AntBuildWriterUtil.toRelative( project.getBasedir(), compileSourceRoots[i] ) );
484                 writer.endElement(); // property
485             }
486         }
487 
488         if ( project.getBuild().getResources() != null )
489         {
490             List<Resource> var = project.getBuild().getResources();
491             Resource[] array = var.toArray( new Resource[var.size()] );
492             for ( int i = 0; i < array.length; i++ )
493             {
494                 writer.startElement( "property" );
495                 writer.addAttribute( "name", "maven.build.resourceDir." + i );
496                 writer.addAttribute( "value",
497                                      AntBuildWriterUtil.toRelative( project.getBasedir(), array[i].getDirectory() ) );
498                 writer.endElement(); // property
499             }
500         }
501 
502         writer.startElement( "property" );
503         writer.addAttribute( "name", "maven.build.testOutputDir" );
504         writer.addAttribute( "value",
505                              "${maven.build.dir}/"
506                                  + AntBuildWriterUtil.toRelative( new File( project.getBuild().getDirectory() ),
507                                                                   project.getBuild().getTestOutputDirectory() ) );
508         writer.endElement(); // property
509 
510         if ( !project.getTestCompileSourceRoots().isEmpty() )
511         {
512             List var = project.getTestCompileSourceRoots();
513             String[] compileSourceRoots = (String[]) var.toArray( new String[var.size()] );
514             for ( int i = 0; i < compileSourceRoots.length; i++ )
515             {
516                 writer.startElement( "property" );
517                 writer.addAttribute( "name", "maven.build.testDir." + i );
518                 writer.addAttribute( "value",
519                                      AntBuildWriterUtil.toRelative( project.getBasedir(), compileSourceRoots[i] ) );
520                 writer.endElement(); // property
521             }
522         }
523 
524         if ( project.getBuild().getTestResources() != null )
525         {
526             List<Resource> var = project.getBuild().getTestResources();
527             Resource[] array = var.toArray( new Resource[var.size()] );
528             for ( int i = 0; i < array.length; i++ )
529             {
530                 writer.startElement( "property" );
531                 writer.addAttribute( "name", "maven.build.testResourceDir." + i );
532                 writer.addAttribute( "value",
533                                      AntBuildWriterUtil.toRelative( project.getBasedir(), array[i].getDirectory() ) );
534                 writer.endElement(); // property
535             }
536         }
537 
538         writer.startElement( "property" );
539         writer.addAttribute( "name", "maven.test.reports" );
540         writer.addAttribute( "value", "${maven.build.dir}/test-reports" );
541         writer.endElement(); // property
542 
543         String reportingOutputDir = project.getReporting().getOutputDirectory();
544         // workaround for MNG-3475
545         if ( !new File( reportingOutputDir ).isAbsolute() )
546         {
547             reportingOutputDir = new File( project.getBasedir(), reportingOutputDir ).getAbsolutePath();
548         }
549         writer.startElement( "property" );
550         writer.addAttribute( "name", "maven.reporting.outputDirectory" );
551         writer.addAttribute( "value",
552                              "${maven.build.dir}/"
553                                  + AntBuildWriterUtil.toRelative( new File( project.getBuild().getDirectory() ),
554                                                                   reportingOutputDir ) );
555         writer.endElement(); // property
556 
557         // ----------------------------------------------------------------------
558         // Setting properties
559         // ----------------------------------------------------------------------
560 
561         XmlWriterUtil.writeLineBreak( writer, 2, 1 );
562 
563         writer.startElement( "property" );
564         writer.addAttribute( "name", "maven.repo.local" );
565         writer.addAttribute( "value", "${user.home}/.m2/repository" );
566         writer.endElement(); // property
567 
568         writer.startElement( "property" );
569         writer.addAttribute( "name", "maven.settings.offline" );
570         writer.addAttribute( "value", String.valueOf( settings.isOffline() ) );
571         writer.endElement(); // property
572 
573         writer.startElement( "property" );
574         writer.addAttribute( "name", "maven.settings.interactiveMode" );
575         writer.addAttribute( "value", String.valueOf( settings.isInteractiveMode() ) );
576         writer.endElement(); // property
577 
578         XmlWriterUtil.writeLineBreak( writer );
579     }
580 
581     /**
582      * Check if the local repository is in the default location: <code>${user.home}/.m2/repository</code>. If that is
583      * the case then return
584      * the path with the system property "user.home" in it. If not then just
585      * return the absolute path to the local repository.
586      */
587     private String getLocalRepositoryPath()
588     {
589         String userHome = System.getProperty( "user.home" );
590         String defaultPath = ( userHome + "/.m2/repository" ).replace( '\\', '/' );
591         String actualPath = localRepository.getAbsolutePath().replace( '\\', '/' );
592         if ( actualPath.equals( defaultPath ) )
593         {
594             return "${user.home}/.m2/repository";
595         }
596         else
597         {
598             return localRepository.getAbsolutePath();
599         }
600     }
601 
602     /**
603      * Write path definition in the writer only for a non-POM project.
604      *
605      * @param writer
606      */
607     private void writeBuildPathDefinition( XMLWriter writer )
608     {
609         if ( AntBuildWriterUtil.isPomPackaging( project ) )
610         {
611             return;
612         }
613 
614         XmlWriterUtil.writeCommentText( writer, "Defining classpaths", 1 );
615 
616         writeBuildPathDefinition( writer, "build.classpath", project.getCompileArtifacts() );
617 
618         writeBuildPathDefinition( writer, "build.test.classpath", project.getTestArtifacts() );
619 
620         XmlWriterUtil.writeLineBreak( writer );
621     }
622 
623     private void writeBuildPathDefinition( XMLWriter writer, String id, List artifacts )
624     {
625         writer.startElement( "path" );
626         writer.addAttribute( "id", id );
627 
628         for ( Object artifact1 : artifacts )
629         {
630             Artifact artifact = (Artifact) artifact1;
631 
632             writer.startElement( "pathelement" );
633 
634             String path;
635             if ( Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) )
636             {
637                 path = getUninterpolatedSystemPath( artifact );
638             }
639             else
640             {
641                 path = "${maven.repo.local}/" + artifactResolverWrapper.getLocalArtifactPath( artifact );
642             }
643             writer.addAttribute( "location", path );
644 
645             writer.endElement(); // pathelement
646         }
647 
648         writer.endElement(); // path
649     }
650 
651     private String getUninterpolatedSystemPath( Artifact artifact )
652     {
653         String managementKey = artifact.getDependencyConflictId();
654 
655         for ( Dependency dependency : project.getOriginalModel().getDependencies() )
656         {
657             if ( managementKey.equals( dependency.getManagementKey() ) )
658             {
659                 return dependency.getSystemPath();
660             }
661         }
662 
663         for ( Profile profile : project.getOriginalModel().getProfiles() )
664         {
665             for ( Dependency dependency : profile.getDependencies() )
666             {
667                 if ( managementKey.equals( dependency.getManagementKey() ) )
668                 {
669                     return dependency.getSystemPath();
670                 }
671             }
672         }
673 
674         String path = artifact.getFile().getAbsolutePath();
675 
676         Properties props = new Properties();
677         props.putAll( project.getProperties() );
678         props.putAll( executionProperties );
679         props.remove( "user.dir" );
680         props.put( "basedir", project.getBasedir().getAbsolutePath() );
681 
682         SortedMap<String, String> candidateProperties = new TreeMap<String, String>();
683         for ( Object o : props.keySet() )
684         {
685             String key = (String) o;
686             String value = new File( props.getProperty( key ) ).getPath();
687             if ( path.startsWith( value ) && value.length() > 0 )
688             {
689                 candidateProperties.put( value, key );
690             }
691         }
692         if ( !candidateProperties.isEmpty() )
693         {
694             String value = candidateProperties.lastKey();
695             String key = candidateProperties.get( value );
696             path = path.substring( value.length() );
697             path = path.replace( '\\', '/' );
698             return "${" + key + "}" + path;
699         }
700 
701         return path;
702     }
703 
704     /**
705      * Write clean target in the writer depending the packaging of the project.
706      *
707      * @param writer the writer
708      */
709     private void writeCleanTarget( XMLWriter writer )
710     {
711         XmlWriterUtil.writeCommentText( writer, "Cleaning up target", 1 );
712 
713         writer.startElement( "target" );
714         writer.addAttribute( "name", "clean" );
715         writer.addAttribute( "description", "Clean the output directory" );
716 
717         if ( AntBuildWriterUtil.isPomPackaging( project ) )
718         {
719             if ( project.getModules() != null )
720             {
721                 for ( Object o : project.getModules() )
722                 {
723                     String moduleSubPath = (String) o;
724                     AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "clean" );
725                 }
726             }
727         }
728         else
729         {
730             writer.startElement( "delete" );
731             writer.addAttribute( "dir", "${maven.build.dir}" );
732             writer.endElement(); // delete
733         }
734 
735         writer.endElement(); // target
736 
737         XmlWriterUtil.writeLineBreak( writer );
738     }
739 
740     /**
741      * Write compile target in the writer depending the packaging of the project.
742      *
743      * @param writer
744      * @param compileSourceRoots
745      * @throws IOException if any
746      */
747     private void writeCompileTarget( XMLWriter writer, List compileSourceRoots )
748         throws IOException
749     {
750         XmlWriterUtil.writeCommentText( writer, "Compilation target", 1 );
751 
752         if ( AntBuildWriterUtil.isPomPackaging( project ) )
753         {
754             writer.startElement( "target" );
755             writer.addAttribute( "name", "compile" );
756             writer.addAttribute( "description", "Compile the code" );
757             if ( project.getModules() != null )
758             {
759                 for ( Object o : project.getModules() )
760                 {
761                     String moduleSubPath = (String) o;
762                     AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "compile" );
763                 }
764             }
765             writer.endElement(); // target
766         }
767         else
768         {
769             writer.startElement( "target" );
770             writer.addAttribute( "name", "compile" );
771             writer.addAttribute( "depends", "get-deps" );
772             writer.addAttribute( "description", "Compile the code" );
773 
774             writeCompileTasks( writer, "${maven.build.outputDir}", compileSourceRoots,
775                                project.getBuild().getResources(), null, false );
776 
777             writer.endElement(); // target
778         }
779 
780         XmlWriterUtil.writeLineBreak( writer );
781     }
782 
783     /**
784      * Write compile-test target in the writer depending the packaging of the project.
785      *
786      * @param writer
787      * @param testCompileSourceRoots
788      * @throws IOException if any
789      */
790     private void writeCompileTestsTarget( XMLWriter writer, List testCompileSourceRoots )
791         throws IOException
792     {
793         XmlWriterUtil.writeCommentText( writer, "Test-compilation target", 1 );
794 
795         if ( AntBuildWriterUtil.isPomPackaging( project ) )
796         {
797             writer.startElement( "target" );
798             writer.addAttribute( "name", "compile-tests" );
799             writer.addAttribute( "description", "Compile the test code" );
800             if ( project.getModules() != null )
801             {
802                 for ( Object o : project.getModules() )
803                 {
804                     String moduleSubPath = (String) o;
805                     AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "compile-tests" );
806                 }
807             }
808             writer.endElement(); // target
809         }
810         else
811         {
812             writer.startElement( "target" );
813             writer.addAttribute( "name", "compile-tests" );
814             AntBuildWriterUtil.addWrapAttribute( writer, "target", "depends", "compile", 2 );
815             AntBuildWriterUtil.addWrapAttribute( writer, "target", "description", "Compile the test code", 2 );
816             AntBuildWriterUtil.addWrapAttribute( writer, "target", "unless", "maven.test.skip", 2 );
817 
818             writeCompileTasks( writer, "${maven.build.testOutputDir}", testCompileSourceRoots,
819                                project.getBuild().getTestResources(), "${maven.build.outputDir}", true );
820 
821             writer.endElement(); // target
822         }
823 
824         XmlWriterUtil.writeLineBreak( writer );
825     }
826 
827     /**
828      * Write test target in the writer depending the packaging of the project.
829      *
830      * @param writer
831      * @param testCompileSourceRoots
832      */
833     private void writeTestTargets( XMLWriter writer, List testCompileSourceRoots )
834         throws IOException
835     {
836         XmlWriterUtil.writeCommentText( writer, "Run all tests", 1 );
837 
838         if ( AntBuildWriterUtil.isPomPackaging( project ) )
839         {
840             writePomParts( writer );
841         }
842         else
843         {
844             writer.startElement( "target" );
845             writer.addAttribute( "name", "test" );
846             AntBuildWriterUtil.addWrapAttribute( writer, "target", "depends", "compile-tests, junit-missing", 2 );
847             AntBuildWriterUtil.addWrapAttribute( writer, "target", "unless", "junit.skipped", 2 );
848             AntBuildWriterUtil.addWrapAttribute( writer, "target", "description", "Run the test cases", 2 );
849 
850             if ( !testCompileSourceRoots.isEmpty() )
851             {
852                 writer.startElement( "mkdir" );
853                 writer.addAttribute( "dir", "${maven.test.reports}" );
854                 writer.endElement(); // mkdir
855 
856                 writer.startElement( "junit" );
857                 writer.addAttribute( "printSummary", "yes" );
858                 writer.addAttribute( "haltonerror", "true" );
859                 writer.addAttribute( "haltonfailure", "true" );
860                 writer.addAttribute( "fork", "true" );
861                 writer.addAttribute( "dir", "." );
862 
863                 writer.startElement( "sysproperty" );
864                 writer.addAttribute( "key", "basedir" );
865                 writer.addAttribute( "value", "." );
866                 writer.endElement(); // sysproperty
867 
868                 writer.startElement( "formatter" );
869                 writer.addAttribute( "type", "xml" );
870                 writer.endElement(); // formatter
871 
872                 writer.startElement( "formatter" );
873                 writer.addAttribute( "type", "plain" );
874                 writer.addAttribute( "usefile", "false" );
875                 writer.endElement(); // formatter
876 
877                 writer.startElement( "classpath" );
878                 writer.startElement( "path" );
879                 writer.addAttribute( "refid", "build.test.classpath" );
880                 writer.endElement(); // path
881                 writer.startElement( "pathelement" );
882                 writer.addAttribute( "location", "${maven.build.outputDir}" );
883                 writer.endElement(); // pathelement
884                 writer.startElement( "pathelement" );
885                 writer.addAttribute( "location", "${maven.build.testOutputDir}" );
886                 writer.endElement(); // pathelement
887                 writer.endElement(); // classpath
888 
889                 writer.startElement( "batchtest" );
890                 writer.addAttribute( "todir", "${maven.test.reports}" );
891                 writer.addAttribute( "unless", "test" );
892 
893                 List includes = getTestIncludes();
894                 List excludes = getTestExcludes();
895 
896                 writeTestFilesets( writer, testCompileSourceRoots, includes, excludes );
897 
898                 writer.endElement(); // batchtest
899 
900                 writer.startElement( "batchtest" );
901                 writer.addAttribute( "todir", "${maven.test.reports}" );
902                 writer.addAttribute( "if", "test" );
903 
904                 includes = Arrays.asList( "**/${test}.java" );
905 
906                 writeTestFilesets( writer, testCompileSourceRoots, includes, excludes );
907 
908                 writer.endElement(); // batchtest
909 
910                 writer.endElement(); // junit
911             }
912             writer.endElement(); // target
913 
914             XmlWriterUtil.writeLineBreak( writer, 2, 1 );
915 
916             writer.startElement( "target" );
917             writer.addAttribute( "name", "test-junit-present" );
918 
919             writer.startElement( "available" );
920             writer.addAttribute( "classname", "junit.framework.Test" );
921             writer.addAttribute( "property", "junit.present" );
922             writer.addAttribute( "classpathref", "build.test.classpath" );
923             writer.endElement(); // available
924 
925             writer.endElement(); // target
926 
927             XmlWriterUtil.writeLineBreak( writer, 2, 1 );
928 
929             writer.startElement( "target" );
930             writer.addAttribute( "name", "test-junit-status" );
931             AntBuildWriterUtil.addWrapAttribute( writer, "target", "depends", "test-junit-present", 2 );
932             writer.startElement( "condition" );
933             writer.addAttribute( "property", "junit.missing" );
934             writer.startElement( "and" );
935             writer.startElement( "isfalse" );
936             writer.addAttribute( "value", "${junit.present}" );
937             writer.endElement(); // isfalse
938             writer.startElement( "isfalse" );
939             writer.addAttribute( "value", "${maven.test.skip}" );
940             writer.endElement(); // isfalse
941             writer.endElement(); // and
942             writer.endElement(); // condition
943             writer.startElement( "condition" );
944             writer.addAttribute( "property", "junit.skipped" );
945             writer.startElement( "or" );
946             writer.startElement( "isfalse" );
947             writer.addAttribute( "value", "${junit.present}" );
948             writer.endElement(); // isfalse
949             writer.startElement( "istrue" );
950             writer.addAttribute( "value", "${maven.test.skip}" );
951             writer.endElement(); // istrue
952             writer.endElement(); // or
953             writer.endElement(); // condition
954             writer.endElement(); // target
955 
956             XmlWriterUtil.writeLineBreak( writer, 2, 1 );
957 
958             writer.startElement( "target" );
959             writer.addAttribute( "name", "junit-missing" );
960             AntBuildWriterUtil.addWrapAttribute( writer, "target", "depends", "test-junit-status", 2 );
961             AntBuildWriterUtil.addWrapAttribute( writer, "target", "if", "junit.missing", 2 );
962 
963             // CHECKSTYLE_OFF: MagicNumber
964             writer.startElement( "echo" );
965             writer.writeText( StringUtils.repeat( "=", 35 ) + " WARNING " + StringUtils.repeat( "=", 35 ) );
966             writer.endElement(); // echo
967 
968             writer.startElement( "echo" );
969             // CHECKSTYLE_OFF: LineLength
970             writer.writeText( " JUnit is not present in the test classpath or your $ANT_HOME/lib directory. Tests not executed." );
971             // CHECKSTYLE_ON: LineLength
972             writer.endElement(); // echo
973 
974             writer.startElement( "echo" );
975             writer.writeText( StringUtils.repeat( "=", 79 ) );
976             writer.endElement(); // echo
977             // CHECKSTYLE_ON: MagicNumber
978 
979             writer.endElement(); // target
980         }
981 
982         XmlWriterUtil.writeLineBreak( writer );
983     }
984 
985     /**
986      * @param writer {@link XMLWriter}
987      */
988     private void writePomParts( XMLWriter writer )
989     {
990         writer.startElement( "target" );
991         writer.addAttribute( "name", "test" );
992         writer.addAttribute( "description", "Run the test cases" );
993         if ( project.getModules() != null )
994         {
995             for ( Object o : project.getModules() )
996             {
997                 String moduleSubPath = (String) o;
998                 AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "test" );
999             }
1000         }
1001         writer.endElement(); // target
1002     }
1003 
1004     /**
1005      * Gets the include patterns for the unit tests.
1006      *
1007      * @return A list of strings with include patterns, might be empty but never <code>null</code>.
1008      */
1009     private List getTestIncludes()
1010         throws IOException
1011     {
1012         // CHECKSTYLE_OFF: LineLength
1013         List includes = getSelectorList( AntBuildWriterUtil.getMavenSurefirePluginOptions( project, "includes", null ) );
1014         // CHECKSTYLE_ON: LineLength
1015         if ( includes == null || includes.isEmpty() )
1016         {
1017             includes = Arrays.asList( "**/Test*.java", "**/*Test.java", "**/*TestCase.java" );
1018         }
1019         return includes;
1020     }
1021 
1022     /**
1023      * Gets the exclude patterns for the unit tests.
1024      *
1025      * @return A list of strings with exclude patterns, might be empty but never <code>null</code>.
1026      */
1027     private List getTestExcludes()
1028         throws IOException
1029     {
1030         // CHECKSTYLE_OFF: LineLength
1031         List excludes = getSelectorList( AntBuildWriterUtil.getMavenSurefirePluginOptions( project, "excludes", null ) );
1032         // CHECKSTYLE_ON: LineLength
1033         if ( excludes == null || excludes.isEmpty() )
1034         {
1035             excludes = Arrays.asList( "**/*Abstract*Test.java" );
1036         }
1037         return excludes;
1038     }
1039 
1040     /**
1041      * Write the <code>&lt;fileset&gt;</code> elements for the test compile source roots.
1042      *
1043      * @param writer
1044      * @param testCompileSourceRoots
1045      * @param includes
1046      * @param excludes
1047      */
1048     private void writeTestFilesets( XMLWriter writer, List testCompileSourceRoots, List includes, List excludes )
1049     {
1050         for ( int i = 0; i < testCompileSourceRoots.size(); i++ )
1051         {
1052             writer.startElement( "fileset" );
1053             writer.addAttribute( "dir", "${maven.build.testDir." + i + "}" );
1054             // TODO: m1 allows additional test exclusions via maven.ant.excludeTests
1055             AntBuildWriterUtil.writeIncludesExcludes( writer, includes, excludes );
1056             writer.endElement(); // fileset
1057         }
1058     }
1059 
1060     /**
1061      * Write javadoc target in the writer depending the packaging of the project.
1062      *
1063      * @param writer
1064      * @throws IOException if any
1065      */
1066     private void writeJavadocTarget( XMLWriter writer )
1067         throws IOException
1068     {
1069         XmlWriterUtil.writeCommentText( writer, "Javadoc target", 1 );
1070 
1071         writer.startElement( "target" );
1072         writer.addAttribute( "name", "javadoc" );
1073         writer.addAttribute( "description", "Generates the Javadoc of the application" );
1074 
1075         if ( AntBuildWriterUtil.isPomPackaging( project ) )
1076         {
1077             if ( project.getModules() != null )
1078             {
1079                 for ( Object o : project.getModules() )
1080                 {
1081                     String moduleSubPath = (String) o;
1082                     AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "javadoc" );
1083                 }
1084             }
1085         }
1086         else
1087         {
1088             AntBuildWriterUtil.writeJavadocTask( writer, project, artifactResolverWrapper );
1089         }
1090 
1091         writer.endElement(); // target
1092 
1093         XmlWriterUtil.writeLineBreak( writer );
1094     }
1095 
1096     /**
1097      * Write package target in the writer depending the packaging of the project.
1098      *
1099      * @param writer
1100      * @throws IOException if any
1101      */
1102     private void writePackageTarget( XMLWriter writer )
1103         throws IOException
1104     {
1105         String synonym = null; // type of the package we are creating (for example jar)
1106         XmlWriterUtil.writeCommentText( writer, "Package target", 1 );
1107 
1108         writer.startElement( "target" );
1109         writer.addAttribute( "name", "package" );
1110 
1111         if ( !AntBuildWriterUtil.isPomPackaging( project ) )
1112         {
1113             writer.addAttribute( "depends", "compile,test" );
1114         }
1115         writer.addAttribute( "description", "Package the application" );
1116 
1117         if ( AntBuildWriterUtil.isPomPackaging( project ) )
1118         {
1119             if ( project.getModules() != null )
1120             {
1121                 for ( Object o : project.getModules() )
1122                 {
1123                     String moduleSubPath = (String) o;
1124                     AntBuildWriterUtil.writeAntTask( writer, project, moduleSubPath, "package" );
1125                 }
1126             }
1127         }
1128         else
1129         {
1130             if ( AntBuildWriterUtil.isJarPackaging( project ) )
1131             {
1132                 AntBuildWriterUtil.writeJarTask( writer, project );
1133                 synonym = "jar";
1134             }
1135             else if ( AntBuildWriterUtil.isEarPackaging( project ) )
1136             {
1137                 AntBuildWriterUtil.writeEarTask( writer, project, artifactResolverWrapper );
1138                 synonym = "ear";
1139             }
1140             else if ( AntBuildWriterUtil.isWarPackaging( project ) )
1141             {
1142                 AntBuildWriterUtil.writeWarTask( writer, project, artifactResolverWrapper );
1143                 synonym = "war";
1144             }
1145             else
1146             {
1147                 writer.startElement( "echo" );
1148                 writer.addAttribute( "message", "No Ant task exists for the packaging '" + project.getPackaging()
1149                     + "'. " + "You could overrided the Ant package target in your build.xml." );
1150                 writer.endElement(); // echo
1151             }
1152         }
1153 
1154         writer.endElement(); // target
1155 
1156         XmlWriterUtil.writeLineBreak( writer );
1157 
1158         if ( synonym != null )
1159         {
1160             // CHECKSTYLE_OFF: LineLength
1161             XmlWriterUtil.writeCommentText( writer, "A dummy target for the package named after the type it creates", 1 );
1162             // CHECKSTYLE_ON: LineLength
1163             writer.startElement( "target" );
1164             writer.addAttribute( "name", synonym );
1165             writer.addAttribute( "depends", "package" );
1166             writer.addAttribute( "description", "Builds the " + synonym + " for the application" );
1167             writer.endElement(); // target
1168 
1169             XmlWriterUtil.writeLineBreak( writer );
1170         }
1171     }
1172 
1173     private void writeCompileTasks( XMLWriter writer, String outputDirectory, List compileSourceRoots, List resources,
1174                                     String additionalClassesDirectory, boolean isTest )
1175         throws IOException
1176     {
1177         writer.startElement( "mkdir" );
1178         writer.addAttribute( "dir", outputDirectory );
1179         writer.endElement(); // mkdir
1180 
1181         // CHECKSTYLE_OFF: LineLength
1182         if ( !compileSourceRoots.isEmpty() )
1183         {
1184             writer.startElement( "javac" );
1185             writer.addAttribute( "destdir", outputDirectory );
1186             Map[] includes = AntBuildWriterUtil.getMavenCompilerPluginOptions( project, "includes", null );
1187             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "includes",
1188                                                  getCommaSeparatedList( includes, "include" ), 3 );
1189             Map[] excludes = AntBuildWriterUtil.getMavenCompilerPluginOptions( project, "excludes", null );
1190             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "excludes",
1191                                                  getCommaSeparatedList( excludes, "exclude" ), 3 );
1192             AntBuildWriterUtil.addWrapAttribute( writer,
1193                                                  "javac",
1194                                                  "encoding",
1195                                                  AntBuildWriterUtil.getMavenCompilerPluginBasicOption( project,
1196                                                                                                        "encoding", null ),
1197                                                  3 );
1198             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "nowarn",
1199                                                  AntBuildWriterUtil.getMavenCompilerPluginBasicOption( project,
1200                                                                                                        "showWarnings",
1201                                                                                                        "false" ), 3 );
1202             AntBuildWriterUtil.addWrapAttribute( writer,
1203                                                  "javac",
1204                                                  "debug",
1205                                                  AntBuildWriterUtil.getMavenCompilerPluginBasicOption( project,
1206                                                                                                        "debug", "true" ),
1207                                                  3 );
1208             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "optimize",
1209                                                  AntBuildWriterUtil.getMavenCompilerPluginBasicOption( project,
1210                                                                                                        "optimize",
1211                                                                                                        "false" ), 3 );
1212             AntBuildWriterUtil.addWrapAttribute( writer,
1213                                                  "javac",
1214                                                  "deprecation",
1215                                                  AntBuildWriterUtil.getMavenCompilerPluginBasicOption( project,
1216                                                                                                        "showDeprecation",
1217                                                                                                        "true" ), 3 );
1218             AntBuildWriterUtil.addWrapAttribute( writer,
1219                                                  "javac",
1220                                                  "target",
1221                                                  AntBuildWriterUtil.getMavenCompilerPluginBasicOption( project,
1222                                                                                                        "target", "1.1" ),
1223                                                  3 );
1224             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "verbose",
1225                                                  AntBuildWriterUtil.getMavenCompilerPluginBasicOption( project,
1226                                                                                                        "verbose",
1227                                                                                                        "false" ), 3 );
1228             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "fork",
1229                                                  AntBuildWriterUtil.getMavenCompilerPluginBasicOption( project, "fork",
1230                                                                                                        "false" ), 3 );
1231             AntBuildWriterUtil.addWrapAttribute( writer, "javac", "memoryMaximumSize",
1232                                                  AntBuildWriterUtil.getMavenCompilerPluginBasicOption( project,
1233                                                                                                        "meminitial",
1234                                                                                                        null ), 3 );
1235             AntBuildWriterUtil.addWrapAttribute( writer,
1236                                                  "javac",
1237                                                  "memoryInitialSize",
1238                                                  AntBuildWriterUtil.getMavenCompilerPluginBasicOption( project,
1239                                                                                                        "maxmem", null ),
1240                                                  3 );
1241             AntBuildWriterUtil.addWrapAttribute( writer,
1242                                                  "javac",
1243                                                  "source",
1244                                                  AntBuildWriterUtil.getMavenCompilerPluginBasicOption( project,
1245                                                                                                        "source", "1.3" ),
1246                                                  3 );
1247 
1248             String[] compileSourceRootsArray =
1249                 (String[]) compileSourceRoots.toArray( new String[compileSourceRoots.size()] );
1250             for ( int i = 0; i < compileSourceRootsArray.length; i++ )
1251             {
1252                 writer.startElement( "src" );
1253                 writer.startElement( "pathelement" );
1254                 if ( isTest )
1255                 {
1256                     writer.addAttribute( "location", "${maven.build.testDir." + i + "}" );
1257                 }
1258                 else
1259                 {
1260                     writer.addAttribute( "location", "${maven.build.srcDir." + i + "}" );
1261                 }
1262                 writer.endElement(); // pathelement
1263                 writer.endElement(); // src
1264             }
1265 
1266             if ( additionalClassesDirectory == null )
1267             {
1268                 writer.startElement( "classpath" );
1269                 if ( isTest )
1270                 {
1271                     writer.addAttribute( "refid", "build.test.classpath" );
1272                 }
1273                 else
1274                 {
1275                     writer.addAttribute( "refid", "build.classpath" );
1276                 }
1277                 writer.endElement(); // classpath
1278             }
1279             else
1280             {
1281                 writer.startElement( "classpath" );
1282                 writer.startElement( "path" );
1283                 if ( isTest )
1284                 {
1285                     writer.addAttribute( "refid", "build.test.classpath" );
1286                 }
1287                 else
1288                 {
1289                     writer.addAttribute( "refid", "build.classpath" );
1290                 }
1291                 writer.endElement(); // path
1292                 writer.startElement( "pathelement" );
1293                 writer.addAttribute( "location", additionalClassesDirectory );
1294                 writer.endElement(); // pathelement
1295                 writer.endElement(); // classpath
1296             }
1297 
1298             writer.endElement(); // javac
1299         }
1300         // CHECKSTYLE_ON: LineLength
1301 
1302         Resource[] array = (Resource[]) resources.toArray( new Resource[resources.size()] );
1303         for ( int i = 0; i < array.length; i++ )
1304         {
1305             Resource resource = array[i];
1306 
1307             if ( new File( resource.getDirectory() ).exists() )
1308             {
1309                 String outputDir = outputDirectory;
1310                 if ( resource.getTargetPath() != null && resource.getTargetPath().length() > 0 )
1311                 {
1312                     outputDir = outputDir + "/" + resource.getTargetPath();
1313 
1314                     writer.startElement( "mkdir" );
1315                     writer.addAttribute( "dir", outputDir );
1316                     writer.endElement(); // mkdir
1317                 }
1318 
1319                 writer.startElement( "copy" );
1320                 writer.addAttribute( "todir", outputDir );
1321 
1322                 writer.startElement( "fileset" );
1323                 if ( isTest )
1324                 {
1325                     writer.addAttribute( "dir", "${maven.build.testResourceDir." + i + "}" );
1326                 }
1327                 else
1328                 {
1329                     writer.addAttribute( "dir", "${maven.build.resourceDir." + i + "}" );
1330                 }
1331 
1332                 AntBuildWriterUtil.writeIncludesExcludes( writer, resource.getIncludes(), resource.getExcludes() );
1333 
1334                 writer.endElement(); // fileset
1335 
1336                 writer.endElement(); // copy
1337             }
1338         }
1339     }
1340 
1341     /**
1342      * Write get-deps target in the writer only for a non-POM project
1343      *
1344      * @param writer
1345      */
1346     private void writeGetDepsTarget( XMLWriter writer )
1347     {
1348         if ( AntBuildWriterUtil.isPomPackaging( project ) )
1349         {
1350             return;
1351         }
1352 
1353         XmlWriterUtil.writeCommentText( writer, "Download dependencies target", 1 );
1354 
1355         writer.startElement( "target" );
1356         writer.addAttribute( "name", "test-offline" );
1357 
1358         writer.startElement( "condition" );
1359         writer.addAttribute( "property", "maven.mode.offline" );
1360         writer.startElement( "equals" );
1361         writer.addAttribute( "arg1", "${maven.settings.offline}" );
1362         writer.addAttribute( "arg2", "true" );
1363         writer.endElement(); // equals
1364         writer.endElement(); // condition
1365         writer.endElement(); // target
1366 
1367         XmlWriterUtil.writeLineBreak( writer, 2, 1 );
1368 
1369         writer.startElement( "target" );
1370         writer.addAttribute( "name", "get-deps" );
1371         AntBuildWriterUtil.addWrapAttribute( writer, "target", "depends", "test-offline", 2 );
1372         AntBuildWriterUtil.addWrapAttribute( writer, "target", "description", "Download all dependencies", 2 );
1373         AntBuildWriterUtil.addWrapAttribute( writer, "target", "unless", "maven.mode.offline", 2 ); // TODO: check, and
1374                                                                                                     // differs from m1
1375 
1376         writer.startElement( "mkdir" );
1377         writer.addAttribute( "dir", "${maven.repo.local}" );
1378         writer.endElement(); // mkdir
1379 
1380         String basedir = project.getBasedir().getAbsolutePath();
1381 
1382         // CHECKSTYLE_OFF: LineLength
1383         // TODO: proxy - probably better to use wagon!
1384         for ( Object o : project.getTestArtifacts() )
1385         {
1386             Artifact artifact = (Artifact) o;
1387 
1388             if ( Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) )
1389             {
1390                 continue;
1391             }
1392 
1393             String path = artifactResolverWrapper.getLocalArtifactPath( artifact );
1394 
1395             if ( !new File( path ).exists() )
1396             {
1397                 File parentDirs = new File( path ).getParentFile();
1398                 if ( parentDirs != null )
1399                 {
1400                     writer.startElement( "mkdir" );
1401                     // Replace \ with / in the parent dir path
1402                     writer.addAttribute( "dir", "${maven.repo.local}/" + parentDirs.getPath().replace( '\\', '/' ) );
1403                     writer.endElement(); // mkdir
1404                 }
1405 
1406                 for ( Object o1 : project.getRepositories() )
1407                 {
1408                     Repository repository = (Repository) o1;
1409                     String url = repository.getUrl();
1410 
1411                     String localDir = getProjectRepoDirectory( url, basedir );
1412                     if ( localDir != null )
1413                     {
1414                         if ( localDir.length() > 0 && !localDir.endsWith( "/" ) )
1415                         {
1416                             localDir += '/';
1417                         }
1418 
1419                         writer.startElement( "copy" );
1420                         writer.addAttribute( "file", localDir + path );
1421                         AntBuildWriterUtil.addWrapAttribute( writer, "copy", "tofile", "${maven.repo.local}/" + path, 3 );
1422                         AntBuildWriterUtil.addWrapAttribute( writer, "copy", "failonerror", "false", 3 );
1423                         writer.endElement(); // copy
1424                     }
1425                     else
1426                     {
1427                         writer.startElement( "get" );
1428                         writer.addAttribute( "src", url + '/' + path );
1429                         AntBuildWriterUtil.addWrapAttribute( writer, "get", "dest", "${maven.repo.local}/" + path, 3 );
1430                         AntBuildWriterUtil.addWrapAttribute( writer, "get", "usetimestamp", "false", 3 );
1431                         AntBuildWriterUtil.addWrapAttribute( writer, "get", "ignoreerrors", "true", 3 );
1432                         writer.endElement(); // get
1433                     }
1434                 }
1435             }
1436         }
1437         // CHECKSTYLE_ON: LineLength
1438 
1439         writer.endElement(); // target
1440 
1441         XmlWriterUtil.writeLineBreak( writer );
1442     }
1443 
1444     /**
1445      * Gets the relative path to a repository that is rooted in the project. The returned path (if any) will always use
1446      * the forward slash ('/') as the directory separator. For example, the path "target/it-repo" will be returned for a
1447      * repository constructed from the URL "file://${basedir}/target/it-repo".
1448      *
1449      * @param repoUrl The URL to the repository, must not be <code>null</code>.
1450      * @param projectDir The absolute path to the base directory of the project, must not be <code>null</code>
1451      * @return The path to the repository (relative to the project base directory) or <code>null</code> if the
1452      *         repository is not rooted in the project.
1453      */
1454     static String getProjectRepoDirectory( String repoUrl, String projectDir )
1455     {
1456         try
1457         {
1458             /*
1459              * NOTE: The usual way of constructing repo URLs rooted in the project is "file://${basedir}" or
1460              * "file:/${basedir}". None of these forms delivers a valid URL on both Unix and Windows (even ignoring URL
1461              * encoding), one platform will end up with the first directory of the path being interpreted as the host
1462              * name...
1463              */
1464             if ( repoUrl.regionMatches( true, 0, "file://", 0, 7 ) )
1465             {
1466                 String temp = repoUrl.substring( 7 );
1467                 if ( !temp.startsWith( "/" ) && !temp.regionMatches( true, 0, "localhost/", 0, 10 ) )
1468                 {
1469                     repoUrl = "file:///" + temp;
1470                 }
1471             }
1472             String path = FileUtils.toFile( new URL( repoUrl ) ).getPath();
1473             if ( path.startsWith( projectDir ) )
1474             {
1475                 path = path.substring( projectDir.length() ).replace( '\\', '/' );
1476                 if ( path.startsWith( "/" ) )
1477                 {
1478                     path = path.substring( 1 );
1479                 }
1480                 if ( path.endsWith( "/" ) )
1481                 {
1482                     path = path.substring( 0, path.length() - 1 );
1483                 }
1484                 return path;
1485             }
1486         }
1487         catch ( Exception e )
1488         {
1489             // not a "file:" URL or simply malformed
1490         }
1491         return null;
1492     }
1493 
1494     // ----------------------------------------------------------------------
1495     // Convenience methods
1496     // ----------------------------------------------------------------------
1497 
1498     /**
1499      * Put a property in properties defined by a name and a value
1500      *
1501      * @param properties not null
1502      * @param name
1503      * @param value not null
1504      */
1505     private static void addProperty( Properties properties, String name, String value )
1506     {
1507         properties.put( name, StringUtils.isNotEmpty( value ) ? value : "" );
1508     }
1509 
1510     /**
1511      * @param includes an array of includes or exludes map
1512      * @param key a key wanted in the map, like <code>include</code> or <code>exclude</code>
1513      * @return a String with comma-separated value of a key in each map
1514      */
1515     private static String getCommaSeparatedList( Map[] includes, String key )
1516     {
1517         if ( ( includes == null ) || ( includes.length == 0 ) )
1518         {
1519             return null;
1520         }
1521 
1522         StringBuilder sb = new StringBuilder();
1523         for ( int i = 0; i < includes.length; i++ )
1524         {
1525             String s = (String) includes[i].get( key );
1526             if ( StringUtils.isEmpty( s ) )
1527             {
1528                 continue;
1529             }
1530 
1531             sb.append( s );
1532 
1533             if ( i < ( includes.length - 1 ) )
1534             {
1535                 sb.append( "," );
1536             }
1537         }
1538 
1539         if ( sb.length() == 0 )
1540         {
1541             return null;
1542         }
1543 
1544         return sb.toString();
1545     }
1546 
1547     /**
1548      * Flattens the specified file selector options into a simple string list. For instance, the input
1549      * <p/>
1550      * 
1551      * <pre>
1552      * [ {include=&quot;*Test.java&quot;}, {include=&quot;*TestCase.java&quot;} ]
1553      * </pre>
1554      * <p/>
1555      * is converted to
1556      * <p/>
1557      * 
1558      * <pre>
1559      * [ &quot;*Test.java&quot;, &quot;*TestCase.java&quot; ]
1560      * </pre>
1561      *
1562      * @param options The file selector options to flatten, may be <code>null</code>.
1563      * @return The string list, might be empty but never <code>null</code>.
1564      */
1565     private static List getSelectorList( Map[] options )
1566     {
1567         List list = new ArrayList();
1568         if ( options != null && options.length > 0 )
1569         {
1570             for ( Map option : options )
1571             {
1572                 list.addAll( option.values() );
1573             }
1574         }
1575         return list;
1576     }
1577 
1578 }