diff options
Diffstat (limited to 'anttasks/src')
-rw-r--r-- | anttasks/src/Android.mk | 29 | ||||
-rw-r--r-- | anttasks/src/com/android/ant/AaptExecLoopTask.java | 218 | ||||
-rw-r--r-- | anttasks/src/com/android/ant/ApkBuilderTask.java | 293 | ||||
-rw-r--r-- | anttasks/src/com/android/ant/SetupTask.java | 208 |
4 files changed, 748 insertions, 0 deletions
diff --git a/anttasks/src/Android.mk b/anttasks/src/Android.mk new file mode 100644 index 0000000..94d6d3f --- /dev/null +++ b/anttasks/src/Android.mk @@ -0,0 +1,29 @@ +# +# Copyright (C) 2008 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. +# +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_JAVA_LIBRARIES := \ + sdklib \ + apkbuilder \ + ant + +LOCAL_MODULE := anttasks + +include $(BUILD_HOST_JAVA_LIBRARY) + diff --git a/anttasks/src/com/android/ant/AaptExecLoopTask.java b/anttasks/src/com/android/ant/AaptExecLoopTask.java new file mode 100644 index 0000000..d2c7162 --- /dev/null +++ b/anttasks/src/com/android/ant/AaptExecLoopTask.java @@ -0,0 +1,218 @@ +/* + * 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.project.ApkConfigurationHelper; +import com.android.sdklib.project.ProjectProperties; +import com.android.sdklib.project.ProjectProperties.PropertyType; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.ExecTask; +import org.apache.tools.ant.types.Path; + +import java.io.File; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +/** + * Task able to run an Exec task on aapt several times. + * It does not follow the exec task format, instead it has its own parameters, which maps + * directly to aapt. + * + */ +public final class AaptExecLoopTask extends Task { + + private String mExecutable; + private String mCommand; + private String mManifest; + private String mResources; + private String mAssets; + private String mAndroidJar; + private String mOutFolder; + private String mBaseName; + + /** + * Sets the value of the "executable" attribute. + * @param executable the value. + */ + public void setExecutable(String executable) { + mExecutable = executable; + } + + /** + * Sets the value of the "command" attribute. + * @param command the value. + */ + public void setCommand(String command) { + mCommand = command; + } + + /** + * Sets the value of the "manifest" attribute. + * @param manifest the value. + */ + public void setManifest(Path manifest) { + mManifest = manifest.toString(); + } + + /** + * Sets the value of the "resources" attribute. + * @param resources the value. + */ + public void setResources(Path resources) { + mResources = resources.toString(); + } + + /** + * Sets the value of the "assets" attribute. + * @param assets the value. + */ + public void setAssets(Path assets) { + mAssets = assets.toString(); + } + + /** + * Sets the value of the "androidjar" attribute. + * @param androidJar the value. + */ + public void setAndroidjar(Path androidJar) { + mAndroidJar = androidJar.toString(); + } + + /** + * Sets the value of the "outfolder" attribute. + * @param outFolder the value. + */ + public void setOutfolder(Path outFolder) { + mOutFolder = outFolder.toString(); + } + + /** + * Sets the value of the "basename" attribute. + * @param baseName the value. + */ + public void setBasename(String baseName) { + mBaseName = baseName; + } + + /* + * (non-Javadoc) + * + * Executes the loop. Based on the values inside default.properties, this will + * create alternate temporary ap_ files. + * + * @see org.apache.tools.ant.Task#execute() + */ + @Override + public void execute() throws BuildException { + Project taskProject = getProject(); + + // first do a full resource package + createPackage(null /*configName*/, null /*resourceFilter*/); + + // now see if we need to create file with filtered resources. + // Get the project base directory. + File baseDir = taskProject.getBaseDir(); + ProjectProperties properties = ProjectProperties.load(baseDir.getAbsolutePath(), + PropertyType.DEFAULT); + + Map<String, String> apkConfigs = ApkConfigurationHelper.getConfigs(properties); + if (apkConfigs.size() > 0) { + Set<Entry<String, String>> entrySet = apkConfigs.entrySet(); + for (Entry<String, String> entry : entrySet) { + createPackage(entry.getKey(), entry.getValue()); + } + } + } + + /** + * Creates a resource package. + * @param configName the name of the filter config. Can be null in which case a full resource + * package will be generated. + * @param resourceFilter the resource configuration filter to pass to aapt (if configName is + * non null) + */ + private void createPackage(String configName, String resourceFilter) { + Project taskProject = getProject(); + + if (configName == null || resourceFilter == null) { + System.out.println("Creating full resource package..."); + } else { + System.out.println(String.format( + "Creating resource package for config '%1$s' (%2$s)...", + configName, resourceFilter)); + } + + // create a task for the default apk. + ExecTask task = new ExecTask(); + task.setExecutable(mExecutable); + task.setFailonerror(true); + + // aapt command. Only "package" is supported at this time really. + task.createArg().setValue(mCommand); + + // filters if needed + if (configName != null && resourceFilter != null) { + task.createArg().setValue("-c"); + task.createArg().setValue(resourceFilter); + } + + // force flag + task.createArg().setValue("-f"); + + // manifest location + task.createArg().setValue("-M"); + task.createArg().setValue(mManifest); + + // resources location + task.createArg().setValue("-S"); + task.createArg().setValue(mResources); + + // assets location. this may not exists, and aapt doesn't like it, so we check first. + File assets = new File(mAssets); + if (assets.isDirectory()) { + task.createArg().setValue("-A"); + task.createArg().setValue(mAssets); + } + + // android.jar + task.createArg().setValue("-I"); + task.createArg().setValue(mAndroidJar); + + // out file. This is based on the outFolder, baseName, and the configName (if applicable) + String filename; + if (configName != null && resourceFilter != null) { + filename = mBaseName + "-" + configName + ".ap_"; + } else { + filename = mBaseName + ".ap_"; + } + + File file = new File(mOutFolder, filename); + task.createArg().setValue("-F"); + task.createArg().setValue(file.getAbsolutePath()); + + // final setup of the task + task.setProject(taskProject); + task.setOwningTarget(getOwningTarget()); + + // execute it. + task.execute(); + } +} diff --git a/anttasks/src/com/android/ant/ApkBuilderTask.java b/anttasks/src/com/android/ant/ApkBuilderTask.java new file mode 100644 index 0000000..22729ec --- /dev/null +++ b/anttasks/src/com/android/ant/ApkBuilderTask.java @@ -0,0 +1,293 @@ +/* + * 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.apkbuilder.ApkBuilder; +import com.android.apkbuilder.ApkBuilder.ApkFile; +import com.android.sdklib.project.ApkConfigurationHelper; +import com.android.sdklib.project.ProjectProperties; +import com.android.sdklib.project.ProjectProperties.PropertyType; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.Path; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +public class ApkBuilderTask extends Task { + + /** + * Class to represent nested elements. Since they all have only one attribute ('path'), the + * same class can be used for all the nested elements (zip, file, sourcefolder, jarfolder, + * nativefolder). + */ + public final static class Value extends ProjectComponent { + String mPath; + + /** + * Sets the value of the "path" attribute. + * @param path the value. + */ + public void setPath(Path path) { + mPath = path.toString(); + } + } + + private String mOutFolder; + private String mBaseName; + private boolean mVerbose = false; + private boolean mSigned = true; + + private final ArrayList<Value> mZipList = new ArrayList<Value>(); + private final ArrayList<Value> mFileList = new ArrayList<Value>(); + private final ArrayList<Value> mSourceList = new ArrayList<Value>(); + private final ArrayList<Value> mJarList = new ArrayList<Value>(); + private final ArrayList<Value> mNativeList = new ArrayList<Value>(); + + private final ArrayList<FileInputStream> mZipArchives = new ArrayList<FileInputStream>(); + private final ArrayList<File> mArchiveFiles = new ArrayList<File>(); + private final ArrayList<ApkFile> mJavaResources = new ArrayList<ApkFile>(); + private final ArrayList<FileInputStream> mResourcesJars = new ArrayList<FileInputStream>(); + private final ArrayList<ApkFile> mNativeLibraries = new ArrayList<ApkFile>(); + + /** + * Sets the value of the "outfolder" attribute. + * @param outFolder the value. + */ + public void setOutfolder(Path outFolder) { + mOutFolder = outFolder.toString(); + } + + /** + * Sets the value of the "basename" attribute. + * @param baseName the value. + */ + public void setBasename(String baseName) { + mBaseName = baseName; + } + + /** + * Sets the value of the "verbose" attribute. + * @param verbose the value. + */ + public void setVerbose(boolean verbose) { + mVerbose = verbose; + } + + /** + * Sets the value of the "signed" attribute. + * @param signed the value. + */ + public void setSigned(boolean signed) { + mSigned = signed; + } + + /** + * Returns an object representing a nested <var>zip</var> element. + */ + public Object createZip() { + Value zip = new Value(); + mZipList.add(zip); + return zip; + } + + /** + * Returns an object representing a nested <var>file</var> element. + */ + public Object createFile() { + Value file = new Value(); + mFileList.add(file); + return file; + } + + /** + * Returns an object representing a nested <var>sourcefolder</var> element. + */ + public Object createSourcefolder() { + Value file = new Value(); + mSourceList.add(file); + return file; + } + + /** + * Returns an object representing a nested <var>jarfolder</var> element. + */ + public Object createJarfolder() { + Value file = new Value(); + mJarList.add(file); + return file; + } + + /** + * Returns an object representing a nested <var>nativefolder</var> element. + */ + public Object createNativefolder() { + Value file = new Value(); + mNativeList.add(file); + return file; + } + + @Override + public void execute() throws BuildException { + Project taskProject = getProject(); + + ApkBuilder apkBuilder = new ApkBuilder(); + apkBuilder.setVerbose(mVerbose); + apkBuilder.setSignedPackage(mSigned); + + try { + // setup the list of everything that needs to go in the archive. + + // go through the list of zip files to add. This will not include + // the resource package, which is handled separaly for each apk to create. + for (Value v : mZipList) { + FileInputStream input = new FileInputStream(v.mPath); + mZipArchives.add(input); + } + + // now go through the list of file to directly add the to the list. + for (Value v : mFileList) { + mArchiveFiles.add(ApkBuilder.getInputFile(v.mPath)); + } + + // now go through the list of file to directly add the to the list. + for (Value v : mSourceList) { + ApkBuilder.processSourceFolderForResource(v.mPath, mJavaResources); + } + + // now go through the list of jar folders. + for (Value v : mJarList) { + ApkBuilder.processJarFolder(v.mPath, mResourcesJars); + } + + // now the native lib folder. + for (Value v : mNativeList) { + String parameter = v.mPath; + File f = new File(parameter); + + // compute the offset to get the relative path + int offset = parameter.length(); + if (parameter.endsWith(File.separator) == false) { + offset++; + } + + ApkBuilder.processNativeFolder(offset, f, mNativeLibraries); + } + + + // first do a full resource package + createApk(apkBuilder, null /*configName*/, null /*resourceFilter*/); + + // now see if we need to create file with filtered resources. + // Get the project base directory. + File baseDir = taskProject.getBaseDir(); + ProjectProperties properties = ProjectProperties.load(baseDir.getAbsolutePath(), + PropertyType.DEFAULT); + + Map<String, String> apkConfigs = ApkConfigurationHelper.getConfigs(properties); + if (apkConfigs.size() > 0) { + Set<Entry<String, String>> entrySet = apkConfigs.entrySet(); + for (Entry<String, String> entry : entrySet) { + createApk(apkBuilder, entry.getKey(), entry.getValue()); + } + } + } catch (FileNotFoundException e) { + throw new BuildException(e); + } catch (IllegalArgumentException e) { + throw new BuildException(e); + } + } + + /** + * Creates an application package. + * @param apkBuilder + * @param configName the name of the filter config. Can be null in which case a full resource + * package will be generated. + * @param resourceFilter the resource configuration filter to pass to aapt (if configName is + * non null) + * @throws FileNotFoundException + */ + private void createApk(ApkBuilder apkBuilder, String configName, String resourceFilter) + throws FileNotFoundException { + // All the files to be included in the archive have already been prep'ed up, except + // the resource package. + // figure out its name. + String filename; + if (configName != null && resourceFilter != null) { + filename = mBaseName + "-" + configName + ".ap_"; + } else { + filename = mBaseName + ".ap_"; + } + + // now we add it to the list of zip archive (it's just a zip file). + + // it's used as a zip archive input + FileInputStream resoucePackageZipFile = new FileInputStream(new File(mOutFolder, filename)); + mZipArchives.add(resoucePackageZipFile); + + // prepare the filename to generate. Same thing as the resource file. + if (configName != null && resourceFilter != null) { + filename = mBaseName + "-" + configName; + } else { + filename = mBaseName; + } + + if (mSigned) { + filename = filename + "-debug.apk"; + } else { + filename = filename + "-unsigned.apk"; + } + + if (configName == null || resourceFilter == null) { + if (mSigned) { + System.out.println(String.format( + "Creating %s and signing it with a debug key...", filename)); + } else { + System.out.println(String.format( + "Creating %s for release...", filename)); + } + } else { + if (mSigned) { + System.out.println(String.format( + "Creating %1$s (with %2$s) and signing it with a debug key...", + filename, resourceFilter)); + } else { + System.out.println(String.format( + "Creating %1$s (with %2$s) for release...", + filename, resourceFilter)); + } + } + + File f = new File(mOutFolder, filename); + + // and generate the apk + apkBuilder.createPackage(f.getAbsoluteFile(), mZipArchives, + mArchiveFiles, mJavaResources, mResourcesJars, mNativeLibraries); + + // we are done. We need to remove the resource package from the list of zip archives + // in case we have another apk to generate. + mZipArchives.remove(resoucePackageZipFile); + } +} diff --git a/anttasks/src/com/android/ant/SetupTask.java b/anttasks/src/com/android/ant/SetupTask.java new file mode 100644 index 0000000..d425a2f --- /dev/null +++ b/anttasks/src/com/android/ant/SetupTask.java @@ -0,0 +1,208 @@ +/* + * 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.SdkManager; +import com.android.sdklib.IAndroidTarget.IOptionalLibrary; +import com.android.sdklib.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 java.io.File; +import java.util.ArrayList; +import java.util.HashSet; + +/** + * 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.getApiVersionName()); + } + System.out.println("API level: " + androidTarget.getApiVersionNumber()); + + // 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; + } +} |