OSDN Git Service

165ea08f64a3e87a337ba5a833c437cba4d1f942
[android-x86/sdk.git] / anttasks / src / com / android / ant / NewSetupTask.java
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.ant;
18
19 import com.android.io.FileWrapper;
20 import com.android.io.FolderWrapper;
21 import com.android.sdklib.AndroidVersion;
22 import com.android.sdklib.IAndroidTarget;
23 import com.android.sdklib.ISdkLog;
24 import com.android.sdklib.SdkConstants;
25 import com.android.sdklib.SdkManager;
26 import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
27 import com.android.sdklib.internal.project.ProjectProperties;
28 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
29 import com.android.sdklib.xml.AndroidManifest;
30 import com.android.sdklib.xml.AndroidXPathFactory;
31
32 import org.apache.tools.ant.BuildException;
33 import org.apache.tools.ant.Project;
34 import org.apache.tools.ant.Task;
35 import org.apache.tools.ant.types.Path;
36 import org.apache.tools.ant.types.Path.PathElement;
37 import org.apache.tools.ant.util.DeweyDecimal;
38 import org.xml.sax.InputSource;
39
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.io.FileNotFoundException;
43 import java.io.FilenameFilter;
44 import java.io.IOException;
45 import java.util.ArrayList;
46 import java.util.HashSet;
47 import java.util.List;
48
49 import javax.xml.xpath.XPath;
50 import javax.xml.xpath.XPathExpressionException;
51
52 /**
53  * Setup Ant task. This task accomplishes:
54  * <ul>
55  * <li>Gets the project target hash string from {@link ProjectProperties#PROPERTY_TARGET},
56  * and resolves it to get the project's {@link IAndroidTarget}.</li>
57  *
58  * <li>Sets up properties so that aapt can find the android.jar and other files/folders in
59  * the resolved target.</li>
60  *
61  * <li>Sets up the boot classpath ref so that the <code>javac</code> task knows where to find
62  * the libraries. This includes the default android.jar from the resolved target but also optional
63  * libraries provided by the target (if any, when the target is an add-on).</li>
64  *
65  * <li>Resolve library dependencies and setup various Path references for them</li>
66  * </ul>
67  *
68  * This is used in the main rules file only.
69  *
70  */
71 public class NewSetupTask extends Task {
72     private final static String ANT_MIN_VERSION = "1.8.0";
73
74     private String mProjectTypeOut;
75     private String mAndroidJarFileOut;
76     private String mAndroidAidlFileOut;
77     private String mRenderScriptExeOut;
78     private String mRenderScriptIncludeDirOut;
79     private String mBootclasspathrefOut;
80     private String mProjectLibrariesRootOut;
81     private String mProjectLibrariesResOut;
82     private String mProjectLibrariesPackageOut;
83     private String mProjectLibrariesJarsOut;
84     private String mProjectLibrariesLibsOut;
85     private String mTargetApiOut;
86
87     public void setProjectTypeOut(String projectTypeOut) {
88         mProjectTypeOut = projectTypeOut;
89     }
90
91     public void setAndroidJarFileOut(String androidJarFileOut) {
92         mAndroidJarFileOut = androidJarFileOut;
93     }
94
95     public void setAndroidAidlFileOut(String androidAidlFileOut) {
96         mAndroidAidlFileOut = androidAidlFileOut;
97     }
98
99     public void setRenderScriptExeOut(String renderScriptExeOut) {
100         mRenderScriptExeOut = renderScriptExeOut;
101     }
102
103     public void setRenderScriptIncludeDirOut(String renderScriptIncludeDirOut) {
104         mRenderScriptIncludeDirOut = renderScriptIncludeDirOut;
105     }
106
107     public void setBootclasspathrefOut(String bootclasspathrefOut) {
108         mBootclasspathrefOut = bootclasspathrefOut;
109     }
110
111     public void setProjectLibrariesRootOut(String projectLibrariesRootOut) {
112         mProjectLibrariesRootOut = projectLibrariesRootOut;
113     }
114
115     public void setProjectLibrariesResOut(String projectLibrariesResOut) {
116         mProjectLibrariesResOut = projectLibrariesResOut;
117     }
118
119     public void setProjectLibrariesPackageOut(String projectLibrariesPackageOut) {
120         mProjectLibrariesPackageOut = projectLibrariesPackageOut;
121     }
122
123     public void setProjectLibrariesJarsOut(String projectLibrariesJarsOut) {
124         mProjectLibrariesJarsOut = projectLibrariesJarsOut;
125     }
126
127     public void setProjectLibrariesLibsOut(String projectLibrariesLibsOut) {
128         mProjectLibrariesLibsOut = projectLibrariesLibsOut;
129     }
130
131     public void setTargetApiOut(String targetApiOut) {
132         mTargetApiOut = targetApiOut;
133     }
134
135     @Override
136     public void execute() throws BuildException {
137         if (mProjectTypeOut == null) {
138             throw new BuildException("Missing attribute projectTypeOut");
139         }
140         if (mAndroidJarFileOut == null) {
141             throw new BuildException("Missing attribute androidJarFileOut");
142         }
143         if (mAndroidAidlFileOut == null) {
144             throw new BuildException("Missing attribute androidAidlFileOut");
145         }
146         if (mRenderScriptExeOut == null) {
147             throw new BuildException("Missing attribute renderScriptExeOut");
148         }
149         if (mRenderScriptIncludeDirOut == null) {
150             throw new BuildException("Missing attribute renderScriptIncludeDirOut");
151         }
152         if (mBootclasspathrefOut == null) {
153             throw new BuildException("Missing attribute bootclasspathrefOut");
154         }
155         if (mProjectLibrariesRootOut == null) {
156             throw new BuildException("Missing attribute projectLibrariesRootOut");
157         }
158         if (mProjectLibrariesResOut == null) {
159             throw new BuildException("Missing attribute projectLibrariesResOut");
160         }
161         if (mProjectLibrariesPackageOut == null) {
162             throw new BuildException("Missing attribute projectLibrariesPackageOut");
163         }
164         if (mProjectLibrariesJarsOut == null) {
165             throw new BuildException("Missing attribute projectLibrariesJarsOut");
166         }
167         if (mProjectLibrariesLibsOut == null) {
168             throw new BuildException("Missing attribute projectLibrariesLibsOut");
169         }
170         if (mTargetApiOut == null) {
171             throw new BuildException("Missing attribute targetApiOut");
172         }
173
174
175         Project antProject = getProject();
176
177         // check the Ant version
178         DeweyDecimal version = getVersion(antProject);
179         DeweyDecimal atLeast = new DeweyDecimal(ANT_MIN_VERSION);
180         if (atLeast.isGreaterThan(version)) {
181             throw new BuildException(
182                     "The Android Ant-based build system requires Ant " +
183                     ANT_MIN_VERSION +
184                     " or later. Current version is " +
185                     version);
186         }
187
188         // get the SDK location
189         File sdkDir = TaskHelper.getSdkLocation(antProject);
190         String sdkOsPath = sdkDir.getPath();
191
192         // Make sure the OS sdk path ends with a directory separator
193         if (sdkOsPath.length() > 0 && !sdkOsPath.endsWith(File.separator)) {
194             sdkOsPath += File.separator;
195         }
196
197         // display SDK Tools revision
198         int toolsRevison = TaskHelper.getToolsRevision(sdkDir);
199         if (toolsRevison != -1) {
200             System.out.println("Android SDK Tools Revision " + toolsRevison);
201         }
202
203         // detect that the platform tools is there.
204         File platformTools = new File(sdkDir, SdkConstants.FD_PLATFORM_TOOLS);
205         if (platformTools.isDirectory() == false) {
206             throw new BuildException(String.format(
207                     "SDK Platform Tools component is missing. " +
208                     "Please install it with the SDK Manager (%1$s%2$c%3$s)",
209                     SdkConstants.FD_TOOLS,
210                     File.separatorChar,
211                     SdkConstants.androidCmdName()));
212         }
213
214         // get the target property value
215         String targetHashString = antProject.getProperty(ProjectProperties.PROPERTY_TARGET);
216
217         boolean isTestProject = false;
218
219         if (antProject.getProperty(ProjectProperties.PROPERTY_TESTED_PROJECT) != null) {
220             isTestProject = true;
221         }
222
223         if (targetHashString == null) {
224             throw new BuildException("Android Target is not set.");
225         }
226
227         // load up the sdk targets.
228         final ArrayList<String> messages = new ArrayList<String>();
229         SdkManager manager = SdkManager.createManager(sdkOsPath, new ISdkLog() {
230             public void error(Throwable t, String errorFormat, Object... args) {
231                 if (errorFormat != null) {
232                     messages.add(String.format("Error: " + errorFormat, args));
233                 }
234                 if (t != null) {
235                     messages.add("Error: " + t.getMessage());
236                 }
237             }
238
239             public void printf(String msgFormat, Object... args) {
240                 messages.add(String.format(msgFormat, args));
241             }
242
243             public void warning(String warningFormat, Object... args) {
244                 messages.add(String.format("Warning: " + warningFormat, args));
245             }
246         });
247
248         if (manager == null) {
249             // since we failed to parse the SDK, lets display the parsing output.
250             for (String msg : messages) {
251                 System.out.println(msg);
252             }
253             throw new BuildException("Failed to parse SDK content.");
254         }
255
256         // resolve it
257         IAndroidTarget androidTarget = manager.getTargetFromHashString(targetHashString);
258
259         if (androidTarget == null) {
260             throw new BuildException(String.format(
261                     "Unable to resolve target '%s'", targetHashString));
262         }
263
264         // display the project info
265         System.out.println("Project Target: " + androidTarget.getName());
266         if (androidTarget.isPlatform() == false) {
267             System.out.println("Vendor: " + androidTarget.getVendor());
268             System.out.println("Platform Version: " + androidTarget.getVersionName());
269         }
270         System.out.println("API level: " + androidTarget.getVersion().getApiString());
271
272         // check if the project is a library
273         boolean isLibrary = false;
274
275         String libraryProp = antProject.getProperty(ProjectProperties.PROPERTY_LIBRARY);
276         if (libraryProp != null) {
277             isLibrary = Boolean.valueOf(libraryProp).booleanValue();
278         }
279
280         if (isLibrary) {
281             System.out.println("Project Type: Android Library");
282         }
283
284         // look for referenced libraries.
285         processReferencedLibraries(antProject, androidTarget);
286
287         // always check the manifest minSdkVersion.
288         checkManifest(antProject, androidTarget.getVersion());
289
290         // sets up the properties to find android.jar/framework.aidl/target tools
291         String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR);
292         antProject.setProperty(mAndroidJarFileOut, androidJar);
293
294         String androidAidl = androidTarget.getPath(IAndroidTarget.ANDROID_AIDL);
295         antProject.setProperty(mAndroidAidlFileOut, androidAidl);
296
297         Path includePath = new Path(antProject);
298         PathElement element = includePath.createPathElement();
299         element.setPath(androidTarget.getPath(IAndroidTarget.ANDROID_RS));
300         element = includePath.createPathElement();
301         element.setPath(androidTarget.getPath(IAndroidTarget.ANDROID_RS_CLANG));
302         antProject.setProperty(mRenderScriptIncludeDirOut, includePath.toString());
303
304         // TODO: figure out the actual compiler to use based on the minSdkVersion
305         antProject.setProperty(mRenderScriptExeOut,
306                 sdkOsPath + SdkConstants.OS_SDK_PLATFORM_TOOLS_FOLDER +
307                 SdkConstants.FN_RENDERSCRIPT);
308
309         // sets up the boot classpath
310
311         // create the Path object
312         Path bootclasspath = new Path(antProject);
313
314         // create a PathElement for the framework jar
315         element = bootclasspath.createPathElement();
316         element.setPath(androidJar);
317
318         // create PathElement for each optional library.
319         IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries();
320         if (libraries != null) {
321             HashSet<String> visitedJars = new HashSet<String>();
322             for (IOptionalLibrary library : libraries) {
323                 String jarPath = library.getJarPath();
324                 if (visitedJars.contains(jarPath) == false) {
325                     visitedJars.add(jarPath);
326
327                     element = bootclasspath.createPathElement();
328                     element.setPath(library.getJarPath());
329                 }
330             }
331         }
332
333         // sets the path in the project with a reference
334         antProject.addReference(mBootclasspathrefOut, bootclasspath);
335
336         // finally set the project type.
337         if (isLibrary) {
338             antProject.setProperty(mProjectTypeOut, "library");
339         } else if (isTestProject) {
340             antProject.setProperty(mProjectTypeOut, "test");
341         } else {
342             antProject.setProperty(mProjectTypeOut, "project");
343         }
344     }
345
346     /**
347      * Checks the manifest <code>minSdkVersion</code> attribute.
348      * @param antProject the ant project
349      * @param androidVersion the version of the platform the project is compiling against.
350      */
351     private void checkManifest(Project antProject, AndroidVersion androidVersion) {
352         try {
353             File manifest = new File(antProject.getBaseDir(), SdkConstants.FN_ANDROID_MANIFEST_XML);
354
355             XPath xPath = AndroidXPathFactory.newXPath();
356
357             // check the package name.
358             String value = xPath.evaluate(
359                     "/"  + AndroidManifest.NODE_MANIFEST +
360                     "/@" + AndroidManifest.ATTRIBUTE_PACKAGE,
361                     new InputSource(new FileInputStream(manifest)));
362             if (value != null) { // aapt will complain if it's missing.
363                 // only need to check that the package has 2 segments
364                 if (value.indexOf('.') == -1) {
365                     throw new BuildException(String.format(
366                             "Application package '%1$s' must have a minimum of 2 segments.",
367                             value));
368                 }
369             }
370
371             // check the minSdkVersion value
372             value = xPath.evaluate(
373                     "/"  + AndroidManifest.NODE_MANIFEST +
374                     "/"  + AndroidManifest.NODE_USES_SDK +
375                     "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + ":" +
376                             AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
377                     new InputSource(new FileInputStream(manifest)));
378
379             if (androidVersion.isPreview()) {
380                 // in preview mode, the content of the minSdkVersion must match exactly the
381                 // platform codename.
382                 String codeName = androidVersion.getCodename();
383                 if (codeName.equals(value) == false) {
384                     throw new BuildException(String.format(
385                             "For '%1$s' SDK Preview, attribute minSdkVersion in AndroidManifest.xml must be '%1$s' (current: %2$s)",
386                             codeName, value));
387                 }
388
389                 // set the API level to the previous API level (which is actually the value in
390                 // androidVersion.)
391                 antProject.setProperty(mTargetApiOut,
392                         Integer.toString(androidVersion.getApiLevel()));
393
394             } else if (value.length() > 0) {
395                 // for normal platform, we'll only display warnings if the value is lower or higher
396                 // than the target api level.
397                 // First convert to an int.
398                 int minSdkValue = -1;
399                 try {
400                     minSdkValue = Integer.parseInt(value);
401                 } catch (NumberFormatException e) {
402                     // looks like it's not a number: error!
403                     throw new BuildException(String.format(
404                             "Attribute %1$s in AndroidManifest.xml must be an Integer!",
405                             AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION));
406                 }
407
408                 // set the target api to the value
409                 antProject.setProperty(mTargetApiOut, value);
410
411                 int projectApiLevel = androidVersion.getApiLevel();
412                 if (minSdkValue < projectApiLevel) {
413                     System.out.println(String.format(
414                             "WARNING: Attribute %1$s in AndroidManifest.xml (%2$d) is lower than the project target API level (%3$d)",
415                             AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
416                             minSdkValue, projectApiLevel));
417                 } else if (minSdkValue > androidVersion.getApiLevel()) {
418                     System.out.println(String.format(
419                             "WARNING: Attribute %1$s in AndroidManifest.xml (%2$d) is higher than the project target API level (%3$d)",
420                             AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
421                             minSdkValue, projectApiLevel));
422                 }
423             } else {
424                 // no minSdkVersion? display a warning
425                 System.out.println(
426                         "WARNING: No minSdkVersion value set. Application will install on all Android versions.");
427
428                 // set the target api to 1
429                 antProject.setProperty(mTargetApiOut, "1");
430             }
431
432         } catch (XPathExpressionException e) {
433             throw new BuildException(e);
434         } catch (FileNotFoundException e) {
435             throw new BuildException(e);
436         }
437     }
438
439     private void processReferencedLibraries(Project antProject, IAndroidTarget androidTarget) {
440         // prepare several paths for future tasks
441         Path rootPath = new Path(antProject);
442         Path resPath = new Path(antProject);
443         Path libsPath = new Path(antProject);
444         Path jarsPath = new Path(antProject);
445         StringBuilder packageStrBuilder = new StringBuilder();
446
447         FilenameFilter filter = new FilenameFilter() {
448             public boolean accept(File dir, String name) {
449                 return name.toLowerCase().endsWith(".jar");
450             }
451         };
452
453         System.out.println("\n------------------\nResolving library dependencies:");
454
455         ArrayList<File> libraries = getProjectLibraries(antProject);
456
457         if (libraries.size() > 0) {
458             System.out.println("------------------\nOrdered libraries:");
459
460             for (File library : libraries) {
461                 String libRootPath = library.getAbsolutePath();
462                 System.out.println(libRootPath);
463
464                 // get the root path.
465                 PathElement element = rootPath.createPathElement();
466                 element.setPath(libRootPath);
467
468                 // get the res path. Always $PROJECT/res as well as the crunch cache.
469                 element = resPath.createPathElement();
470                 element.setPath(libRootPath + "/" + SdkConstants.FD_OUTPUT +
471                         "/" + SdkConstants.FD_RES);
472                 element = resPath.createPathElement();
473                 element.setPath(libRootPath + "/" + SdkConstants.FD_RESOURCES);
474
475                 // get the libs path. Always $PROJECT/libs
476                 element = libsPath.createPathElement();
477                 element.setPath(libRootPath + "/" + SdkConstants.FD_NATIVE_LIBS);
478
479                 // get the jars from it too.
480                 // 1. the library code jar
481                 element = jarsPath.createPathElement();
482                 element.setPath(libRootPath + "/" + SdkConstants.FD_OUTPUT +
483                         "/" + SdkConstants.FN_CLASSES_JAR);
484
485                 // 2. the 3rd party jar files
486                 File libsFolder = new File(library, SdkConstants.FD_NATIVE_LIBS);
487                 File[] jarFiles = libsFolder.listFiles(filter);
488                 if (jarFiles != null) {
489                     for (File jarFile : jarFiles) {
490                         element = jarsPath.createPathElement();
491                         element.setPath(jarFile.getAbsolutePath());
492                     }
493                 }
494
495                 // get the package from the manifest.
496                 FileWrapper manifest = new FileWrapper(library,
497                         SdkConstants.FN_ANDROID_MANIFEST_XML);
498
499                 try {
500                     String value = AndroidManifest.getPackage(manifest);
501                     if (value != null) { // aapt will complain if it's missing.
502                         packageStrBuilder.append(';');
503                         packageStrBuilder.append(value);
504                     }
505                 } catch (Exception e) {
506                     throw new BuildException(e);
507                 }
508             }
509         } else {
510             System.out.println("No library dependencies.\n");
511         }
512
513         System.out.println("------------------\n");
514
515         // even with no libraries, always setup these so that various tasks in Ant don't complain
516         // (the task themselves can handle a ref to an empty Path)
517         antProject.addReference(mProjectLibrariesJarsOut, jarsPath);
518         antProject.addReference(mProjectLibrariesLibsOut, libsPath);
519
520         // the rest is done only if there's a library.
521         if (jarsPath.list().length > 0) {
522             antProject.addReference(mProjectLibrariesRootOut, rootPath);
523             antProject.addReference(mProjectLibrariesResOut, resPath);
524             antProject.setProperty(mProjectLibrariesPackageOut, packageStrBuilder.toString());
525         }
526     }
527
528     /**
529      * Returns all the library dependencies of a given Ant project.
530      * @param antProject the Ant project
531      * @return a list of properties, sorted from highest priority to lowest.
532      */
533     private ArrayList<File> getProjectLibraries(final Project antProject) {
534         ArrayList<File> libraries = new ArrayList<File>();
535         File baseDir = antProject.getBaseDir();
536
537         // get the top level list of library dependencies.
538         List<File> topLevelLibraries = getDirectDependencies(baseDir, new IPropertySource() {
539             public String getProperty(String name) {
540                 return antProject.getProperty(name);
541             }
542         });
543
544         // process the libraries in case they depend on other libraries.
545         resolveFullLibraryDependencies(topLevelLibraries, libraries);
546
547         return libraries;
548     }
549
550     /**
551      * Resolves a given list of libraries, finds out if they depend on other libraries, and
552      * returns a full list of all the direct and indirect dependencies in the proper order (first
553      * is higher priority when calling aapt).
554      * @param inLibraries the libraries to resolve
555      * @param outLibraries where to store all the libraries.
556      */
557     private void resolveFullLibraryDependencies(List<File> inLibraries, List<File> outLibraries) {
558         // loop in the inverse order to resolve dependencies on the libraries, so that if a library
559         // is required by two higher level libraries it can be inserted in the correct place
560         for (int i = inLibraries.size() - 1  ; i >= 0 ; i--) {
561             File library = inLibraries.get(i);
562
563             // get the default.property file for it
564             final ProjectProperties projectProp = ProjectProperties.load(
565                     new FolderWrapper(library), PropertyType.PROJECT);
566
567             // get its libraries
568             List<File> dependencies = getDirectDependencies(library, new IPropertySource() {
569                 public String getProperty(String name) {
570                     return projectProp.getProperty(name);
571                 }
572             });
573
574             // resolve the dependencies for those libraries
575             resolveFullLibraryDependencies(dependencies, outLibraries);
576
577             // and add the current one (if needed) in front (higher priority)
578             if (outLibraries.contains(library) == false) {
579                 outLibraries.add(0, library);
580             }
581         }
582     }
583
584     public interface IPropertySource {
585         String getProperty(String name);
586     }
587
588     /**
589      * Returns the top level library dependencies of a given <var>source</var> representing a
590      * project properties.
591      * @param baseFolder the base folder of the project (to resolve relative paths)
592      * @param source a source of project properties.
593      */
594     private List<File> getDirectDependencies(File baseFolder, IPropertySource source) {
595         ArrayList<File> libraries = new ArrayList<File>();
596
597         // first build the list. they are ordered highest priority first.
598         int index = 1;
599         while (true) {
600             String propName = ProjectProperties.PROPERTY_LIB_REF + Integer.toString(index++);
601             String rootPath = source.getProperty(propName);
602
603             if (rootPath == null) {
604                 break;
605             }
606
607             try {
608                 File library = new File(baseFolder, rootPath).getCanonicalFile();
609
610                 // check for validity
611                 File projectProp = new File(library, PropertyType.PROJECT.getFilename());
612                 if (projectProp.isFile() == false) {
613                     // error!
614                     throw new BuildException(String.format(
615                             "%1$s resolve to a path with no %2$s file for project %3$s", rootPath,
616                             PropertyType.PROJECT.getFilename(), baseFolder.getAbsolutePath()));
617                 }
618
619                 if (libraries.contains(library) == false) {
620                     System.out.println(String.format("%1$s: %2$s => %3$s",
621                             baseFolder.getAbsolutePath(), rootPath, library.getAbsolutePath()));
622
623                     libraries.add(library);
624                 }
625             } catch (IOException e) {
626                 throw new BuildException("Failed to resolve library path: " + rootPath, e);
627             }
628         }
629
630         return libraries;
631     }
632
633     /**
634      * Returns the Ant version as a {@link DeweyDecimal} object.
635      *
636      * This is based on the implementation of
637      * org.apache.tools.ant.taskdefs.condition.AntVersion.getVersion()
638      *
639      * @param antProject the current ant project.
640      * @return the ant version.
641      */
642     private DeweyDecimal getVersion(Project antProject) {
643         char[] versionString = antProject.getProperty("ant.version").toCharArray();
644         StringBuilder sb = new StringBuilder();
645         boolean foundFirstDigit = false;
646         for (int i = 0; i < versionString.length; i++) {
647             if (Character.isDigit(versionString[i])) {
648                 sb.append(versionString[i]);
649                 foundFirstDigit = true;
650             }
651             if (versionString[i] == '.' && foundFirstDigit) {
652                 sb.append(versionString[i]);
653             }
654             if (Character.isLetter(versionString[i]) && foundFirstDigit) {
655                 break;
656             }
657         }
658         return new DeweyDecimal(sb.toString());
659     }
660 }