diff options
Diffstat (limited to 'anttasks/src/com/android/ant/GetTargetTask.java')
-rw-r--r-- | anttasks/src/com/android/ant/GetTargetTask.java | 294 |
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); + } + } +} |