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