From 8dbaa84bff430d04e7bbd9f875bcd61e72ae278c Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Thu, 20 Jul 2017 10:59:58 +0100 Subject: [PATCH] [Xamarin.Android.Build.Tasks] Path is too long Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=30147 Windows (still) has a max path limit of 260 characters.. This can cause us a problem if the user creates a project in a directory which already takes up most of that limit.. Like on their Desktop! This is because of the intermediate structure that was introduced to handle embedding resources into the assemblies. The current intermediate structure is as follows `$(IntermediateOutputPath)\__library_project_imports__\$(AssemblyName)\library_project_imports\` `$(IntermediateOutputPath)\__library_project_imports__\$(AssemblyName)\native_library_imports\` Now consider that `$(AssemblyName)` can sometimes end up with something lile "Xamarin.Android.Support.v7.AppCompat.21.0.3.0" you can easily see how we start getting into trouble. There was an attempt to fix this up a while ago (722dcc05) by introducing the `$(UseShortFileNames)` property. However while this did introduce a directory structure which was shorter is broke backwards compatability with older versions of xamarin-android. This is because of the structore of the zip files we that are embedded into the assemblies. The current system just extracts the zip into the `$(IntermediateOutputPath)\__library_project_imports__\$(AssemblyName)\` directory and assumes that it contains a `library_project_imports` directory. All the build tasks also assumed that as well. So the fix needs to be done on a number of fronts. Firstly we need to update the `$(_LibraryProjectImportsDirectoryName)` and `$(_NativeLibraryImportsDirectoryName)` to be something shorter. Next up we need to shorten "__library_project_imports__" to something else verbose as well. This should cut down on the amount of the MAX_PATH we chew up. The real key to this is to NOT change the structure of the zip files in the assemblies! So when we generate a zip from "jlibs" we make sure that the folder in the zip is called "library_project_imports". And when we extract the zip file we makle sure that "library_project_imports" is replaced by the shorter name. This will ensure that we are backward compatbile with older versions BUT more importantly we get to use the shorter directory structure. The files for native librarys are not extracted to disk but are extracted from memory so as long as the structure remains the same i.e "native_library_imports" that code does not need to change. The other thing we need to do is to update ResolveLibraryProjectImports Task to upgrade the system when it runs. So if we already have a "libraryprojectimports.cache" in place we just use that as is. But if we re-run the ResolveLibraryProjectImports task (due to a change or a clean build) we detect if we have the older structure in place and just remove it.. Since we are going to regenerate the entire cache again anyway we might as well start from scratch. With this in place it becomes possible we can now enable `$(UseShortFileNames)` by default! So the new structure that is created is as follows `$(IntermediateOutputPath)\lp\\jl` `$(IntermediateOutputPath)\lp\\nl` The will be a single integer value which can be mapped to the source assembly via a new map.cache file. This .cache file will contain a list of assemblys. We use the `Index` of the assembly in the list to figure out which cache directory to use. UnnamedProject Library1 In this case `Library1` would have an index of `1` and `UnnamedProject` will be `0`. The old behaviour can be enabled by setting `$(UseShortFileNames)` to `False`. Note in order to keep existing bindings generator behaviour consistent, the BindingsGenerator task will not use the `$(UseShortFileNames)` property to control how it generates its .cs files. Instead a new property `$(UseShortGeneratorFileNames)` which can be used to control if the generator produces short names (e.g 1.cs, 2.cs). This will be `False` by default. --- src/Xamarin.Android.Build.Tasks/Tasks/Aapt.cs | 6 +- .../Tasks/CreateLibraryResourceArchive.cs | 2 +- .../CreateManagedLibraryResourceArchive.cs | 2 +- .../Tasks/CreateNativeLibraryArchive.cs | 2 +- .../Tasks/ResolveLibraryProjectImports.cs | 191 ++++++++++-------- .../BindingBuildTest.cs | 23 ++- .../Xamarin.Android.Build.Tests/BuildTest.cs | 48 +++++ .../IncrementalBuildTest.cs | 8 +- .../Utilities/AssemblyIdentityMap.cs | 43 ++++ .../Utilities/Files.cs | 9 +- .../Utilities/MonoAndroidHelper.cs | 5 - .../Xamarin.Android.Bindings.targets | 33 +-- .../Xamarin.Android.Build.Tasks.csproj | 1 + .../Xamarin.Android.Common.targets | 36 ++-- 14 files changed, 273 insertions(+), 136 deletions(-) create mode 100644 src/Xamarin.Android.Build.Tasks/Utilities/AssemblyIdentityMap.cs diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Aapt.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Aapt.cs index de9ff551bd8..bb8195ba299 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Aapt.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Aapt.cs @@ -71,12 +71,14 @@ public class Aapt : AsyncTask public string ImportsDirectory { get; set; } public string OutputImportDirectory { get; set; } public bool UseShortFileNames { get; set; } + public string AssemblyIdentityMapFile { get; set; } public string ResourceNameCaseMap { get; set; } public bool ExplicitCrunch { get; set; } Dictionary resource_name_case_map = new Dictionary (); + AssemblyIdentityMap assemblyMap = new AssemblyIdentityMap (); bool ManifestIsUpToDate (string manifestFile) { @@ -197,6 +199,8 @@ public override bool Execute () foreach (var arr in ResourceNameCaseMap.Split (';').Select (l => l.Split ('|')).Where (a => a.Length == 2)) resource_name_case_map [arr [1]] = arr [0]; // lowercase -> original + assemblyMap.Load (AssemblyIdentityMapFile); + ThreadingTasks.Parallel.ForEach (ManifestFiles, () => 0, DoExecute, (obj) => { Complete (); }); base.Execute (); @@ -315,7 +319,7 @@ string ExpandString (string s) return s.Substring (0, st + 1) + ExpandString (s.Substring (st + 1)); int ast = st + "${library.imports:".Length; string aname = s.Substring (ast, ed - ast); - return s.Substring (0, st) + Path.Combine (OutputImportDirectory, UseShortFileNames ? MonoAndroidHelper.GetLibraryImportDirectoryNameForAssembly (aname) : aname, ImportsDirectory) + Path.DirectorySeparatorChar + ExpandString (s.Substring (ed + 1)); + return s.Substring (0, st) + Path.Combine (OutputImportDirectory, UseShortFileNames ? assemblyMap.GetLibraryImportDirectoryNameForAssembly (aname) : aname, ImportsDirectory) + Path.DirectorySeparatorChar + ExpandString (s.Substring (ed + 1)); } else return s; diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateLibraryResourceArchive.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateLibraryResourceArchive.cs index 213d075df14..a7cecb94509 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CreateLibraryResourceArchive.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateLibraryResourceArchive.cs @@ -94,7 +94,7 @@ public override bool Execute () if (Files.ArchiveZip (outpath, f => { using (var zip = new ZipArchiveEx (f)) { - zip.AddDirectory (OutputDirectory, outDirInfo.Name); + zip.AddDirectory (OutputDirectory, "library_project_imports"); } })) { Log.LogDebugMessage ("Saving contents to " + outpath); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateManagedLibraryResourceArchive.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateManagedLibraryResourceArchive.cs index a1610fdc34f..9a564ea083a 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CreateManagedLibraryResourceArchive.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateManagedLibraryResourceArchive.cs @@ -108,7 +108,7 @@ public override bool Execute () if (Files.ArchiveZip (outpath, f => { using (var zip = new ZipArchiveEx (f)) { - zip.AddDirectory (OutputDirectory, outDirInfo.Name); + zip.AddDirectory (OutputDirectory, "library_project_imports"); } })) { Log.LogDebugMessage ("Saving contents to " + outpath); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateNativeLibraryArchive.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateNativeLibraryArchive.cs index fe691afdb95..14f7c211c2f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CreateNativeLibraryArchive.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateNativeLibraryArchive.cs @@ -66,7 +66,7 @@ public override bool Execute () if (Files.ArchiveZip (outpath, f => { using (var zip = new ZipArchiveEx (f)) { - zip.AddDirectory (OutputDirectory, outDirInfo.Name); + zip.AddDirectory (OutputDirectory, "native_library_imports"); } })) { Log.LogDebugMessage ("Saving contents to " + outpath); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ResolveLibraryProjectImports.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ResolveLibraryProjectImports.cs index eb542d9ea2c..dd61d02c460 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/ResolveLibraryProjectImports.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/ResolveLibraryProjectImports.cs @@ -34,6 +34,9 @@ public class ResolveLibraryProjectImports : Task [Required] public bool UseShortFileNames { get; set; } + [Required] + public string AssemblyIdentityMapFile { get; set; } + public string CacheFile { get; set;} [Output] @@ -51,13 +54,14 @@ public class ResolveLibraryProjectImports : Task [Output] public ITaskItem [] ResolvedResourceDirectoryStamps { get; set; } - string imports_dir = "library_project_imports"; + AssemblyIdentityMap assemblyMap = new AssemblyIdentityMap(); public ResolveLibraryProjectImports () { } // Extracts library project contents under e.g. obj/Debug/[__library_projects__/*.jar | res/*/*] + // Extracts library project contents under e.g. obj/Debug/[lp/*.jar | res/*/*] public override bool Execute () { Log.LogDebugMessage ("ResolveLibraryProjectImports Task"); @@ -72,6 +76,8 @@ public override bool Execute () var resolvedAssetDirectories = new List (); var resolvedEnvironmentFiles = new List (); + assemblyMap.Load (AssemblyIdentityMapFile); + using (var resolver = new DirectoryAssemblyResolver (Log.LogWarning, loadDebugSymbols: false)) { Extract (resolver, jars, resolvedResourceDirectories, resolvedAssetDirectories, resolvedEnvironmentFiles); } @@ -112,6 +118,8 @@ public override bool Execute () document.Save (CacheFile); } + assemblyMap.Save (AssemblyIdentityMapFile); + Log.LogDebugTaskItems (" Jars: ", Jars.Select (s => new TaskItem (s)).ToArray ()); Log.LogDebugTaskItems (" ResolvedResourceDirectories: ", ResolvedResourceDirectories.Select (s => new TaskItem (s)).ToArray ()); Log.LogDebugTaskItems (" ResolvedAssetDirectories: ", ResolvedAssetDirectories.Select (s => new TaskItem (s)).ToArray ()); @@ -135,6 +143,7 @@ static string GetTargetAssembly (ITaskItem assemblyName) } // Extracts library project contents under e.g. obj/Debug/[__library_projects__/*.jar | res/*/*] + // Extracts library project contents under e.g. obj/Debug/[lp/*.jar | res/*/*] void Extract ( DirectoryAssemblyResolver res, ICollection jars, @@ -142,6 +151,12 @@ void Extract ( ICollection resolvedAssetDirectories, ICollection resolvedEnvironments) { + // lets "upgrade" the old directory. + string oldPath = Path.GetFullPath (Path.Combine (OutputImportDirectory, "..", "__library_projects__")); + if (!OutputImportDirectory.Contains ("__library_projects__") && Directory.Exists (oldPath)) { + MonoAndroidHelper.SetDirectoryWriteable (Path.Combine (oldPath, "..")); + Directory.Delete (oldPath, recursive: true); + } var outdir = new DirectoryInfo (OutputImportDirectory); if (!outdir.Exists) outdir.Create (); @@ -154,26 +169,95 @@ void Extract ( .Select (a => GetTargetAssembly (a)) .Where (a => a != null) .Distinct ()) { - foreach (var imp in new string [] {imports_dir, "library_project_imports"}.Distinct ()) { - string assemblyIdentName = Path.GetFileNameWithoutExtension (assemblyPath); - if (UseShortFileNames) { - assemblyIdentName = Xamarin.Android.Tasks.MonoAndroidHelper.GetLibraryImportDirectoryNameForAssembly (assemblyIdentName); - } - string outDirForDll = Path.Combine (OutputImportDirectory, assemblyIdentName); - string importsDir = Path.Combine (outDirForDll, imp); + string assemblyIdentName = Path.GetFileNameWithoutExtension (assemblyPath); + if (UseShortFileNames) { + assemblyIdentName = assemblyMap.GetLibraryImportDirectoryNameForAssembly (assemblyIdentName); + } + string outDirForDll = Path.Combine (OutputImportDirectory, assemblyIdentName); + string importsDir = Path.Combine (outDirForDll, ImportsDirectory); +#if SEPARATE_CRUNCH + // FIXME: review these binResDir thing and enable this. Eclipse does this. + // Enabling these blindly causes build failure on ActionBarSherlock. + //string binResDir = Path.Combine (importsDir, "bin", "res"); + //string binAssemblyDir = Path.Combine (importsDir, "bin", "assets"); +#endif + string resDir = Path.Combine (importsDir, "res"); + string assemblyDir = Path.Combine (importsDir, "assets"); + + // Skip already-extracted resources. + var stamp = new FileInfo (Path.Combine (outdir.FullName, assemblyIdentName + ".stamp")); + if (stamp.Exists && stamp.LastWriteTime > new FileInfo (assemblyPath).LastWriteTime) { + Log.LogDebugMessage ("Skipped resource lookup for {0}: extracted files are up to date", assemblyPath); #if SEPARATE_CRUNCH - // FIXME: review these binResDir thing and enable this. Eclipse does this. + // FIXME: review these binResDir/binAssemblyDir thing and enable this. Eclipse does this. // Enabling these blindly causes build failure on ActionBarSherlock. - //string binResDir = Path.Combine (importsDir, "bin", "res"); - //string binAssemblyDir = Path.Combine (importsDir, "bin", "assets"); + if (Directory.Exists (binResDir)) + resolvedResourceDirectories.Add (binResDir); + if (Directory.Exists (binAssemblyDir)) + resolvedAssetDirectories.Add (binAssemblyDir); #endif - string resDir = Path.Combine (importsDir, "res"); - string assemblyDir = Path.Combine (importsDir, "assets"); + if (Directory.Exists (resDir)) + resolvedResourceDirectories.Add (resDir); + if (Directory.Exists (assemblyDir)) + resolvedAssetDirectories.Add (assemblyDir); + continue; + } + + if (Directory.Exists (outDirForDll)) + Directory.Delete (outDirForDll, true); + + var assembly = res.GetAssembly (assemblyPath); + + foreach (var mod in assembly.Modules) { + // android environment files + foreach (var envtxt in mod.Resources + .Where (r => r.Name.StartsWith ("__AndroidEnvironment__", StringComparison.OrdinalIgnoreCase)) + .Where (r => r is EmbeddedResource) + .Cast ()) { + if (!Directory.Exists (outDirForDll)) + Directory.CreateDirectory (outDirForDll); + var finfo = new FileInfo (Path.Combine (outDirForDll, envtxt.Name)); + using (var fs = finfo.Create ()) { + var data = envtxt.GetResourceData (); + fs.Write (data, 0, data.Length); + } + resolvedEnvironments.Add (finfo.FullName); + } + + // embedded jars (EmbeddedJar, EmbeddedReferenceJar) + var resjars = mod.Resources + .Where (r => r.Name.EndsWith (".jar", StringComparison.InvariantCultureIgnoreCase)) + .Select (r => (EmbeddedResource) r); + foreach (var resjar in resjars) { + var data = resjar.GetResourceData (); + if (!Directory.Exists (importsDir)) + Directory.CreateDirectory (importsDir); + using (var outfs = File.Create (Path.Combine (importsDir, resjar.Name))) + outfs.Write (data, 0, data.Length); + } - // Skip already-extracted resources. - var stamp = new FileInfo (Path.Combine (outdir.FullName, assemblyIdentName + ".stamp")); - if (stamp.Exists && stamp.LastWriteTime > new FileInfo (assemblyPath).LastWriteTime) { - Log.LogDebugMessage ("Skipped resource lookup for {0}: extracted files are up to date", assemblyPath); + // embedded AndroidResourceLibrary archive + var reszip = mod.Resources.FirstOrDefault (r => r.Name == "__AndroidLibraryProjects__.zip") as EmbeddedResource; + if (reszip != null) { + if (!Directory.Exists (outDirForDll)) + Directory.CreateDirectory (outDirForDll); + var finfo = new FileInfo (Path.Combine (outDirForDll, reszip.Name)); + using (var fs = finfo.Create ()) { + var data = reszip.GetResourceData (); + fs.Write (data, 0, data.Length); + } + + // temporarily extracted directory will look like: + // __library_projects__/[dllname]/[library_project_imports | jlibs]/bin + using (var zip = MonoAndroidHelper.ReadZipFile (finfo.FullName)) { + Files.ExtractAll (zip, outDirForDll, modifyCallback: (entryFullName) => { + return entryFullName.Replace ("library_project_imports", ImportsDirectory); + }); + } + + // We used to *copy* the resources to overwrite other resources, + // which resulted in missing resource issue. + // Here we replaced copy with use of '-S' option and made it to work. #if SEPARATE_CRUNCH // FIXME: review these binResDir/binAssemblyDir thing and enable this. Eclipse does this. // Enabling these blindly causes build failure on ActionBarSherlock. @@ -186,80 +270,13 @@ void Extract ( resolvedResourceDirectories.Add (resDir); if (Directory.Exists (assemblyDir)) resolvedAssetDirectories.Add (assemblyDir); - continue; - } - - if (Directory.Exists (outDirForDll)) - Directory.Delete (outDirForDll, true); - - Directory.CreateDirectory (importsDir); - - var assembly = res.GetAssembly (assemblyPath); - - foreach (var mod in assembly.Modules) { - // android environment files - foreach (var envtxt in mod.Resources - .Where (r => r.Name.StartsWith ("__AndroidEnvironment__", StringComparison.OrdinalIgnoreCase)) - .Where (r => r is EmbeddedResource) - .Cast ()) { - if (!Directory.Exists (outDirForDll)) - Directory.CreateDirectory (outDirForDll); - var finfo = new FileInfo (Path.Combine (outDirForDll, envtxt.Name)); - using (var fs = finfo.Create ()) { - var data = envtxt.GetResourceData (); - fs.Write (data, 0, data.Length); - } - resolvedEnvironments.Add (finfo.FullName); - } - // embedded jars (EmbeddedJar, EmbeddedReferenceJar) - var resjars = mod.Resources - .Where (r => r.Name.EndsWith (".jar", StringComparison.InvariantCultureIgnoreCase)) - .Select (r => (EmbeddedResource) r); - foreach (var resjar in resjars) { - var data = resjar.GetResourceData (); - using (var outfs = File.Create (Path.Combine (importsDir, resjar.Name))) - outfs.Write (data, 0, data.Length); - } - - // embedded AndroidResourceLibrary archive - var reszip = mod.Resources.FirstOrDefault (r => r.Name == "__AndroidLibraryProjects__.zip") as EmbeddedResource; - if (reszip != null) { - if (!Directory.Exists (outDirForDll)) - Directory.CreateDirectory (outDirForDll); - var finfo = new FileInfo (Path.Combine (outDirForDll, reszip.Name)); - using (var fs = finfo.Create ()) { - var data = reszip.GetResourceData (); - fs.Write (data, 0, data.Length); - } - - // temporarily extracted directory will look like: - // __library_projects__/[dllname]/[library_project_imports | jlibs]/bin - using (var zip = MonoAndroidHelper.ReadZipFile (finfo.FullName)) - Files.ExtractAll (zip, outDirForDll); - - // We used to *copy* the resources to overwrite other resources, - // which resulted in missing resource issue. - // Here we replaced copy with use of '-S' option and made it to work. -#if SEPARATE_CRUNCH - // FIXME: review these binResDir/binAssemblyDir thing and enable this. Eclipse does this. - // Enabling these blindly causes build failure on ActionBarSherlock. - if (Directory.Exists (binResDir)) - resolvedResourceDirectories.Add (binResDir); - if (Directory.Exists (binAssemblyDir)) - resolvedAssetDirectories.Add (binAssemblyDir); -#endif - if (Directory.Exists (resDir)) - resolvedResourceDirectories.Add (resDir); - if (Directory.Exists (assemblyDir)) - resolvedAssetDirectories.Add (assemblyDir); - - finfo.Delete (); - } + finfo.Delete (); } + } + if (Directory.Exists (importsDir)) stamp.Create ().Close (); - } } foreach (var f in outdir.GetFiles ("*.jar") diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs index 46b5fda7165..62a5ee6707e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BindingBuildTest.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using System.IO; using System.Linq; +using System.Collections.Generic; namespace Xamarin.Android.Build.Tests { @@ -263,7 +264,7 @@ public void BindngFilterUnsupportedNativeAbiLibraries () } [Test] - public void BindingCheckHiddenFiles () + public void BindingCheckHiddenFiles ([Values (true, false)] bool useShortFileNames) { var binding = new XamarinAndroidBindingProject () { UseLatestPlatformSdk = true, @@ -273,15 +274,31 @@ public void BindingCheckHiddenFiles () binding.Jars.Add (new AndroidItem.LibraryProjectZip ("Jars\\mylibrary.aar") { WebContent = "https://www.dropbox.com/s/astiqp8jo97x91h/mylibrary.aar?dl=1" }); + binding.SetProperty (binding.ActiveConfigurationProperties, "UseShortFileNames", useShortFileNames); using (var bindingBuilder = CreateDllBuilder (Path.Combine ("temp", "BindingCheckHiddenFiles", "Binding"))) { bindingBuilder.Verbosity = Microsoft.Build.Framework.LoggerVerbosity.Diagnostic; Assert.IsTrue (bindingBuilder.Build (binding), "binding build should have succeeded"); var proj = new XamarinAndroidApplicationProject (); proj.OtherBuildItems.Add (new BuildItem ("ProjectReference", "..\\Binding\\UnnamedProject.csproj")); + proj.SetProperty (proj.ActiveConfigurationProperties, "UseShortFileNames", useShortFileNames); using (var b = CreateApkBuilder (Path.Combine ("temp", "BindingCheckHiddenFiles", "App"))) { Assert.IsTrue (b.Build (proj), "Build should have succeeded."); - var dsStorePath = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "__library_projects__", - "UnnamedProject", "library_project_imports"); + var assemblyMap = b.Output.GetIntermediaryPath (Path.Combine ("lp", "map.cache")); + if (useShortFileNames) + Assert.IsTrue (File.Exists (assemblyMap), $"{assemblyMap} should exist."); + else + Assert.IsFalse (File.Exists (assemblyMap), $"{assemblyMap} should not exist."); + var assemblyIdentityMap = new List (); + if (useShortFileNames) { + foreach (var s in File.ReadLines (assemblyMap)) { + assemblyIdentityMap.Add (s); + } + } + var assmeblyIdentity = useShortFileNames ? assemblyIdentityMap.IndexOf ("UnnamedProject").ToString () : "UnnamedProject"; + var libaryImportsFolder = useShortFileNames ? "lp" : "__library_projects__"; + var jlibs = useShortFileNames ? "jl" : "library_project_imports"; + var dsStorePath = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, libaryImportsFolder, + assmeblyIdentity, jlibs); Assert.IsTrue (Directory.Exists (dsStorePath), "{0} should exist.", dsStorePath); Assert.IsFalse (File.Exists (Path.Combine (dsStorePath, ".DS_Store")), "{0} should NOT exist.", Path.Combine (dsStorePath, ".DS_Store")); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index 5590095a50a..ce1dbd8f7a5 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -1740,6 +1740,54 @@ public void BuildInDesignTimeMode () } } + [Test] + public void CheckLibraryImportsUpgrade ([Values(false, true)] bool useShortFileNames) + { + var path = Path.Combine ("temp", "CheckLibraryImportsUpgrade"); + var libproj = new XamarinAndroidLibraryProject () { + IsRelease = true, + ProjectName = "Library1" + }; + var proj = new XamarinAndroidApplicationProject () { + IsRelease = true, + ProjectName = "App1", + }; + proj.References.Add (new BuildItem ("ProjectReference", $"..\\Library1\\Library1.csproj")); + proj.SetProperty ("_AndroidLibrayProjectIntermediatePath", Path.Combine (proj.IntermediateOutputPath, "__library_projects__")); + proj.SetProperty (proj.ActiveConfigurationProperties, "UseShortFileNames", useShortFileNames); + using (var libb = CreateDllBuilder (Path.Combine (path, libproj.ProjectName), false, false)) { + Assert.IsTrue (libb.Build (libproj), "Build should have succeeded."); + using (var builder = CreateApkBuilder (Path.Combine (path, proj.ProjectName), false, false)) { + builder.Verbosity = LoggerVerbosity.Diagnostic; + Assert.IsTrue (builder.Build (proj), "Build should have succeeded."); + Assert.IsTrue (Directory.Exists (Path.Combine (Root, path, proj.ProjectName, proj.IntermediateOutputPath, "__library_projects__")), + "The __library_projects__ directory should exist."); + proj.RemoveProperty ("_AndroidLibrayProjectIntermediatePath"); + Assert.IsTrue (builder.Build (proj), "Build should have succeeded."); + Assert.IsTrue (Directory.Exists (Path.Combine (Root, path, proj.ProjectName, proj.IntermediateOutputPath, "__library_projects__")), + "The __library_projects__ directory should exist."); + Assert.IsTrue (libb.Clean (libproj), "Clean should have succeeded."); + Assert.IsTrue (libb.Build (libproj), "Build should have succeeded."); + Assert.IsTrue (builder.Build (proj), "Build should have succeeded."); + var zipFile = libb.Output.GetIntermediaryPath ("__AndroidLibraryProjects__.zip"); + Assert.IsTrue (File.Exists (zipFile)); + using (var zip = ZipHelper.OpenZip (zipFile)) { + Assert.IsTrue (zip.ContainsEntry ("library_project_imports/__res_name_case_map.txt"), $"{zipFile} should contain a library_project_imports/__res_name_case_map.txt entry"); + } + if (!useShortFileNames) { + Assert.IsTrue (Directory.Exists (Path.Combine (Root, path, proj.ProjectName, proj.IntermediateOutputPath, "__library_projects__")), + "The __library_projects__ directory should exist."); + } else { + Assert.IsFalse (Directory.Exists (Path.Combine (Root, path, proj.ProjectName, proj.IntermediateOutputPath, "__library_projects__")), + "The __library_projects__ directory should not exist."); + Assert.IsTrue (Directory.Exists (Path.Combine (Root, path, proj.ProjectName, proj.IntermediateOutputPath, "lp")), + "The lp directory should exist."); + } + + } + } + } + [Test] public void BuildAMassiveApp() { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs index 3c35dea1417..a3e25663aaa 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs @@ -80,7 +80,7 @@ public TestMe createTestMe () { } [Test] - public void ResolveNativeLibrariesInManagedReferences () + public void ResolveNativeLibrariesInManagedReferences ([Values(true, false)] bool useShortFileNames) { var lib = new XamarinAndroidLibraryProject () { ProjectName = "Lib", @@ -108,6 +108,7 @@ public Class1 () }, }, }; + lib.SetProperty (lib.ActiveConfigurationProperties, "UseShortFileNames", useShortFileNames); var so = lib.OtherBuildItems.First (x => x.Include () == "libs/armeabi-v7a/libfoo.so"); var lib2 = new XamarinAndroidLibraryProject () { @@ -139,7 +140,7 @@ public Class2 () }, }, }; - + lib2.SetProperty (lib.ActiveConfigurationProperties, "UseShortFileNames", useShortFileNames); var path = Path.Combine (Root, "temp", "ResolveNativeLibrariesInManagedReferences"); using (var libbuilder = CreateDllBuilder (Path.Combine(path, "Lib"))) { @@ -154,6 +155,7 @@ public Class2 () new BuildItem.ProjectReference (@"..\Lib2\Lib2.csproj", "Lib2", lib2.ProjectGuid), } }; + app.SetProperty (app.ActiveConfigurationProperties, "UseShortFileNames", useShortFileNames); using (var builder = CreateApkBuilder (Path.Combine (path, "App"))) { builder.Verbosity = LoggerVerbosity.Diagnostic; Assert.IsTrue (builder.Build (app), "app 1st. build failed"); @@ -171,7 +173,7 @@ public Class2 () Assert.IsNotNull (libfoo, "libfoo.so should exist in the .apk"); Assert.AreEqual (so.TextContent ().Length, new FileInfo (Path.Combine (Root, libbuilder.ProjectDirectory, lib.IntermediateOutputPath, - "native_library_imports", "armeabi-v7a", "libfoo.so")).Length, + useShortFileNames ? "nl" : "native_library_imports", "armeabi-v7a", "libfoo.so")).Length, "intermediate size mismatch"); libfoo = ZipHelper.ReadFileFromZip (Path.Combine (Root, builder.ProjectDirectory, app.OutputPath, app.PackageName + ".apk"), "lib/armeabi-v7a/libfoo.so"); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyIdentityMap.cs b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyIdentityMap.cs new file mode 100644 index 00000000000..8b1c280e9d2 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyIdentityMap.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Xamarin.Android.Tasks +{ + internal class AssemblyIdentityMap + { + List map = new List (); + + public void Load (string mapFile) + { + map.Clear (); + if (!File.Exists (mapFile)) + return; + foreach (var s in File.ReadLines (mapFile)) { + if (!map.Contains (s)) + map.Add (s); + } + } + + public string GetLibraryImportDirectoryNameForAssembly (string assemblyIdentity) + { + if (map.Contains (assemblyIdentity)) { + return map.IndexOf (assemblyIdentity).ToString (); + } + map.Add (assemblyIdentity); + return map.IndexOf (assemblyIdentity).ToString (); + } + + public void Save (string mapFile) + { + if (map.Count == 0) + return; + var sb = new StringBuilder (); + foreach (var item in map) { + sb.AppendLine (item); + } + File.WriteAllText (mapFile, sb.ToString ()); + } + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/Files.cs b/src/Xamarin.Android.Build.Tasks/Utilities/Files.cs index 771011e9ac4..ee48b03c867 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/Files.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/Files.cs @@ -193,7 +193,7 @@ public static ZipArchive ReadZipFile (string filename, bool strictConsistencyChe return ZipArchive.Open (filename, FileMode.Open, strictConsistencyChecks: strictConsistencyChecks); } - public static void ExtractAll(ZipArchive zip, string destination, Action progressCallback = null) + public static void ExtractAll(ZipArchive zip, string destination, Action progressCallback = null, Func modifyCallback = null) { int i = 0; int total = (int)zip.EntryCount; @@ -202,14 +202,15 @@ public static void ExtractAll(ZipArchive zip, string destination, Action b.ToString ("X02"))); - } - public static Dictionary LoadAcwMapFile (string acwPath) { var acw_map = new Dictionary (); diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets index d9c1590b010..b4ad15d9f05 100755 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets @@ -61,7 +61,8 @@ Copyright (C) 2012 Xamarin Inc. All rights reserved. MonoAndroid v5.0 v2.3 - False + True + False jar2xml @@ -102,19 +103,18 @@ Copyright (C) 2012 Xamarin Inc. All rights reserved. $(IntermediateOutputPath)api.xml <_GeneratorStampFile>$(IntermediateOutputPath)generator.stamp None - - <_LibraryProjectImportsDirectoryName>library_project_imports - <_NativeLibraryImportsDirectoryName>native_library_imports + <_LibraryProjectImportsDirectoryName Condition=" '$(_LibraryProjectImportsDirectoryName)'=='' And '$(UseShortFileNames)' == 'True' ">jl + <_NativeLibraryImportsDirectoryName Condition=" '$(_NativeLibraryImportsDirectoryName)'=='' And '$(UseShortFileNames)' == 'True' ">nl + + <_LibraryProjectImportsDirectoryName Condition=" '$(_LibraryProjectImportsDirectoryName)'==''">library_project_imports + <_NativeLibraryImportsDirectoryName Condition=" '$(_NativeLibraryImportsDirectoryName)'==''">native_library_imports <_AndroidResourcePathsCache>$(IntermediateOutputPath)resourcepaths.cache <_AndroidLibraryImportsCache>$(IntermediateOutputPath)libraryimports.cache <_AndroidLibraryProjectImportsCache>$(IntermediateOutputPath)libraryprojectimports.cache - - + <_AndroidLibrayProjectIntermediatePath Condition=" '$(_AndroidLibrayProjectIntermediatePath)' == '' And '$(UseShortFileNames)' == 'True' ">$(IntermediateOutputPath)lp\ + <_AndroidLibrayProjectIntermediatePath Condition=" '$(_AndroidLibrayProjectIntermediatePath)' == '' ">$(IntermediateOutputPath)__library_projects__\ + <_AndroidLibrayProjectAssemblyMapFile>$(_AndroidLibrayProjectIntermediatePath)map.cache + True False @@ -318,7 +318,8 @@ Copyright (C) 2012 Xamarin Inc. All rights reserved. ImportsDirectory="$(_LibraryProjectImportsDirectoryName)" UseShortFileNames="$(UseShortFileNames)" OutputDirectory="$(IntermediateOutputPath)" - OutputImportDirectory="$(IntermediateOutputPath)__library_projects__\"> + AssemblyIdentityMapFile="$(_AndroidLibrayProjectAssemblyMapFile)" + OutputImportDirectory="$(_AndroidLibrayProjectIntermediatePath)"> @@ -333,7 +334,7 @@ Copyright (C) 2012 Xamarin Inc. All rights reserved. - @@ -418,7 +419,7 @@ Copyright (C) 2012 Xamarin Inc. All rights reserved. Java7DocPaths="$(Java7DocPaths)" Java8DocPaths="$(Java8DocPaths)" JavaDocs="@(JavaDocIndex)" - LibraryProjectJars="$(IntermediateOutputPath)__library_projects__\*.jar" + LibraryProjectJars="$(_AndroidLibrayProjectIntermediatePath)\*.jar" /> @@ -451,7 +452,7 @@ Copyright (C) 2012 Xamarin Inc. All rights reserved. TypeMappingReportFile="$(GeneratedOutputPath)type-mapping.txt" ToolPath="$(_MonoAndroidBinDirectory)" ToolExe="$(BindingsGeneratorToolExe)" - UseShortFileNames="$(UseShortFileNames)" + UseShortFileNames="$(UseShortGeneratorFileNames)" /> @@ -567,10 +568,12 @@ Copyright (C) 2012 Xamarin Inc. All rights reserved. + + diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj index e2586cafde4..cc0f3102082 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj @@ -474,6 +474,7 @@ + <_MonoAndroidEnum Include="$(AndroidGeneratedClassDirectory)\Android.Content.PM.LaunchMode.cs" /> diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 68050ff759f..eee06906cc3 100755 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -199,7 +199,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. False False - False + True @@ -227,18 +227,18 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. Gdb - - <_LibraryProjectImportsDirectoryName>library_project_imports - <_NativeLibraryImportsDirectoryName>native_library_imports + <_LibraryProjectImportsDirectoryName Condition=" '$(_LibraryProjectImportsDirectoryName)'=='' And '$(UseShortFileNames)' == 'True' ">jl + <_NativeLibraryImportsDirectoryName Condition=" '$(_NativeLibraryImportsDirectoryName)'=='' And '$(UseShortFileNames)' == 'True' ">nl + + <_LibraryProjectImportsDirectoryName Condition=" '$(_LibraryProjectImportsDirectoryName)'==''">library_project_imports + <_NativeLibraryImportsDirectoryName Condition=" '$(_NativeLibraryImportsDirectoryName)'==''">native_library_imports <_AndroidResourcePathsCache>$(IntermediateOutputPath)resourcepaths.cache <_AndroidLibraryImportsCache>$(IntermediateOutputPath)libraryimports.cache <_AndroidLibraryProjectImportsCache>$(IntermediateOutputPath)libraryprojectimports.cache - + <_AndroidLibrayProjectIntermediatePath Condition=" '$(_AndroidLibrayProjectIntermediatePath)' == '' And '$(UseShortFileNames)' == 'True' ">$(IntermediateOutputPath)lp\ + <_AndroidLibrayProjectIntermediatePath Condition=" '$(_AndroidLibrayProjectIntermediatePath)' == '' ">$(IntermediateOutputPath)__library_projects__\ + <_AndroidLibrayProjectAssemblyMapFile>$(_AndroidLibrayProjectIntermediatePath)map.cache + $(EnableProguard) False @@ -1077,7 +1077,8 @@ because xbuild doesn't support framework reference assemblies. ImportsDirectory="$(_LibraryProjectImportsDirectoryName)" UseShortFileNames="$(UseShortFileNames)" OutputDirectory="$(IntermediateOutputPath)" - OutputImportDirectory="$(IntermediateOutputPath)__library_projects__\"> + AssemblyIdentityMapFile="$(_AndroidLibrayProjectAssemblyMapFile)" + OutputImportDirectory="$(_AndroidLibrayProjectIntermediatePath)"> @@ -1137,7 +1138,7 @@ because xbuild doesn't support framework reference assemblies. - @@ -1205,7 +1206,7 @@ because xbuild doesn't support framework reference assemblies. @@ -1271,7 +1273,7 @@ because xbuild doesn't support framework reference assemblies. + @@ -2445,6 +2450,7 @@ because xbuild doesn't support framework reference assemblies. +