Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] deletebinobj fix for resources
Browse files Browse the repository at this point in the history
Fixes: xamarin#2193

I have been able to reproduce a #deletebinobj bug with the following
steps:
1. `File | New Project | Android App (Xamarin) | Tabbed App`, set
   `Minimum Android Version` to `Android 5.0 (Lollipop)`
2. Build
3. Add a new `TextView` to `Resources\layout\activity_main.axml`, with
   an id of `textView1`
4. Build and Deploy

Get a crash at runtime:

    Android.Views.InflateException: Binary XML file line #1: Error inflating class android.support.design.widget.BottomNavigationView occurred

A `Rebuild` fixes the problem, and the app starts correctly again.

After comparing `obj` directories from before and after, I noticed
`obj\Debug\android\src\android\support\compat\R.java` was missing the
field for `R.id.textView1`!

    public static int textView1=0x7f080091;

This doesn't match the error message we are getting here at all... But
this file is updated by the `_GenerateJavaDesignerForComponent`
MSBuild target. Further research showed that this target was getting
skipped at step no. 4 above, because it was found to be up to date
according to its inputs and outputs.

To verify this was the case, I could delete
`obj\Debug\Component.R.cs.flag` which would also resolve the runtime
exception (instead of a full `Rebuild`).

First, I created a new test:
- `CustomViewAddResourceId` builds a project with
  `<android.support.design.widget.BottomNavigationView />`
- Adds a `textView1` layout, builds again
- Verifies that `obj\Debug\android\src\android\support\compat\R.java`
  contains `textView1`

To fix the problem, I did the following:
- Added `$(_AndroidResgenFlagFile)` as an input to
  `_GenerateJavaDesignerForComponent`, so it will run again when
  `_UpdateAndroidResgen` runs
- Removed
  `AndroidComponentResgenFlagFile="$(_AndroidComponentResgenFlagFile)"`
  from the call to the `<Aapt />` MSBuild task, so it will re-run
  `aapt` and generate `R.java`.

However, things are still breaking when `$(AndroidUseAapt2)` is
enabled. Removing `AndroidComponentResgenFlagFile` from the
`<Aapt2Link />` MSBuild task did not solve the problem here...
I'm not seeing exactly what generates `R.java` when
`$(AndroidUseAapt2)` is enabled? Perhaps another target that calls
`<Aapt2Compile />`?

This PR isn't quite complete until we also get this new test passing
under aapt2.
  • Loading branch information
jonathanpeppers committed Sep 19, 2018
1 parent 3440227 commit 6066f23
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 2 deletions.
Expand Up @@ -1372,5 +1372,42 @@ public void LightlyModifyLayout ()
Assert.IsFalse (b.Output.IsTargetSkipped ("_UpdateAndroidResgen"), "`_UpdateAndroidResgen` should not be skipped!");
}
}

[Test]
public void CustomViewAddResourceId ([Values (false, true)] bool useAapt2)
{
var proj = new XamarinAndroidApplicationProject ();
proj.SetProperty ("AndroidUseAapt2", useAapt2.ToString ());
proj.LayoutMain = proj.LayoutMain.Replace ("</LinearLayout>", "<android.support.design.widget.BottomNavigationView android:id=\"@+id/navigation\" /></LinearLayout>");
proj.Packages.Add (KnownPackages.Android_Arch_Core_Common_26_1_0);
proj.Packages.Add (KnownPackages.Android_Arch_Lifecycle_Common_26_1_0);
proj.Packages.Add (KnownPackages.Android_Arch_Lifecycle_Runtime_26_1_0);
proj.Packages.Add (KnownPackages.AndroidSupportV4_27_0_2_1);
proj.Packages.Add (KnownPackages.SupportCompat_27_0_2_1);
proj.Packages.Add (KnownPackages.SupportCoreUI_27_0_2_1);
proj.Packages.Add (KnownPackages.SupportCoreUtils_27_0_2_1);
proj.Packages.Add (KnownPackages.SupportDesign_27_0_2_1);
proj.Packages.Add (KnownPackages.SupportFragment_27_0_2_1);
proj.Packages.Add (KnownPackages.SupportMediaCompat_27_0_2_1);
proj.Packages.Add (KnownPackages.SupportV7AppCompat_27_0_2_1);
proj.Packages.Add (KnownPackages.SupportV7CardView_27_0_2_1);
proj.Packages.Add (KnownPackages.SupportV7MediaRouter_27_0_2_1);
proj.Packages.Add (KnownPackages.SupportV7RecyclerView_27_0_2_1);
using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) {
Assert.IsTrue (b.Build (proj), "first build should have succeeded");

//Add a new android:id
var textView1 = "textView1";
proj.LayoutMain = proj.LayoutMain.Replace ("</LinearLayout>", $"<TextView android:id=\"@+id/{textView1}\" /></LinearLayout>");
proj.Touch (@"Resources\layout\Main.axml");

Assert.IsTrue (b.Build (proj, doNotCleanupOnUpdate: true), "second build should have succeeded");

var r_java = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "src", "android", "support", "compat", "R.java");
FileAssert.Exists (r_java);
var r_java_contents = File.ReadAllLines (r_java);
Assert.IsTrue (StringAssertEx.ContainsText (r_java_contents, textView1), $"android/support/compat/R.java should contain `{textView1}`!");
}
}
}
}
Expand Up @@ -1526,7 +1526,7 @@ because xbuild doesn't support framework reference assemblies.
</PropertyGroup>

<Target Name="_GenerateJavaDesignerForComponent"
Inputs="@(_AdditonalAndroidResourceCacheFiles);@(_LibraryResourceDirectoryStamps)"
Inputs="@(_AdditonalAndroidResourceCacheFiles);@(_LibraryResourceDirectoryStamps);$(_AndroidResgenFlagFile)"
Outputs="$(_AndroidComponentResgenFlagFile)"
DependsOnTargets="$(_GenerateJavaDesignerForComponentDependsOnTargets)">

Expand All @@ -1545,7 +1545,6 @@ because xbuild doesn't support framework reference assemblies.
ResourceDirectory="$(MonoAndroidResDirIntermediate)"
AdditionalResourceDirectories="@(LibraryResourceDirectories)"
AdditionalAndroidResourcePaths="@(_AdditonalAndroidResourceCachePaths)"
AndroidComponentResgenFlagFile="$(_AndroidComponentResgenFlagFile)"
ToolPath="$(AaptToolPath)"
ToolExe="$(AaptToolExe)"
ApiLevel="$(_AndroidTargetSdkVersion)"
Expand Down

0 comments on commit 6066f23

Please sign in to comment.