Support for multi module setups

This CL adds support for building multi module apps
with proper context.

For library modules, we only generate base classes in the initial
compilation. We also generate a temporary BR file which does
not have final methods.

When final app is being generated, all layout binders, adapters
and Bindable information gets merged and all final classes
are generated in their appropriate packages.

This CL also adds support for Test runs and any
number of build variants.

Bug: 19714904
Change-Id: I9b50b54db05f3fa206eec33709d43c2ac94a9e5e
diff --git a/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/BuildInfoUtil.java b/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/BuildInfoUtil.java
new file mode 100644
index 0000000..bd24233
--- /dev/null
+++ b/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/BuildInfoUtil.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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.databinding.annotationprocessor;
+
+import com.google.common.base.Preconditions;
+
+import android.binding.BindingBuildInfo;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+
+public class BuildInfoUtil {
+    private static BindingBuildInfo sCached;
+    public static BindingBuildInfo load(RoundEnvironment roundEnvironment) {
+        if (sCached == null) {
+            sCached = extractNotNull(roundEnvironment, BindingBuildInfo.class);
+        }
+        return sCached;
+    }
+
+    private static <T extends Annotation> T extractNotNull(RoundEnvironment roundEnv,
+            Class<T> annotationClass) {
+        T result = null;
+        for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) {
+            final T info = element.getAnnotation(annotationClass);
+            if (info == null) {
+                continue; // It gets confused between BindingAppInfo and BinderBundle
+            }
+            Preconditions.checkState(result == null, "Should have only one %s",
+                    annotationClass.getCanonicalName());
+            result = info;
+        }
+        return result;
+
+    }
+}
diff --git a/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessBindable.java b/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessBindable.java
index ca554db..099a0ea 100644
--- a/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessBindable.java
+++ b/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessBindable.java
@@ -1,30 +1,39 @@
+/*
+ * Copyright (C) 2015 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.databinding.annotationprocessor;
 
-import com.android.databinding.reflection.ModelAnalyzer;
+import com.google.common.base.Preconditions;
 
-import org.apache.commons.io.IOUtils;
+import com.android.databinding.util.GenerationalClassUtil;
+import com.android.databinding.util.L;
 
 import android.binding.Bindable;
+import android.binding.BindingBuildInfo;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
 import java.io.Serializable;
-import java.io.Writer;
-import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
-import javax.annotation.processing.AbstractProcessor;
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedAnnotationTypes;
 import javax.annotation.processing.SupportedSourceVersion;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
@@ -34,91 +43,100 @@
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
 import javax.lang.model.type.TypeKind;
-import javax.tools.Diagnostic;
-import javax.tools.FileObject;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardLocation;
 
-@SupportedAnnotationTypes({"android.binding.Bindable"})
+// binding app info and library info are necessary to trigger this.
 @SupportedSourceVersion(SourceVersion.RELEASE_7)
-public class ProcessBindable extends AbstractProcessor {
-    Intermediate mProperties = new IntermediateV1();
-
-    public ProcessBindable() {
-    }
+public class ProcessBindable extends ProcessDataBinding.ProcessingStep {
+    private static final String INTERMEDIATE_FILE_EXT = "-br.bin";
+    Intermediate mProperties;
 
     @Override
-    public synchronized void init(ProcessingEnvironment processingEnv) {
-        super.init(processingEnv);
-        ModelAnalyzer.setProcessingEnvironment(processingEnv);
-    }
-
-    @Override
-    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+    public boolean onHandleStep(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv,
+            BindingBuildInfo buildInfo) {
+        if (mProperties == null) {
+            mProperties = new IntermediateV1(buildInfo.modulePackage());
+        }
         for (Element element : roundEnv.getElementsAnnotatedWith(Bindable.class)) {
             Element enclosingElement = element.getEnclosingElement();
             ElementKind kind = enclosingElement.getKind();
             if (kind != ElementKind.CLASS && kind != ElementKind.INTERFACE) {
-                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                        "Bindable must be on a member field or method. The enclosing type is " +
-                            enclosingElement.getKind(), element);
-                continue;
+                L.e("Bindable must be on a member field or method. The enclosing type is %s",
+                        enclosingElement.getKind());
             }
             TypeElement enclosing = (TypeElement) enclosingElement;
             String name = getPropertyName(element);
             if (name != null) {
+                Preconditions.checkNotNull(mProperties, "Must receive app / library info before "
+                        + "Bindable fields.");
                 mProperties.addProperty(enclosing.getQualifiedName().toString(), name);
             }
         }
-        if (roundEnv.processingOver()) {
-            writeIntermediateFile(mProperties);
-            generateBR(mProperties);
-        }
-        return true;
+        return false;
     }
 
-    private void generateBR(Intermediate intermediate) {
-        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
-                "************* Generating BR file from Bindable attributes");
-        HashSet<String> properties = new HashSet<>();
-        intermediate.captureProperties(properties);
-        mergeClassPathResources(properties);
-        try {
-            ArrayList<String> sortedProperties = new ArrayList<String>();
-            sortedProperties.addAll(properties);
-            Collections.sort(sortedProperties);
-
-            JavaFileObject fileObject = processingEnv.getFiler()
-                    .createSourceFile("android.binding.BR");
-            Writer writer = fileObject.openWriter();
-            writer.write("package android.binding;\n\n" +
-                            "public final class BR {\n" +
-                            "    public static final int _all = 0;\n"
-            );
-            int id = 0;
-            for (String property : sortedProperties) {
-                id++;
-                writer.write("    public static final int " + property + " = " + id + ";\n");
-            }
-            writer.write("    public static int getId(String key) {\n");
-            writer.write("        switch(key) {\n");
-            id = 0;
-            for (String property : sortedProperties) {
-                id++;
-                writer.write("            case \"" + property + "\": return " + id + ";\n");
-            }
-            writer.write("        }\n");
-            writer.write("        return -1;\n");
-            writer.write("    }");
-            writer.write("}\n");
-
-            writer.close();
-        } catch (IOException e) {
-            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                    "Could not generate BR file " + e.getLocalizedMessage());
+    @Override
+    public void onProcessingOver(RoundEnvironment roundEnvironment,
+            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+        if (mProperties != null) {
+            GenerationalClassUtil.writeIntermediateFile(processingEnvironment,
+                    mProperties.getPackage(),
+                    createIntermediateFileName(mProperties.getPackage()), mProperties);
+            generateBRClasses(!buildInfo.isLibrary(), mProperties.getPackage());
         }
     }
 
+    private String createIntermediateFileName(String appPkg) {
+        return appPkg + INTERMEDIATE_FILE_EXT;
+    }
+
+    private void generateBRClasses(boolean useFinalFields, String pkg) {
+        L.d("************* Generating BR file %s. use final: %s", pkg, useFinalFields);
+        HashSet<String> properties = new HashSet<>();
+        mProperties.captureProperties(properties);
+        List<Intermediate> previousIntermediates = loadPreviousBRFiles();
+        for (Intermediate intermediate : previousIntermediates) {
+            intermediate.captureProperties(properties);
+        }
+        writeBRClass(useFinalFields, pkg, properties);
+        if (useFinalFields) {
+            // generate BR for all previous packages
+            for (Intermediate intermediate : previousIntermediates) {
+                writeBRClass(true, intermediate.getPackage(),
+                        properties);
+            }
+        }
+    }
+
+    private void writeBRClass(boolean useFinalFields, String pkg, HashSet<String> properties) {
+        ArrayList<String> sortedProperties = new ArrayList<String>();
+        sortedProperties.addAll(properties);
+        Collections.sort(sortedProperties);
+        StringBuilder out = new StringBuilder();
+        String modifier = "public static " + (useFinalFields ? "final" : "") + " int ";
+        out.append("package " + pkg + ";\n\n" +
+                        "public class BR {\n" +
+                        "    " + modifier + "_all = 0;\n"
+        );
+        int id = 0;
+        for (String property : sortedProperties) {
+            id++;
+            out.append("    " + modifier + property + " = " + id + ";\n");
+        }
+        out.append("    public static int getId(String key) {\n");
+        out.append("        switch(key) {\n");
+        id = 0;
+        for (String property : sortedProperties) {
+            id++;
+            out.append("            case \"" + property + "\": return " + id + ";\n");
+        }
+        out.append("        }\n");
+        out.append("        return -1;\n");
+        out.append("    }");
+        out.append("}\n");
+
+        getWriter().writeToFile(pkg + ".BR", out.toString() );
+    }
+
     private String getPropertyName(Element element) {
         switch (element.getKind()) {
             case FIELD:
@@ -126,8 +144,7 @@
             case METHOD:
                 return stripPrefixFromMethod((ExecutableElement) element);
             default:
-                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                        "@Bindable is not allowed on " + element.getKind(), element);
+                L.e("@Bindable is not allowed on %s", element.getKind());
                 return null;
         }
     }
@@ -159,8 +176,7 @@
         } else if (isBooleanGetter(element)) {
             propertyName = name.subSequence(2, name.length());
         } else {
-            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                    "@Bindable associated with method must follow JavaBeans convention", element);
+            L.e("@Bindable associated with method must follow JavaBeans convention %s", element);
             return null;
         }
         char firstChar = propertyName.charAt(0);
@@ -207,102 +223,32 @@
                 element.getReturnType().getKind() == TypeKind.BOOLEAN;
     }
 
-    private Intermediate readIntermediateFile() {
-        Intermediate properties = null;
-        ObjectInputStream in = null;
-        try {
-            FileObject intermediate = processingEnv.getFiler()
-                    .getResource(StandardLocation.CLASS_OUTPUT,
-                            ProcessBindable.class.getPackage().getName(), "binding_properties.bin");
-            if (new File(intermediate.getName()).exists()) {
-                in = new ObjectInputStream(intermediate.openInputStream());
-                properties = (Intermediate) in.readObject();
-            }
-        } catch (IOException e) {
-            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                    "Could not read Binding properties intermediate file: " +
-                    e.getLocalizedMessage());
-        } catch (ClassNotFoundException e) {
-            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                    "Could not read Binding properties intermediate file: " +
-                            e.getLocalizedMessage());
-        } finally {
-            try {
-                if (in != null) {
-                    in.close();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
-        if (properties == null) {
-            properties = new IntermediateV1();
-        }
-        return properties;
+    private List<Intermediate> loadPreviousBRFiles() {
+        return GenerationalClassUtil
+                .loadObjects(getClass().getClassLoader(),
+                        new GenerationalClassUtil.ExtensionFilter(INTERMEDIATE_FILE_EXT));
     }
 
-    private void mergeClassPathResources(HashSet<String> intermediateProperties) {
-        try {
-            String resourcePath = ProcessBindable.class.getPackage().getName()
-                    .replace('.', '/') + "/binding_properties.bin";
-            Enumeration<URL> resources = getClass().getClassLoader()
-                    .getResources(resourcePath);
-            while (resources.hasMoreElements()) {
-                URL url = resources.nextElement();
-                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
-                        "Merging binding adapters from " + url);
-                InputStream inputStream = null;
-                try {
-                    inputStream = url.openStream();
-                    ObjectInputStream in = new ObjectInputStream(inputStream);
-                    Intermediate properties = (Intermediate) in.readObject();
-                    if (properties != null) {
-                        properties.captureProperties(intermediateProperties);
-                    }
-                } catch (IOException e) {
-                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                            "Could not merge in Bindables from " + url + ": " +
-                                    e.getLocalizedMessage());
-                } catch (ClassNotFoundException e) {
-                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                            "Could not read Binding properties intermediate file: " +
-                                    e.getLocalizedMessage());
-                } finally {
-                    IOUtils.closeQuietly(inputStream);
-                }
-            }
-        } catch (IOException e) {
-            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                    "Could not read Binding properties intermediate file: " +
-                            e.getLocalizedMessage());
-        }
-    }
+    private interface Intermediate extends Serializable {
 
-    private void writeIntermediateFile(Intermediate properties) {
-        try {
-            FileObject intermediate = processingEnv.getFiler().createResource(
-                    StandardLocation.CLASS_OUTPUT, ProcessBindable.class.getPackage().getName(),
-                    "binding_properties.bin");
-            ObjectOutputStream out = new ObjectOutputStream(intermediate.openOutputStream());
-            out.writeObject(properties);
-            out.close();
-        } catch (IOException e) {
-            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                    "Could not write to intermediate file: " + e.getLocalizedMessage());
-        }
-    }
-
-    private interface Intermediate {
         void captureProperties(Set<String> properties);
 
         void addProperty(String className, String propertyName);
+
+        String getPackage();
     }
 
     private static class IntermediateV1 implements Serializable, Intermediate {
-        private static final long serialVersionUID = 1L;
 
+        private static final long serialVersionUID = 2L;
+
+        private String mPackage;
         private final HashMap<String, HashSet<String>> mProperties = new HashMap<>();
 
+        public IntermediateV1(String aPackage) {
+            mPackage = aPackage;
+        }
+
         @Override
         public void captureProperties(Set<String> properties) {
             for (HashSet<String> propertySet : mProperties.values()) {
@@ -319,5 +265,10 @@
             }
             properties.add(propertyName);
         }
+
+        @Override
+        public String getPackage() {
+            return mPackage;
+        }
     }
 }
diff --git a/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessDataBinding.java b/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessDataBinding.java
new file mode 100644
index 0000000..dd64840
--- /dev/null
+++ b/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessDataBinding.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 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.databinding.annotationprocessor;
+
+import com.android.databinding.reflection.ModelAnalyzer;
+import com.android.databinding.writer.AnnotationJavaFileWriter;
+import com.android.databinding.writer.JavaFileWriter;
+
+import android.binding.BindingBuildInfo;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+
+@SupportedAnnotationTypes({
+        "android.binding.BindingAdapter",
+        "android.binding.Untaggable",
+        "android.binding.BindingMethods",
+        "android.binding.BindingConversion",
+        "android.binding.BindingBuildInfo"}
+)
+@SupportedSourceVersion(SourceVersion.RELEASE_7)
+/**
+ * Parent annotation processor that dispatches sub steps to ensure execution order.
+ * Use initProcessingSteps to add a new step.
+ */
+public class ProcessDataBinding extends AbstractProcessor {
+    private List<ProcessingStep> mProcessingSteps;
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        if (mProcessingSteps == null) {
+            initProcessingSteps();
+        }
+        final BindingBuildInfo buildInfo = BuildInfoUtil.load(roundEnv);
+        if (buildInfo == null) {
+            return false;
+        }
+        boolean done = true;
+        for (ProcessingStep step : mProcessingSteps) {
+            done = step.runStep(roundEnv, processingEnv, buildInfo) && done;
+        }
+        if (roundEnv.processingOver()) {
+            for (ProcessingStep step : mProcessingSteps) {
+                step.onProcessingOver(roundEnv, processingEnv, buildInfo);
+            }
+        }
+        return done;
+    }
+
+    private void initProcessingSteps() {
+        mProcessingSteps = Arrays.asList(
+                new ProcessMethodAdapters(),
+                new ProcessExpressions(),
+                new ProcessBindable()
+        );
+        AnnotationJavaFileWriter javaFileWriter = new AnnotationJavaFileWriter(processingEnv);
+        for (ProcessingStep step : mProcessingSteps) {
+            step.mJavaFileWriter = javaFileWriter;
+        }
+    }
+
+    @Override
+    public synchronized void init(ProcessingEnvironment processingEnv) {
+        super.init(processingEnv);
+        ModelAnalyzer.setProcessingEnvironment(processingEnv);
+    }
+
+    /**
+     * To ensure execution order and binding build information, we use processing steps.
+     */
+    public abstract static class ProcessingStep {
+        private boolean mDone;
+        private JavaFileWriter mJavaFileWriter;
+
+        protected JavaFileWriter getWriter() {
+            return mJavaFileWriter;
+        }
+
+        private boolean runStep(RoundEnvironment roundEnvironment,
+                ProcessingEnvironment processingEnvironment,
+                BindingBuildInfo buildInfo) {
+            if (mDone) {
+                return true;
+            }
+            mDone = onHandleStep(roundEnvironment, processingEnvironment, buildInfo);
+            return mDone;
+        }
+
+        /**
+         * Invoked in each annotation processing step.
+         *
+         * @return True if it is done and should never be invoked again.
+         */
+        abstract public boolean onHandleStep(RoundEnvironment roundEnvironment,
+                ProcessingEnvironment processingEnvironment,
+                BindingBuildInfo buildInfo);
+
+        /**
+         * Invoked when processing is done. A good place to generate the output if the
+         * processor requires multiple steps.
+         */
+        abstract public void onProcessingOver(RoundEnvironment roundEnvironment,
+                ProcessingEnvironment processingEnvironment,
+                BindingBuildInfo buildInfo);
+    }
+}
diff --git a/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessExpressions.java b/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessExpressions.java
index 2b047bb..7f5ab94 100644
--- a/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessExpressions.java
+++ b/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessExpressions.java
@@ -19,114 +19,171 @@
 import com.android.databinding.CompilerChef;
 import com.android.databinding.reflection.SdkUtil;
 import com.android.databinding.store.ResourceBundle;
+import com.android.databinding.util.GenerationalClassUtil;
 import com.android.databinding.util.L;
-import com.android.databinding.writer.AnnotationJavaFileWriter;
 
-import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 
-import android.binding.BinderBundle;
-import android.binding.BindingAppInfo;
+import android.binding.BindingBuildInfo;
 
-import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.FilenameFilter;
 import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.util.Set;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
-import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.annotation.processing.SupportedSourceVersion;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic;
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Unmarshaller;
 
-@SupportedAnnotationTypes({"android.binding.BinderBundle", "android.binding.BindingAppInfo"})
-@SupportedSourceVersion(SourceVersion.RELEASE_7)
-public class ProcessExpressions extends AbstractProcessor {
+public class ProcessExpressions extends ProcessDataBinding.ProcessingStep {
 
-    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-        ResourceBundle resourceBundle = null;
-        for (Element element : roundEnv.getElementsAnnotatedWith(BindingAppInfo.class)) {
-            final BindingAppInfo appInfo = element.getAnnotation(BindingAppInfo.class);
-            if (appInfo == null) {
-                continue; // It gets confused between BindingAppInfo and BinderBundle
-            }
-            SdkUtil.initialize(appInfo.minSdk(), new File(appInfo.sdkRoot()));
-            if (element.getKind() != ElementKind.CLASS) {
-                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                        "BindingAppInfo associated with wrong type. Should be a class.", element);
-                continue;
-            }
-            if (resourceBundle == null) {
-                resourceBundle = new ResourceBundle(appInfo.applicationPackage());
-                processLayouts(resourceBundle, roundEnv);
-            } else {
-                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                        "BindingAppInfo must be applied to only one class.", element);
-            }
+    private static final String LAYOUT_INFO_FILE_SUFFIX = "-layoutinfo.bin";
+
+    @Override
+    public boolean onHandleStep(RoundEnvironment roundEnvironment,
+            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+        ResourceBundle resourceBundle;
+        SdkUtil.initialize(buildInfo.minSdk(), new File(buildInfo.sdkRoot()));
+        resourceBundle = new ResourceBundle(buildInfo.modulePackage());
+        List<Intermediate> intermediateList =
+                GenerationalClassUtil.loadObjects(getClass().getClassLoader(),
+                        new GenerationalClassUtil.ExtensionFilter(LAYOUT_INFO_FILE_SUFFIX));
+        IntermediateV1 mine = createIntermediateFromLayouts(buildInfo.layoutInfoDir());
+        if (mine != null) {
+            mine.removeOverridden(intermediateList);
+            intermediateList.add(mine);
+            saveIntermediate(processingEnvironment, buildInfo, mine);
         }
-
+        // generate them here so that bindable parser can read
+        try {
+            generateBinders(resourceBundle, buildInfo, intermediateList);
+        } catch (Throwable t) {
+            L.e(t, "cannot generate view binders");
+        }
         return true;
     }
 
-    private void processLayouts(ResourceBundle resourceBundle, RoundEnvironment roundEnv) {
-        Unmarshaller unmarshaller = null;
-        for (Element element : roundEnv.getElementsAnnotatedWith(BinderBundle.class)) {
-            final BinderBundle binderBundle = element.getAnnotation(BinderBundle.class);
-            if (binderBundle == null) {
-                continue;
-            }
-            if (element.getKind() != ElementKind.CLASS) {
-                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                        "BinderBundle associated with wrong type. Should be a class.", element);
-                continue;
-            }
-            ByteArrayInputStream in = null;
-            try {
-                if (unmarshaller == null) {
-                    JAXBContext context =
-                            JAXBContext.newInstance(ResourceBundle.LayoutFileBundle.class);
-                    unmarshaller = context.createUnmarshaller();
-                }
-                String binderBundle64 = binderBundle.value();
-                byte[] buf = Base64.decodeBase64(binderBundle64);
-                in = new ByteArrayInputStream(buf);
-                Reader reader = new InputStreamReader(in);
-                ResourceBundle.LayoutFileBundle layoutFileBundle
-                        = (ResourceBundle.LayoutFileBundle)
-                        unmarshaller.unmarshal(reader);
-                resourceBundle
-                        .addLayoutBundle(layoutFileBundle, layoutFileBundle.getLayoutId());
-            } catch (Exception e) {
-                StringWriter stringWriter = new StringWriter();
-                e.printStackTrace(new PrintWriter(stringWriter));
-                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                        "Could not generate Binders from binder data store: " +
-                                stringWriter.getBuffer().toString(), element);
-            } finally {
-                if (in != null) {
-                    IOUtils.closeQuietly(in);
-                }
-            }
+    private void saveIntermediate(ProcessingEnvironment processingEnvironment,
+            BindingBuildInfo buildInfo, IntermediateV1 intermediate) {
+        GenerationalClassUtil.writeIntermediateFile(processingEnvironment,
+                buildInfo.modulePackage(), buildInfo.modulePackage() + LAYOUT_INFO_FILE_SUFFIX,
+                intermediate);
+    }
 
+    @Override
+    public void onProcessingOver(RoundEnvironment roundEnvironment,
+            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+
+    }
+
+    private void generateBinders(ResourceBundle resourceBundle, BindingBuildInfo buildInfo,
+            List<Intermediate> intermediates)
+            throws Throwable {
+        for (Intermediate intermediate : intermediates) {
+            intermediate.appendTo(resourceBundle);
+        }
+        writeResourceBundle(resourceBundle, buildInfo.isLibrary());
+    }
+
+    private IntermediateV1 createIntermediateFromLayouts(String layoutInfoFolderPath) {
+        final File layoutInfoFolder = new File(layoutInfoFolderPath);
+        if (!layoutInfoFolder.isDirectory()) {
+            L.d("layout info folder does not exist, skipping for %s", layoutInfoFolderPath);
+            return null;
+        }
+        IntermediateV1 result = new IntermediateV1();
+        for (File layoutFile : layoutInfoFolder.listFiles(new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                return name.endsWith(".xml");
+            }
+        })) {
+            try {
+                result.addEntry(layoutFile.getName(), FileUtils.readFileToString(layoutFile));
+            } catch (IOException e) {
+                L.e(e, "cannot load layout file information. Try a clean build");
+            }
+        }
+        return result;
+    }
+
+    private void writeResourceBundle(ResourceBundle resourceBundle, boolean forLibraryModule)
+            throws JAXBException {
+        CompilerChef compilerChef = CompilerChef.createChef(resourceBundle, getWriter());
+        if (compilerChef.hasAnythingToGenerate()) {
+            compilerChef.writeViewBinderInterfaces();
+            if (!forLibraryModule) {
+                compilerChef.writeDbrFile();
+                compilerChef.writeViewBinders();
+            }
+        }
+    }
+
+    public static interface Intermediate extends Serializable {
+
+        Intermediate upgrade();
+
+        public void appendTo(ResourceBundle resourceBundle) throws Throwable;
+    }
+
+    public static class IntermediateV1 implements Intermediate {
+
+        transient Unmarshaller mUnmarshaller;
+
+        // name to xml content map
+        Map<String, String> mLayoutInfoMap = new HashMap<>();
+
+        @Override
+        public Intermediate upgrade() {
+            return this;
         }
 
-        CompilerChef compilerChef = CompilerChef.createChef(resourceBundle,
-                new AnnotationJavaFileWriter(processingEnv));
-        if (compilerChef.hasAnythingToGenerate()) {
-            compilerChef.writeDbrFile();
-            compilerChef.writeViewBinderInterfaces();
-            compilerChef.writeViewBinders();
+        @Override
+        public void appendTo(ResourceBundle resourceBundle) throws JAXBException {
+            if (mUnmarshaller == null) {
+                JAXBContext context = JAXBContext
+                        .newInstance(ResourceBundle.LayoutFileBundle.class);
+                mUnmarshaller = context.createUnmarshaller();
+            }
+            for (String content : mLayoutInfoMap.values()) {
+                final InputStream is = IOUtils.toInputStream(content);
+                try {
+                    final ResourceBundle.LayoutFileBundle bundle
+                            = (ResourceBundle.LayoutFileBundle) mUnmarshaller.unmarshal(is);
+                    resourceBundle.addLayoutBundle(bundle, bundle.getLayoutId());
+                    L.d("loaded layout info file %s", bundle);
+                } finally {
+                    IOUtils.closeQuietly(is);
+                }
+            }
+        }
+
+        public void addEntry(String name, String contents) {
+            mLayoutInfoMap.put(name, contents);
+        }
+
+        public void removeOverridden(List<Intermediate> existing) {
+            // this is the way we get rid of files that are copied from previous modules
+            // it is important to do this before saving the intermediate file
+            for (Intermediate old : existing) {
+                if (old instanceof IntermediateV1) {
+                    IntermediateV1 other = (IntermediateV1) old;
+                    for (String key : other.mLayoutInfoMap.keySet()) {
+                        // TODO we should consider the original file as the key here
+                        // but aapt probably cannot provide that information
+                        if (mLayoutInfoMap.remove(key) != null) {
+                            L.d("removing %s from bundle because it came from another module", key);
+                        }
+                    }
+                }
+            }
         }
     }
 }
diff --git a/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessMethodAdapters.java b/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessMethodAdapters.java
index 6ed5632..a13e3a2 100644
--- a/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessMethodAdapters.java
+++ b/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessMethodAdapters.java
@@ -15,24 +15,25 @@
  */
 package com.android.databinding.annotationprocessor;
 
+import com.google.common.base.Preconditions;
+
 import android.binding.BindingAdapter;
+import android.binding.BindingBuildInfo;
 import android.binding.BindingConversion;
 import android.binding.BindingMethod;
 import android.binding.BindingMethods;
 import android.binding.Untaggable;
 
+import com.android.databinding.reflection.ModelAnalyzer;
 import com.android.databinding.store.SetterStore;
+import com.android.databinding.util.L;
 
 import java.io.IOException;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
-import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.annotation.processing.SupportedSourceVersion;
-import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ElementKind;
 import javax.lang.model.element.ExecutableElement;
@@ -42,50 +43,46 @@
 import javax.lang.model.type.TypeKind;
 import javax.tools.Diagnostic;
 
-@SupportedAnnotationTypes({
-        "android.binding.BindingAdapter",
-        "android.binding.Untaggable",
-        "android.binding.BindingMethods",
-        "android.binding.BindingConversion"})
-@SupportedSourceVersion(SourceVersion.RELEASE_7)
-public class ProcessMethodAdapters extends AbstractProcessor {
-    private boolean mProcessed;
-
+public class ProcessMethodAdapters extends ProcessDataBinding.ProcessingStep {
     public ProcessMethodAdapters() {
     }
 
     @Override
-    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-        if (mProcessed) {
-            return true;
-        }
-
-        SetterStore store = SetterStore.get(processingEnv);
+    public boolean onHandleStep(RoundEnvironment roundEnv,
+            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+        L.d("processing adapters");
+        final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
+        Preconditions.checkNotNull(modelAnalyzer, "Model analyzer should be"
+                + " initialized first");
+        SetterStore store = SetterStore.get(modelAnalyzer);
         clearIncrementalClasses(roundEnv, store);
 
-        addBindingAdapters(roundEnv, store);
-        addRenamed(roundEnv, store);
-        addConversions(roundEnv, store);
-        addUntaggable(roundEnv, store);
+        addBindingAdapters(roundEnv, processingEnvironment, store);
+        addRenamed(roundEnv, processingEnvironment, store);
+        addConversions(roundEnv, processingEnvironment, store);
+        addUntaggable(roundEnv, processingEnvironment, store);
 
         try {
-            store.write(processingEnv);
+            store.write(buildInfo.modulePackage(), processingEnvironment);
         } catch (IOException e) {
-            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                    "Could not write BindingAdapter intermediate file: " + e.getLocalizedMessage());
-            e.printStackTrace();
+            L.e(e, "Could not write BindingAdapter intermediate file.");
         }
-        mProcessed = true;
         return true;
     }
 
-    private void addBindingAdapters(RoundEnvironment roundEnv, SetterStore store) {
+    @Override
+    public void onProcessingOver(RoundEnvironment roundEnvironment,
+            ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+
+    }
+
+    private void addBindingAdapters(RoundEnvironment roundEnv, ProcessingEnvironment
+            processingEnv, SetterStore store) {
         for (Element element : roundEnv.getElementsAnnotatedWith(BindingAdapter.class)) {
             if (element.getKind() != ElementKind.METHOD ||
                     !element.getModifiers().contains(Modifier.STATIC) ||
                     !element.getModifiers().contains(Modifier.PUBLIC)) {
-                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                        "@BindingAdapter on invalid element: " + element);
+                L.e("@BindingAdapter on invalid element: %s", element);
                 continue;
             }
             BindingAdapter bindingAdapter = element.getAnnotation(BindingAdapter.class);
@@ -93,22 +90,20 @@
             ExecutableElement executableElement = (ExecutableElement) element;
             List<? extends VariableElement> parameters = executableElement.getParameters();
             if (parameters.size() != 2) {
-                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                        "@BindingAdapter does not take two parameters: " + element);
+                L.e("@BindingAdapter does not take two parameters: %s",element);
                 continue;
             }
             try {
-                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
-                        "------------------ @BindingAdapter for " + element);
+                L.d("------------------ @BindingAdapter for %s", element);
                 store.addBindingAdapter(bindingAdapter.value(), executableElement);
             } catch (IllegalArgumentException e) {
-                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                        "@BindingAdapter for duplicate View and parameter type: " + element);
+                L.e(e, "@BindingAdapter for duplicate View and parameter type: %s", element);
             }
         }
     }
 
-    private void addRenamed(RoundEnvironment roundEnv, SetterStore store) {
+    private void addRenamed(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv,
+            SetterStore store) {
         for (Element element : roundEnv.getElementsAnnotatedWith(BindingMethods.class)) {
             BindingMethods bindingMethods = element.getAnnotation(BindingMethods.class);
             for (BindingMethod bindingMethod : bindingMethods.value()) {
@@ -118,7 +113,8 @@
         }
     }
 
-    private void addConversions(RoundEnvironment roundEnv, SetterStore store) {
+    private void addConversions(RoundEnvironment roundEnv,
+            ProcessingEnvironment processingEnv, SetterStore store) {
         for (Element element : roundEnv.getElementsAnnotatedWith(BindingConversion.class)) {
             if (element.getKind() != ElementKind.METHOD ||
                     !element.getModifiers().contains(Modifier.STATIC) ||
@@ -145,7 +141,8 @@
         }
     }
 
-    private void addUntaggable(RoundEnvironment roundEnv, SetterStore store) {
+    private void addUntaggable(RoundEnvironment roundEnv,
+            ProcessingEnvironment processingEnv, SetterStore store) {
         for (Element element : roundEnv.getElementsAnnotatedWith(Untaggable.class)) {
             Untaggable untaggable = element.getAnnotation(Untaggable.class);
             store.addUntaggableTypes(untaggable.value(), (TypeElement) element);
diff --git a/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
index 7b82540..27f59e0 100644
--- a/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
+++ b/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -1,3 +1 @@
-com.android.databinding.annotationprocessor.ProcessBindable
-com.android.databinding.annotationprocessor.ProcessMethodAdapters
-com.android.databinding.annotationprocessor.ProcessExpressions
+com.android.databinding.annotationprocessor.ProcessDataBinding
\ No newline at end of file
diff --git a/baseLibrary/build.gradle b/baseLibrary/build.gradle
index 66f61b3..ecb7205 100644
--- a/baseLibrary/build.gradle
+++ b/baseLibrary/build.gradle
@@ -46,7 +46,6 @@
 }
 
 dependencies {
-    compile 'com.tunnelvisionlabs:antlr4:4.4'
     testCompile 'junit:junit:4.11'
 }
 
diff --git a/baseLibrary/src/main/java/android/binding/BindingAppInfo.java b/baseLibrary/src/main/java/android/binding/BindingBuildInfo.java
similarity index 73%
rename from baseLibrary/src/main/java/android/binding/BindingAppInfo.java
rename to baseLibrary/src/main/java/android/binding/BindingBuildInfo.java
index a90f332..62b6043 100644
--- a/baseLibrary/src/main/java/android/binding/BindingAppInfo.java
+++ b/baseLibrary/src/main/java/android/binding/BindingBuildInfo.java
@@ -7,12 +7,12 @@
  *
  *      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,
+ * 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 android.binding;
 
 import java.lang.annotation.ElementType;
@@ -22,9 +22,15 @@
 
 @Target({ElementType.TYPE})
 @Retention(RetentionPolicy.SOURCE)
-public @interface BindingAppInfo {
+public @interface BindingBuildInfo {
     String buildId();
-    String applicationPackage();
+    String modulePackage();
     String sdkRoot();
     int minSdk();
+
+    /**
+     * The folder that includes xml files which are exported by aapt or gradle plugin from layout files
+     */
+    String layoutInfoDir();
+    boolean isLibrary();
 }
diff --git a/build.gradle b/build.gradle
index b86af2b..c63b74b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,13 +3,12 @@
 databindingProperties.mavenRepoDir = "${projectDir}/${databindingProperties.mavenRepoName}"
 ext.config = databindingProperties
 
-
 println "local maven repo is ${ext.config.mavenRepoDir}."
 
 new File(ext.config.mavenRepoDir).mkdir()
 subprojects {
     apply plugin: 'maven'
-    group = 'com.android.databinding'
+    group = config.group
     version = config.snapshotVersion
     repositories {
         mavenCentral()
@@ -30,14 +29,22 @@
     delete "${config.mavenRepoDir}"
 }
 
+def buildExtensionsTask = project.tasks.create "buildExtensionsTask", Exec
+buildExtensionsTask.workingDir file('extensions').getAbsolutePath()
+//on linux
+buildExtensionsTask.commandLine './gradlew'
+buildExtensionsTask.args 'clean', 'uploadArchives', '--info', '--stacktrace'
+buildExtensionsTask.dependsOn subprojects.uploadArchives
+
 file('integration-tests').listFiles().findAll { it.isDirectory() }.each {
-    println("${it.getAbsolutePath()}")
+    println("Creating run test task for  ${it.getAbsolutePath()}.")
     def testTask = project.tasks.create "runTestsOf${it.getName().capitalize()}", Exec
-    testTask.workingDir 'integration-tests/TestApp'
+    testTask.workingDir it.getAbsolutePath()
     //on linux
     testTask.commandLine './gradlew'
-    testTask.args 'clean', 'connectedCheck', '--info'
+    testTask.args 'clean', 'connectedCheck', '--info', '--stacktrace'
     testTask.dependsOn subprojects.uploadArchives
+    testTask.dependsOn buildExtensionsTask
 }
 
 task runIntegrationTests {
@@ -51,11 +58,17 @@
 allprojects {
     afterEvaluate { project ->
         runAllTests.dependsOn project.tasks.findAll {task -> task.name.equals('test')}
+        runAllTests.dependsOn project.tasks.findAll {task -> task.name.equals('connectedCheck')}
     }
 }
 
+subprojects.uploadArchives.each { it.shouldRunAfter deleteRepo  }
+buildExtensionsTask.shouldRunAfter deleteRepo
+tasks['runTestsOfMultiModuleTestApp'].shouldRunAfter tasks['runTestsOfIndependentLibrary']
+
 
 task rebuildRepo() {
     dependsOn deleteRepo
     dependsOn subprojects.uploadArchives
+    dependsOn buildExtensionsTask
 }
\ No newline at end of file
diff --git a/compiler/src/main/java/com/android/databinding/LayoutBinder.java b/compiler/src/main/java/com/android/databinding/LayoutBinder.java
index a285e16..a2aa248 100644
--- a/compiler/src/main/java/com/android/databinding/LayoutBinder.java
+++ b/compiler/src/main/java/com/android/databinding/LayoutBinder.java
@@ -43,6 +43,7 @@
     private final ExpressionParser mExpressionParser;
     private final List<BindingTarget> mBindingTargets;
     private String mPackage;
+    private String mModulePackage;
     private String mProjectPackage;
     private String mBaseClassName;
     private final HashMap<String, String> mUserDefinedVariables = new HashMap<String, String>();
@@ -57,7 +58,8 @@
         mBindingTargets = new ArrayList<BindingTarget>();
         mBundle = layoutBundle;
         mProjectPackage = resourceBundle.getAppPackage();
-        mPackage = mProjectPackage + ".generated";
+        mModulePackage = layoutBundle.getModulePackage();
+        mPackage = layoutBundle.getModulePackage() + ".generated";
         mBaseClassName = ParserHelper.INSTANCE$.toClassName(layoutBundle.getFileName()) + "Binding";
         // copy over data.
         for (Map.Entry<String, String> variable : mBundle.getVariables().entrySet()) {
@@ -159,6 +161,10 @@
         return mPackage;
     }
 
+    public String getModulePackage() {
+        return mModulePackage;
+    }
+
     public void setPackage(String aPackage) {
         mPackage = aPackage;
     }
diff --git a/compiler/src/main/java/com/android/databinding/LayoutXmlProcessor.java b/compiler/src/main/java/com/android/databinding/LayoutXmlProcessor.java
index ed170bf..d0ce506 100644
--- a/compiler/src/main/java/com/android/databinding/LayoutXmlProcessor.java
+++ b/compiler/src/main/java/com/android/databinding/LayoutXmlProcessor.java
@@ -20,15 +20,16 @@
 import com.android.databinding.store.ResourceBundle;
 import com.android.databinding.writer.JavaFileWriter;
 
-import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.lang3.StringEscapeUtils;
 import org.xml.sax.SAXException;
 
+import android.binding.BindingBuildInfo;
+
 import java.io.File;
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
@@ -44,24 +45,51 @@
  * processor to work with.
  */
 public class LayoutXmlProcessor {
-
-    public static final String RESOURCE_BUNDLE_PACKAGE = "com.android.databinding.layouts.";
-    public static final String APPLICATION_INFO_CLASS = "ApplicationBindingInfo";
+    // hardcoded in baseAdapters
+    public static final String RESOURCE_BUNDLE_PACKAGE = "com.android.databinding.layouts";
+    public static final String CLASS_NAME = "DataBindingInfo";
     private final JavaFileWriter mFileWriter;
     private final ResourceBundle mResourceBundle;
     private final int mMinSdk;
 
     private boolean mProcessingComplete;
     private boolean mWritten;
+    private final boolean mIsLibrary;
     private final String mBuildId = UUID.randomUUID().toString();
-    private final List<File> mResourceFolders;
+    // can be a list of xml files or folders that contain XML files
+    private final List<File> mResources;
 
-    public LayoutXmlProcessor(String applicationPackage, List<File> resourceFolders,
-            JavaFileWriter fileWriter, int minSdk) {
+    public LayoutXmlProcessor(String applicationPackage, List<File> resources,
+            JavaFileWriter fileWriter, int minSdk, boolean isLibrary) {
         mFileWriter = fileWriter;
         mResourceBundle = new ResourceBundle(applicationPackage);
-        mResourceFolders = resourceFolders;
+        mResources = resources;
         mMinSdk = minSdk;
+        mIsLibrary = isLibrary;
+    }
+
+    public static List<File> getLayoutFiles(List<File> resources) {
+        List<File> result = new ArrayList<File>();
+        for (File resource : Iterables.filter(resources, fileExists)) {
+            if (resource.isDirectory()) {
+                for (File layoutFolder : resource.listFiles(layoutFolderFilter)) {
+                    for (File xmlFile : layoutFolder.listFiles(xmlFileFilter)) {
+                        result.add(xmlFile);
+                    }
+
+                }
+            } else if (xmlFileFilter.accept(resource.getParentFile(), resource.getName())) {
+                result.add(resource);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * used by the studio plugin
+     */
+    public ResourceBundle getResourceBundle() {
+        return mResourceBundle;
     }
 
     public boolean processResources()
@@ -72,92 +100,101 @@
         }
         LayoutFileParser layoutFileParser = new LayoutFileParser();
         int layoutId = 0;
-        for (File resFolder : Iterables.filter(mResourceFolders, fileExists)) {
-            for (File layoutFolder : resFolder.listFiles(layoutFolderFilter)) {
-                for (File xmlFile : layoutFolder.listFiles(xmlFileFilter)) {
-                    final ResourceBundle.LayoutFileBundle bindingLayout = layoutFileParser
-                            .parseXml(xmlFile, mResourceBundle.getAppPackage(), layoutId);
-                    if (bindingLayout != null && !bindingLayout.isEmpty()) {
-                        mResourceBundle.addLayoutBundle(bindingLayout, layoutId);
-                        layoutId++;
-                    }
-                }
+        for (File xmlFile : getLayoutFiles(mResources)) {
+            final ResourceBundle.LayoutFileBundle bindingLayout = layoutFileParser
+                    .parseXml(xmlFile, mResourceBundle.getAppPackage(), layoutId);
+            if (bindingLayout != null && !bindingLayout.isEmpty()) {
+                mResourceBundle.addLayoutBundle(bindingLayout, layoutId);
+                layoutId++;
             }
         }
         mProcessingComplete = true;
         return true;
     }
 
-    public ResourceBundle getResourceBundle() {
-        return mResourceBundle;
-    }
-
-    public void writeIntermediateFile(File sdkDir) throws JAXBException {
+    public void writeIntermediateFile(File sdkDir, File xmlOutDir) throws JAXBException {
         if (mWritten) {
             return;
         }
         JAXBContext context = JAXBContext.newInstance(ResourceBundle.LayoutFileBundle.class);
         Marshaller marshaller = context.createMarshaller();
-        writeAppInfo(marshaller, sdkDir);
+        writeInfoClass(marshaller, sdkDir, xmlOutDir);
         for (List<ResourceBundle.LayoutFileBundle> layouts : mResourceBundle.getLayoutBundles()
                 .values()) {
             for (ResourceBundle.LayoutFileBundle layout : layouts) {
-                writeAnnotatedFile(layout, marshaller);
+                writeXmlFile(xmlOutDir, layout, marshaller);
             }
         }
         mWritten = true;
     }
 
-    private void writeAnnotatedFile(ResourceBundle.LayoutFileBundle layout, Marshaller marshaller)
+    private void writeXmlFile(File xmlOutDir, ResourceBundle.LayoutFileBundle layout,
+            Marshaller marshaller) throws JAXBException {
+        String filename = generateExportFileName(layout) + ".xml";
+        String xml = toXML(layout, marshaller);
+        mFileWriter.writeToFile(new File(xmlOutDir, filename), xml);
+    }
+
+    public String getInfoClassFullName() {
+        return RESOURCE_BUNDLE_PACKAGE + "." + CLASS_NAME;
+    }
+
+    private String toXML(ResourceBundle.LayoutFileBundle layout, Marshaller marshaller)
             throws JAXBException {
-        StringBuilder className = new StringBuilder(layout.getFileName());
-        className.append('-').append(layout.getDirectory());
-        for (int i = className.length() - 1; i >= 0; i--) {
-            char c = className.charAt(i);
-            if (c == '-') {
-                className.deleteCharAt(i);
-                c = Character.toUpperCase(className.charAt(i));
-                className.setCharAt(i, c);
-            }
-        }
-        className.setCharAt(0, Character.toUpperCase(className.charAt(0)));
         StringWriter writer = new StringWriter();
         marshaller.marshal(layout, writer);
-        String xml = writer.getBuffer().toString();
-        String classString = "import android.binding.BinderBundle;\n\n" +
-                "@BinderBundle(\"" +
-                Base64.encodeBase64String(xml.getBytes(StandardCharsets.UTF_8)) +
-                "\")\n" +
-                "public class " + className + " {}\n";
-        mFileWriter.writeToFile(RESOURCE_BUNDLE_PACKAGE + className, classString);
+        return writer.getBuffer().toString();
     }
 
-    private void writeAppInfo(Marshaller marshaller, File sdkDir) {
+    /**
+     * Generates a string identifier that can uniquely identify the given layout bundle.
+     * This identifier can be used when we need to export data about this layout bundle.
+     */
+    private String generateExportFileName(ResourceBundle.LayoutFileBundle layout) {
+        StringBuilder name = new StringBuilder(layout.getFileName());
+        name.append('-').append(layout.getDirectory());
+        for (int i = name.length() - 1; i >= 0; i--) {
+            char c = name.charAt(i);
+            if (c == '-') {
+                name.deleteCharAt(i);
+                c = Character.toUpperCase(name.charAt(i));
+                name.setCharAt(i, c);
+            }
+        }
+        return name.toString();
+    }
+
+    private void writeInfoClass(Marshaller marshaller, File sdkDir, File xmlOutDir) {
         final String sdkPath = StringEscapeUtils.escapeJava(sdkDir.getAbsolutePath());
-        String classString = "import android.binding.BindingAppInfo;\n\n" +
-                "@BindingAppInfo(buildId=\"" + mBuildId + "\", " +
-                "applicationPackage=\"" + mResourceBundle.getAppPackage() + "\", " +
+        final Class annotation = BindingBuildInfo.class;
+        final String layoutInfoPath = StringEscapeUtils.escapeJava(xmlOutDir.getAbsolutePath());
+        String classString = "package " + RESOURCE_BUNDLE_PACKAGE + ";\n\n" +
+                "import " + annotation.getCanonicalName() + ";\n\n" +
+                "@" + annotation.getSimpleName() + "(buildId=\"" + mBuildId + "\", " +
+                "modulePackage=\"" + mResourceBundle.getAppPackage() + "\", " +
                 "sdkRoot=\"" + sdkPath + "\", " +
+                "layoutInfoDir=\"" + layoutInfoPath + "\"," +
+                "isLibrary=" + mIsLibrary + "," +
                 "minSdk=" + mMinSdk + ")\n" +
-                "public class " + APPLICATION_INFO_CLASS + " {}\n";
-        mFileWriter.writeToFile(RESOURCE_BUNDLE_PACKAGE + APPLICATION_INFO_CLASS, classString);
+                "public class " + CLASS_NAME + " {}\n";
+        mFileWriter.writeToFile(mResourceBundle.getAppPackage() + "." + CLASS_NAME, classString);
     }
 
-    private final Predicate<File> fileExists = new Predicate<File>() {
+    private static final Predicate<File> fileExists = new Predicate<File>() {
         @Override
         public boolean apply(File input) {
             return input.exists() && input.canRead();
         }
     };
 
-    private final FilenameFilter layoutFolderFilter = new FilenameFilter() {
+    private static final FilenameFilter layoutFolderFilter = new FilenameFilter() {
         @Override
         public boolean accept(File dir, String name) {
             return name.startsWith("layout");
         }
     };
 
-    private final FilenameFilter xmlFileFilter = new FilenameFilter() {
+    private static final FilenameFilter xmlFileFilter = new FilenameFilter() {
         @Override
         public boolean accept(File dir, String name) {
             return name.toLowerCase().endsWith(".xml");
diff --git a/compiler/src/main/java/com/android/databinding/reflection/ModelAnalyzer.java b/compiler/src/main/java/com/android/databinding/reflection/ModelAnalyzer.java
index 63cbc38..9a7ef2f 100644
--- a/compiler/src/main/java/com/android/databinding/reflection/ModelAnalyzer.java
+++ b/compiler/src/main/java/com/android/databinding/reflection/ModelAnalyzer.java
@@ -209,8 +209,6 @@
 
     public abstract ModelClass findClass(String className, Map<String, String> imports);
 
-    public abstract List<URL> getResources(String name);
-
     public abstract ModelClass findClass(Class classType);
 
     public abstract TypeUtil createTypeUtil();
diff --git a/compiler/src/main/java/com/android/databinding/reflection/annotation/AnnotationAnalyzer.java b/compiler/src/main/java/com/android/databinding/reflection/annotation/AnnotationAnalyzer.java
index ca40f2e..92c6aa8 100644
--- a/compiler/src/main/java/com/android/databinding/reflection/annotation/AnnotationAnalyzer.java
+++ b/compiler/src/main/java/com/android/databinding/reflection/annotation/AnnotationAnalyzer.java
@@ -439,21 +439,6 @@
     }
 
     @Override
-    public List<URL> getResources(String name) {
-        ArrayList<URL> urls = new ArrayList<URL>();
-        try {
-            Enumeration<URL> resources = getClass().getClassLoader().getResources(name);
-            while (resources.hasMoreElements()) {
-                urls.add(resources.nextElement());
-            }
-        } catch (IOException e) {
-            L.e(e, "IOException while getting resources:");
-        }
-
-        return urls;
-    }
-
-    @Override
     public ModelClass findClass(Class classType) {
         return findClass(classType.getCanonicalName(), null);
     }
diff --git a/compiler/src/main/java/com/android/databinding/store/LayoutFileParser.java b/compiler/src/main/java/com/android/databinding/store/LayoutFileParser.java
index 752562b..99efe6b 100644
--- a/compiler/src/main/java/com/android/databinding/store/LayoutFileParser.java
+++ b/compiler/src/main/java/com/android/databinding/store/LayoutFileParser.java
@@ -67,7 +67,7 @@
 
         ResourceBundle.LayoutFileBundle bundle = new ResourceBundle.LayoutFileBundle(
                 ParserHelper.INSTANCE$.stripExtension(xml.getName()), layoutId,
-                xml.getParentFile().getName());
+                xml.getParentFile().getName(), pkg);
 
         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         final DocumentBuilder builder = factory.newDocumentBuilder();
diff --git a/compiler/src/main/java/com/android/databinding/store/ResourceBundle.java b/compiler/src/main/java/com/android/databinding/store/ResourceBundle.java
index 767cc34..a781417 100644
--- a/compiler/src/main/java/com/android/databinding/store/ResourceBundle.java
+++ b/compiler/src/main/java/com/android/databinding/store/ResourceBundle.java
@@ -57,7 +57,15 @@
             mLayoutBundles.put(bundle.mFileName, new ArrayList<LayoutFileBundle>());
         }
         bundle.mLayoutId = layoutId;
-        mLayoutBundles.get(bundle.mFileName).add(bundle);
+        final List<LayoutFileBundle> bundles = mLayoutBundles.get(bundle.mFileName);
+        for (LayoutFileBundle existing : bundles) {
+            if (existing.equals(bundle)) {
+                L.d("skipping layout bundle %s because it already exists.", bundle);
+                return;
+            }
+        }
+        L.d("adding bundle %s", bundle);
+        bundles.add(bundle);
     }
 
     public HashMap<String, List<LayoutFileBundle>> getLayoutBundles() {
@@ -200,6 +208,8 @@
         public int mLayoutId;
         @XmlAttribute(name="layout", required = true)
         public String mFileName;
+        @XmlAttribute(name="modulePackage", required = true)
+        public String mModulePackage;
         private String mConfigName;
 
         @XmlAttribute(name="directory", required = true)
@@ -222,10 +232,12 @@
         public LayoutFileBundle() {
         }
 
-        public LayoutFileBundle(String fileName, int layoutId, String directory) {
+        public LayoutFileBundle(String fileName, int layoutId, String directory,
+                String modulePackage) {
             mFileName = fileName;
             mLayoutId = layoutId;
             mDirectory = directory;
+            mModulePackage = modulePackage;
         }
 
         public void addVariable(String name, String type) {
@@ -288,6 +300,57 @@
         public List<BindingTargetBundle> getBindingTargetBundles() {
             return mBindingTargetBundles;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            LayoutFileBundle bundle = (LayoutFileBundle) o;
+
+            if (mConfigName != null ? !mConfigName.equals(bundle.mConfigName)
+                    : bundle.mConfigName != null) {
+                return false;
+            }
+            if (mDirectory != null ? !mDirectory.equals(bundle.mDirectory)
+                    : bundle.mDirectory != null) {
+                return false;
+            }
+            if (mFileName != null ? !mFileName.equals(bundle.mFileName)
+                    : bundle.mFileName != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = mFileName != null ? mFileName.hashCode() : 0;
+            result = 31 * result + (mConfigName != null ? mConfigName.hashCode() : 0);
+            result = 31 * result + (mDirectory != null ? mDirectory.hashCode() : 0);
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "LayoutFileBundle{" +
+                    "mHasVariations=" + mHasVariations +
+                    ", mDirectory='" + mDirectory + '\'' +
+                    ", mConfigName='" + mConfigName + '\'' +
+                    ", mModulePackage='" + mModulePackage + '\'' +
+                    ", mFileName='" + mFileName + '\'' +
+                    ", mLayoutId=" + mLayoutId +
+                    '}';
+        }
+
+        public String getModulePackage() {
+            return mModulePackage;
+        }
     }
 
     @XmlAccessorType(XmlAccessType.NONE)
diff --git a/compiler/src/main/java/com/android/databinding/store/SetterStore.java b/compiler/src/main/java/com/android/databinding/store/SetterStore.java
index fe14b7e..a34c8c5 100644
--- a/compiler/src/main/java/com/android/databinding/store/SetterStore.java
+++ b/compiler/src/main/java/com/android/databinding/store/SetterStore.java
@@ -18,6 +18,7 @@
 import com.android.databinding.reflection.ModelAnalyzer;
 import com.android.databinding.reflection.ModelClass;
 import com.android.databinding.reflection.ModelMethod;
+import com.android.databinding.util.GenerationalClassUtil;
 import com.android.databinding.util.L;
 
 import org.apache.commons.lang3.StringUtils;
@@ -30,6 +31,7 @@
 import java.io.Serializable;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -50,7 +52,7 @@
 
 public class SetterStore {
 
-    public static final String SETTER_STORE_FILE_NAME = "setter_store.bin";
+    public static final String SETTER_STORE_FILE_EXT = "-setter_store.bin";
 
     private static SetterStore sStore;
 
@@ -62,70 +64,24 @@
         mStore = store;
     }
 
-    public static SetterStore get(ProcessingEnvironment processingEnvironment) {
-        if (sStore == null) {
-            InputStream in = null;
-            try {
-                Filer filer = processingEnvironment.getFiler();
-                FileObject resource = filer.getResource(StandardLocation.CLASS_OUTPUT,
-                        SetterStore.class.getPackage().getName(), SETTER_STORE_FILE_NAME);
-                if (resource != null && new File(resource.getName()).exists()) {
-                    in = resource.openInputStream();
-                    if (in != null) {
-                        sStore = load(in);
-                    }
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            } catch (ClassNotFoundException e) {
-                e.printStackTrace();
-            } finally {
-                if (in != null) {
-                    try {
-                        in.close();
-                    } catch (IOException e) {
-                        e.printStackTrace();
-                    }
-                }
-            }
-            if (sStore == null) {
-                sStore = new SetterStore(null, new IntermediateV1());
-            }
-        }
-        return sStore;
-    }
-
     public static SetterStore get(ModelAnalyzer modelAnalyzer) {
         if (sStore == null) {
-            sStore = load(modelAnalyzer);
+            sStore = load(modelAnalyzer, SetterStore.class.getClassLoader());
         }
         return sStore;
     }
 
-    private static SetterStore load(ModelAnalyzer modelAnalyzer) {
+    private static SetterStore load(ModelAnalyzer modelAnalyzer, ClassLoader classLoader) {
         IntermediateV1 store = new IntermediateV1();
-        String resourceName = SetterStore.class.getPackage().getName().replace('.', '/') +
-                '/' + SETTER_STORE_FILE_NAME;
-        try {
-            for (URL resource : modelAnalyzer.getResources(resourceName)) {
-                merge(store, resource);
-            }
-            return new SetterStore(modelAnalyzer, store);
-        } catch (IOException e) {
-            L.e(e, "Could not read SetterStore intermediate file");
-        } catch (ClassNotFoundException e) {
-            L.e(e, "Could not read SetterStore intermediate file");
+        List<Intermediate> previousStores = GenerationalClassUtil
+                .loadObjects(classLoader,
+                        new GenerationalClassUtil.ExtensionFilter(SETTER_STORE_FILE_EXT));
+        for (Intermediate intermediate : previousStores) {
+            merge(store, intermediate);
         }
         return new SetterStore(modelAnalyzer, store);
     }
 
-    private static SetterStore load(InputStream inputStream)
-            throws IOException, ClassNotFoundException {
-        ObjectInputStream in = new ObjectInputStream(inputStream);
-        Intermediate intermediate = (Intermediate) in.readObject();
-        return new SetterStore(null, (IntermediateV1) intermediate.upgrade());
-    }
-
     public void addRenamedMethod(String attribute, String declaringClass, String method,
             TypeElement declaredOn) {
         HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute);
@@ -135,10 +91,12 @@
         }
         MethodDescription methodDescription =
                 new MethodDescription(declaredOn.getQualifiedName().toString(), method);
+        L.d("STORE addmethod desc %s", methodDescription);
         renamed.put(declaringClass, methodDescription);
     }
 
     public void addBindingAdapter(String attribute, ExecutableElement bindingMethod) {
+        L.d("STORE addBindingAdapter %s %s", attribute, bindingMethod);
         HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute);
 
         if (adapters == null) {
@@ -158,6 +116,7 @@
     }
 
     public void addUntaggableTypes(String[] typeNames, TypeElement declaredOn) {
+        L.d("STORE addUntaggableTypes %s %s", Arrays.toString(typeNames), declaredOn);
         String declaredType = declaredOn.getQualifiedName().toString();
         for (String type : typeNames) {
             mStore.untaggableTypes.put(type, declaredType);
@@ -187,6 +146,7 @@
     }
 
     public void addConversionMethod(ExecutableElement conversionMethod) {
+        L.d("STORE addConversionMethod %s", conversionMethod);
         List<? extends VariableElement> parameters = conversionMethod.getParameters();
         String fromType = getQualifiedName(parameters.get(0).asType());
         String toType = getQualifiedName(conversionMethod.getReturnType());
@@ -248,21 +208,10 @@
         keys.clear();
     }
 
-    public void write(ProcessingEnvironment processingEnvironment) throws IOException {
-        Filer filer = processingEnvironment.getFiler();
-        FileObject resource = filer.createResource(StandardLocation.CLASS_OUTPUT,
-                SetterStore.class.getPackage().getName(), "setter_store.bin");
-        L.d("============= Writing intermediate file: %s", resource.getName());
-        ObjectOutputStream out = null;
-        try {
-            out = new ObjectOutputStream(resource.openOutputStream());
-
-            out.writeObject(mStore);
-        } finally {
-            if (out != null) {
-                out.close();
-            }
-        }
+    public void write(String projectPackage, ProcessingEnvironment processingEnvironment)
+            throws IOException {
+        GenerationalClassUtil.writeIntermediateFile(processingEnvironment,
+                projectPackage, projectPackage + SETTER_STORE_FILE_EXT, mStore);
     }
 
     public SetterCall getSetterCall(String attribute, ModelClass viewType,
@@ -503,27 +452,12 @@
         }
     }
 
-    private static void merge(IntermediateV1 store,
-            URL nextUrl) throws IOException, ClassNotFoundException {
-        InputStream inputStream = null;
-        JarFile jarFile = null;
-        try {
-            inputStream = nextUrl.openStream();
-            ObjectInputStream in = new ObjectInputStream(inputStream);
-            Intermediate intermediate = (Intermediate) in.readObject();
-            IntermediateV1 intermediateV1 = (IntermediateV1) intermediate.upgrade();
-            merge(store.adapterMethods, intermediateV1.adapterMethods);
-            merge(store.renamedMethods, intermediateV1.renamedMethods);
-            merge(store.conversionMethods, intermediateV1.conversionMethods);
-            store.untaggableTypes.putAll(intermediateV1.untaggableTypes);
-        } finally {
-            if (inputStream != null) {
-                inputStream.close();
-            }
-            if (jarFile != null) {
-                jarFile.close();
-            }
-        }
+    private static void merge(IntermediateV1 store, Intermediate dumpStore) {
+        IntermediateV1 intermediateV1 = (IntermediateV1) dumpStore.upgrade();
+        merge(store.adapterMethods, intermediateV1.adapterMethods);
+        merge(store.renamedMethods, intermediateV1.renamedMethods);
+        merge(store.conversionMethods, intermediateV1.conversionMethods);
+        store.untaggableTypes.putAll(intermediateV1.untaggableTypes);
     }
 
     private static <K, V> void merge(HashMap<K, HashMap<V, MethodDescription>> first,
@@ -619,7 +553,7 @@
         }
     }
 
-    private interface Intermediate {
+    private interface Intermediate extends Serializable {
         Intermediate upgrade();
     }
 
diff --git a/compiler/src/main/java/com/android/databinding/util/GenerationalClassUtil.java b/compiler/src/main/java/com/android/databinding/util/GenerationalClassUtil.java
new file mode 100644
index 0000000..c970ad5
--- /dev/null
+++ b/compiler/src/main/java/com/android/databinding/util/GenerationalClassUtil.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2015 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.databinding.util;
+
+import com.android.databinding.util.L;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.filefilter.IOFileFilter;
+import org.apache.commons.io.filefilter.TrueFileFilter;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+
+/**
+ * A utility class that helps adding build specific objects to the jar file
+ * and their extraction later on.
+ */
+public class GenerationalClassUtil {
+    public static <T extends Serializable> List<T> loadObjects(ClassLoader classLoader, Filter filter) {
+        final List<T> result = new ArrayList<T>();
+        if (!(classLoader instanceof URLClassLoader)) {
+            L.d("class loader is not url class loader (%s). skipping.", classLoader.getClass());
+            return result;
+        }
+        final URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
+        for (URL url : urlClassLoader.getURLs()) {
+            L.d("checking url %s for intermediate data", url);
+            try {
+                final File file = new File(url.toURI());
+                if (!file.exists()) {
+                    L.d("cannot load file for %s", url);
+                    continue;
+                }
+                if (file.isDirectory()) {
+                    // probably exported classes dir.
+                    loadFromDirectory(filter, result, file);
+                } else {
+                    // assume it is a zip file
+                    loadFomZipFile(filter, result, file);
+                }
+            } catch (IOException e) {
+                L.d("cannot open zip file from %s", url);
+            } catch (URISyntaxException e) {
+                L.d("cannot open zip file from %s", url);
+            }
+        }
+        return result;
+    }
+
+    private static <T extends Serializable> void loadFromDirectory(final Filter filter, List<T> result,
+            File directory) {
+        //noinspection unchecked
+        Collection<File> files = FileUtils.listFiles(directory, new IOFileFilter() {
+            @Override
+            public boolean accept(File file) {
+                return filter.accept(file.getName());
+            }
+
+            @Override
+            public boolean accept(File dir, String name) {
+                return filter.accept(name);
+            }
+        }, TrueFileFilter.INSTANCE);
+        for (File file : files) {
+            InputStream inputStream = null;
+            try {
+                inputStream = FileUtils.openInputStream(file);
+                T item = fromInputStream(result, inputStream);
+                L.d("loaded item %s from file", item);
+                if (item != null) {
+                    result.add(item);
+                }
+            } catch (IOException e) {
+                L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath());
+            } catch (ClassNotFoundException e) {
+                L.e(e, "Could not read Binding properties intermediate file. %s", file.getAbsolutePath());
+            } finally {
+                IOUtils.closeQuietly(inputStream);
+            }
+        }
+    }
+
+    private static <T extends Serializable> void loadFomZipFile(Filter filter,
+            List<T> result, File file) throws IOException {
+        ZipFile zipFile = new ZipFile(file);
+        Enumeration<? extends ZipEntry> entries = zipFile.entries();
+        while (entries.hasMoreElements()) {
+            ZipEntry entry = entries.nextElement();
+            if (!filter.accept(entry.getName())) {
+                continue;
+            }
+            L.d("loading data from file %s", entry.getName());
+            InputStream inputStream = null;
+            try {
+                inputStream = zipFile.getInputStream(entry);
+                T item = fromInputStream(result, inputStream);
+                L.d("loaded item %s from zip file", item);
+                if (item != null) {
+                    result.add(item);
+                }
+            } catch (IOException e) {
+                L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath());
+            } catch (ClassNotFoundException e) {
+                L.e(e, "Could not read Binding properties intermediate file. %s", file.getAbsolutePath());
+            } finally {
+                IOUtils.closeQuietly(inputStream);
+            }
+        }
+    }
+
+    private static <T extends Serializable> T fromInputStream(List<T> result,
+            InputStream inputStream) throws IOException, ClassNotFoundException {
+        ObjectInputStream in = new ObjectInputStream(inputStream);
+        return (T) in.readObject();
+
+    }
+
+    public static void writeIntermediateFile(ProcessingEnvironment processingEnv,
+            String packageName, String fileName, Serializable object) {
+        ObjectOutputStream oos = null;
+        try {
+            FileObject intermediate = processingEnv.getFiler().createResource(
+                    StandardLocation.CLASS_OUTPUT, packageName,
+                    fileName);
+            OutputStream ios = intermediate.openOutputStream();
+            oos = new ObjectOutputStream(ios);
+            oos.writeObject(object);
+            oos.close();
+            L.d("wrote intermediate bindable file %s %s", packageName, fileName);
+        } catch (IOException e) {
+            L.e(e, "Could not write to intermediate file: %s", fileName);
+        } finally {
+            IOUtils.closeQuietly(oos);
+        }
+    }
+
+
+    public static interface Filter {
+        public boolean accept(String entryName);
+    }
+
+    public static class ExtensionFilter implements Filter {
+        private final String mExtension;
+        public ExtensionFilter(String extension) {
+            mExtension = extension;
+        }
+
+        @Override
+        public boolean accept(String entryName) {
+            return entryName.endsWith(mExtension);
+        }
+    }
+}
diff --git a/compiler/src/main/java/com/android/databinding/writer/AnnotationJavaFileWriter.java b/compiler/src/main/java/com/android/databinding/writer/AnnotationJavaFileWriter.java
index 67d274e..d24ac04 100644
--- a/compiler/src/main/java/com/android/databinding/writer/AnnotationJavaFileWriter.java
+++ b/compiler/src/main/java/com/android/databinding/writer/AnnotationJavaFileWriter.java
@@ -15,8 +15,14 @@
  */
 package com.android.databinding.writer;
 
+import com.google.common.base.Preconditions;
+
+import com.android.databinding.util.L;
+
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.Writer;
 
@@ -24,7 +30,7 @@
 import javax.tools.Diagnostic;
 import javax.tools.JavaFileObject;
 
-public class AnnotationJavaFileWriter implements JavaFileWriter {
+public class AnnotationJavaFileWriter extends JavaFileWriter {
     private final ProcessingEnvironment mProcessingEnvironment;
 
     public AnnotationJavaFileWriter(ProcessingEnvironment processingEnvironment) {
@@ -35,13 +41,13 @@
     public void writeToFile(String canonicalName, String contents) {
         Writer writer = null;
         try {
+            L.d("writing file %s", canonicalName);
             JavaFileObject javaFileObject =
                     mProcessingEnvironment.getFiler().createSourceFile(canonicalName);
             writer = javaFileObject.openWriter();
             writer.write(contents);
         } catch (IOException e) {
-            mProcessingEnvironment.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                    "Could not write to " + canonicalName + ": " + e.getLocalizedMessage());
+            L.e(e, "Could not write to %s", canonicalName);
         } finally {
             if (writer != null) {
                 IOUtils.closeQuietly(writer);
diff --git a/compiler/src/main/java/com/android/databinding/writer/JavaFileWriter.java b/compiler/src/main/java/com/android/databinding/writer/JavaFileWriter.java
index f7b3c9c..9fa9347 100644
--- a/compiler/src/main/java/com/android/databinding/writer/JavaFileWriter.java
+++ b/compiler/src/main/java/com/android/databinding/writer/JavaFileWriter.java
@@ -13,6 +13,23 @@
 
 package com.android.databinding.writer;
 
-public interface JavaFileWriter {
-    public void writeToFile(String canonicalName, String contents);
+import com.android.databinding.util.L;
+
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+
+public abstract class JavaFileWriter {
+    public abstract void writeToFile(String canonicalName, String contents);
+    public void writeToFile(File exactPath, String contents) {
+        File parent = exactPath.getParentFile();
+        parent.mkdirs();
+        try {
+            L.d("writing file %s", exactPath.getAbsoluteFile());
+            FileUtils.writeStringToFile(exactPath, contents);
+        } catch (IOException e) {
+            L.e(e, "Could not write to %s", exactPath);
+        }
+    }
 }
diff --git a/compiler/src/main/kotlin/com/android/databinding/ext/ext.kt b/compiler/src/main/kotlin/com/android/databinding/ext/ext.kt
index b7abeb7..11804ff 100644
--- a/compiler/src/main/kotlin/com/android/databinding/ext/ext.kt
+++ b/compiler/src/main/kotlin/com/android/databinding/ext/ext.kt
@@ -77,4 +77,4 @@
 }
 
 public fun String.br() : String =
-    "android.binding.BR.${if (this == "") "_all" else this}"
+    "BR.${if (this == "") "_all" else this}"
diff --git a/compiler/src/main/kotlin/com/android/databinding/util/XmlEditor.kt b/compiler/src/main/kotlin/com/android/databinding/util/XmlEditor.kt
index b1531fc..ad79405 100644
--- a/compiler/src/main/kotlin/com/android/databinding/util/XmlEditor.kt
+++ b/compiler/src/main/kotlin/com/android/databinding/util/XmlEditor.kt
@@ -131,7 +131,8 @@
         Preconditions.checkNotNull(rootNodeContext, "Cannot find root node for ${f.getName()}")
         Preconditions.checkState(rootNodeHasTag == false, """You cannot set a tag in the layout
         root if you are using binding. Invalid file: ${f}""")
-        val rootNodeBounds = Pair(rootNodeContext!!.getStart().toPosition(), rootNodeContext!!.getStop().toEndPosition())
+        val rootNodeBounds = Pair(rootNodeContext!!.getStart().toPosition(),
+                rootNodeContext!!.getStop().toEndPosition())
 
         log { "root node bounds: ${rootNodeBounds}" }
         val out = StringBuilder()
diff --git a/compiler/src/main/kotlin/com/android/databinding/writer/DataBinderWriter.kt b/compiler/src/main/kotlin/com/android/databinding/writer/DataBinderWriter.kt
index d52a612..7b3638e 100644
--- a/compiler/src/main/kotlin/com/android/databinding/writer/DataBinderWriter.kt
+++ b/compiler/src/main/kotlin/com/android/databinding/writer/DataBinderWriter.kt
@@ -19,13 +19,13 @@
     fun write() =
             kcode("") {
                 nl("package $pkg;")
-                nl("import $projectPackage.R;")
+                nl("import $projectPackage.BR;")
                 nl("public class $className implements com.android.databinding.library.DataBinderMapper {") {
                     tab("@Override")
                     tab("public com.android.databinding.library.ViewDataBinding getDataBinder(android.view.View view, int layoutId) {") {
                         tab("switch(layoutId) {") {
                             layoutBinders.groupBy{it.getLayoutname()}.forEach {
-                                tab("case R.layout.${it.value.first!!.getLayoutname()}:") {
+                                tab("case ${it.value.first!!.getModulePackage()}.R.layout.${it.value.first!!.getLayoutname()}:") {
                                     if (it.value.size() == 1) {
                                         tab("return new ${it.value.first!!.getPackage()}.${it.value.first!!.getClassName()}(view);")
                                     } else {
@@ -51,7 +51,7 @@
                     tab("}")
 
                     tab("public int getId(String key) {") {
-                        tab("return android.binding.BR.getId(key);")
+                        tab("return BR.getId(key);")
                     } tab("}")
                 }
                 nl("}")
diff --git a/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt b/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt
index fec3b46..e82de91 100644
--- a/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt
+++ b/compiler/src/main/kotlin/com/android/databinding/writer/LayoutBinderWriter.kt
@@ -331,7 +331,8 @@
     public fun write() : String  {
         layoutBinder.resolveWhichExpressionsAreUsed()
         return kcode("package ${layoutBinder.getPackage()};") {
-            nl("import ${layoutBinder.getProjectPackage()}.R;")
+            nl("import ${layoutBinder.getModulePackage()}.R;")
+            nl("import ${layoutBinder.getModulePackage()}.BR;")
             nl("import android.view.View;")
             nl("public class ${className} extends ${baseClassName} {") {
                 tab(declareIncludeViews())
@@ -401,7 +402,7 @@
                         if (originalTag != null) {
                             tagValue = "\"${originalTag}\""
                             if (originalTag.startsWith("@")) {
-                                var packageName = layoutBinder.getProjectPackage()
+                                var packageName = layoutBinder.getModulePackage()
                                 if (originalTag.startsWith("@android:")) {
                                     packageName = "android"
                                 }
@@ -749,15 +750,15 @@
             }
             nl("")
             tab("public static ${baseClassName} inflate(android.view.ViewGroup root) {") {
-                tab("return DataBindingUtil.<${baseClassName}>inflate(root.getContext(), ${layoutBinder.getProjectPackage()}.R.layout.${layoutBinder.getLayoutname()}, root, true);")
+                tab("return DataBindingUtil.<${baseClassName}>inflate(root.getContext(), ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, true);")
             }
             tab("}")
             tab("public static ${baseClassName} inflate(android.content.Context context) {") {
-                tab("return DataBindingUtil.<${baseClassName}>inflate(context, ${layoutBinder.getProjectPackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false);")
+                tab("return DataBindingUtil.<${baseClassName}>inflate(context, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false);")
             }
             tab("}")
             tab("public static ${baseClassName} bind(android.view.View view) {") {
-                tab("return (${baseClassName})DataBindingUtil.bindTo(view, ${layoutBinder.getProjectPackage()}.R.layout.${layoutBinder.getLayoutname()});")
+                tab("return (${baseClassName})DataBindingUtil.bindTo(view, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()});")
             }
             tab("}")
             nl("}")
diff --git a/compiler/src/test/java/com/android/databinding/MockLayoutBinder.java b/compiler/src/test/java/com/android/databinding/MockLayoutBinder.java
index 6ef97d2..dc57437 100644
--- a/compiler/src/test/java/com/android/databinding/MockLayoutBinder.java
+++ b/compiler/src/test/java/com/android/databinding/MockLayoutBinder.java
@@ -19,6 +19,6 @@
 
     public MockLayoutBinder() {
         super(new ResourceBundle("com.test"),
-                new ResourceBundle.LayoutFileBundle("blah.xml", 1, "."));
+                new ResourceBundle.LayoutFileBundle("blah.xml", 1, ".", "com.test.submodule"));
     }
 }
\ No newline at end of file
diff --git a/compiler/src/test/java/com/android/databinding/reflection/java/JavaAnalyzer.java b/compiler/src/test/java/com/android/databinding/reflection/java/JavaAnalyzer.java
index c6c7cf1..1e88f84 100644
--- a/compiler/src/test/java/com/android/databinding/reflection/java/JavaAnalyzer.java
+++ b/compiler/src/test/java/com/android/databinding/reflection/java/JavaAnalyzer.java
@@ -314,23 +314,6 @@
         }
     }
 
-    @Override
-    public List<URL> getResources(String name) {
-        List<URL> urlList = new ArrayList<URL>();
-        Enumeration<URL> urls = null;
-        try {
-            urls = mClassLoader.getResources(name);
-            if (urls != null) {
-                while (urls.hasMoreElements()) {
-                    urlList.add(urls.nextElement());
-                }
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        return urlList;
-    }
-
     public static void initForTests() {
         Map<String, String> env = System.getenv();
         for (Map.Entry<String, String> entry : env.entrySet()) {
diff --git a/databinding.properties b/databinding.properties
index 7974fa9..d0c3576 100644
--- a/databinding.properties
+++ b/databinding.properties
@@ -6,3 +6,6 @@
 javaTargetCompatibility = 1.6
 javaSourceCompatibility = 1.6
 mavenRepoName=maven-repo
+group=com.android.databinding
+testGroup=com.android.databinding.test
+
diff --git a/extensions/baseAdapters/build.gradle b/extensions/baseAdapters/build.gradle
new file mode 100644
index 0000000..74e9036
--- /dev/null
+++ b/extensions/baseAdapters/build.gradle
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+
+apply plugin: 'maven'
+apply plugin: 'com.android.library'
+apply plugin: 'com.android.databinding'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1.2"
+
+    defaultConfig {
+        minSdkVersion 7
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    packagingOptions {
+        exclude 'META-INF/services/javax.annotation.processing.Processor'
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+    }
+}
+
+dependencies {
+    compile "com.android.databinding:baseLibrary:${config.snapshotVersion}"
+    provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}"
+    compile 'com.android.support:support-v4:+'
+    compile 'com.android.support:cardview-v7:+'
+    compile 'com.android.support:appcompat-v7:+'
+}
+
+configurations {
+    jarArchives
+}
+
+
+//create jar tasks
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+
+    if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
+        return; // Skip debug builds.
+    }
+    // @Jar version is needed to run compiler tests
+    def task = project.tasks.create "jar${name.capitalize()}", Jar
+    task.dependsOn variant.javaCompile
+    task.from variant.javaCompile.destinationDir
+    def packageName = "com.android.databinding.library.baseAdapters"
+    def appPkgAsClass = packageName.replace('.', '/')
+    task.exclude("com/android/databinding/layouts/*.*")
+    task.exclude("$appPkgAsClass/generated/*")
+    task.exclude("$appPkgAsClass/BR.*")
+    artifacts.add('jarArchives', task);
+}
+
+uploadArchives {
+}
+
+uploadJarArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: "file://${config.mavenRepoDir}")
+            pom.artifactId = "adapters"
+            pom.whenConfigured {
+                println("configured pom, $it")
+                it.dependencies.find {dep -> dep.groupId == 'com.android.support' && dep.artifactId == 'support-v4' }.optional = true
+                it.dependencies.find {dep -> dep.groupId == 'com.android.support' && dep.artifactId == 'cardview-v7' }.optional = true
+                it.dependencies.find {dep -> dep.groupId == 'com.android.support' && dep.artifactId == 'appcompat-v7' }.optional = true
+            }
+        }
+    }
+}
+
+uploadArchives.dependsOn uploadJarArchives
diff --git a/extensions/baseAdapters/src/main/AndroidManifest.xml b/extensions/baseAdapters/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..38cf779
--- /dev/null
+++ b/extensions/baseAdapters/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.databinding.library.baseAdapters">
+</manifest>
diff --git a/library/src/main/java/android/binding/adapters/AbsListViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/AbsListViewBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/AbsListViewBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/AbsListViewBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/AbsSeekBarBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/AbsSeekBarBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/AbsSeekBarBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/AbsSeekBarBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/AbsSpinnerBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/AbsSpinnerBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/AbsSpinnerBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/AbsSpinnerBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/AutoCompleteTextViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/AutoCompleteTextViewBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/AutoCompleteTextViewBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/AutoCompleteTextViewBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/CardViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/CardViewBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/CardViewBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/CardViewBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/CheckedTextViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/CheckedTextViewBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/CheckedTextViewBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/CheckedTextViewBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/CompoundButtonBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/CompoundButtonBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/CompoundButtonBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/CompoundButtonBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/Converters.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/Converters.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/Converters.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/Converters.java
diff --git a/library/src/main/java/android/binding/adapters/FrameLayoutBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/FrameLayoutBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/FrameLayoutBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/FrameLayoutBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/ImageViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/ImageViewBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/ImageViewBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/ImageViewBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/LinearLayoutBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/LinearLayoutBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/LinearLayoutBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/LinearLayoutBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/ProgressBarBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/ProgressBarBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/ProgressBarBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/ProgressBarBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/RadioGroupBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/RadioGroupBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/RadioGroupBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/RadioGroupBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/SpinnerBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/SpinnerBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/SpinnerBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/SpinnerBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/SwitchBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/SwitchBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/SwitchBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/SwitchBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/SwitchCompatBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/SwitchCompatBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/SwitchCompatBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/SwitchCompatBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/TabWidgetBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/TabWidgetBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/TabWidgetBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/TabWidgetBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/TableLayoutBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/TableLayoutBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/TableLayoutBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/TableLayoutBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/TextViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/TextViewBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/TextViewBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/TextViewBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/ViewBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/ViewBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/ViewBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/ViewBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/ViewGroupBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/ViewGroupBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/ViewGroupBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/ViewGroupBindingAdapter.java
diff --git a/library/src/main/java/android/binding/adapters/ViewStubBindingAdapter.java b/extensions/baseAdapters/src/main/java/android/bindings/adapters/ViewStubBindingAdapter.java
similarity index 100%
rename from library/src/main/java/android/binding/adapters/ViewStubBindingAdapter.java
rename to extensions/baseAdapters/src/main/java/android/bindings/adapters/ViewStubBindingAdapter.java
diff --git a/extensions/build.gradle b/extensions/build.gradle
new file mode 100644
index 0000000..cfa2697
--- /dev/null
+++ b/extensions/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+
+buildscript {
+    def Properties dataBindingProperties = new Properties()
+    dataBindingProperties.load(new FileInputStream("${projectDir}/../databinding.properties"))
+    dataBindingProperties.mavenRepoDir = "${projectDir}/../${dataBindingProperties.mavenRepoName}"
+    ext.config = dataBindingProperties
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.1.3'
+        classpath "com.android.databinding:dataBinder:${config.snapshotVersion}"
+    }
+}
+
+subprojects {
+    apply plugin: 'maven'
+    group = config.group
+    version = config.snapshotVersion
+    repositories {
+        mavenCentral()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+}
\ No newline at end of file
diff --git a/extensions/gradle/wrapper/gradle-wrapper.jar b/extensions/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/extensions/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/extensions/gradle/wrapper/gradle-wrapper.properties b/extensions/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..e5fd879
--- /dev/null
+++ b/extensions/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Mar 12 15:27:48 PDT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/extensions/gradlew b/extensions/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/extensions/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/baseLibrary/src/main/java/android/binding/BinderBundle.java b/extensions/settings.gradle
similarity index 66%
copy from baseLibrary/src/main/java/android/binding/BinderBundle.java
copy to extensions/settings.gradle
index 1a59bd5..80ebcc8 100644
--- a/baseLibrary/src/main/java/android/binding/BinderBundle.java
+++ b/extensions/settings.gradle
@@ -13,15 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.binding;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.SOURCE)
-public @interface BinderBundle {
-    String value();
-}
+/**
+ * These are projects that requires a compiled version of data binding.
+ */
+include ':baseAdapters'
diff --git a/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt b/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt
index 7732f96..d89552c 100644
--- a/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt
+++ b/gradlePlugin/src/main/kotlin/DataBindingProcessLayoutsTask.kt
@@ -32,6 +32,9 @@
     public fun doIt() {
         Log.d {"running process layouts task"}
         xmlProcessor.processResources()
-        xmlProcessor.writeIntermediateFile(sdkDir)
+    }
+
+    public fun writeFiles(xmlOutFolder : File) {
+        xmlProcessor.writeIntermediateFile(sdkDir, xmlOutFolder)
     }
 }
\ No newline at end of file
diff --git a/gradlePlugin/src/main/kotlin/plugin.kt b/gradlePlugin/src/main/kotlin/plugin.kt
index 3741ba9..b991ed6 100644
--- a/gradlePlugin/src/main/kotlin/plugin.kt
+++ b/gradlePlugin/src/main/kotlin/plugin.kt
@@ -54,10 +54,22 @@
 import com.android.builder.model.ApiVersion
 import com.android.databinding.util.Log
 import org.gradle.api.Action
+import com.android.build.gradle.BaseExtension
+import com.android.build.gradle.LibraryExtension
+import com.android.build.gradle.api.LibraryVariant
+import com.android.build.gradle.api.ApplicationVariant
+import com.android.build.gradle.api.BaseVariant
+import com.android.build.gradle.internal.variant.BaseVariantData
+import com.android.build.gradle.internal.variant.ApkVariantData
+import com.android.build.gradle.internal.variant.LibraryVariantData
+import com.android.build.gradle.internal.api.LibraryVariantImpl
+import com.android.build.gradle.api.TestVariant
+import com.android.build.gradle.internal.variant.TestVariantData
+import com.android.build.gradle.internal.api.TestVariantImpl
 
 class DataBinderPlugin : Plugin<Project> {
 
-    inner class GradleFileWriter(var outputBase: String) : JavaFileWriter {
+    inner class GradleFileWriter(var outputBase: String) : JavaFileWriter() {
         override fun writeToFile(canonicalName: String, contents: String) {
             val f = File("$outputBase/${canonicalName.replaceAll("\\.", "/")}.java")
             log("Asked to write to ${canonicalName}. outputting to:${f.getAbsolutePath()}")
@@ -66,158 +78,148 @@
         }
     }
 
-    var xmlProcessor: LayoutXmlProcessor by Delegates.notNull()
-    var project: Project by Delegates.notNull()
-
-    var generatedBinderSrc: File by Delegates.notNull()
-
-    var generatedBinderOut: File by Delegates.notNull()
-
-    var androidJar: File by Delegates.notNull()
-
-    var variantData: ApplicationVariantData by Delegates.notNull()
-
-    var codeGenTargetFolder: File by Delegates.notNull()
-
-    var viewBinderSource: File by Delegates.notNull()
-
-    var sdkDir: File by Delegates.notNull()
-
-    val viewBinderSourceRoot by Delegates.lazy {
-        File(project.getBuildDir(), "databinder")
-    }
-
-
-    var fileWriter: GradleFileWriter by Delegates.notNull()
-
-    val viewBinderCompileOutput by Delegates.lazy { File(viewBinderSourceRoot, "out") }
-
     override fun apply(project: Project?) {
         if (project == null) return
-        val generateIntermediateFile = MethodClosure(this, "generateIntermediateFile")
-        val preprocessLayoutFiles = MethodClosure(this, "preprocessLayoutFiles")
-        this.project = project
         project.afterEvaluate {
-            // TODO read from app
-            val variants = arrayListOf("Debug")
-            xmlProcessor = createXmlProcessor(project)
-            log("after eval")
-            //processDebugResources
-            variants.forEach { variant ->
-                val processResTasks = it.getTasksByName("process${variant}Resources", true)
-                processResTasks.forEach {
-                    Log.d { "${it} depends on ${it.getDependsOn()}" }
-                }
-                project.getTasks().create("processDataBinding${variant}Resources",
-                        javaClass<DataBindingProcessLayoutsTask>(),
-                        object : Action<DataBindingProcessLayoutsTask> {
-                            override fun execute(task: DataBindingProcessLayoutsTask) {
-                                task.xmlProcessor = xmlProcessor
-                                task.sdkDir = sdkDir
-                                processResTasks.forEach {
-                                    // until we add these as a new source folder,
-                                    // do it the old way
-
-                                    // TODO uncomment this and comment below
-                                    // it.dependsOn(task)
-                                    it.doFirst(preprocessLayoutFiles)
-                                    it.doLast(generateIntermediateFile)
-                                }
-                                processResTasks.forEach {
-                                    it.getDependsOn().filterNot { it == task }.forEach {
-                                        Log.d { "adding dependency on ${it} for ${task}" }
-                                        task.dependsOn(it)
-                                    }
-                                }
-                            }
-                        });
-            }
+            createXmlProcessor(project)
         }
     }
 
     fun log(s: String) {
-        System.out.println("PLOG: $s")
+        System.out.println("[qwqw data binding]: $s")
     }
 
-    fun createXmlProcessor(p: Project): LayoutXmlProcessor {
-        val ss = p.getExtensions().getByName("android") as AppExtension
-        sdkDir = ss.getSdkDirectory()
-        val minSdkVersion = ss.getDefaultConfig().getMinSdkVersion()
-        androidJar = File(ss.getSdkDirectory().getAbsolutePath()
-                + "/platforms/${ss.getCompileSdkVersion()}/android.jar")
-        log("creating parser!")
+    fun createXmlProcessor(p: Project) {
+        val androidExt = p.getExtensions().getByName("android")
+        if (androidExt !is BaseExtension) {
+            return
+        }
         log("project build dir:${p.getBuildDir()}")
+        // TODO this will differ per flavor
+
+        if (androidExt is AppExtension) {
+            createXmlProcessorForApp(p, androidExt)
+        } else if (androidExt is LibraryExtension) {
+            createXmlProcessorForLibrary(p, androidExt)
+        } else {
+            throw RuntimeException("cannot understand android extension. What is it? ${androidExt}")
+        }
+    }
+
+    fun createXmlProcessorForLibrary(project : Project, lib : LibraryExtension) {
+        val sdkDir = lib.getSdkDirectory()
+        lib.getTestVariants().forEach { variant ->
+            log("test variant $variant. dir name ${variant.getDirName()}")
+            val variantData = getVariantData(variant)
+            attachXmlProcessor(project, variantData, sdkDir, false)//tests extend apk variant
+        }
+        lib.getLibraryVariants().forEach { variant ->
+            log("lib variant $variant . dir name ${variant.getDirName()}")
+            val variantData = getVariantData(variant)
+            attachXmlProcessor(project, variantData, sdkDir, true)
+        }
+    }
+
+    fun getVariantData(appVariant : LibraryVariant) : LibraryVariantData {
+        val clazz = javaClass<LibraryVariantImpl>()
+        val field = clazz.getDeclaredField("variantData")
+        field.setAccessible(true)
+        return field.get(appVariant) as LibraryVariantData
+    }
+
+    fun getVariantData(testVariant : TestVariant) : TestVariantData {
+        val clazz = javaClass<TestVariantImpl>()
+        val field = clazz.getDeclaredField("variantData")
+        field.setAccessible(true)
+        return field.get(testVariant) as TestVariantData
+    }
+
+    fun getVariantData(appVariant : ApplicationVariant) : ApplicationVariantData {
         val clazz = javaClass<ApplicationVariantImpl>()
         val field = clazz.getDeclaredField("variantData")
         field.setAccessible(true)
-        var appVariant = ss.getApplicationVariants().first { it is ApplicationVariantImpl }
-        variantData = field.get(appVariant) as ApplicationVariantData
+        return field.get(appVariant) as ApplicationVariantData
+    }
 
+    fun createXmlProcessorForApp(project : Project, appExt: AppExtension) {
+        val sdkDir = appExt.getSdkDirectory()
+        appExt.getTestVariants().forEach { testVariant ->
+            val variantData = getVariantData(testVariant)
+            attachXmlProcessor(project, variantData, sdkDir, false)
+        }
+        appExt.getApplicationVariants().forEach { appVariant ->
+            val variantData = getVariantData(appVariant)
+            attachXmlProcessor(project, variantData, sdkDir, false)
+        }
+    }
 
-        val packageName = variantData.generateRClassTask.getPackageForR()
+    fun attachXmlProcessor(project : Project, variantData : BaseVariantData<*>, sdkDir : File,
+            isLibrary : Boolean) {
+        val configuration = variantData.getVariantConfiguration()
+        val minSdkVersion = configuration.getMinSdkVersion()
+        val generateRTask = variantData.generateRClassTask
+        val packageName = generateRTask.getPackageForR()
+        log("r task name $generateRTask . text symbols output dir: ${generateRTask.getTextSymbolOutputDir()}")
+        val fullName = configuration.getFullName()
         val sources = variantData.getJavaSources()
         sources.forEach({
-            log("source: ${it}");
+            if (it is FileCollection) {
+                it.forEach {
+                    log("sources for ${variantData} ${it}}")
+                }
+            } else {
+                log("sources for ${variantData}: ${it}");
+            }
         })
         val resourceFolders = arrayListOf(variantData.mergeResourcesTask.getOutputDir())
         log("MERGE RES OUTPUT ${variantData.mergeResourcesTask.getOutputDir()}")
-        //TODO
-        codeGenTargetFolder = variantData.generateRClassTask.getSourceOutputDir()
-        val resGenTargetFolder = variantData.generateRClassTask.getResDir()
+        val codeGenTargetFolder = generateRTask.getSourceOutputDir()
+        // TODO unnecessary?
+
+        // TODO attach to test module as well!
+
         variantData.addJavaSourceFoldersToModel(codeGenTargetFolder)
-        variantData.addJavaSourceFoldersToModel(viewBinderSourceRoot)
-
-        val jCompileTask = variantData.javaCompileTask
-        val dexTask = variantData.dexTask
-        val options = jCompileTask.getOptions()
-        log("compile options: ${options.optionMap()}")
-        viewBinderSource = File(viewBinderSourceRoot.getAbsolutePath() + "/src")
-        viewBinderSource.mkdirs()
-        variantData.registerJavaGeneratingTask(project.task("dataBinderDummySourceGenTask",
-                MethodClosure(this, "dummySourceGenTask")),
-                        File(viewBinderSourceRoot.getAbsolutePath() + "/src/"))
-        viewBinderCompileOutput.mkdirs()
-        log("view binder source will be ${viewBinderSource}")
-        log("adding out dir to input files ${viewBinderCompileOutput}")
-        var inputFiles = dexTask.getInputFiles()
-        var inputDir = dexTask.getInputDir()
-        log("current input files for dex are ${inputFiles} or dir ${inputDir}")
-        if (inputDir != null && inputFiles == null) {
-            inputFiles = arrayListOf(inputDir)
-            dexTask.setInputDir(null)
-        }
-        inputFiles.add(viewBinderCompileOutput)
-        dexTask.setInputFiles(inputFiles)
-
-        dexTask.doFirst(MethodClosure(this, "preDexAnalysis"))
         val writerOutBase = codeGenTargetFolder.getAbsolutePath();
-        fileWriter = GradleFileWriter(writerOutBase)
-        return LayoutXmlProcessor(packageName, resourceFolders, fileWriter,
-                minSdkVersion.getApiLevel())
-    }
+        val fileWriter = GradleFileWriter(writerOutBase)
+        val xmlProcessor = LayoutXmlProcessor(packageName, resourceFolders, fileWriter,
+                minSdkVersion.getApiLevel(), isLibrary)
+        val processResTask = generateRTask
 
+        val xmlOutDir = "${project.getBuildDir()}/layout-info/${configuration.getDirName()}";
+        log("xml output for ${variantData} is ${xmlOutDir}")
+        val dataBindingTaskName = "dataBinding${processResTask.getName().capitalize()}"
+        log("created task $dataBindingTaskName")
+        project.getTasks().create(dataBindingTaskName,
+                javaClass<DataBindingProcessLayoutsTask>(),
+                object : Action<DataBindingProcessLayoutsTask> {
+                    override fun execute(task: DataBindingProcessLayoutsTask) {
+                        task.xmlProcessor = xmlProcessor
+                        task.sdkDir = sdkDir
+                        Log.d { "TASK adding dependency on ${task} for ${processResTask}" }
+                        processResTask.dependsOn(task)
+                        processResTask.getDependsOn().filterNot { it == task }.forEach {
+                            Log.d { "adding dependency on ${it} for ${task}" }
+                            task.dependsOn(it)
+                        }
+                        processResTask.doLast {
+                            task.writeFiles(File(xmlOutDir))
+                        }
+                    }
+                });
 
-    fun dummySourceGenTask(o: Any?) {
-        System.out.println("running dummySourceGenTask")
-    }
-
-    fun preDexAnalysis(o: Any?) {
-        val jCompileTask = variantData.javaCompileTask
-        val dexTask = variantData.dexTask
-        log("dex task files: ${dexTask.getInputFiles()} ${dexTask.getInputFiles().javaClass}")
-        log("compile CP: ${jCompileTask.getClasspath().getAsPath()}")
-        val jarUrl = androidJar.toURI().toURL()
-        val androidClassLoader = URLClassLoader(array(jarUrl))
-        val cpFiles = arrayListOf<File>()
-        cpFiles.addAll(dexTask.getInputFiles())
-        cpFiles.addAll(jCompileTask.getClasspath().getFiles())
-    }
-
-    fun preprocessLayoutFiles(o: Any?) {
-        xmlProcessor.processResources()
-    }
-
-    fun generateIntermediateFile(o: Any?) {
-        xmlProcessor.writeIntermediateFile(sdkDir)
+        if (isLibrary) {
+            val packageJarTaskName = "package${fullName.capitalize()}Jar"
+            val packageTask = project.getTasks().findByName(packageJarTaskName)
+            if (packageTask !is org.gradle.api.tasks.bundling.Jar) {
+                throw RuntimeException("cannot find package task in $project $variantData project $packageJarTaskName")
+            }
+            val excludePattern = "com/android/databinding/layouts/*.*"
+            val appPkgAsClass = packageName.replace('.', '/')
+            packageTask.exclude(excludePattern)
+            packageTask.exclude("$appPkgAsClass/generated/*")
+            packageTask.exclude("$appPkgAsClass/BR.*")
+            packageTask.exclude(xmlProcessor.getInfoClassFullName().replace('.', '/') + ".class")
+            log("excludes ${packageTask.getExcludes()}")
+        }
     }
 }
diff --git a/integration-tests/IndependentLibrary/app/.gitignore b/integration-tests/IndependentLibrary/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/integration-tests/IndependentLibrary/app/build.gradle b/integration-tests/IndependentLibrary/app/build.gradle
new file mode 100644
index 0000000..552e1a5
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/build.gradle
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+apply plugin: 'maven'
+apply plugin: 'com.android.library'
+apply plugin: 'com.android.databinding'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1.2"
+
+    defaultConfig {
+        minSdkVersion 7
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    packagingOptions {
+        exclude 'META-INF/services/javax.annotation.processing.Processor'
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile "com.android.databinding:library:${config.snapshotVersion}"
+    provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}"
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: "file://${config.mavenRepoDir}")
+            pom.artifactId = 'independent-library'
+            pom.version = config.snapshotVersion
+            pom.groupId = config.testGroup
+        }
+    }
+}
+
+connectedCheck.dependsOn uploadArchives
\ No newline at end of file
diff --git a/integration-tests/IndependentLibrary/app/proguard-rules.pro b/integration-tests/IndependentLibrary/app/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/integration-tests/IndependentLibrary/app/src/androidTest/java/com/android/databinding/test/independentlibrary/ApplicationTest.java b/integration-tests/IndependentLibrary/app/src/androidTest/java/com/android/databinding/test/independentlibrary/ApplicationTest.java
new file mode 100644
index 0000000..0b267a3
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/src/androidTest/java/com/android/databinding/test/independentlibrary/ApplicationTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 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.databinding.test.independentlibrary;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}
\ No newline at end of file
diff --git a/integration-tests/IndependentLibrary/app/src/androidTest/java/com/android/databinding/test/independentlibrary/LibraryActivityTest.java b/integration-tests/IndependentLibrary/app/src/androidTest/java/com/android/databinding/test/independentlibrary/LibraryActivityTest.java
new file mode 100644
index 0000000..939d1ba
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/src/androidTest/java/com/android/databinding/test/independentlibrary/LibraryActivityTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 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.databinding.test.independentlibrary;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+import android.widget.TextView;
+
+public class LibraryActivityTest extends ActivityInstrumentationTestCase2<LibraryActivity> {
+    public LibraryActivityTest() {
+        super(LibraryActivity.class);
+    }
+
+    public void testTextViewContents() throws Throwable {
+        final LibraryActivity activity = getActivity();
+        assertNotNull("test sanity", activity);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TextView textView = (TextView) activity.findViewById(R.id.fooTextView);
+                final String expected = LibraryActivity.FIELD_VALUE + " " +
+                        LibraryActivity.FIELD_VALUE;
+                assertEquals(expected, textView.getText().toString());
+            }
+        });
+    }
+}
diff --git a/integration-tests/IndependentLibrary/app/src/main/AndroidManifest.xml b/integration-tests/IndependentLibrary/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ff2388c
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.databinding.test.independentlibrary">
+
+    <application android:allowBackup="true"
+                 android:label="@string/app_name">
+        <activity android:name=".LibraryActivity"/>
+    </application>
+
+</manifest>
diff --git a/integration-tests/IndependentLibrary/app/src/main/java/com/android/databinding/test/independentlibrary/LibraryActivity.java b/integration-tests/IndependentLibrary/app/src/main/java/com/android/databinding/test/independentlibrary/LibraryActivity.java
new file mode 100644
index 0000000..2d2e024
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/src/main/java/com/android/databinding/test/independentlibrary/LibraryActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 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.databinding.test.independentlibrary;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.databinding.test.independentlibrary.vo.MyBindableObject;
+import com.android.databinding.test.independentlibrary.generated.LibraryLayoutBinding;
+public class LibraryActivity extends Activity {
+    public static final String FIELD_VALUE = "BAR";
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        LibraryLayoutBinding binding = LibraryLayoutBinding.inflate(this);
+        setContentView(binding.getRoot());
+        MyBindableObject object = new MyBindableObject();
+        object.setField(FIELD_VALUE);
+        binding.setFoo(object);
+        binding.executePendingBindings();
+    }
+}
diff --git a/integration-tests/IndependentLibrary/app/src/main/java/com/android/databinding/test/independentlibrary/LibraryAdapter.java b/integration-tests/IndependentLibrary/app/src/main/java/com/android/databinding/test/independentlibrary/LibraryAdapter.java
new file mode 100644
index 0000000..e7a2ea6
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/src/main/java/com/android/databinding/test/independentlibrary/LibraryAdapter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 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.databinding.test.independentlibrary;
+
+import android.binding.BindingAdapter;
+import android.view.View;
+
+public class LibraryAdapter {
+    @BindingAdapter("myTagAttr")
+    public static void set(View view, String someTag) {
+        view.setTag(someTag);
+    }
+}
diff --git a/integration-tests/IndependentLibrary/app/src/main/java/com/android/databinding/test/independentlibrary/vo/MyBindableObject.java b/integration-tests/IndependentLibrary/app/src/main/java/com/android/databinding/test/independentlibrary/vo/MyBindableObject.java
new file mode 100644
index 0000000..c4c5cc8
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/src/main/java/com/android/databinding/test/independentlibrary/vo/MyBindableObject.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 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.databinding.test.independentlibrary.vo;
+
+import com.android.databinding.library.BaseObservable;
+import com.android.databinding.test.independentlibrary.BR;
+
+import android.binding.Bindable;
+
+public class MyBindableObject extends BaseObservable {
+    @Bindable
+    private String mField;
+
+    public String getField() {
+        return mField;
+    }
+
+    public void setField(String field) {
+        mField = field;
+        notifyPropertyChanged(BR.field);
+    }
+}
diff --git a/integration-tests/IndependentLibrary/app/src/main/res/layout/library_layout.xml b/integration-tests/IndependentLibrary/app/src/main/res/layout/library_layout.xml
new file mode 100644
index 0000000..a7ba285
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/src/main/res/layout/library_layout.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <variable name="foo" type="com.android.databinding.test.independentlibrary.vo.MyBindableObject"/>
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+        android:id="@+id/fooTextView"
+            android:text='@{foo.field +  " " + foo.field}'/>
+</LinearLayout>
\ No newline at end of file
diff --git a/integration-tests/IndependentLibrary/app/src/main/res/values/strings.xml b/integration-tests/IndependentLibrary/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..8e6caf7
--- /dev/null
+++ b/integration-tests/IndependentLibrary/app/src/main/res/values/strings.xml
@@ -0,0 +1,18 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<resources>
+    <string name="app_name">IndependentLibrary</string>
+</resources>
diff --git a/integration-tests/IndependentLibrary/build.gradle b/integration-tests/IndependentLibrary/build.gradle
new file mode 100644
index 0000000..d74b7e6
--- /dev/null
+++ b/integration-tests/IndependentLibrary/build.gradle
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+buildscript {
+    def Properties dataBindingProperties = new Properties()
+    dataBindingProperties.load(new FileInputStream("${projectDir}/../../databinding.properties"))
+    dataBindingProperties.mavenRepoDir = "${projectDir}/../../${dataBindingProperties.mavenRepoName}"
+    ext.config = dataBindingProperties
+    println "loaded config"
+
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.1.3'
+        classpath "com.android.databinding:dataBinder:${config.snapshotVersion}"
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+}
diff --git a/integration-tests/IndependentLibrary/gradle.properties b/integration-tests/IndependentLibrary/gradle.properties
new file mode 100644
index 0000000..efd2362
--- /dev/null
+++ b/integration-tests/IndependentLibrary/gradle.properties
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2015 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.
+#
+
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.jar b/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties b/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..de86a57
--- /dev/null
+++ b/integration-tests/IndependentLibrary/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2015 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.
+#
+
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/integration-tests/IndependentLibrary/gradlew b/integration-tests/IndependentLibrary/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/integration-tests/IndependentLibrary/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/integration-tests/IndependentLibrary/gradlew.bat b/integration-tests/IndependentLibrary/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/integration-tests/IndependentLibrary/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/integration-tests/IndependentLibrary/settings.gradle b/integration-tests/IndependentLibrary/settings.gradle
new file mode 100644
index 0000000..e2afad2
--- /dev/null
+++ b/integration-tests/IndependentLibrary/settings.gradle
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+include ':app'
diff --git a/integration-tests/MultiModuleTestApp/app/.gitignore b/integration-tests/MultiModuleTestApp/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/integration-tests/MultiModuleTestApp/app/build.gradle b/integration-tests/MultiModuleTestApp/app/build.gradle
new file mode 100644
index 0000000..8079ff5
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/build.gradle
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'com.android.databinding'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "22"
+
+    defaultConfig {
+        applicationId "com.android.databinding.multimoduletestapp"
+        minSdkVersion 7
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    packagingOptions {
+        exclude 'META-INF/services/javax.annotation.processing.Processor'
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+    }
+}
+
+println "combined ${config.testGroup}.independent-library:${config.snapshotVersion}"
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile project(':testlibrary')
+    compile "com.android.support:support-v4:+"
+    provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}"
+    compile "${config.testGroup}:independent-library:${config.snapshotVersion}"
+}
diff --git a/integration-tests/MultiModuleTestApp/app/proguard-rules.pro b/integration-tests/MultiModuleTestApp/app/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/ApplicationTest.java b/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/ApplicationTest.java
new file mode 100644
index 0000000..f179748
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/ApplicationTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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.databinding.multimoduletestapp;
+
+import com.android.databinding.testlibrary.ObservableInLibrary;
+
+import android.app.Application;
+import android.binding.Observable;
+import android.binding.OnPropertyChangedListener;
+import android.test.ApplicationTestCase;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}
\ No newline at end of file
diff --git a/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java b/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java
new file mode 100644
index 0000000..32dbffe
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/androidTest/java/com/android/databinding/multimoduletestapp/EventIdsTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015 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.databinding.multimoduletestapp;
+
+import com.android.databinding.testlibrary.ObservableInLibrary;
+
+import android.binding.Observable;
+import android.binding.OnPropertyChangedListener;
+import android.os.Debug;
+import android.test.AndroidTestCase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.android.databinding.multimoduletestapp.BR;
+
+public class EventIdsTest extends AndroidTestCase {
+    public void testLibraryObservable() {
+        ObservableInLibrary observableInLibrary = new ObservableInLibrary();
+        EventCounter ec = new EventCounter();
+        observableInLibrary.addOnPropertyChangedListener(ec);
+        ec.assertProperty(BR.libField1, 0);
+        ec.assertProperty(BR.libField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInLibrary.setLibField1("a");
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInLibrary.setLibField2("b");
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 1);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInLibrary.setSharedField(3);
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 1);
+        ec.assertProperty(BR.sharedField, 1);
+    }
+
+    public void testAppObservable() {
+        ObservableInMainApp observableInMainApp = new ObservableInMainApp();
+        EventCounter ec = new EventCounter();
+        observableInMainApp.addOnPropertyChangedListener(ec);
+        ec.assertProperty(BR.appField1, 0);
+        ec.assertProperty(BR.appField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInMainApp.setAppField2(3);
+        ec.assertProperty(BR.appField1, 0);
+        ec.assertProperty(BR.appField2, 1);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInMainApp.setAppField1("b");
+        ec.assertProperty(BR.appField1, 1);
+        ec.assertProperty(BR.appField2, 1);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observableInMainApp.setSharedField(5);
+        ec.assertProperty(BR.appField1, 1);
+        ec.assertProperty(BR.appField2, 1);
+        ec.assertProperty(BR.sharedField, 1);
+    }
+
+    public void testExtendingObservable() {
+        ObservableExtendingLib observable = new ObservableExtendingLib();
+        EventCounter ec = new EventCounter();
+        observable.addOnPropertyChangedListener(ec);
+
+        ec.assertProperty(BR.childClassField, 0);
+        ec.assertProperty(BR.libField1, 0);
+        ec.assertProperty(BR.libField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observable.setChildClassField("a");
+        ec.assertProperty(BR.childClassField, 1);
+        ec.assertProperty(BR.libField1, 0);
+        ec.assertProperty(BR.libField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observable.setLibField1("b");
+        ec.assertProperty(BR.childClassField, 1);
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 0);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observable.setLibField2("c");
+        ec.assertProperty(BR.childClassField, 1);
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 1);
+        ec.assertProperty(BR.sharedField, 0);
+
+        observable.setSharedField(2);
+        ec.assertProperty(BR.childClassField, 1);
+        ec.assertProperty(BR.libField1, 1);
+        ec.assertProperty(BR.libField2, 1);
+        ec.assertProperty(BR.sharedField, 1);
+    }
+
+    private static class EventCounter implements OnPropertyChangedListener {
+        Map<Integer, Integer> mCounter = new HashMap<>();
+
+        @Override
+        public void onPropertyChanged(Observable observable, int propertyId) {
+            mCounter.put(propertyId, get(propertyId) + 1);
+        }
+
+        public int get(int propertyId) {
+            Integer val = mCounter.get(propertyId);
+            return val == null ? 0 : val;
+        }
+
+        private void assertProperty(int propertyId, int value) {
+            assertEquals(get(propertyId), value);
+        }
+    }
+}
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/AndroidManifest.xml b/integration-tests/MultiModuleTestApp/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..aaa0719
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.databinding.multimoduletestapp" >
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/java/com/android/databinding/multimoduletestapp/MainActivity.java b/integration-tests/MultiModuleTestApp/app/src/main/java/com/android/databinding/multimoduletestapp/MainActivity.java
new file mode 100644
index 0000000..f8a012a
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/java/com/android/databinding/multimoduletestapp/MainActivity.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 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.databinding.multimoduletestapp;
+
+import com.android.databinding.multimoduletestapp.generated.ActivityMainBinding;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+
+public class MainActivity extends Activity {
+    ActivityMainBinding mBinder;
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mBinder = ActivityMainBinding.inflate(this);
+        setContentView(mBinder.getRoot());
+    }
+
+    public ActivityMainBinding getBinder() {
+        return mBinder;
+    }
+
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        getMenuInflater().inflate(R.menu.menu_main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle action bar item clicks here. The action bar will
+        // automatically handle clicks on the Home/Up button, so long
+        // as you specify a parent activity in AndroidManifest.xml.
+        int id = item.getItemId();
+
+        //noinspection SimplifiableIfStatement
+        if (id == R.id.action_settings) {
+            return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/java/com/android/databinding/multimoduletestapp/ObservableExtendingLib.java b/integration-tests/MultiModuleTestApp/app/src/main/java/com/android/databinding/multimoduletestapp/ObservableExtendingLib.java
new file mode 100644
index 0000000..b558790
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/java/com/android/databinding/multimoduletestapp/ObservableExtendingLib.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 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.databinding.multimoduletestapp;
+
+import com.android.databinding.testlibrary.ObservableInLibrary;
+
+import android.binding.Bindable;
+import com.android.databinding.multimoduletestapp.BR;
+
+public class ObservableExtendingLib extends ObservableInLibrary {
+    @Bindable
+    private String mChildClassField;
+
+    public String getChildClassField() {
+        return mChildClassField;
+    }
+
+    public void setChildClassField(String childClassField) {
+        mChildClassField = childClassField;
+        notifyPropertyChanged(BR.childClassField);
+    }
+}
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/java/com/android/databinding/multimoduletestapp/ObservableInMainApp.java b/integration-tests/MultiModuleTestApp/app/src/main/java/com/android/databinding/multimoduletestapp/ObservableInMainApp.java
new file mode 100644
index 0000000..c870fd4
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/java/com/android/databinding/multimoduletestapp/ObservableInMainApp.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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.databinding.multimoduletestapp;
+
+import android.binding.Bindable;
+
+import com.android.databinding.library.BaseObservable;
+import com.android.databinding.multimoduletestapp.BR;
+
+public class ObservableInMainApp extends BaseObservable {
+    @Bindable
+    private String mAppField1;
+    @Bindable
+    private int mAppField2;
+    @Bindable
+    private int mSharedField;
+
+    public String getAppField1() {
+        return mAppField1;
+    }
+
+    public void setAppField1(String appField1) {
+        mAppField1 = appField1;
+        notifyPropertyChanged(BR.appField1);
+    }
+
+    public int getAppField2() {
+        return mAppField2;
+    }
+
+    public void setAppField2(int appField2) {
+        mAppField2 = appField2;
+        notifyPropertyChanged(BR.appField2);
+    }
+
+    public int getSharedField() {
+        return mSharedField;
+    }
+
+    public void setSharedField(int sharedField) {
+        mSharedField = sharedField;
+        notifyPropertyChanged(BR.sharedField);
+    }
+}
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_main.xml b/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..4549e81
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
+    <variable name="foo" type="String"/>
+    <TextView android:text='@{foo + " " + foo}' android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</RelativeLayout>
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_test_library_main.xml b/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_test_library_main.xml
new file mode 100644
index 0000000..5a34049
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/layout/activity_test_library_main.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    >
+    <TextView android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</RelativeLayout>
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/menu/menu_main.xml b/integration-tests/MultiModuleTestApp/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..4674d4d
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
+    <item android:id="@+id/action_settings" android:title="@string/action_settings"
+        android:orderInCategory="100" android:showAsAction="never" />
+</menu>
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/values-v21/styles.xml b/integration-tests/MultiModuleTestApp/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..9b24d4f
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<resources>
+    <style name="AppTheme" parent="android:Theme.Material.Light">
+    </style>
+</resources>
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/values-w820dp/dimens.xml b/integration-tests/MultiModuleTestApp/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..4719591
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<resources>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/values/dimens.xml b/integration-tests/MultiModuleTestApp/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..c06ae3f
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/values/strings.xml b/integration-tests/MultiModuleTestApp/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..20d68aa
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<resources>
+
+    <string name="app_name">Multi Module Test App</string>
+    <string name="hello_world">Hello world!</string>
+    <string name="action_settings">Settings</string>
+
+</resources>
diff --git a/integration-tests/MultiModuleTestApp/app/src/main/res/values/styles.xml b/integration-tests/MultiModuleTestApp/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..7dc23c8
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/app/src/main/res/values/styles.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+    </style>
+
+</resources>
diff --git a/integration-tests/MultiModuleTestApp/build.gradle b/integration-tests/MultiModuleTestApp/build.gradle
new file mode 100644
index 0000000..b9340e5
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/build.gradle
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+buildscript {
+    def Properties dataBindingProperties = new Properties()
+    dataBindingProperties.load(new FileInputStream("${projectDir}/../../databinding.properties"))
+    dataBindingProperties.mavenRepoDir = "${projectDir}/../../${dataBindingProperties.mavenRepoName}"
+    ext.config = dataBindingProperties
+    println "loaded config"
+
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.1.3'
+        classpath "com.android.databinding:dataBinder:${config.snapshotVersion}"
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+        maven {
+            url config.mavenRepoDir
+        }
+    }
+}
diff --git a/integration-tests/MultiModuleTestApp/gradle.properties b/integration-tests/MultiModuleTestApp/gradle.properties
new file mode 100644
index 0000000..5b24ba3
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/gradle.properties
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2015 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.
+#
+
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.jar b/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties b/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..992e276
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2015 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.
+#
+
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/integration-tests/MultiModuleTestApp/gradlew b/integration-tests/MultiModuleTestApp/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/integration-tests/MultiModuleTestApp/gradlew.bat b/integration-tests/MultiModuleTestApp/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/baseLibrary/src/main/java/android/binding/BinderBundle.java b/integration-tests/MultiModuleTestApp/settings.gradle
similarity index 66%
copy from baseLibrary/src/main/java/android/binding/BinderBundle.java
copy to integration-tests/MultiModuleTestApp/settings.gradle
index 1a59bd5..c79cb3d 100644
--- a/baseLibrary/src/main/java/android/binding/BinderBundle.java
+++ b/integration-tests/MultiModuleTestApp/settings.gradle
@@ -13,15 +13,5 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.binding;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.SOURCE)
-public @interface BinderBundle {
-    String value();
-}
+include ':app', ':testlibrary'
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/.gitignore b/integration-tests/MultiModuleTestApp/testlibrary/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/build.gradle b/integration-tests/MultiModuleTestApp/testlibrary/build.gradle
new file mode 100644
index 0000000..4b2c87c
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/build.gradle
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'com.android.databinding'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1.2"
+
+    defaultConfig {
+        minSdkVersion 7
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    packagingOptions {
+        exclude 'META-INF/services/javax.annotation.processing.Processor'
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile "com.android.databinding:library:${config.snapshotVersion}"
+    provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}"
+}
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/proguard-rules.pro b/integration-tests/MultiModuleTestApp/testlibrary/proguard-rules.pro
new file mode 100644
index 0000000..b7210d1
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/yboyar/android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/baseLibrary/src/main/java/android/binding/BinderBundle.java b/integration-tests/MultiModuleTestApp/testlibrary/src/androidTest/java/com/android/databinding/testlibrary/ApplicationTest.java
similarity index 62%
copy from baseLibrary/src/main/java/android/binding/BinderBundle.java
copy to integration-tests/MultiModuleTestApp/testlibrary/src/androidTest/java/com/android/databinding/testlibrary/ApplicationTest.java
index 1a59bd5..9efa645 100644
--- a/baseLibrary/src/main/java/android/binding/BinderBundle.java
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/androidTest/java/com/android/databinding/testlibrary/ApplicationTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.binding;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+package com.android.databinding.testlibrary;
 
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.SOURCE)
-public @interface BinderBundle {
-    String value();
-}
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}
\ No newline at end of file
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/AndroidManifest.xml b/integration-tests/MultiModuleTestApp/testlibrary/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..263d663
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.databinding.testlibrary" >
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name" >
+        <activity
+            android:name=".TestLibraryMainActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/com/android/databinding/testlibrary/ObservableInLibrary.java b/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/com/android/databinding/testlibrary/ObservableInLibrary.java
new file mode 100644
index 0000000..f5470f1
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/com/android/databinding/testlibrary/ObservableInLibrary.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 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.databinding.testlibrary;
+
+import android.binding.Bindable;
+
+import com.android.databinding.library.BaseObservable;
+import com.android.databinding.testlibrary.BR;
+
+public class ObservableInLibrary extends BaseObservable {
+
+    @Bindable
+    private String mLibField1;
+
+    @Bindable
+    private String mLibField2;
+
+    @Bindable
+    private int mSharedField;
+
+    public String getLibField1() {
+        return mLibField1;
+    }
+
+    public void setLibField1(String libField1) {
+        mLibField1 = libField1;
+        notifyPropertyChanged(BR.libField1);
+    }
+
+    public String getLibField2() {
+        return mLibField2;
+    }
+
+    public void setLibField2(String libField2) {
+        mLibField2 = libField2;
+        notifyPropertyChanged(BR.libField2);
+    }
+
+    public int getSharedField() {
+        return mSharedField;
+    }
+
+    public void setSharedField(int sharedField) {
+        mSharedField = sharedField;
+        notifyPropertyChanged(BR.sharedField);
+    }
+}
diff --git a/baseLibrary/src/main/java/android/binding/BinderBundle.java b/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/com/android/databinding/testlibrary/TestLibObject.java
similarity index 66%
rename from baseLibrary/src/main/java/android/binding/BinderBundle.java
rename to integration-tests/MultiModuleTestApp/testlibrary/src/main/java/com/android/databinding/testlibrary/TestLibObject.java
index 1a59bd5..330b870 100644
--- a/baseLibrary/src/main/java/android/binding/BinderBundle.java
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/com/android/databinding/testlibrary/TestLibObject.java
@@ -13,15 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.binding;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+package com.android.databinding.testlibrary;
 
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.SOURCE)
-public @interface BinderBundle {
-    String value();
+import android.binding.Bindable;
+
+public class TestLibObject {
+    @Bindable
+    private String mField;
+
+    public String getField() {
+        return mField;
+    }
+
+    public void setField(String field) {
+        this.mField = field;
+    }
 }
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/com/android/databinding/testlibrary/TestLibraryMainActivity.java b/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/com/android/databinding/testlibrary/TestLibraryMainActivity.java
new file mode 100644
index 0000000..812b222
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/java/com/android/databinding/testlibrary/TestLibraryMainActivity.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 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.databinding.testlibrary;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import com.android.databinding.testlibrary.R;
+import com.android.databinding.testlibrary.generated.ActivityTestLibraryMainBinding;
+public class TestLibraryMainActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ActivityTestLibraryMainBinding binder = ActivityTestLibraryMainBinding.inflate(this);
+        setContentView(binder.getRoot());
+
+    }
+
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        getMenuInflater().inflate(R.menu.menu_test_library_main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle action bar item clicks here. The action bar will
+        // automatically handle clicks on the Home/Up button, so long
+        // as you specify a parent activity in AndroidManifest.xml.
+        int id = item.getItemId();
+
+        //noinspection SimplifiableIfStatement
+        if (id == R.id.action_settings) {
+            return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-hdpi/ic_launcher.png b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-mdpi/ic_launcher.png b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xhdpi/ic_launcher.png b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xxhdpi/ic_launcher.png b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/activity_test_library_main.xml b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/activity_test_library_main.xml
new file mode 100644
index 0000000..b731cc0
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/layout/activity_test_library_main.xml
@@ -0,0 +1,29 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    tools:context=".TestLibraryMainActivity">
+    <variable name="obj1" type="com.android.databinding.testlibrary.TestLibObject"/>
+
+    <TextView android:text="@{obj1.field}" android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</RelativeLayout>
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/menu/menu_test_library_main.xml b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/menu/menu_test_library_main.xml
new file mode 100644
index 0000000..68d936d
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/menu/menu_test_library_main.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" tools:context=".TestLibraryMainActivity">
+    <item android:id="@+id/action_settings" android:title="@string/action_settings"
+        android:orderInCategory="100" android:showAsAction="never" />
+</menu>
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values-w820dp/dimens.xml b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..4719591
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<resources>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/dimens.xml b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..c06ae3f
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/strings.xml b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/strings.xml
new file mode 100644
index 0000000..d930ddb
--- /dev/null
+++ b/integration-tests/MultiModuleTestApp/testlibrary/src/main/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<resources>
+
+    <string name="app_name">Test Library</string>
+    <string name="hello_world">Hello world!</string>
+    <string name="action_settings">Settings</string>
+
+</resources>
diff --git a/integration-tests/TestApp/app/build.gradle b/integration-tests/TestApp/app/build.gradle
index 2fe5cf4..11f1757 100644
--- a/integration-tests/TestApp/app/build.gradle
+++ b/integration-tests/TestApp/app/build.gradle
@@ -27,6 +27,7 @@
 dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
     compile "com.android.databinding:library:${config.snapshotVersion}"
+    compile "com.android.databinding:adapters:${config.snapshotVersion}"
     compile "com.android.support:support-v4:+"
     provided "com.android.databinding:annotationprocessor:${config.snapshotVersion}"
 }
diff --git a/integration-tests/TestApp/app/src/androidTest/java/com/android/databinding/testapp/ProcessBindableTest.java b/integration-tests/TestApp/app/src/androidTest/java/com/android/databinding/testapp/ProcessBindableTest.java
index a092d0d..0b57927 100644
--- a/integration-tests/TestApp/app/src/androidTest/java/com/android/databinding/testapp/ProcessBindableTest.java
+++ b/integration-tests/TestApp/app/src/androidTest/java/com/android/databinding/testapp/ProcessBindableTest.java
@@ -22,7 +22,7 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.HashSet;
-
+import com.android.databinding.testapp.BR;
 public class ProcessBindableTest extends BaseDataBinderTest<BasicBindingBinding> {
     private static String[] EXPECTED_BINDING_NAMES = {
             "bindableField1",
@@ -40,7 +40,7 @@
     }
 
     public void testFieldsGenerated() throws IllegalAccessException {
-        Field[] fields = android.binding.BR.class.getFields();
+        Field[] fields = BR.class.getFields();
 
         ArrayMap<String, Integer> fieldValues = new ArrayMap<>();
         int modifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL;
diff --git a/integration-tests/TestApp/app/src/main/java/com/android/databinding/testapp/vo/PublicFinalWithObservableTestVo.java b/integration-tests/TestApp/app/src/main/java/com/android/databinding/testapp/vo/PublicFinalWithObservableTestVo.java
index dd415de..2c9695b 100644
--- a/integration-tests/TestApp/app/src/main/java/com/android/databinding/testapp/vo/PublicFinalWithObservableTestVo.java
+++ b/integration-tests/TestApp/app/src/main/java/com/android/databinding/testapp/vo/PublicFinalWithObservableTestVo.java
@@ -17,6 +17,7 @@
 import com.android.databinding.testapp.R;
 
 import android.binding.Bindable;
+import com.android.databinding.testapp.BR;
 
 public class PublicFinalWithObservableTestVo {
     public final int myField;
@@ -36,7 +37,7 @@
 
         public void setVal(int val) {
             this.val = val;
-            notifyPropertyChanged(android.binding.BR.val);
+            notifyPropertyChanged(BR.val);
         }
     }
 }
diff --git a/integration-tests/TestApp/app/src/main/java/com/android/databinding/testapp/vo/TextViewBindingObject.java b/integration-tests/TestApp/app/src/main/java/com/android/databinding/testapp/vo/TextViewBindingObject.java
index b98ded2..482be18 100644
--- a/integration-tests/TestApp/app/src/main/java/com/android/databinding/testapp/vo/TextViewBindingObject.java
+++ b/integration-tests/TestApp/app/src/main/java/com/android/databinding/testapp/vo/TextViewBindingObject.java
@@ -25,6 +25,7 @@
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.TextView;
+import com.android.databinding.testapp.BR;
 
 public class TextViewBindingObject extends BindingAdapterBindingObject {
 
@@ -236,7 +237,7 @@
 
     public void setCapitalize(TextKeyListener.Capitalize capitalize) {
         mCapitalize = capitalize;
-        notifyPropertyChanged(android.binding.BR.capitalize);
+        notifyPropertyChanged(BR.capitalize);
     }
 
     public boolean isPhoneNumber() {
diff --git a/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml b/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml
index caf0389..5906fd1 100644
--- a/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml
+++ b/integration-tests/TestApp/app/src/main/res/layout-land/multi_res_layout.xml
@@ -30,8 +30,7 @@
 
     <include layout="@layout/included_layout" android:id="@+id/includedLayoutConflict"
              bind:innerObject="@{objectInLand}"
-             bind:innerValue="@{`modified ` + objectInLand.intValue}"
-            />
+             bind:innerValue='@{"modified " + objectInLand.intValue}' />
     <include layout="@layout/basic_binding" android:id="@+id/includedLayoutShared"
              bind:a="@{objectInDefault.stringValue}"
             />
diff --git a/library/build.gradle b/library/build.gradle
index c91e117..bb078f7 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -59,9 +59,6 @@
     compile fileTree(dir: 'libs', include: ['*.jar'])
     compile project(":baseLibrary")
     compile 'com.android.support:support-v4:+'
-    provided project(":annotationprocessor")
-    provided 'com.android.support:cardview-v7:+'
-    provided 'com.android.support:appcompat-v7:+'
 }
 
 configurations {
diff --git a/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java b/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java
index 6785b5f..aa7661a 100644
--- a/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java
+++ b/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/MainActivity.java
@@ -13,8 +13,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import android.binding.BR;
-
 import com.android.databinding.library.DataBindingUtil;
 import com.android.databinding.library.PropertyChangeRegistry;
 import com.android.example.bindingdemo.generated.ListItemBinding;
@@ -26,7 +24,7 @@
 import java.util.Arrays;
 import java.util.List;
 
-
+import  com.android.example.bindingdemo.BR;
 public class MainActivity extends ActionBarActivity implements Observable {
     @Bindable
     UserAdapter tkAdapter;
@@ -78,7 +76,7 @@
             return;
         }
         this.selected = selected;
-        mListeners.notifyChange(this, android.binding.BR.selected);
+        mListeners.notifyChange(this, BR.selected);
     }
 
     @Bindable
@@ -190,7 +188,7 @@
             }
             userList.add(user);
             notifyItemInserted(userList.size() - 1);
-            mListeners.notifyChange(this, android.binding.BR.itemCount);
+            mListeners.notifyChange(this, BR.itemCount);
         }
 
         public void remove(User user) {
@@ -200,7 +198,7 @@
             }
             userList.remove(i);
             notifyItemRemoved(i);
-            mListeners.notifyChange(this, android.binding.BR.itemCount);
+            mListeners.notifyChange(this, BR.itemCount);
         }
 
         @Override
diff --git a/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/User.java b/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/User.java
index 929d695..9252321 100644
--- a/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/User.java
+++ b/samples/BindingDemo/app/src/main/java/com/android/example/bindingdemo/vo/User.java
@@ -4,6 +4,7 @@
 import android.graphics.Color;
 
 import com.android.databinding.library.BaseObservable;
+import com.android.example.bindingdemo.BR;
 
 import java.util.Objects;
 
@@ -33,7 +34,7 @@
             return;
         }
         this.group = group;
-        notifyPropertyChanged(android.binding.BR.group);
+        notifyPropertyChanged(BR.group);
     }
 
     public int getGroup() {
@@ -49,7 +50,7 @@
             return;
         }
         this.name = name;
-        notifyPropertyChanged(android.binding.BR.name);
+        notifyPropertyChanged(BR.name);
     }
 
     public String getLastName() {
@@ -61,7 +62,7 @@
             return;
         }
         this.lastName = lastName;
-        notifyPropertyChanged(android.binding.BR.lastName);
+        notifyPropertyChanged(BR.lastName);
     }
 
     public int getPhotoResource() {
@@ -73,7 +74,7 @@
             return;
         }
         this.photoResource = photoResource;
-        notifyPropertyChanged(android.binding.BR.photoResource);
+        notifyPropertyChanged(BR.photoResource);
     }
 
     public int getFavoriteColor() {
diff --git a/samples/BindingDemo/app/src/test/java/com/android/example/bindingdemo/vo/UnitTest.java b/samples/BindingDemo/app/src/test/java/com/android/example/bindingdemo/vo/UnitTest.java
index 3bda143..7f0ba3a 100644
--- a/samples/BindingDemo/app/src/test/java/com/android/example/bindingdemo/vo/UnitTest.java
+++ b/samples/BindingDemo/app/src/test/java/com/android/example/bindingdemo/vo/UnitTest.java
@@ -12,6 +12,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import com.android.example.bindingdemo.BR;
 public class UnitTest {
 
     private User testUser;
@@ -33,7 +34,7 @@
         OnPropertyChangedListener mockListener = mock(OnPropertyChangedListener.class);
         testUser.addOnPropertyChangedListener(mockListener);
         testUser.setName("Tom");
-        verify(mockListener).onPropertyChanged(testUser, android.binding.BR.name);
+        verify(mockListener).onPropertyChanged(testUser, BR.name);
         verifyNoMoreInteractions(mockListener);
     }
 }
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index c790623..2190518 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,4 +1,4 @@
-include ':baseLibrary'
+include ':baseLibrary', ':app'
 include ':library'
 include ':compiler'
 include ':gradlePlugin'