If the Ant project is targetting a preview platform, the manifest must declare minSdkVersion to be the platform codename.
269 lines
10 KiB
Java
269 lines
10 KiB
Java
/*
|
|
* Copyright (C) 2009 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.IAndroidTarget;
|
|
import com.android.sdklib.ISdkLog;
|
|
import com.android.sdklib.SdkConstants;
|
|
import com.android.sdklib.SdkManager;
|
|
import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
|
|
import com.android.sdklib.internal.project.ProjectProperties;
|
|
|
|
import org.apache.tools.ant.BuildException;
|
|
import org.apache.tools.ant.Project;
|
|
import org.apache.tools.ant.taskdefs.ImportTask;
|
|
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 java.util.Iterator;
|
|
|
|
import javax.xml.XMLConstants;
|
|
import javax.xml.namespace.NamespaceContext;
|
|
import javax.xml.xpath.XPath;
|
|
import javax.xml.xpath.XPathExpressionException;
|
|
import javax.xml.xpath.XPathFactory;
|
|
|
|
/**
|
|
* Setup/Import Ant task. This task accomplishes:
|
|
* <ul>
|
|
* <li>Gets the project target hash string from {@link ProjectProperties#PROPERTY_TARGET},
|
|
* and resolves it to get the project's {@link IAndroidTarget}.</li>
|
|
* <li>Sets up properties so that aapt can find the android.jar in the resolved target.</li>
|
|
* <li>Sets up the boot classpath ref so that the <code>javac</code> task knows where to find
|
|
* the libraries. This includes the default android.jar from the resolved target but also optional
|
|
* libraries provided by the target (if any, when the target is an add-on).</li>
|
|
* <li>Imports the build rules located in the resolved target so that the build actually does
|
|
* something. This can be disabled with the attribute <var>import</var> set to <code>false</code>
|
|
* </li></ul>
|
|
*
|
|
* This is used in build.xml/template.
|
|
*
|
|
*/
|
|
public final class SetupTask extends ImportTask {
|
|
private final static String ANDROID_RULES = "android_rules.xml";
|
|
|
|
// ant property with the path to the android.jar
|
|
private final static String PROPERTY_ANDROID_JAR = "android-jar";
|
|
// ant property with the path to the framework.jar
|
|
private final static String PROPERTY_ANDROID_AIDL = "android-aidl";
|
|
// ant property with the path to the aapt tool
|
|
private final static String PROPERTY_AAPT = "aapt";
|
|
// ant property with the path to the aidl tool
|
|
private final static String PROPERTY_AIDL = "aidl";
|
|
// ant property with the path to the dx tool
|
|
private final static String PROPERTY_DX = "dx";
|
|
// ref id to the <path> object containing all the boot classpaths.
|
|
private final static String REF_CLASSPATH = "android.target.classpath";
|
|
|
|
private boolean mDoImport = true;
|
|
|
|
@Override
|
|
public void execute() throws BuildException {
|
|
Project antProject = getProject();
|
|
|
|
// get the SDK location
|
|
String sdkLocation = antProject.getProperty(ProjectProperties.PROPERTY_SDK);
|
|
|
|
// check if it's valid and exists
|
|
if (sdkLocation == null || sdkLocation.length() == 0) {
|
|
throw new BuildException("SDK Location is not set.");
|
|
}
|
|
|
|
File sdk = new File(sdkLocation);
|
|
if (sdk.isDirectory() == false) {
|
|
throw new BuildException(String.format("SDK Location '%s' is not valid.", sdkLocation));
|
|
}
|
|
|
|
// 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(sdkLocation, new ISdkLog() {
|
|
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());
|
|
}
|
|
}
|
|
|
|
public void printf(String msgFormat, Object... args) {
|
|
messages.add(String.format(msgFormat, args));
|
|
}
|
|
|
|
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 target '%s'", targetHashString));
|
|
}
|
|
|
|
// display it
|
|
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());
|
|
|
|
// if needed check the manifest so that it matches the target
|
|
if (androidTarget.getVersion().isPreview()) {
|
|
// for preview, the manifest minSdkVersion node *must* match the target codename
|
|
checkManifest(antProject, androidTarget.getVersion().getCodename());
|
|
}
|
|
|
|
// sets up the properties to find android.jar/framework.aidl/target tools
|
|
String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR);
|
|
antProject.setProperty(PROPERTY_ANDROID_JAR, androidJar);
|
|
|
|
antProject.setProperty(PROPERTY_ANDROID_AIDL,
|
|
androidTarget.getPath(IAndroidTarget.ANDROID_AIDL));
|
|
antProject.setProperty(PROPERTY_AAPT, androidTarget.getPath(IAndroidTarget.AAPT));
|
|
antProject.setProperty(PROPERTY_AIDL, androidTarget.getPath(IAndroidTarget.AIDL));
|
|
antProject.setProperty(PROPERTY_DX, androidTarget.getPath(IAndroidTarget.DX));
|
|
|
|
// 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());
|
|
}
|
|
}
|
|
}
|
|
|
|
// finally sets the path in the project with a reference
|
|
antProject.addReference(REF_CLASSPATH, bootclasspath);
|
|
|
|
// find the file to import, and import it.
|
|
String templateFolder = androidTarget.getPath(IAndroidTarget.TEMPLATES);
|
|
|
|
// Now the import section. This is only executed if the task actually has to import a file.
|
|
if (mDoImport) {
|
|
// make sure the file exists.
|
|
File templates = new File(templateFolder);
|
|
if (templates.isDirectory() == false) {
|
|
throw new BuildException(String.format("Template directory '%s' is missing.",
|
|
templateFolder));
|
|
}
|
|
|
|
// now check the rules file exists.
|
|
File rules = new File(templateFolder, ANDROID_RULES);
|
|
if (rules.isFile() == false) {
|
|
throw new BuildException(String.format("Build rules file '%s' is missing.",
|
|
templateFolder));
|
|
}
|
|
|
|
// set the file location to import
|
|
setFile(rules.getAbsolutePath());
|
|
|
|
// and import
|
|
super.execute();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the value of the "import" attribute.
|
|
* @param value the value.
|
|
*/
|
|
public void setImport(boolean value) {
|
|
mDoImport = value;
|
|
}
|
|
|
|
private void checkManifest(Project antProject, String codename) {
|
|
try {
|
|
File manifest = new File(antProject.getBaseDir(), "AndroidManifest.xml");
|
|
|
|
XPath xPath = XPathFactory.newInstance().newXPath();
|
|
xPath.setNamespaceContext(new NamespaceContext() {
|
|
public String getNamespaceURI(String prefix) {
|
|
if (prefix != null) {
|
|
if (prefix.equals("android")) {
|
|
return SdkConstants.NS_RESOURCES;
|
|
}
|
|
}
|
|
|
|
return XMLConstants.NULL_NS_URI;
|
|
}
|
|
|
|
public String getPrefix(String namespaceURI) {
|
|
// This isn't necessary for our use.
|
|
assert false;
|
|
return null;
|
|
}
|
|
|
|
public Iterator getPrefixes(String namespaceURI) {
|
|
// This isn't necessary for our use.
|
|
assert false;
|
|
return null;
|
|
}
|
|
});
|
|
|
|
String value = xPath.evaluate("/manifest/uses-sdk/@android:minSdkVersion",
|
|
new InputSource(new FileInputStream(manifest)));
|
|
|
|
if (codename.equals(value) == false) {
|
|
throw new BuildException(String.format("For '%1$s' SDK Preview, application manifest must declare minSdkVersion to '%1$s'",
|
|
codename));
|
|
}
|
|
} catch (XPathExpressionException e) {
|
|
throw new BuildException(e);
|
|
} catch (FileNotFoundException e) {
|
|
throw new BuildException(e);
|
|
}
|
|
}
|
|
}
|