/* * 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.AndroidVersion; 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 com.android.sdklib.internal.project.ProjectProperties.PropertyType; import com.android.sdklib.io.FileWrapper; 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.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.io.FilenameFilter; import java.util.ArrayList; import java.util.HashSet; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; /** * Setup/Import Ant task. This task accomplishes: *
javac
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).false
* minSdkVersion
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'",
codeName));
}
} 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));
}
int projectApiLevel = androidVersion.getApiLevel();
if (minSdkValue < projectApiLevel) {
System.out.println(String.format(
"WARNING: Attribute %1$s in AndroidManifest.xml (%2$d) is lower than the project target API level (%3$d)",
AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
minSdkValue, projectApiLevel));
} else 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.");
}
} catch (XPathExpressionException e) {
throw new BuildException(e);
} catch (FileNotFoundException e) {
throw new BuildException(e);
}
}
private void processReferencedLibraries(Project antProject, IAndroidTarget androidTarget) {
// prepare several paths for future tasks
Path sourcePath = new Path(antProject);
Path resPath = new Path(antProject);
Path libsPath = new Path(antProject);
Path jarsPath = new Path(antProject);
StringBuilder sb = new StringBuilder();
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".jar");
}
};
// get the build version for the current target. It'll be tested if there's at least
// one library.
boolean supportLibrary = androidTarget.getProperty(SdkConstants.PROP_SDK_SUPPORT_LIBRARY,
false);
int index = 1;
while (true) {
String propName = ProjectProperties.PROPERTY_LIB_REF + Integer.toString(index++);
String rootPath = antProject.getProperty(propName);
if (rootPath == null) {
break;
}
if (supportLibrary == false) {
throw new BuildException(String.format(
"The build system for this project target (%1$s) does not support libraries",
androidTarget.getFullName()));
}
// get the source path. default is src but can be overriden by the property
// "source.dir" in build.properties.
PathElement element = sourcePath.createPathElement();
ProjectProperties prop = ProjectProperties.load(rootPath, PropertyType.BUILD);
String sourceDir = SdkConstants.FD_SOURCES;
if (prop != null) {
String value = prop.getProperty(ProjectProperties.PROPERTY_BUILD_SOURCE_DIR);
if (value != null) {
sourceDir = value;
}
}
element.setPath(rootPath + "/" + sourceDir);
// get the res path. Always $PROJECT/res
element = resPath.createPathElement();
element.setPath(rootPath + "/" + SdkConstants.FD_RESOURCES);
// get the libs path. Always $PROJECT/libs
element = libsPath.createPathElement();
element.setPath(rootPath + "/" + SdkConstants.FD_NATIVE_LIBS);
// get the jars from it too
File libsFolder = new File(rootPath, SdkConstants.FD_NATIVE_LIBS);
File[] jarFiles = libsFolder.listFiles(filter);
for (File jarFile : jarFiles) {
element = jarsPath.createPathElement();
element.setPath(jarFile.getAbsolutePath());
}
// get the package from the manifest.
FileWrapper manifest = new FileWrapper(rootPath, SdkConstants.FN_ANDROID_MANIFEST_XML);
try {
String value = AndroidManifest.getPackage(manifest);
if (value != null) { // aapt will complain if it's missing.
sb.append(';');
sb.append(value);
}
} catch (Exception e) {
throw new BuildException(e);
}
}
// even with no libraries, always setup these so that various tasks in Ant don't complain
// (the task themselves can handle a ref to an empty Path)
antProject.addReference("android.libraries.src", sourcePath);
antProject.addReference("android.libraries.jars", jarsPath);
antProject.addReference("android.libraries.libs", libsPath);
// the rest is done only if there's a library.
if (sourcePath.list().length > 0) {
antProject.addReference("android.libraries.res", resPath);
antProject.setProperty("android.libraries.package", sb.toString());
}
}
}