| /* |
| * 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.build.ApkBuilder; |
| import com.android.sdklib.build.ApkCreationException; |
| import com.android.sdklib.build.DuplicateFileException; |
| import com.android.sdklib.build.SealedApkException; |
| |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.Task; |
| import org.apache.tools.ant.types.Path; |
| |
| import java.io.File; |
| import java.io.FilenameFilter; |
| import java.util.ArrayList; |
| import java.util.regex.Pattern; |
| |
| public class ApkBuilderTask extends Task { |
| |
| private final static Pattern PATTERN_JAR_EXT = Pattern.compile("^.+\\.jar$", |
| Pattern.CASE_INSENSITIVE); |
| |
| private String mOutFolder; |
| private String mApkFilepath; |
| private String mResourceFile; |
| private boolean mVerbose = false; |
| private boolean mDebugPackaging = false; |
| private boolean mDebugSigning = false; |
| private boolean mHasCode = true; |
| private String mAbiFilter = null; |
| |
| private Path mDexPath; |
| |
| private final ArrayList<Path> mZipList = new ArrayList<Path>(); |
| private final ArrayList<Path> mFileList = new ArrayList<Path>(); |
| private final ArrayList<Path> mSourceList = new ArrayList<Path>(); |
| private final ArrayList<Path> mJarfolderList = new ArrayList<Path>(); |
| private final ArrayList<Path> mJarfileList = new ArrayList<Path>(); |
| private final ArrayList<Path> mNativeList = new ArrayList<Path>(); |
| |
| /** |
| * Sets the value of the "outfolder" attribute. |
| * @param outFolder the value. |
| */ |
| public void setOutfolder(Path outFolder) { |
| mOutFolder = TaskHelper.checkSinglePath("outfolder", outFolder); |
| } |
| |
| /** |
| * Sets the full filepath to the apk to generate. |
| * @param filepath |
| */ |
| public void setApkfilepath(String filepath) { |
| mApkFilepath = filepath; |
| } |
| |
| /** |
| * Sets the resourcefile attribute |
| * @param resourceFile |
| */ |
| public void setResourcefile(String resourceFile) { |
| mResourceFile = resourceFile; |
| } |
| |
| /** |
| * Sets the value of the "verbose" attribute. |
| * @param verbose the value. |
| */ |
| public void setVerbose(boolean verbose) { |
| mVerbose = verbose; |
| } |
| |
| /** |
| * Sets the value of the "debug" attribute. |
| * @param debug the debug mode value. |
| */ |
| public void setDebug(boolean debug) { |
| System.out.println("WARNNG: Using deprecated 'debug' attribute in ApkBuilderTask." + |
| "Use 'debugpackaging' and 'debugsigning' instead."); |
| mDebugPackaging = debug; |
| mDebugSigning = debug; |
| } |
| |
| /** |
| * Sets the value of the "debugpackaging" attribute. |
| * @param debug the debug mode value. |
| */ |
| public void setDebugpackaging(boolean debug) { |
| mDebugPackaging = debug; |
| } |
| |
| /** |
| * Sets the value of the "debugsigning" attribute. |
| * @param debug the debug mode value. |
| */ |
| public void setDebugsigning(boolean debug) { |
| mDebugSigning = debug; |
| } |
| |
| /** |
| * Sets an ABI filter. If non <code>null</code>, then only native libraries matching the given |
| * ABI will be packaged with the APK. |
| * @param abiFilter the ABI to accept (and reject all other). If null or empty string, no ABIs |
| * are rejected. This must be a single ABI name as defined by the Android NDK. For a list |
| * of valid ABI names, see $NDK/docs/CPU-ARCH-ABIS.TXT |
| */ |
| public void setAbifilter(String abiFilter) { |
| if (abiFilter != null && abiFilter.length() > 0) { |
| mAbiFilter = abiFilter.trim(); |
| } else { |
| mAbiFilter = null; |
| } |
| } |
| |
| /** |
| * Sets the hascode attribute. Default is true. |
| * If set to false, then <dex> and <sourcefolder> nodes are ignored and not processed. |
| * @param hasCode the value of the attribute. |
| */ |
| public void setHascode(boolean hasCode) { |
| mHasCode = hasCode; |
| } |
| |
| /** |
| * Returns an object representing a nested <var>zip</var> element. |
| */ |
| public Object createZip() { |
| Path path = new Path(getProject()); |
| mZipList.add(path); |
| return path; |
| } |
| |
| /** |
| * Returns an object representing a nested <var>dex</var> element. |
| * This is similar to a nested <var>file</var> element, except when {@link #mHasCode} |
| * is <code>false</code> in which case it's ignored. |
| */ |
| public Object createDex() { |
| if (mDexPath == null) { |
| return mDexPath = new Path(getProject()); |
| } else { |
| throw new BuildException("Only one <dex> inner element can be provided"); |
| } |
| } |
| |
| /** |
| * Returns an object representing a nested <var>file</var> element. |
| */ |
| public Object createFile() { |
| System.out.println("WARNING: Using deprecated <file> inner element in ApkBuilderTask." + |
| "Use <dex path=...> instead."); |
| Path path = new Path(getProject()); |
| mFileList.add(path); |
| return path; |
| } |
| |
| /** |
| * Returns an object representing a nested <var>sourcefolder</var> element. |
| */ |
| public Object createSourcefolder() { |
| Path path = new Path(getProject()); |
| mSourceList.add(path); |
| return path; |
| } |
| |
| /** |
| * Returns an object representing a nested <var>jarfolder</var> element. |
| */ |
| public Object createJarfolder() { |
| Path path = new Path(getProject()); |
| mJarfolderList.add(path); |
| return path; |
| } |
| |
| /** |
| * Returns an object representing a nested <var>jarfile</var> element. |
| */ |
| public Object createJarfile() { |
| Path path = new Path(getProject()); |
| mJarfileList.add(path); |
| return path; |
| } |
| |
| /** |
| * Returns an object representing a nested <var>nativefolder</var> element. |
| */ |
| public Object createNativefolder() { |
| Path path = new Path(getProject()); |
| mNativeList.add(path); |
| return path; |
| } |
| |
| @Override |
| public void execute() throws BuildException { |
| |
| File outputFile; |
| if (mApkFilepath != null) { |
| outputFile = new File(mApkFilepath); |
| } else { |
| throw new BuildException("missing attribute 'apkFilepath'"); |
| } |
| |
| if (mResourceFile == null) { |
| throw new BuildException("missing attribute 'resourcefile'"); |
| } |
| |
| if (mOutFolder == null) { |
| throw new BuildException("missing attribute 'outfolder'"); |
| } |
| |
| // check dexPath is only one file. |
| File dexFile = null; |
| if (mHasCode) { |
| String[] dexFiles = mDexPath.list(); |
| if (dexFiles.length != 1) { |
| throw new BuildException(String.format( |
| "Expected one dex file but path value resolve to %d files.", |
| dexFiles.length)); |
| } |
| dexFile = new File(dexFiles[0]); |
| } |
| |
| try { |
| if (mDebugSigning) { |
| System.out.println(String.format( |
| "Creating %s and signing it with a debug key...", outputFile.getName())); |
| } else { |
| System.out.println(String.format( |
| "Creating %s for release...", outputFile.getName())); |
| } |
| |
| ApkBuilder apkBuilder = new ApkBuilder( |
| outputFile, |
| new File(mOutFolder, mResourceFile), |
| dexFile, |
| mDebugSigning ? ApkBuilder.getDebugKeystore() : null, |
| mVerbose ? System.out : null); |
| apkBuilder.setDebugMode(mDebugPackaging); |
| |
| |
| // add the content of the zip files. |
| for (Path pathList : mZipList) { |
| for (String path : pathList.list()) { |
| apkBuilder.addZipFile(new File(path)); |
| } |
| } |
| |
| // add the files that go to the root of the archive (this is deprecated) |
| for (Path pathList : mFileList) { |
| for (String path : pathList.list()) { |
| File f = new File(path); |
| apkBuilder.addFile(f, f.getName()); |
| } |
| } |
| |
| // now go through the list of file to directly add the to the list. |
| if (mHasCode) { |
| for (Path pathList : mSourceList) { |
| for (String path : pathList.list()) { |
| apkBuilder.addSourceFolder(new File(path)); |
| } |
| } |
| } |
| |
| // now go through the list of jar folders. |
| for (Path pathList : mJarfolderList) { |
| for (String path : pathList.list()) { |
| // it's ok if top level folders are missing |
| File folder = new File(path); |
| if (folder.isDirectory()) { |
| String[] filenames = folder.list(new FilenameFilter() { |
| public boolean accept(File dir, String name) { |
| return PATTERN_JAR_EXT.matcher(name).matches(); |
| } |
| }); |
| |
| for (String filename : filenames) { |
| apkBuilder.addResourcesFromJar(new File(folder, filename)); |
| } |
| } |
| } |
| } |
| |
| // now go through the list of jar files. |
| for (Path pathList : mJarfileList) { |
| for (String path : pathList.list()) { |
| apkBuilder.addResourcesFromJar(new File(path)); |
| } |
| } |
| |
| // now the native lib folder. |
| for (Path pathList : mNativeList) { |
| for (String path : pathList.list()) { |
| // it's ok if top level folders are missing |
| File folder = new File(path); |
| if (folder.isDirectory()) { |
| apkBuilder.addNativeLibraries(folder, mAbiFilter); |
| } |
| } |
| } |
| |
| |
| // close the archive |
| apkBuilder.sealApk(); |
| |
| } catch (DuplicateFileException e) { |
| System.err.println(String.format( |
| "Found duplicate file for APK: %1$s\nOrigin 1: %2$s\nOrigin 2: %3$s", |
| e.getArchivePath(), e.getFile1(), e.getFile2())); |
| throw new BuildException(e); |
| } catch (ApkCreationException e) { |
| throw new BuildException(e); |
| } catch (SealedApkException e) { |
| throw new BuildException(e); |
| } catch (IllegalArgumentException e) { |
| throw new BuildException(e); |
| } |
| } |
| } |