From 50b3e57fa887dc7182facfb178ef842103aeeaf3 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Tue, 23 Aug 2011 10:18:17 -0700 Subject: Add support for proper dependency detection in the aidl ant compilation step. Aidl compilation now generates dependency files that are reused on further compilations to only recompile files that needs it based on source files modifications. Also clean up output (and dependency) files when a source file is deleted. Change-Id: I3131463fd7939ffc4b5bbdfa49940e03f0249a28 --- anttasks/src/com/android/ant/AaptExecLoopTask.java | 528 -------------------- anttasks/src/com/android/ant/AaptExecTask.java | 530 +++++++++++++++++++++ anttasks/src/com/android/ant/AidlExecTask.java | 127 ++++- anttasks/src/com/android/ant/BaseTask.java | 3 +- anttasks/src/com/android/ant/DependencyGraph.java | 97 ++-- anttasks/src/com/android/ant/RenderScriptTask.java | 9 + 6 files changed, 720 insertions(+), 574 deletions(-) delete mode 100644 anttasks/src/com/android/ant/AaptExecLoopTask.java create mode 100644 anttasks/src/com/android/ant/AaptExecTask.java (limited to 'anttasks') diff --git a/anttasks/src/com/android/ant/AaptExecLoopTask.java b/anttasks/src/com/android/ant/AaptExecLoopTask.java deleted file mode 100644 index 8a593f9..0000000 --- a/anttasks/src/com/android/ant/AaptExecLoopTask.java +++ /dev/null @@ -1,528 +0,0 @@ -/* - * 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 org.apache.tools.ant.BuildException; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.taskdefs.ExecTask; -import org.apache.tools.ant.types.Path; - -import java.io.File; -import java.util.ArrayList; - -/** - * Task to execute aapt. - * - *

It does not follow the exec task format, instead it has its own parameters, which maps - * directly to aapt.

- *

It is able to run aapt several times if library setup requires generating several - * R.java files. - *

The following map shows how to use the task for each supported aapt command line - * parameter.

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Aapt OptionAnt NameType
path to aaptexecutableattribute (Path)
commandcommandattribute (String)
-vverboseattribute (boolean)
-fforceattribute (boolean)
-M AndroidManifest.xmlmanifestattribute (Path)
-I base-packageandroidjarattribute (Path)
-A asset-source-dirassetsattribute (Path
-S resource-sources<res path="">nested element(s)
with attribute (Path)
-0 extension<nocompress extension="">
<nocompress>
nested element(s)
with attribute (String)
-F apk-fileapkfolder
outfolder
apkbasename
basename
attribute (Path)
attribute (Path) deprecated
attribute (String)
attribute (String) deprecated
-J R-file-dirrfolderattribute (Path)
-m always enabled
- */ -public final class AaptExecLoopTask extends BaseTask { - - /** - * Class representing a <nocompress> node in the main task XML. - * This let the developers prevent compression of some files in assets/ and res/raw/ - * by extension. - * If the extension is null, this will disable compression for all files in assets/ and - * res/raw/ - */ - public final static class NoCompress { - String mExtension; - - /** - * Sets the value of the "extension" attribute. - * @param extention the extension. - */ - public void setExtension(String extention) { - mExtension = extention; - } - } - - private String mExecutable; - private String mCommand; - private boolean mForce = true; // true due to legacy reasons - private boolean mDebug = false; - private boolean mVerbose = false; - private boolean mUseCrunchCache = false; - private int mVersionCode = 0; - private String mVersionName; - private String mManifest; - private ArrayList mResources; - private String mAssets; - private String mAndroidJar; - private String mApkFolder; - private String mApkName; - private String mResourceFilter; - private String mRFolder; - private final ArrayList mNoCompressList = new ArrayList(); - private String mProjectLibrariesResName; - private String mProjectLibrariesPackageName; - - /** - * Sets the value of the "executable" attribute. - * @param executable the value. - */ - public void setExecutable(Path executable) { - mExecutable = TaskHelper.checkSinglePath("executable", executable); - } - - /** - * Sets the value of the "command" attribute. - * @param command the value. - */ - public void setCommand(String command) { - mCommand = command; - } - - /** - * Sets the value of the "force" attribute. - * @param force the value. - */ - public void setForce(boolean force) { - mForce = force; - } - - /** - * Sets the value of the "verbose" attribute. - * @param verbose the value. - */ - public void setVerbose(boolean verbose) { - mVerbose = verbose; - } - - /** - * Sets the value of the "usecrunchcache" attribute - * @param usecrunch whether to use the crunch cache. - */ - public void setNoCrunch(boolean nocrunch) { - mUseCrunchCache = nocrunch; - } - - public void setVersioncode(String versionCode) { - if (versionCode.length() > 0) { - try { - mVersionCode = Integer.decode(versionCode); - } catch (NumberFormatException e) { - System.out.println(String.format( - "WARNING: Ignoring invalid version code value '%s'.", versionCode)); - } - } - } - - /** - * Sets the value of the "versionName" attribute - * @param versionName the value - */ - public void setVersionname(String versionName) { - mVersionName = versionName; - } - - public void setDebug(boolean value) { - mDebug = value; - } - - /** - * Sets the value of the "manifest" attribute. - * @param manifest the value. - */ - public void setManifest(Path manifest) { - mManifest = TaskHelper.checkSinglePath("manifest", manifest); - } - - /** - * Sets the value of the "resources" attribute. - * @param resources the value. - * - * @deprecated Use nested element(s) - */ - @Deprecated - public void setResources(Path resources) { - System.out.println("WARNNG: Using deprecated 'resources' attribute in AaptExecLoopTask." + - "Use nested element(s) instead."); - if (mResources == null) { - mResources = new ArrayList(); - } - - mResources.add(new Path(getProject(), resources.toString())); - } - - /** - * Sets the value of the "assets" attribute. - * @param assets the value. - */ - public void setAssets(Path assets) { - mAssets = TaskHelper.checkSinglePath("assets", assets); - } - - /** - * Sets the value of the "androidjar" attribute. - * @param androidJar the value. - */ - public void setAndroidjar(Path androidJar) { - mAndroidJar = TaskHelper.checkSinglePath("androidjar", androidJar); - } - - /** - * Sets the value of the "outfolder" attribute. - * @param outFolder the value. - * @deprecated use {@link #setApkfolder(Path)} - */ - @Deprecated - public void setOutfolder(Path outFolder) { - System.out.println("WARNNG: Using deprecated 'outfolder' attribute in AaptExecLoopTask." + - "Use 'apkfolder' (path) instead."); - mApkFolder = TaskHelper.checkSinglePath("outfolder", outFolder); - } - - /** - * Sets the value of the "apkfolder" attribute. - * @param apkFolder the value. - */ - public void setApkfolder(Path apkFolder) { - mApkFolder = TaskHelper.checkSinglePath("apkfolder", apkFolder); - } - - /** - * Sets the value of the resourcefilename attribute - * @param apkName the value - */ - public void setResourcefilename(String apkName) { - mApkName = apkName; - } - - /** - * Sets the value of the "rfolder" attribute. - * @param rFolder the value. - */ - public void setRfolder(Path rFolder) { - mRFolder = TaskHelper.checkSinglePath("rfolder", rFolder); - } - - public void setresourcefilter(String filter) { - if (filter != null && filter.length() > 0) { - mResourceFilter = filter; - } - } - - public void setProjectLibrariesResName(String projectLibrariesResName) { - mProjectLibrariesResName = projectLibrariesResName; - } - - public void setProjectLibrariesPackageName(String projectLibrariesPackageName) { - mProjectLibrariesPackageName = projectLibrariesPackageName; - } - - - /** - * Returns an object representing a nested nocompress element. - */ - public Object createNocompress() { - NoCompress nc = new NoCompress(); - mNoCompressList.add(nc); - return nc; - } - - /** - * Returns an object representing a nested res element. - */ - public Object createRes() { - if (mResources == null) { - mResources = new ArrayList(); - } - - Path path = new Path(getProject()); - mResources.add(path); - - return path; - } - - /* - * (non-Javadoc) - * - * Executes the loop. Based on the values inside project.properties, this will - * create alternate temporary ap_ files. - * - * @see org.apache.tools.ant.Task#execute() - */ - @Override - public void execute() throws BuildException { - if (mProjectLibrariesResName == null) { - throw new BuildException("Missing attribute projectLibrariesResName"); - } - if (mProjectLibrariesPackageName == null) { - throw new BuildException("Missing attribute projectLibrariesPackageName"); - } - - Project taskProject = getProject(); - - String libPkgProp = null; - - // if the parameters indicate generation of the R class, check if - // more R classes need to be created for libraries. - if (mRFolder != null && new File(mRFolder).isDirectory()) { - libPkgProp = taskProject.getProperty(mProjectLibrariesPackageName); - if (libPkgProp != null) { - // Replace ";" with ":" since that's what aapt expects - libPkgProp = libPkgProp.replace(';', ':'); - } - } - // Call aapt. If there are libraries, we'll pass a non-null string of libs. - callAapt(libPkgProp); - } - - @Override - protected String getExecTaskName() { - return "aapt"; - } - - /** - * Calls aapt with the given parameters. - * @param resourceFilter the resource configuration filter to pass to aapt (if configName is - * non null) - * @param extraPackages an optional list of colon-separated packages. Can be null - * Ex: com.foo.one:com.foo.two:com.foo.lib - */ - private void callAapt(String extraPackages) { - Project taskProject = getProject(); - - final boolean generateRClass = mRFolder != null && new File(mRFolder).isDirectory(); - - // Get whether we have libraries - Object libResRef = taskProject.getReference(mProjectLibrariesResName); - - // Set up our folders to check for changed files - ArrayList watchPaths = new ArrayList(); - // We need to watch for changes in the main project res folder - for (Path pathList : mResources) { - for (String path : pathList.list()) { - watchPaths.add(new File(path)); - } - } - // and if libraries exist, in their res folders - if (libResRef instanceof Path) { - for (String path : ((Path)libResRef).list()) { - watchPaths.add(new File(path)); - } - } - // If we're here to generate a .ap_ file we need to watch assets as well - if (!generateRClass) { - File assetsDir = new File(mAssets); - if (mAssets != null && assetsDir.isDirectory()) { - watchPaths.add(assetsDir); - } - } - - // Now we figure out what we need to do - if (generateRClass) { - // Check to see if our dependencies have changed. If not, then skip - if (initDependencies(mRFolder + File.separator + "R.java.d", watchPaths) - && dependenciesHaveChanged() == false) { - System.out.println("No changed resources. R.java and Manifest.java untouched."); - return; - } - } else { - // Find our dependency file. It should have the same name as our target .ap_ but - // with a .d extension - String dependencyFilePath = mApkFolder + File.separator + mApkName; - dependencyFilePath += ".d"; - - // Check to see if our dependencies have changed - if (initDependencies(dependencyFilePath , watchPaths) - && dependenciesHaveChanged() == false) { - System.out.println("No changed resources or assets. " + mApkName - + " remains untouched"); - return; - } - if (mResourceFilter == null) { - System.out.println("Creating full resource package..."); - } else { - System.out.println(String.format( - "Creating resource package with filter: (%1$s)...", - mResourceFilter)); - } - } - - // create a task for the default apk. - ExecTask task = new ExecTask(); - task.setExecutable(mExecutable); - task.setFailonerror(true); - - task.setTaskName(getExecTaskName()); - - // aapt command. Only "package" is supported at this time really. - task.createArg().setValue(mCommand); - - // No crunch flag - if (mUseCrunchCache) { - task.createArg().setValue("--no-crunch"); - } - - // force flag - if (mForce) { - task.createArg().setValue("-f"); - } - - // verbose flag - if (mVerbose) { - task.createArg().setValue("-v"); - } - - if (mDebug) { - task.createArg().setValue("--debug-mode"); - } - - if (generateRClass) { - task.createArg().setValue("-m"); - } - - // filters if needed - if (mResourceFilter != null) { - task.createArg().setValue("-c"); - task.createArg().setValue(mResourceFilter); - } - - // no compress flag - // first look to see if there's a NoCompress object with no specified extension - boolean compressNothing = false; - for (NoCompress nc : mNoCompressList) { - if (nc.mExtension == null) { - task.createArg().setValue("-0"); - task.createArg().setValue(""); - compressNothing = true; - break; - } - } - - if (compressNothing == false) { - for (NoCompress nc : mNoCompressList) { - task.createArg().setValue("-0"); - task.createArg().setValue(nc.mExtension); - } - } - - if (extraPackages != null) { - task.createArg().setValue("--extra-packages"); - task.createArg().setValue(extraPackages); - } - - // if the project contains libraries, force auto-add-overlay - if (libResRef != null) { - task.createArg().setValue("--auto-add-overlay"); - } - - if (mVersionCode != 0) { - task.createArg().setValue("--version-code"); - task.createArg().setValue(Integer.toString(mVersionCode)); - } - - if ((mVersionName != null) && (mVersionName.length() > 0)) { - task.createArg().setValue("--version-name"); - task.createArg().setValue(mVersionName); - } - - // manifest location - if (mManifest != null) { - task.createArg().setValue("-M"); - task.createArg().setValue(mManifest); - } - - // resources locations. - if (mResources.size() > 0) { - for (Path pathList : mResources) { - for (String path : pathList.list()) { - // This may not exists, and aapt doesn't like it, so we check first. - File res = new File(path); - if (res.isDirectory()) { - task.createArg().setValue("-S"); - task.createArg().setValue(path); - } - } - } - } - - // add other resources coming from library project - if (libResRef instanceof Path) { - for (String path : ((Path)libResRef).list()) { - // This may not exists, and aapt doesn't like it, so we check first. - File res = new File(path); - if (res.isDirectory()) { - task.createArg().setValue("-S"); - task.createArg().setValue(path); - } - } - } - - // assets location. This may not exists, and aapt doesn't like it, so we check first. - if (mAssets != null && new File(mAssets).isDirectory()) { - task.createArg().setValue("-A"); - task.createArg().setValue(mAssets); - } - - // android.jar - if (mAndroidJar != null) { - task.createArg().setValue("-I"); - task.createArg().setValue(mAndroidJar); - } - - // apk file. This is based on the apkFolder, apkBaseName, and the configName (if applicable) - String filename = null; - if (mApkName != null) { - filename = mApkName; - } - - if (filename != null) { - File file = new File(mApkFolder, filename); - task.createArg().setValue("-F"); - task.createArg().setValue(file.getAbsolutePath()); - } - - // R class generation - if (generateRClass) { - task.createArg().setValue("-J"); - task.createArg().setValue(mRFolder); - } - - // Use dependency generation - task.createArg().setValue("--generate-dependencies"); - - // final setup of the task - task.setProject(taskProject); - task.setOwningTarget(getOwningTarget()); - - // execute it. - task.execute(); - } -} diff --git a/anttasks/src/com/android/ant/AaptExecTask.java b/anttasks/src/com/android/ant/AaptExecTask.java new file mode 100644 index 0000000..3e2350f --- /dev/null +++ b/anttasks/src/com/android/ant/AaptExecTask.java @@ -0,0 +1,530 @@ +/* + * 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 org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.ExecTask; +import org.apache.tools.ant.types.Path; + +import java.io.File; +import java.util.ArrayList; + +/** + * Task to execute aapt. + * + *

It does not follow the exec task format, instead it has its own parameters, which maps + * directly to aapt.

+ *

It is able to run aapt several times if library setup requires generating several + * R.java files. + *

The following map shows how to use the task for each supported aapt command line + * parameter.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Aapt OptionAnt NameType
path to aaptexecutableattribute (Path)
commandcommandattribute (String)
-vverboseattribute (boolean)
-fforceattribute (boolean)
-M AndroidManifest.xmlmanifestattribute (Path)
-I base-packageandroidjarattribute (Path)
-A asset-source-dirassetsattribute (Path
-S resource-sources<res path="">nested element(s)
with attribute (Path)
-0 extension<nocompress extension="">
<nocompress>
nested element(s)
with attribute (String)
-F apk-fileapkfolder
outfolder
apkbasename
basename
attribute (Path)
attribute (Path) deprecated
attribute (String)
attribute (String) deprecated
-J R-file-dirrfolderattribute (Path)
-m always enabled
+ */ +public final class AaptExecTask extends BaseTask { + + /** + * Class representing a <nocompress> node in the main task XML. + * This let the developers prevent compression of some files in assets/ and res/raw/ + * by extension. + * If the extension is null, this will disable compression for all files in assets/ and + * res/raw/ + */ + public final static class NoCompress { + String mExtension; + + /** + * Sets the value of the "extension" attribute. + * @param extention the extension. + */ + public void setExtension(String extention) { + mExtension = extention; + } + } + + private String mExecutable; + private String mCommand; + private boolean mForce = true; // true due to legacy reasons + private boolean mDebug = false; + private boolean mVerbose = false; + private boolean mUseCrunchCache = false; + private int mVersionCode = 0; + private String mVersionName; + private String mManifest; + private ArrayList mResources; + private String mAssets; + private String mAndroidJar; + private String mApkFolder; + private String mApkName; + private String mResourceFilter; + private String mRFolder; + private final ArrayList mNoCompressList = new ArrayList(); + private String mProjectLibrariesResName; + private String mProjectLibrariesPackageName; + + /** + * Sets the value of the "executable" attribute. + * @param executable the value. + */ + public void setExecutable(Path executable) { + mExecutable = TaskHelper.checkSinglePath("executable", executable); + } + + /** + * Sets the value of the "command" attribute. + * @param command the value. + */ + public void setCommand(String command) { + mCommand = command; + } + + /** + * Sets the value of the "force" attribute. + * @param force the value. + */ + public void setForce(boolean force) { + mForce = force; + } + + /** + * Sets the value of the "verbose" attribute. + * @param verbose the value. + */ + public void setVerbose(boolean verbose) { + mVerbose = verbose; + } + + /** + * Sets the value of the "usecrunchcache" attribute + * @param usecrunch whether to use the crunch cache. + */ + public void setNoCrunch(boolean nocrunch) { + mUseCrunchCache = nocrunch; + } + + public void setVersioncode(String versionCode) { + if (versionCode.length() > 0) { + try { + mVersionCode = Integer.decode(versionCode); + } catch (NumberFormatException e) { + System.out.println(String.format( + "WARNING: Ignoring invalid version code value '%s'.", versionCode)); + } + } + } + + /** + * Sets the value of the "versionName" attribute + * @param versionName the value + */ + public void setVersionname(String versionName) { + mVersionName = versionName; + } + + public void setDebug(boolean value) { + mDebug = value; + } + + /** + * Sets the value of the "manifest" attribute. + * @param manifest the value. + */ + public void setManifest(Path manifest) { + mManifest = TaskHelper.checkSinglePath("manifest", manifest); + } + + /** + * Sets the value of the "resources" attribute. + * @param resources the value. + * + * @deprecated Use nested element(s) + */ + @Deprecated + public void setResources(Path resources) { + System.out.println("WARNNG: Using deprecated 'resources' attribute in AaptExecLoopTask." + + "Use nested element(s) instead."); + if (mResources == null) { + mResources = new ArrayList(); + } + + mResources.add(new Path(getProject(), resources.toString())); + } + + /** + * Sets the value of the "assets" attribute. + * @param assets the value. + */ + public void setAssets(Path assets) { + mAssets = TaskHelper.checkSinglePath("assets", assets); + } + + /** + * Sets the value of the "androidjar" attribute. + * @param androidJar the value. + */ + public void setAndroidjar(Path androidJar) { + mAndroidJar = TaskHelper.checkSinglePath("androidjar", androidJar); + } + + /** + * Sets the value of the "outfolder" attribute. + * @param outFolder the value. + * @deprecated use {@link #setApkfolder(Path)} + */ + @Deprecated + public void setOutfolder(Path outFolder) { + System.out.println("WARNNG: Using deprecated 'outfolder' attribute in AaptExecLoopTask." + + "Use 'apkfolder' (path) instead."); + mApkFolder = TaskHelper.checkSinglePath("outfolder", outFolder); + } + + /** + * Sets the value of the "apkfolder" attribute. + * @param apkFolder the value. + */ + public void setApkfolder(Path apkFolder) { + mApkFolder = TaskHelper.checkSinglePath("apkfolder", apkFolder); + } + + /** + * Sets the value of the resourcefilename attribute + * @param apkName the value + */ + public void setResourcefilename(String apkName) { + mApkName = apkName; + } + + /** + * Sets the value of the "rfolder" attribute. + * @param rFolder the value. + */ + public void setRfolder(Path rFolder) { + mRFolder = TaskHelper.checkSinglePath("rfolder", rFolder); + } + + public void setresourcefilter(String filter) { + if (filter != null && filter.length() > 0) { + mResourceFilter = filter; + } + } + + public void setProjectLibrariesResName(String projectLibrariesResName) { + mProjectLibrariesResName = projectLibrariesResName; + } + + public void setProjectLibrariesPackageName(String projectLibrariesPackageName) { + mProjectLibrariesPackageName = projectLibrariesPackageName; + } + + + /** + * Returns an object representing a nested nocompress element. + */ + public Object createNocompress() { + NoCompress nc = new NoCompress(); + mNoCompressList.add(nc); + return nc; + } + + /** + * Returns an object representing a nested res element. + */ + public Object createRes() { + if (mResources == null) { + mResources = new ArrayList(); + } + + Path path = new Path(getProject()); + mResources.add(path); + + return path; + } + + /* + * (non-Javadoc) + * + * Executes the loop. Based on the values inside project.properties, this will + * create alternate temporary ap_ files. + * + * @see org.apache.tools.ant.Task#execute() + */ + @Override + public void execute() throws BuildException { + if (mProjectLibrariesResName == null) { + throw new BuildException("Missing attribute projectLibrariesResName"); + } + if (mProjectLibrariesPackageName == null) { + throw new BuildException("Missing attribute projectLibrariesPackageName"); + } + + Project taskProject = getProject(); + + String libPkgProp = null; + + // if the parameters indicate generation of the R class, check if + // more R classes need to be created for libraries. + if (mRFolder != null && new File(mRFolder).isDirectory()) { + libPkgProp = taskProject.getProperty(mProjectLibrariesPackageName); + if (libPkgProp != null) { + // Replace ";" with ":" since that's what aapt expects + libPkgProp = libPkgProp.replace(';', ':'); + } + } + // Call aapt. If there are libraries, we'll pass a non-null string of libs. + callAapt(libPkgProp); + } + + @Override + protected String getExecTaskName() { + return "aapt"; + } + + /** + * Calls aapt with the given parameters. + * @param resourceFilter the resource configuration filter to pass to aapt (if configName is + * non null) + * @param extraPackages an optional list of colon-separated packages. Can be null + * Ex: com.foo.one:com.foo.two:com.foo.lib + */ + private void callAapt(String extraPackages) { + Project taskProject = getProject(); + + final boolean generateRClass = mRFolder != null && new File(mRFolder).isDirectory(); + + // Get whether we have libraries + Object libResRef = taskProject.getReference(mProjectLibrariesResName); + + // Set up our folders to check for changed files + ArrayList watchPaths = new ArrayList(); + // We need to watch for changes in the main project res folder + for (Path pathList : mResources) { + for (String path : pathList.list()) { + watchPaths.add(new File(path)); + } + } + // and if libraries exist, in their res folders + if (libResRef instanceof Path) { + for (String path : ((Path)libResRef).list()) { + watchPaths.add(new File(path)); + } + } + // If we're here to generate a .ap_ file we need to watch assets as well + if (!generateRClass) { + File assetsDir = new File(mAssets); + if (mAssets != null && assetsDir.isDirectory()) { + watchPaths.add(assetsDir); + } + } + + // Now we figure out what we need to do + if (generateRClass) { + // Check to see if our dependencies have changed. If not, then skip + if (initDependencies(mRFolder + File.separator + "R.java.d", watchPaths) + && dependenciesHaveChanged() == false) { + System.out.println("No changed resources. R.java and Manifest.java untouched."); + return; + } else { + System.out.println("Generating resource IDs..."); + } + } else { + // Find our dependency file. It should have the same name as our target .ap_ but + // with a .d extension + String dependencyFilePath = mApkFolder + File.separator + mApkName; + dependencyFilePath += ".d"; + + // Check to see if our dependencies have changed + if (initDependencies(dependencyFilePath , watchPaths) + && dependenciesHaveChanged() == false) { + System.out.println("No changed resources or assets. " + mApkName + + " remains untouched"); + return; + } + if (mResourceFilter == null) { + System.out.println("Creating full resource package..."); + } else { + System.out.println(String.format( + "Creating resource package with filter: (%1$s)...", + mResourceFilter)); + } + } + + // create a task for the default apk. + ExecTask task = new ExecTask(); + task.setExecutable(mExecutable); + task.setFailonerror(true); + + task.setTaskName(getExecTaskName()); + + // aapt command. Only "package" is supported at this time really. + task.createArg().setValue(mCommand); + + // No crunch flag + if (mUseCrunchCache) { + task.createArg().setValue("--no-crunch"); + } + + // force flag + if (mForce) { + task.createArg().setValue("-f"); + } + + // verbose flag + if (mVerbose) { + task.createArg().setValue("-v"); + } + + if (mDebug) { + task.createArg().setValue("--debug-mode"); + } + + if (generateRClass) { + task.createArg().setValue("-m"); + } + + // filters if needed + if (mResourceFilter != null) { + task.createArg().setValue("-c"); + task.createArg().setValue(mResourceFilter); + } + + // no compress flag + // first look to see if there's a NoCompress object with no specified extension + boolean compressNothing = false; + for (NoCompress nc : mNoCompressList) { + if (nc.mExtension == null) { + task.createArg().setValue("-0"); + task.createArg().setValue(""); + compressNothing = true; + break; + } + } + + if (compressNothing == false) { + for (NoCompress nc : mNoCompressList) { + task.createArg().setValue("-0"); + task.createArg().setValue(nc.mExtension); + } + } + + if (extraPackages != null) { + task.createArg().setValue("--extra-packages"); + task.createArg().setValue(extraPackages); + } + + // if the project contains libraries, force auto-add-overlay + if (libResRef != null) { + task.createArg().setValue("--auto-add-overlay"); + } + + if (mVersionCode != 0) { + task.createArg().setValue("--version-code"); + task.createArg().setValue(Integer.toString(mVersionCode)); + } + + if ((mVersionName != null) && (mVersionName.length() > 0)) { + task.createArg().setValue("--version-name"); + task.createArg().setValue(mVersionName); + } + + // manifest location + if (mManifest != null) { + task.createArg().setValue("-M"); + task.createArg().setValue(mManifest); + } + + // resources locations. + if (mResources.size() > 0) { + for (Path pathList : mResources) { + for (String path : pathList.list()) { + // This may not exists, and aapt doesn't like it, so we check first. + File res = new File(path); + if (res.isDirectory()) { + task.createArg().setValue("-S"); + task.createArg().setValue(path); + } + } + } + } + + // add other resources coming from library project + if (libResRef instanceof Path) { + for (String path : ((Path)libResRef).list()) { + // This may not exists, and aapt doesn't like it, so we check first. + File res = new File(path); + if (res.isDirectory()) { + task.createArg().setValue("-S"); + task.createArg().setValue(path); + } + } + } + + // assets location. This may not exists, and aapt doesn't like it, so we check first. + if (mAssets != null && new File(mAssets).isDirectory()) { + task.createArg().setValue("-A"); + task.createArg().setValue(mAssets); + } + + // android.jar + if (mAndroidJar != null) { + task.createArg().setValue("-I"); + task.createArg().setValue(mAndroidJar); + } + + // apk file. This is based on the apkFolder, apkBaseName, and the configName (if applicable) + String filename = null; + if (mApkName != null) { + filename = mApkName; + } + + if (filename != null) { + File file = new File(mApkFolder, filename); + task.createArg().setValue("-F"); + task.createArg().setValue(file.getAbsolutePath()); + } + + // R class generation + if (generateRClass) { + task.createArg().setValue("-J"); + task.createArg().setValue(mRFolder); + } + + // Use dependency generation + task.createArg().setValue("--generate-dependencies"); + + // final setup of the task + task.setProject(taskProject); + task.setOwningTarget(getOwningTarget()); + + // execute it. + task.execute(); + } +} diff --git a/anttasks/src/com/android/ant/AidlExecTask.java b/anttasks/src/com/android/ant/AidlExecTask.java index 5fa1f30..98cac9b 100644 --- a/anttasks/src/com/android/ant/AidlExecTask.java +++ b/anttasks/src/com/android/ant/AidlExecTask.java @@ -27,7 +27,10 @@ import org.apache.tools.ant.types.PatternSet.NameEntry; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; +import java.util.List; +import java.util.Set; /** * Task to execute aidl. @@ -93,21 +96,57 @@ public class AidlExecTask extends Task { } } - // now loop on all the source folders to find all the aidl to compile - // and compile them - for (String sourceFolder : sourceFolders) { - // create a fileset to find all the aidl files in the current source folder - FileSet fs = new FileSet(); - fs.setProject(taskProject); - fs.setDir(new File(sourceFolder)); - NameEntry include = fs.createInclude(); - include.setName("**/*.aidl"); - - // loop through the results of the file set - Iterator iter = fs.iterator(); - while (iter.hasNext()) { - Object next = iter.next(); + // gather all the aidl files from all the source folders. + Set sourceFiles = getFileListByExtension(taskProject, sourceFolders, "**/*.aidl"); + if (sourceFiles.size() > 0) { + System.out.println(String.format("Found %d aidl files.", sourceFiles.size())); + } + + // go look for all dependency files in the gen folder. + Set depFiles = getFileListByExtension(taskProject, mGenFolder, "**/*.d"); + + // parse all the dep files and keep the ones that are aidl and check if they require + // compilation again. + ArrayList toCompile = new ArrayList(); + ArrayList toRemove = new ArrayList(); + ArrayList depsToRemove = new ArrayList(); + for (String depFile : depFiles) { + DependencyGraph graph = new DependencyGraph(depFile, null /*watchPaths*/); + + // get the source file. it's the first item in the pre-reqs + List preReqs = graph.getPrereqs(); + File sourceFile = preReqs.get(0); + String sourceFilePath = sourceFile.getAbsolutePath(); + + // The gen folder may contain other dependency files not generated by aidl. + // We only care if the first pre-rep is an aidl file. + if (sourceFilePath.toLowerCase().endsWith(".aidl")) { + // remove from the list of sourceFiles to mark as "processed" (but not compiled + // yet, that'll be done by adding it to toCompile) + if (sourceFiles.remove(sourceFilePath) == false) { + // looks like the source file does not exist anymore! + // we'll have to remove the output! + List outputFiles = graph.getTargets(); + toRemove.addAll(outputFiles); + + // also need to remove the dep file. + depsToRemove.add(depFile); + } else if (graph.dependenciesHaveChanged(null /*extensionsToCheck*/, + false /*printStatus*/)) { + // need to recompile! + toCompile.add(sourceFilePath); + } + } + } + + // add to the list of files to compile, whatever is left in sourceFiles. Those are + // new files that have never been compiled. + toCompile.addAll(sourceFiles); + if (toCompile.size() > 0) { + System.out.println(String.format("Compiling %d aidl files.", toCompile.size())); + + for (String toCompilePath : toCompile) { ExecTask task = new ExecTask(); task.setProject(taskProject); task.setOwningTarget(getOwningTarget()); @@ -123,11 +162,69 @@ public class AidlExecTask extends Task { task.createArg().setValue("-I" + importFolder); } - task.createArg().setValue(next.toString()); + // set auto dependency file creation + task.createArg().setValue("-a"); + + task.createArg().setValue(toCompilePath); // execute it. task.execute(); } + } else { + System.out.println(String.format("No aidl files to compile.")); + } + + if (toRemove.size() > 0) { + System.out.println(String.format("%d obsolete output files to remove.", + toRemove.size())); + for (File toRemoveFile : toRemove) { + if (toRemoveFile.delete() == false) { + System.err.println("Failed to remove " + toRemoveFile.getAbsolutePath()); + } + } + } + + // remove the dependency files that are obsolete + if (depsToRemove.size() > 0) { + System.out.println(String.format("%d obsolete dependency files to remove.", + depsToRemove.size())); + for (String path : depsToRemove) { + if (new File(path).delete() == false) { + System.err.println("Failed to remove " + path); + } + } + } + } + + private Set getFileListByExtension(Project taskProject, + List sourceFolders, String filter) { + HashSet sourceFiles = new HashSet(); + for (String sourceFolder : sourceFolders) { + sourceFiles.addAll(getFileListByExtension(taskProject, sourceFolder, filter)); } + + return sourceFiles; } + + private Set getFileListByExtension(Project taskProject, + String sourceFolder, String filter) { + HashSet sourceFiles = new HashSet(); + + // create a fileset to find all the files in the folder + FileSet fs = new FileSet(); + fs.setProject(taskProject); + fs.setDir(new File(sourceFolder)); + NameEntry include = fs.createInclude(); + include.setName(filter); + + // loop through the results of the file set + Iterator iter = fs.iterator(); + while (iter.hasNext()) { + sourceFiles.add(iter.next().toString()); + } + + return sourceFiles; + } + + } diff --git a/anttasks/src/com/android/ant/BaseTask.java b/anttasks/src/com/android/ant/BaseTask.java index 0ff7bf1..68dd6b5 100644 --- a/anttasks/src/com/android/ant/BaseTask.java +++ b/anttasks/src/com/android/ant/BaseTask.java @@ -102,6 +102,7 @@ public abstract class BaseTask extends Task { } assert mDependencies != null : "Dependencies have not been initialized"; - return mDependencies.dependenciesHaveChanged(mRestrictTouchedExtensionsTo); + return mDependencies.dependenciesHaveChanged(mRestrictTouchedExtensionsTo, + true /*printStatus*/); } } diff --git a/anttasks/src/com/android/ant/DependencyGraph.java b/anttasks/src/com/android/ant/DependencyGraph.java index 4c85860..11c09e4 100644 --- a/anttasks/src/com/android/ant/DependencyGraph.java +++ b/anttasks/src/com/android/ant/DependencyGraph.java @@ -23,7 +23,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -34,8 +34,10 @@ import java.util.Set; public class DependencyGraph { // Files that we know about from the dependency file - private Set mTargets = Collections.emptySet(); - private Set mPrereqs = mTargets; + private List mTargets = Collections.emptyList(); + private List mPrereqs = mTargets; + private boolean mMissingDepFile = false; + private long mDepFileLastModified; private final ArrayList mWatchPaths; public DependencyGraph(String dependencyFilePath, ArrayList watchPaths) { @@ -48,34 +50,45 @@ public class DependencyGraph { * @param extensionsToCheck a set of extensions. Only files with an extension in this set will * be considered for a modification check. All deleted/created files will still be * checked. If this is null, all files will be checked for modification date + * @param printStatus will print to {@link System#out} the dependencies status. * @return true if new prerequisites have appeared, target files are missing or if * prerequisite files have been modified since the last target generation. */ - public boolean dependenciesHaveChanged(Set extensionsToCheck) { - boolean noFile = (mTargets.size() == 0); + public boolean dependenciesHaveChanged(Set extensionsToCheck, boolean printStatus) { boolean missingPrereq = missingPrereqFile(); boolean newPrereq = newPrereqFile(); boolean missingTarget = missingTargetFile(); boolean modPrereq = modifiedPrereq(extensionsToCheck); - if (noFile) { - System.out.println("No Dependency File Found"); - } - if (missingPrereq) { - System.out.println("Found Deleted Prereq File"); - } - if (newPrereq) { - System.out.println("Found New Prereq File"); - } - if (missingTarget) { - System.out.println("Found Deleted Target File"); - } - if (modPrereq) { - System.out.println("Found Modified Prereq File"); + if (printStatus) { + if (mMissingDepFile) { + System.out.println("No Dependency File Found"); + } + if (missingPrereq) { + System.out.println("Found Deleted Prereq File"); + } + if (newPrereq) { + System.out.println("Found New Prereq File"); + } + if (missingTarget) { + System.out.println("Found Deleted Target File"); + } + if (modPrereq) { + System.out.println("Found Modified Prereq File"); + } } + // If no dependency file has been set up, then we'll just return true // if we have a dependency file, we'll check to see what's been changed - return noFile || missingPrereq || newPrereq || missingTarget || modPrereq; + return mMissingDepFile || missingPrereq || newPrereq || missingTarget || modPrereq; + } + + public List getTargets() { + return mTargets; + } + + public List getPrereqs() { + return mPrereqs; } /** @@ -84,6 +97,16 @@ public class DependencyGraph { * @param dependencyFilePath the dependency file */ private void parseDependencyFile(String dependencyFilePath) { + // first check if the dependency file is here. + File depFile = new File(dependencyFilePath); + if (depFile.isFile() == false) { + mMissingDepFile = true; + return; + } + + // get the modification time of the dep file as we may need it later + mDepFileLastModified = depFile.lastModified(); + // Read in our dependency file String content = readFile(dependencyFilePath); if (content == null) { @@ -121,14 +144,20 @@ public class DependencyGraph { prereqs = files[1].trim().split(" "); } - mTargets = new HashSet(targets.length); + mTargets = new ArrayList(targets.length); for (String path : targets) { - mTargets.add(new File(path)); + if (path.length() > 0) { + mTargets.add(new File(path)); + } } - mPrereqs = new HashSet(prereqs.length); + mTargets = Collections.unmodifiableList(mTargets); + mPrereqs = new ArrayList(prereqs.length); for (String path : prereqs) { - mPrereqs.add(new File(path)); + if (path.length() > 0) { + mPrereqs.add(new File(path)); + } } + mPrereqs = Collections.unmodifiableList(mPrereqs); } /** @@ -137,9 +166,11 @@ public class DependencyGraph { * @return true if a new file is encountered in the dependency folders */ private boolean newPrereqFile() { - for (File dir : mWatchPaths) { - if (newFileInTree(dir)) { - return true; + if (mWatchPaths != null) { + for (File dir : mWatchPaths) { + if (newFileInTree(dir)) { + return true; + } } } // If we make it all the way through our directories we're good. @@ -212,9 +243,14 @@ public class DependencyGraph { private boolean modifiedPrereq(Set extensionsToCheck) { // Find the oldest target long oldestTarget = Long.MAX_VALUE; - for (File target : mTargets) { - if (target.lastModified() < oldestTarget) { - oldestTarget = target.lastModified(); + // if there's no output, then compare to the time of the dependency file. + if (mTargets.size() == 0) { + oldestTarget = mDepFileLastModified; + } else { + for (File target : mTargets) { + if (target.lastModified() < oldestTarget) { + oldestTarget = target.lastModified(); + } } } @@ -273,4 +309,5 @@ public class DependencyGraph { // Don't include the leading '.' in the extension return filename.substring(filename.lastIndexOf('.') + 1); } + } diff --git a/anttasks/src/com/android/ant/RenderScriptTask.java b/anttasks/src/com/android/ant/RenderScriptTask.java index 5aa6612..08eeeed 100644 --- a/anttasks/src/com/android/ant/RenderScriptTask.java +++ b/anttasks/src/com/android/ant/RenderScriptTask.java @@ -105,6 +105,8 @@ public class RenderScriptTask extends Task { File exe = new File(mExecutable); String execTaskName = exe.getName(); + int count = 0; + // now loop on all the source folders to find all the renderscript to compile // and compile them for (String sourceFolder : sourceFolders) { @@ -145,5 +147,12 @@ public class RenderScriptTask extends Task { task.execute(); } } + + if (count > 0) { + System.out.println(String.format("Compiled %d renderscript files.", count)); + } else { + System.out.println("No renderscript files to compile."); + } + } } -- cgit v1.1