aboutsummaryrefslogtreecommitdiffstats
path: root/anttasks/src/com/android/ant/GetTargetTask.java
diff options
context:
space:
mode:
Diffstat (limited to 'anttasks/src/com/android/ant/GetTargetTask.java')
-rw-r--r--anttasks/src/com/android/ant/GetTargetTask.java294
1 files changed, 294 insertions, 0 deletions
diff --git a/anttasks/src/com/android/ant/GetTargetTask.java b/anttasks/src/com/android/ant/GetTargetTask.java
new file mode 100644
index 0000000..f4578b2
--- /dev/null
+++ b/anttasks/src/com/android/ant/GetTargetTask.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ant;
+
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
+import com.android.sdklib.ISdkLog;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.SdkManager;
+import com.android.sdklib.internal.project.ProjectProperties;
+import com.android.sdklib.xml.AndroidManifest;
+import com.android.sdklib.xml.AndroidXPathFactory;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Path.PathElement;
+import org.xml.sax.InputSource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpressionException;
+
+/**
+ * Task to resolve the target of the current Android project.
+ *
+ * Out params:
+ * <code>bootClassPathOut</code>: The boot class path of the project.
+ *
+ * <code>androidJarFileOut</code>: the android.jar used by the project.
+ *
+ * <code>androidAidlFileOut</code>: the framework.aidl used by the project.
+ *
+ * <code>targetApiOut</code>: the build API level.
+ *
+ * <code>minSdkVersionOut</code>: the app's minSdkVersion.
+ *
+ */
+public class GetTargetTask extends Task {
+
+ private String mBootClassPathOut;
+ private String mAndroidJarFileOut;
+ private String mAndroidAidlFileOut;
+ private String mTargetApiOut;
+ private String mMinSdkVersionOut;
+
+ public void setBootClassPathOut(String bootClassPathOut) {
+ mBootClassPathOut = bootClassPathOut;
+ }
+
+ public void setAndroidJarFileOut(String androidJarFileOut) {
+ mAndroidJarFileOut = androidJarFileOut;
+ }
+
+ public void setAndroidAidlFileOut(String androidAidlFileOut) {
+ mAndroidAidlFileOut = androidAidlFileOut;
+ }
+
+ public void setTargetApiOut(String targetApiOut) {
+ mTargetApiOut = targetApiOut;
+ }
+
+ public void setMinSdkVersionOut(String minSdkVersionOut) {
+ mMinSdkVersionOut = minSdkVersionOut;
+ }
+
+ @Override
+ public void execute() throws BuildException {
+ if (mBootClassPathOut == null) {
+ throw new BuildException("Missing attribute bootClassPathOut");
+ }
+ if (mAndroidJarFileOut == null) {
+ throw new BuildException("Missing attribute androidJarFileOut");
+ }
+ if (mAndroidAidlFileOut == null) {
+ throw new BuildException("Missing attribute androidAidlFileOut");
+ }
+ if (mTargetApiOut == null) {
+ throw new BuildException("Missing attribute targetApiOut");
+ }
+ if (mMinSdkVersionOut == null) {
+ throw new BuildException("Missing attribute mMinSdkVersionOut");
+ }
+
+ Project antProject = getProject();
+
+ // get the SDK location
+ File sdkDir = TaskHelper.getSdkLocation(antProject);
+
+ // get the target property value
+ String targetHashString = antProject.getProperty(ProjectProperties.PROPERTY_TARGET);
+
+ if (targetHashString == null) {
+ throw new BuildException("Android Target is not set.");
+ }
+
+ // load up the sdk targets.
+ final ArrayList<String> messages = new ArrayList<String>();
+ SdkManager manager = SdkManager.createManager(sdkDir.getPath(), new ISdkLog() {
+ @Override
+ public void error(Throwable t, String errorFormat, Object... args) {
+ if (errorFormat != null) {
+ messages.add(String.format("Error: " + errorFormat, args));
+ }
+ if (t != null) {
+ messages.add("Error: " + t.getMessage());
+ }
+ }
+
+ @Override
+ public void printf(String msgFormat, Object... args) {
+ messages.add(String.format(msgFormat, args));
+ }
+
+ @Override
+ public void warning(String warningFormat, Object... args) {
+ messages.add(String.format("Warning: " + warningFormat, args));
+ }
+ });
+
+ if (manager == null) {
+ // since we failed to parse the SDK, lets display the parsing output.
+ for (String msg : messages) {
+ System.out.println(msg);
+ }
+ throw new BuildException("Failed to parse SDK content.");
+ }
+
+ // resolve it
+ IAndroidTarget androidTarget = manager.getTargetFromHashString(targetHashString);
+
+ if (androidTarget == null) {
+ throw new BuildException(String.format(
+ "Unable to resolve project target '%s'", targetHashString));
+ }
+
+ // display the project info
+ System.out.println( "Project Target: " + androidTarget.getName());
+ if (androidTarget.isPlatform() == false) {
+ System.out.println("Vendor: " + androidTarget.getVendor());
+ System.out.println("Platform Version: " + androidTarget.getVersionName());
+ }
+ System.out.println( "API level: " + androidTarget.getVersion().getApiString());
+
+ antProject.setProperty(mMinSdkVersionOut,
+ Integer.toString(androidTarget.getVersion().getApiLevel()));
+
+ // always check the manifest minSdkVersion.
+ checkManifest(antProject, androidTarget.getVersion());
+
+ // sets up the properties to find android.jar/framework.aidl/target tools
+ String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR);
+ antProject.setProperty(mAndroidJarFileOut, androidJar);
+
+ String androidAidl = androidTarget.getPath(IAndroidTarget.ANDROID_AIDL);
+ antProject.setProperty(mAndroidAidlFileOut, androidAidl);
+
+ // sets up the boot classpath
+
+ // create the Path object
+ Path bootclasspath = new Path(antProject);
+
+ // create a PathElement for the framework jar
+ PathElement element = bootclasspath.createPathElement();
+ element.setPath(androidJar);
+
+ // create PathElement for each optional library.
+ IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries();
+ if (libraries != null) {
+ HashSet<String> visitedJars = new HashSet<String>();
+ for (IOptionalLibrary library : libraries) {
+ String jarPath = library.getJarPath();
+ if (visitedJars.contains(jarPath) == false) {
+ visitedJars.add(jarPath);
+
+ element = bootclasspath.createPathElement();
+ element.setPath(library.getJarPath());
+ }
+ }
+ }
+
+ // sets the path in the project with a reference
+ antProject.addReference(mBootClassPathOut, bootclasspath);
+ }
+
+ /**
+ * Checks the manifest <code>minSdkVersion</code> attribute.
+ * @param antProject the ant project
+ * @param androidVersion the version of the platform the project is compiling against.
+ */
+ private void checkManifest(Project antProject, AndroidVersion androidVersion) {
+ try {
+ File manifest = new File(antProject.getBaseDir(), SdkConstants.FN_ANDROID_MANIFEST_XML);
+
+ XPath xPath = AndroidXPathFactory.newXPath();
+
+ // check the package name.
+ String value = xPath.evaluate(
+ "/" + AndroidManifest.NODE_MANIFEST +
+ "/@" + AndroidManifest.ATTRIBUTE_PACKAGE,
+ new InputSource(new FileInputStream(manifest)));
+ if (value != null) { // aapt will complain if it's missing.
+ // only need to check that the package has 2 segments
+ if (value.indexOf('.') == -1) {
+ throw new BuildException(String.format(
+ "Application package '%1$s' must have a minimum of 2 segments.",
+ value));
+ }
+ }
+
+ // check the minSdkVersion value
+ value = xPath.evaluate(
+ "/" + AndroidManifest.NODE_MANIFEST +
+ "/" + AndroidManifest.NODE_USES_SDK +
+ "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + ":" +
+ AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
+ new InputSource(new FileInputStream(manifest)));
+
+ if (androidVersion.isPreview()) {
+ // in preview mode, the content of the minSdkVersion must match exactly the
+ // platform codename.
+ String codeName = androidVersion.getCodename();
+ if (codeName.equals(value) == false) {
+ throw new BuildException(String.format(
+ "For '%1$s' SDK Preview, attribute minSdkVersion in AndroidManifest.xml must be '%1$s' (current: %2$s)",
+ codeName, value));
+ }
+
+ // set the API level to the previous API level (which is actually the value in
+ // androidVersion.)
+ antProject.setProperty(mTargetApiOut,
+ Integer.toString(androidVersion.getApiLevel()));
+
+ } else if (value.length() > 0) {
+ // for normal platform, we'll only display warnings if the value is lower or higher
+ // than the target api level.
+ // First convert to an int.
+ int minSdkValue = -1;
+ try {
+ minSdkValue = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ // looks like it's not a number: error!
+ throw new BuildException(String.format(
+ "Attribute %1$s in AndroidManifest.xml must be an Integer!",
+ AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION));
+ }
+
+ // set the target api to the value
+ antProject.setProperty(mTargetApiOut, value);
+
+ int projectApiLevel = androidVersion.getApiLevel();
+ if (minSdkValue > androidVersion.getApiLevel()) {
+ System.out.println(String.format(
+ "WARNING: Attribute %1$s in AndroidManifest.xml (%2$d) is higher than the project target API level (%3$d)",
+ AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
+ minSdkValue, projectApiLevel));
+ }
+ } else {
+ // no minSdkVersion? display a warning
+ System.out.println(
+ "WARNING: No minSdkVersion value set. Application will install on all Android versions.");
+
+ // set the target api to 1
+ antProject.setProperty(mTargetApiOut, "1");
+ }
+
+ } catch (XPathExpressionException e) {
+ throw new BuildException(e);
+ } catch (FileNotFoundException e) {
+ throw new BuildException(e);
+ }
+ }
+}