• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.sdklib.IAndroidTarget;
20 import com.android.sdklib.ISdkLog;
21 import com.android.sdklib.SdkManager;
22 import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
23 import com.android.sdklib.internal.project.ProjectProperties;
24 import com.android.sdklib.xml.AndroidXPathFactory;
25 import com.android.sdklib.xml.ManifestConstants;
26 
27 import org.apache.tools.ant.BuildException;
28 import org.apache.tools.ant.Project;
29 import org.apache.tools.ant.taskdefs.ImportTask;
30 import org.apache.tools.ant.types.Path;
31 import org.apache.tools.ant.types.Path.PathElement;
32 import org.xml.sax.InputSource;
33 
34 import java.io.File;
35 import java.io.FileInputStream;
36 import java.io.FileNotFoundException;
37 import java.util.ArrayList;
38 import java.util.HashSet;
39 
40 import javax.xml.xpath.XPath;
41 import javax.xml.xpath.XPathExpressionException;
42 
43 /**
44  * Setup/Import Ant task. This task accomplishes:
45  * <ul>
46  * <li>Gets the project target hash string from {@link ProjectProperties#PROPERTY_TARGET},
47  * and resolves it to get the project's {@link IAndroidTarget}.</li>
48  * <li>Sets up properties so that aapt can find the android.jar in the resolved target.</li>
49  * <li>Sets up the boot classpath ref so that the <code>javac</code> task knows where to find
50  * the libraries. This includes the default android.jar from the resolved target but also optional
51  * libraries provided by the target (if any, when the target is an add-on).</li>
52  * <li>Imports the build rules located in the resolved target so that the build actually does
53  * something. This can be disabled with the attribute <var>import</var> set to <code>false</code>
54  * </li></ul>
55  *
56  * This is used in build.xml/template.
57  *
58  */
59 public final class SetupTask extends ImportTask {
60     private final static String ANDROID_RULES = "android_rules.xml";
61 
62     // ant property with the path to the android.jar
63     private final static String PROPERTY_ANDROID_JAR = "android-jar";
64     // ant property with the path to the framework.jar
65     private final static String PROPERTY_ANDROID_AIDL = "android-aidl";
66     // ant property with the path to the aapt tool
67     private final static String PROPERTY_AAPT = "aapt";
68     // ant property with the path to the aidl tool
69     private final static String PROPERTY_AIDL = "aidl";
70     // ant property with the path to the dx tool
71     private final static String PROPERTY_DX = "dx";
72     // ref id to the <path> object containing all the boot classpaths.
73     private final static String REF_CLASSPATH = "android.target.classpath";
74 
75     private boolean mDoImport = true;
76 
77     @Override
execute()78     public void execute() throws BuildException {
79         Project antProject = getProject();
80 
81         // get the SDK location
82         String sdkLocation = antProject.getProperty(ProjectProperties.PROPERTY_SDK);
83 
84         // check if it's valid and exists
85         if (sdkLocation == null || sdkLocation.length() == 0) {
86             throw new BuildException("SDK Location is not set.");
87         }
88 
89         File sdk = new File(sdkLocation);
90         if (sdk.isDirectory() == false) {
91             throw new BuildException(String.format("SDK Location '%s' is not valid.", sdkLocation));
92         }
93 
94         // get the target property value
95         String targetHashString = antProject.getProperty(ProjectProperties.PROPERTY_TARGET);
96         if (targetHashString == null) {
97             throw new BuildException("Android Target is not set.");
98         }
99 
100         // load up the sdk targets.
101         final ArrayList<String> messages = new ArrayList<String>();
102         SdkManager manager = SdkManager.createManager(sdkLocation, new ISdkLog() {
103             public void error(Throwable t, String errorFormat, Object... args) {
104                 if (errorFormat != null) {
105                     messages.add(String.format("Error: " + errorFormat, args));
106                 }
107                 if (t != null) {
108                     messages.add("Error: " + t.getMessage());
109                 }
110             }
111 
112             public void printf(String msgFormat, Object... args) {
113                 messages.add(String.format(msgFormat, args));
114             }
115 
116             public void warning(String warningFormat, Object... args) {
117                 messages.add(String.format("Warning: " + warningFormat, args));
118             }
119         });
120 
121         if (manager == null) {
122             // since we failed to parse the SDK, lets display the parsing output.
123             for (String msg : messages) {
124                 System.out.println(msg);
125             }
126             throw new BuildException("Failed to parse SDK content.");
127         }
128 
129         // resolve it
130         IAndroidTarget androidTarget = manager.getTargetFromHashString(targetHashString);
131 
132         if (androidTarget == null) {
133             throw new BuildException(String.format(
134                     "Unable to resolve target '%s'", targetHashString));
135         }
136 
137         // display it
138         System.out.println("Project Target: " + androidTarget.getName());
139         if (androidTarget.isPlatform() == false) {
140             System.out.println("Vendor: " + androidTarget.getVendor());
141             System.out.println("Platform Version: " + androidTarget.getVersionName());
142         }
143         System.out.println("API level: " + androidTarget.getVersion().getApiString());
144 
145         // if needed check the manifest so that it matches the target
146         if (androidTarget.getVersion().isPreview()) {
147             // for preview, the manifest minSdkVersion node *must* match the target codename
148             checkManifest(antProject, androidTarget.getVersion().getCodename());
149         }
150 
151         // sets up the properties to find android.jar/framework.aidl/target tools
152         String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR);
153         antProject.setProperty(PROPERTY_ANDROID_JAR, androidJar);
154 
155         antProject.setProperty(PROPERTY_ANDROID_AIDL,
156                 androidTarget.getPath(IAndroidTarget.ANDROID_AIDL));
157         antProject.setProperty(PROPERTY_AAPT, androidTarget.getPath(IAndroidTarget.AAPT));
158         antProject.setProperty(PROPERTY_AIDL, androidTarget.getPath(IAndroidTarget.AIDL));
159         antProject.setProperty(PROPERTY_DX, androidTarget.getPath(IAndroidTarget.DX));
160 
161         // sets up the boot classpath
162 
163         // create the Path object
164         Path bootclasspath = new Path(antProject);
165 
166         // create a PathElement for the framework jar
167         PathElement element = bootclasspath.createPathElement();
168         element.setPath(androidJar);
169 
170         // create PathElement for each optional library.
171         IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries();
172         if (libraries != null) {
173             HashSet<String> visitedJars = new HashSet<String>();
174             for (IOptionalLibrary library : libraries) {
175                 String jarPath = library.getJarPath();
176                 if (visitedJars.contains(jarPath) == false) {
177                     visitedJars.add(jarPath);
178 
179                     element = bootclasspath.createPathElement();
180                     element.setPath(library.getJarPath());
181                 }
182             }
183         }
184 
185         // finally sets the path in the project with a reference
186         antProject.addReference(REF_CLASSPATH, bootclasspath);
187 
188         // find the file to import, and import it.
189         String templateFolder = androidTarget.getPath(IAndroidTarget.TEMPLATES);
190 
191         // Now the import section. This is only executed if the task actually has to import a file.
192         if (mDoImport) {
193             // make sure the file exists.
194             File templates = new File(templateFolder);
195             if (templates.isDirectory() == false) {
196                 throw new BuildException(String.format("Template directory '%s' is missing.",
197                         templateFolder));
198             }
199 
200             // now check the rules file exists.
201             File rules = new File(templateFolder, ANDROID_RULES);
202             if (rules.isFile() == false) {
203                 throw new BuildException(String.format("Build rules file '%s' is missing.",
204                         templateFolder));
205            }
206 
207             // set the file location to import
208             setFile(rules.getAbsolutePath());
209 
210             // and import
211             super.execute();
212         }
213     }
214 
215     /**
216      * Sets the value of the "import" attribute.
217      * @param value the value.
218      */
setImport(boolean value)219     public void setImport(boolean value) {
220         mDoImport = value;
221     }
222 
checkManifest(Project antProject, String codename)223     private void checkManifest(Project antProject, String codename) {
224         try {
225             File manifest = new File(antProject.getBaseDir(), "AndroidManifest.xml");
226 
227             XPath xPath = AndroidXPathFactory.newXPath();
228 
229             String value = xPath.evaluate("/" + ManifestConstants.NODE_MANIFEST +"/" +
230                     ManifestConstants.NODE_USES_SDK + "/@" +
231                     AndroidXPathFactory.DEFAULT_NS_PREFIX + ":" +
232                     ManifestConstants.ATTRIBUTE_MIN_SDK_VERSION,
233                     new InputSource(new FileInputStream(manifest)));
234 
235             if (codename.equals(value) == false) {
236                 throw new BuildException(String.format("For '%1$s' SDK Preview, application manifest must declare minSdkVersion to '%1$s'",
237                         codename));
238             }
239         } catch (XPathExpressionException e) {
240             throw new BuildException(e);
241         } catch (FileNotFoundException e) {
242             throw new BuildException(e);
243         }
244     }
245 }
246