Add BuildConfig to ant SDK project.

The BuildConfig class contains a single boolean constant
called DEBUG which is true only for debug/instrumented builds.

This allows developers to create debug only code that's automatically
stripped from release builds.

Also fixed some issues with dependency check for other tasks, notably
the aapt task that would always find new files due to not filtering
out files that are ignored by aapt itself (hidden files, version control
files, etc...)

Change-Id: I4391a87c064a185d6b337ca46e3a9f0e43c5174d
diff --git a/anttasks/src/com/android/ant/AaptExecTask.java b/anttasks/src/com/android/ant/AaptExecTask.java
index 45adc7c..8731732 100644
--- a/anttasks/src/com/android/ant/AaptExecTask.java
+++ b/anttasks/src/com/android/ant/AaptExecTask.java
@@ -25,6 +25,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Task to execute aapt.
@@ -95,6 +96,46 @@
     private boolean mNonConstantId;
 
     /**
+     * Input path that ignores the same file that aapt does.
+     */
+    private static class ResFolderInputPath extends InputPath {
+        public ResFolderInputPath(File file, Set<String> extensionsToCheck) {
+            super(file, extensionsToCheck);
+        }
+
+        @Override
+        public boolean ignores(File file) {
+            String name = file.getName();
+            char firstChar = name.charAt(0);
+
+            if (firstChar == '.' || (firstChar == '_' && file.isDirectory()) ||
+                    name.charAt(name.length()-1) == '~') {
+                return true;
+            }
+
+            if ("CVS".equals(name) ||
+                    "thumbs.db".equalsIgnoreCase(name) ||
+                    "picasa.ini".equalsIgnoreCase(name)) {
+                return true;
+            }
+
+            String ext = getExtension(name);
+            if ("scc".equalsIgnoreCase(ext)) {
+                return true;
+            }
+
+            return false;
+        }
+    }
+
+    private final static InputPathFactory sPathFactory = new InputPathFactory() {
+
+        public InputPath createPath(File file, Set<String> extensionsToCheck) {
+            return new ResFolderInputPath(file, extensionsToCheck);
+        }
+    };
+
+    /**
      * Sets the value of the "executable" attribute.
      * @param executable the value.
      */
@@ -351,7 +392,8 @@
         if (generateRClass) {
             // in this case we only want to run aapt if an XML file was touched, or if any
             // file is added/removed
-            List<InputPath> inputPaths = getInputPaths(paths, Collections.singleton("xml"));
+            List<InputPath> inputPaths = getInputPaths(paths, Collections.singleton("xml"),
+                    sPathFactory);
 
             // let's not forget the manifest as an input path (with no extension restrictions).
             if (mManifest != null) {
@@ -369,7 +411,8 @@
         } else {
             // in this case we want to run aapt if any file was updated/removed/added in any of the
             // input paths
-            List<InputPath> inputPaths = getInputPaths(paths, null /*extensionsToCheck*/);
+            List<InputPath> inputPaths = getInputPaths(paths, null /*extensionsToCheck*/,
+                    sPathFactory);
 
             // let's not forget the manifest as an input path.
             if (mManifest != null) {
diff --git a/anttasks/src/com/android/ant/BuildConfigTask.java b/anttasks/src/com/android/ant/BuildConfigTask.java
new file mode 100644
index 0000000..08f91e9
--- /dev/null
+++ b/anttasks/src/com/android/ant/BuildConfigTask.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 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.internal.build.BuildConfigGenerator;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Path;
+
+import java.io.IOException;
+
+public class BuildConfigTask extends BuildTypedTask {
+
+    private String mGenFolder;
+    private String mAppPackage;
+
+    public void setGenFolder(Path path) {
+        mGenFolder = TaskHelper.checkSinglePath("genFolder", path);
+    }
+
+    public void setPackage(String appPackage) {
+        mAppPackage = appPackage;
+    }
+
+
+    @Override
+    public void execute() throws BuildException {
+        if (mGenFolder == null) {
+            throw new BuildException("Missing attribute genFolder");
+        }
+        if (mAppPackage == null) {
+            throw new BuildException("Missing attribute package");
+        }
+
+        if (hasBuildTypeChanged()) {
+            if (isNewBuild()) {
+                System.out.println("Generating BuildConfig class.");
+            } else {
+                System.out.println("Build type changed: Generating new BuildConfig class.");
+            }
+            BuildConfigGenerator generator = new BuildConfigGenerator(
+                    mGenFolder, mAppPackage,
+                    Boolean.parseBoolean(getBuildType()));
+
+            try {
+                generator.generate();
+            } catch (IOException e) {
+                throw new BuildException("Failed to create BuildConfig class", e);
+            }
+        } else {
+            System.out.println("No need to generate new BuildConfig.");
+        }
+    }
+}
diff --git a/anttasks/src/com/android/ant/BuildTypedTask.java b/anttasks/src/com/android/ant/BuildTypedTask.java
new file mode 100644
index 0000000..c697bac
--- /dev/null
+++ b/anttasks/src/com/android/ant/BuildTypedTask.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 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.Task;
+
+/**
+ * Base class for tasks that should exec when the build type change.
+ */
+public abstract class BuildTypedTask extends Task {
+
+    private String mPreviousBuildType;
+    private String mBuildType;
+
+    /** Sets the current build type */
+    public void setBuildType(String buildType) {
+        mBuildType = buildType;
+    }
+
+    /** Sets the previous build type */
+    public void setPreviousBuildType(String previousBuildType) {
+        mPreviousBuildType = previousBuildType;
+    }
+
+    protected String getBuildType() {
+        return mBuildType;
+    }
+
+    /**
+     * Returns if it is a new build. If the built type is not input
+     * from the XML, this always returns true.
+     * A build type is defined by having an empty previousBuildType.
+     */
+    protected boolean isNewBuild() {
+        return mBuildType == null || mPreviousBuildType.length() == 0;
+    }
+
+    /**
+     * Returns true if the build type changed.
+     */
+    protected boolean hasBuildTypeChanged() {
+        // no build type? return false as the feature is simply not used
+        if (mBuildType == null && mPreviousBuildType == null) {
+            return false;
+        }
+
+        return mBuildType.equals(mPreviousBuildType) == false;
+    }
+}
diff --git a/anttasks/src/com/android/ant/DependencyGraph.java b/anttasks/src/com/android/ant/DependencyGraph.java
index 1885c17..8671359 100644
--- a/anttasks/src/com/android/ant/DependencyGraph.java
+++ b/anttasks/src/com/android/ant/DependencyGraph.java
@@ -35,6 +35,8 @@
  */
 public class DependencyGraph {
 
+    private final static boolean DEBUG = false;
+
     private static enum DependencyStatus {
         NONE, NEW_FILE, UPDATED_FILE, MISSING_FILE, ERROR;
     }
@@ -189,6 +191,9 @@
         mPrereqs = new HashSet<File>(prereqs.length);
         for (String path : prereqs) {
             if (path.length() > 0) {
+                if (DEBUG) {
+                    System.out.println("PREREQ: " + path);
+                }
                 File f = new File(path);
                 if (mFirstPrereq == null) {
                     mFirstPrereq = f;
@@ -290,12 +295,19 @@
         // files to go through manually
         if (mPrereqs.remove(file) == false) {
             // turns out this is a new file!
+
+            if (DEBUG) {
+                System.out.println("NEW FILE: " + file.getAbsolutePath());
+            }
             return DependencyStatus.NEW_FILE;
         } else {
             // check the time stamp on this file if it's a file we care about based what the
             // input folder decides.
             if (inputFolder.checksForModification(file)) {
                 if (file.lastModified() > oldestTarget) {
+                    if (DEBUG) {
+                        System.out.println("UPDATED FILE: " + file.getAbsolutePath());
+                    }
                     return DependencyStatus.UPDATED_FILE;
                 }
             }
@@ -319,6 +331,9 @@
         // Loop through our prereq files and make sure they still exist
         for (File prereq : mPrereqs) {
             if (prereq.exists() == false) {
+                if (DEBUG) {
+                    System.out.println("MISSING FILE: " + prereq.getAbsolutePath());
+                }
                 return DependencyStatus.MISSING_FILE;
             }
 
@@ -336,6 +351,10 @@
                             // check if we need to check this type of file, and if yes, check it.
                             if (input.checksForModification(prereq)) {
                                 if (prereq.lastModified() > oldestTarget) {
+                                    if (DEBUG) {
+                                        System.out.println(
+                                                "UPDATED FILE: " + prereq.getAbsolutePath());
+                                    }
                                     return DependencyStatus.UPDATED_FILE;
                                 }
                             }
@@ -345,6 +364,10 @@
                         if (prereq.equals(inputFile)) {
                             if (input.checksForModification(prereq)) {
                                 if (prereq.lastModified() > oldestTarget) {
+                                    if (DEBUG) {
+                                        System.out.println(
+                                                "UPDATED FILE: " + prereq.getAbsolutePath());
+                                    }
                                     return DependencyStatus.UPDATED_FILE;
                                 }
                             }
@@ -354,6 +377,9 @@
             } else {
                 // no input? we consider all files.
                 if (prereq.lastModified() > oldestTarget) {
+                    if (DEBUG) {
+                        System.out.println("UPDATED FILE: " + prereq.getAbsolutePath());
+                    }
                     return DependencyStatus.UPDATED_FILE;
                 }
             }
diff --git a/anttasks/src/com/android/ant/DexExecTask.java b/anttasks/src/com/android/ant/DexExecTask.java
index 6be0a98..2d9479e 100644
--- a/anttasks/src/com/android/ant/DexExecTask.java
+++ b/anttasks/src/com/android/ant/DexExecTask.java
@@ -129,7 +129,8 @@
         String depFile = mOutput + ".d";
 
         // get InputPath with no extension restrictions
-        List<InputPath> inputPaths = getInputPaths(paths, null /*extensionsToCheck*/);
+        List<InputPath> inputPaths = getInputPaths(paths, null /*extensionsToCheck*/,
+                null /*factory*/);
 
         if (initDependencies(depFile, inputPaths) && dependenciesHaveChanged() == false) {
             System.out.println(
diff --git a/anttasks/src/com/android/ant/InputPath.java b/anttasks/src/com/android/ant/InputPath.java
index 3327385..b1a98b5 100644
--- a/anttasks/src/com/android/ant/InputPath.java
+++ b/anttasks/src/com/android/ant/InputPath.java
@@ -72,23 +72,33 @@
      * @return true if the file or folder are ignored.
      */
     public boolean ignores(File file) {
-        return false;
+        // always ignore hidden files/folders.
+        return file.getName().startsWith(".") == false;
     }
 
     /**
      *  Gets the extension (if present) on a file by looking at the filename
-     *  @param file the file to get the extension of
+     *  @param file the file to get the extension from
      *  @return the extension if present, or the empty string if the filename doesn't have
      *          and extension.
      */
    protected static String getExtension(File file) {
-       String filename = file.getName();
-       int index = filename.lastIndexOf('.');
+       return getExtension(file.getName());
+   }
+
+   /**
+    *  Gets the extension (if present) on a file by looking at the filename
+    *  @param fileName the filename to get the extension from
+    *  @return the extension if present, or the empty string if the filename doesn't have
+    *          and extension.
+    */
+   protected static String getExtension(String fileName) {
+       int index = fileName.lastIndexOf('.');
        if (index == -1) {
            return "";
        }
        // Don't include the leading '.' in the extension
-       return filename.substring(index + 1);
+       return fileName.substring(index + 1);
    }
 
 }
diff --git a/anttasks/src/com/android/ant/SingleDependencyTask.java b/anttasks/src/com/android/ant/SingleDependencyTask.java
index dc3e15d..926e59c 100644
--- a/anttasks/src/com/android/ant/SingleDependencyTask.java
+++ b/anttasks/src/com/android/ant/SingleDependencyTask.java
@@ -17,7 +17,6 @@
 package com.android.ant;
 
 import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.Task;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -29,22 +28,22 @@
 /**
  * A base class for ant tasks that use a single dependency files to control (re)execution.
  */
-public abstract class SingleDependencyTask extends Task {
+public abstract class SingleDependencyTask extends BuildTypedTask {
 
     private DependencyGraph mDependencies;
-    private String mPreviousBuildType;
-    private String mBuildType;
-
-    public void setPreviousBuildType(String previousBuildType) {
-        mPreviousBuildType = previousBuildType;
-    }
-
-    public void setBuildType(String buildType) {
-        mBuildType = buildType;
-    }
 
     protected abstract String getExecTaskName();
 
+    protected interface InputPathFactory {
+        InputPath createPath(File file, Set<String> extensionsToCheck);
+    }
+
+    private final static InputPathFactory sDefaultFactory = new InputPathFactory() {
+        public InputPath createPath(File file, Set<String> extensionsToCheck) {
+            return new InputPath(file, extensionsToCheck);
+        }
+    };
+
     /**
      * Creates a list of {@link InputPath} from a list of {@link File} and an optional list of
      * extensions. All the {@link InputPath} will share the same extension restrictions.
@@ -55,11 +54,15 @@
      * @return a list of {@link InputPath}
      */
     protected static List<InputPath> getInputPaths(List<File> paths,
-            Set<String> extensionsToCheck) {
+            Set<String> extensionsToCheck, InputPathFactory factory) {
         List<InputPath> result = new ArrayList<InputPath>(paths.size());
 
+        if (factory == null ) {
+            factory = sDefaultFactory;
+        }
+
         for (File f : paths) {
-            result.add(new InputPath(f, extensionsToCheck));
+            result.add(factory.createPath(f, extensionsToCheck));
         }
 
         return result;
@@ -73,7 +76,7 @@
      * @return true if the dependency graph was successfully initialized
      */
     protected boolean initDependencies(String dependencyFile, List<InputPath> inputPaths) {
-        if (mBuildType != null && mBuildType.equals(mPreviousBuildType) == false) {
+        if (hasBuildTypeChanged()) {
             // we don't care about deps, we need to execute the task no matter what.
             return true;
         }
@@ -93,15 +96,19 @@
      *         have changed since the last run
      */
     protected boolean dependenciesHaveChanged() {
-        if (mBuildType != null && mBuildType.equals(mPreviousBuildType) == false) {
-            String execName = getExecTaskName();
-            if (execName == null) {
-                System.out.println(
-                        "Current build type is different than previous build: forced task run.");
-            } else {
-                System.out.println(
-                        "Current build type is different than previous build: forced " +
-                        execName + " run.");
+        if (hasBuildTypeChanged()) {
+            // if this is not a new build, display that build type change is forcing running
+            // the task.
+            if (isNewBuild() == false) {
+                String execName = getExecTaskName();
+                if (execName == null) {
+                    System.out.println(
+                            "Current build type is different than previous build: forced task run.");
+                } else {
+                    System.out.println(
+                            "Current build type is different than previous build: forced " +
+                            execName + " run.");
+                }
             }
             return true;
         }
diff --git a/files/ant/build.xml b/files/ant/build.xml
index cc8f398..91dbb1f 100644
--- a/files/ant/build.xml
+++ b/files/ant/build.xml
@@ -7,7 +7,7 @@
         regular projects, library projects, or test projects.
 
         At the beginning of the file is a list of properties that can be overridden
-        by adding them to your build.properties (properties are immutable, so their
+        by adding them to your ant.properties (properties are immutable, so their
         first definition sticks and is never changed).
 
         Follows:
@@ -23,7 +23,9 @@
         - help target
     -->
 
-    <!-- ********** Overrideable Properties ********** -->
+    <!-- ******************************************************* -->
+    <!-- *************** Overrideable Properties *************** -->
+    <!-- ******************************************************* -->
 
     <!-- You can override these values in your build.xml or build.properties.
          Overriding any other properties may result in broken build. -->
@@ -52,7 +54,9 @@
     <!-- Verbosity -->
     <property name="verbose" value="false" />
 
-    <!-- ********** Custom Tasks ********** -->
+    <!-- ******************************************************* -->
+    <!-- ********************* Custom Tasks ******************** -->
+    <!-- ******************************************************* -->
 
     <!-- jar file from where the tasks are loaded -->
     <path id="android.antlibs">
@@ -76,6 +80,10 @@
             classname="com.android.ant.RenderScriptTask"
             classpathref="android.antlibs" />
 
+    <taskdef name="buildconfig"
+            classname="com.android.ant.BuildConfigTask"
+            classpathref="android.antlibs" />
+
     <taskdef name="dex"
             classname="com.android.ant.DexExecTask"
             classpathref="android.antlibs" />
@@ -106,7 +114,9 @@
     <!-- End of emma configuration -->
 
 
-    <!-- ********** Other Properties ********** -->
+    <!-- ******************************************************* -->
+    <!-- ******************* Other Properties ****************** -->
+    <!-- ******************************************************* -->
     <!-- overriding these properties may break the build
          unless the whole file is updated -->
 
@@ -174,7 +184,9 @@
     <!-- properties for packaging -->
     <property name="build.packaging.nocrunch" value="true" />
 
-    <!-- ********** Macros ********** -->
+    <!-- ******************************************************* -->
+    <!-- ************************ Macros *********************** -->
+    <!-- ******************************************************* -->
 
     <!-- macro to do a task on if project.is.library is false.
          elseText attribute is displayed otherwise -->
@@ -244,9 +256,7 @@
             <dex executable="${dx}"
                     output="${intermediate.dex.file}"
                     nolocals="@{nolocals}"
-                    verbose="${verbose}"
-                    previousBuildType="${build.last.target}"
-                    buildType="${build.target}">
+                    verbose="${verbose}">
                 <path path="${out.dex.input.absolute.dir}"/>
                 <path refid="out.dex.jar.input.ref" />
                 <external-libs />
@@ -351,7 +361,9 @@
         </sequential>
     </macrodef>
 
-    <!-- ********** Build Targets ********** -->
+    <!-- ******************************************************* -->
+    <!-- ******************** Build Targets ******************** -->
+    <!-- ******************************************************* -->
 
     <!-- this target simply force running -setup making
          the project info be read. To be used as
@@ -444,13 +456,12 @@
 
         <!-- read the previous build mode -->
         <property file="${out.build.prop.file}" />
-        <!-- if empty the prop won't be set, so set it to the current target
-             to provide a default value equal to the current build -->
-        <property name="build.last.target" value="${build.target}" />
-        <!-- also set the default value for whether the build is instrumented -->
-        <property name="build.last.is.instrumented" value="${build.is.instrumented}" />
-        <property name="build.last.is.packaging.debug" value="${build.is.packaging.debug}" />
-        <property name="build.last.is.signing.debug" value="${build.is.signing.debug}" />
+        <!-- if empty the props won't be set, meaning it's a new build.
+             To force a build, set the prop to empty values. -->
+        <property name="build.last.target" value="" />
+        <property name="build.last.is.instrumented" value="" />
+        <property name="build.last.is.packaging.debug" value="" />
+        <property name="build.last.is.signing.debug" value="" />
 
         <!-- compile the libraries if any -->
         <if>
@@ -502,20 +513,39 @@
             <path refid="project.libraries.jars" />
         </path>
 
-        <!-- special case for instrumented: if the previous build was
-             instrumented but not this one, clear out the compiled code -->
+        <!-- If the "debug" build type changed, clear out the compiled code.
+             This is to make sure the new BuildConfig.DEBUG value is picked up
+             as javac can't deal with this type of change in its dependency computation. -->
         <if>
             <condition>
                 <and>
-                    <istrue value="${build.last.is.instrumented}" />
-                    <isfalse value="${build.is.instrumented}" />
+                    <length string="${build.last.is.packaging.debug}" trim="true" when="greater" length="0" />
+                    <not><equals
+                            arg1="${build.is.packaging.debug}"
+                            arg2="${build.last.is.packaging.debug}" /></not>
                 </and>
             </condition>
             <then>
-                <echo>Switching from instrumented to non-instrumented build.</echo>
-                <echo>Deleting previous compilation output:</echo>
+                <echo>Switching between debug and non debug build: Deleting previous compilation output...</echo>
                 <delete dir="${out.classes.absolute.dir}" verbose="${verbose}" />
             </then>
+            <else>
+                <!-- Else, we may still need to clean the code, for another reason.
+                     special case for instrumented: if the previous build was
+                     instrumented but not this one, clear out the compiled code -->
+                <if>
+                    <condition>
+                        <and>
+                            <istrue value="${build.last.is.instrumented}" />
+                            <isfalse value="${build.is.instrumented}" />
+                        </and>
+                    </condition>
+                    <then>
+                        <echo>Switching from instrumented to non-instrumented build: Deleting previous compilation output...</echo>
+                        <delete dir="${out.classes.absolute.dir}" verbose="${verbose}" />
+                    </then>
+                </if>
+            </else>
         </if>
 
         <echo>Creating output directories if needed...</echo>
@@ -569,6 +599,17 @@
                 <res path="${out.res.absolute.dir}" />
                 <res path="${resource.absolute.dir}" />
             </aapt>
+
+            <echo>----------</echo>
+            <echo>Handling BuildConfig class...</echo>
+            <xpath input="AndroidManifest.xml" expression="/manifest/@package"
+                    output="manifest.package" />
+            <buildconfig
+                    genFolder="${gen.absolute.dir}"
+                    package="${manifest.package}"
+                    buildType="${build.is.packaging.debug}"
+                    previousBuildType="${build.last.is.packaging.debug}"/>
+
         </do-only-if-manifest-hasCode>
     </target>
 
@@ -593,7 +634,7 @@
             </condition>
             <javac encoding="${java.encoding}"
                     source="${java.source}" target="${java.target}"
-                    debug="true" extdirs=""
+                    debug="true" extdirs="" includeantruntime="false"
                     destdir="${out.classes.absolute.dir}"
                     bootclasspathref="android.target.classpath"
                     verbose="${verbose}"
@@ -817,7 +858,9 @@
                 message="Cannot run two different modes at the same time. If you are running more than one debug/release/instrument type targets, call them from different Ant calls." />
     </target>
 
-    <!-- ********** Debug specific targets ********** -->
+    <!-- ******************************************************* -->
+    <!-- **************** Debug specific targets *************** -->
+    <!-- ******************************************************* -->
 
     <target name="-set-debug-files" depends="-set-mode-check">
 
@@ -863,7 +906,9 @@
     </target>
 
 
-    <!-- ********** Release specific targets ********** -->
+    <!-- ******************************************************* -->
+    <!-- *************** Release specific targets ************** -->
+    <!-- ******************************************************* -->
 
     <!-- called through target 'release'. Only executed if the keystore and
          key alias are known but not their password. -->
@@ -974,7 +1019,9 @@
         <record-build-info />
     </target>
 
-    <!-- ********** Instrumented specific targets ********** -->
+    <!-- ******************************************************* -->
+    <!-- ************ Instrumented specific targets ************ -->
+    <!-- ******************************************************* -->
 
     <!-- These targets are specific for the project under test when it
          gets compiled by the test projects in a way that will make it
@@ -1001,7 +1048,9 @@
         <record-build-info />
     </target>
 
-    <!-- ********** Test project specific targets ********** -->
+    <!-- ******************************************************* -->
+    <!-- ************ Test project specific targets ************ -->
+    <!-- ******************************************************* -->
 
     <!-- enable code coverage -->
     <target name="emma">
@@ -1089,7 +1138,9 @@
     </target>
 
 
-    <!-- ********** Install/uninstall specific targets ********** -->
+    <!-- ******************************************************* -->
+    <!-- ********** Install/uninstall specific targets ********* -->
+    <!-- ******************************************************* -->
 
     <target name="install"
                 description="Installs the newly build package. Must be used in conjunction with a build target
@@ -1107,7 +1158,7 @@
                             <resourceexists>
                                 <file file="${out.final.file}"/>
                             </resourceexists>
-                            </condition>
+                        </condition>
                         <then>
                             <echo>Installing ${out.final.file} onto default emulator or device...</echo>
                             <exec executable="${adb}" failonerror="true">
@@ -1198,6 +1249,10 @@
     </target>
 
 
+    <!-- ******************************************************* -->
+    <!-- ************************* Help ************************ -->
+    <!-- ******************************************************* -->
+
     <target name="help">
         <!-- displays starts at col 13
               |13                                                              80| -->
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java
index a475e1b..774d9f4 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/build/ApkBuilder.java
@@ -949,6 +949,8 @@
             "class".equalsIgnoreCase(extension) == false &&         // Java class files
             "scc".equalsIgnoreCase(extension) == false &&           // VisualSourceSafe
             "swp".equalsIgnoreCase(extension) == false &&           // vi swap file
+            "thumbs.db".equalsIgnoreCase(fileName) == false &&      // image index file
+            "picasa.ini".equalsIgnoreCase(fileName) == false &&     // image index file
             "package.html".equalsIgnoreCase(fileName) == false &&   // Javadoc
             "overview.html".equalsIgnoreCase(fileName) == false;    // Javadoc
     }
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/BuildConfig.template b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/BuildConfig.template
new file mode 100644
index 0000000..0344b55
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/BuildConfig.template
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package #PACKAGE#;
+
+public final class BuildConfig {
+    public final static boolean DEBUG = #DEBUG#;
+}
\ No newline at end of file
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/BuildConfigGenerator.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/BuildConfigGenerator.java
new file mode 100644
index 0000000..fb84bfd
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/BuildConfigGenerator.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2011 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.sdklib.internal.build;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Class able to generate a BuildConfig class in Android project.
+ * The BuildConfig class contains constants related to the build target.
+ */
+public class BuildConfigGenerator {
+
+    private final static String PH_PACKAGE = "#PACKAGE#";
+    private final static String PH_DEBUG = "#DEBUG#";
+
+    private final String mGenFolder;
+    private final String mAppPackage;
+    private final boolean mDebug;
+
+    /**
+     * Creates a generator
+     * @param genFolder the gen folder of the project
+     * @param appPackage the application package
+     * @param debug whether it's a debug build
+     */
+    public BuildConfigGenerator(String genFolder, String appPackage, boolean debug) {
+        mGenFolder = genFolder;
+        mAppPackage = appPackage;
+        mDebug = debug;
+    }
+
+    /**
+     * Generates the BuildConfig class.
+     */
+    public void generate() throws IOException {
+        String template = readEmbeddedTextFile("BuildConfig.template");
+
+        Map<String, String> map = new HashMap<String, String>();
+        map.put(PH_PACKAGE, mAppPackage);
+        map.put(PH_DEBUG, Boolean.toString(mDebug));
+
+        String content = replaceParameters(template, map);
+
+        File genFolder = new File(mGenFolder);
+        File pkgFolder = new File(genFolder, mAppPackage.replaceAll("\\.", File.separator));
+        if (pkgFolder.isDirectory() == false) {
+            pkgFolder.mkdirs();
+        }
+
+        File buildConfigJava = new File(pkgFolder, "BuildConfig.java");
+        writeFile(buildConfigJava, content);
+    }
+
+    /**
+     * Reads and returns the content of a text file embedded in the jar file.
+     * @param filepath the file path to the text file
+     * @return null if the file could not be read
+     * @throws IOException
+     */
+    private String readEmbeddedTextFile(String filepath) throws IOException {
+        InputStream is = BuildConfigGenerator.class.getResourceAsStream(filepath);
+        if (is != null) {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+
+            String line;
+            StringBuilder total = new StringBuilder(reader.readLine());
+            while ((line = reader.readLine()) != null) {
+                total.append('\n');
+                total.append(line);
+            }
+
+            return total.toString();
+        }
+
+        // this really shouldn't happen unless the sdklib packaging is broken.
+        throw new IOException("BuildConfig template is missing!");
+    }
+
+    private void writeFile(File file, String content) throws IOException {
+        FileOutputStream fos = null;
+        try {
+            fos = new FileOutputStream(file);
+            InputStream source = new ByteArrayInputStream(content.getBytes("UTF-8"));
+
+            byte[] buffer = new byte[1024];
+            int count = 0;
+            while ((count = source.read(buffer)) != -1) {
+                fos.write(buffer, 0, count);
+            }
+        } finally {
+            if (fos != null) {
+                fos.close();
+            }
+        }
+    }
+
+    /**
+     * Replaces placeholders found in a string with values.
+     *
+     * @param str the string to search for placeholders.
+     * @param parameters a map of <placeholder, Value> to search for in the string
+     * @return A new String object with the placeholder replaced by the values.
+     */
+    private String replaceParameters(String str, Map<String, String> parameters) {
+
+        for (Entry<String, String> entry : parameters.entrySet()) {
+            String value = entry.getValue();
+            if (value != null) {
+                str = str.replaceAll(entry.getKey(), value);
+            }
+        }
+
+        return str;
+    }
+}