diff --git a/tests/mtouch/MTouch.cs b/tests/mtouch/MTouch.cs
index 2420d0e81b07..5e8b9184a4e4 100644
--- a/tests/mtouch/MTouch.cs
+++ b/tests/mtouch/MTouch.cs
@@ -363,6 +363,21 @@ public void RebuildTest_DontLink ()
}
}
+ void DumpFileStats (MTouchTool mtouch)
+ {
+ if (mtouch.Verbosity < 1)
+ return;
+ var directory = mtouch.Cache;
+ var files = Directory.GetFileSystemEntries (directory, "*", SearchOption.AllDirectories).ToList ();
+ files.Sort ((string x, string y) => string.CompareOrdinal (x, y));
+ var max = files.Max ((v) => v.Length);
+
+ var format = " {0,-" + max + "} {1}";
+ foreach (var file in files) {
+ Console.WriteLine (format, file, File.GetLastWriteTimeUtc (file).ToString ("HH:mm:ss.fffffff"));
+ }
+ }
+
[Test]
[TestCase ("single", "", false, new string [] { } )]
[TestCase ("dual", "armv7,arm64", false, new string [] { })]
@@ -397,17 +412,20 @@ public void RebuildTest_WithExtensions (string name, string abi, bool debug, str
mtouch.DSym = false; // faster test
mtouch.MSym = false; // faster test
mtouch.NoStrip = true; // faster test
+ //mtouch.Verbosity = 20; // Set the mtouch verbosity to something to print the mtouch output to the terminal. This will also enable additional debug output.
var timestamp = DateTime.MinValue;
mtouch.AssertExecute (MTouchAction.BuildDev, "first build");
Console.WriteLine ($"{DateTime.Now} **** FIRST BUILD DONE ****");
+ DumpFileStats (mtouch);
timestamp = DateTime.Now;
System.Threading.Thread.Sleep (1000); // make sure all new timestamps are at least a second older. HFS+ has a 1s timestamp resolution :(
mtouch.AssertExecute (MTouchAction.BuildDev, "second build");
Console.WriteLine ($"{DateTime.Now} **** SECOND BUILD DONE ****");
+ DumpFileStats (mtouch);
mtouch.AssertNoneModified (timestamp, name);
extension.AssertNoneModified (timestamp, name);
@@ -416,6 +434,7 @@ public void RebuildTest_WithExtensions (string name, string abi, bool debug, str
new FileInfo (extension.RootAssembly).LastWriteTimeUtc = DateTime.UtcNow;
mtouch.AssertExecute (MTouchAction.BuildDev, "touch extension executable");
Console.WriteLine ($"{DateTime.Now} **** TOUCH EXTENSION EXECUTABLE DONE ****");
+ DumpFileStats (mtouch);
mtouch.AssertNoneModified (timestamp, name);
extension.AssertNoneModified (timestamp, name);
@@ -423,6 +442,7 @@ public void RebuildTest_WithExtensions (string name, string abi, bool debug, str
new FileInfo (mtouch.RootAssembly).LastWriteTimeUtc = DateTime.UtcNow;
mtouch.AssertExecute (MTouchAction.BuildDev, "touch main app executable");
Console.WriteLine ($"{DateTime.Now} **** TOUCH MAIN APP EXECUTABLE DONE ****");
+ DumpFileStats (mtouch);
mtouch.AssertNoneModified (timestamp, name);
extension.AssertNoneModified (timestamp, name);
@@ -440,6 +460,7 @@ public void RebuildTest_WithExtensions (string name, string abi, bool debug, str
extension.CreateTemporaryServiceExtension (extraCode: codeB);
mtouch.AssertExecute (MTouchAction.BuildDev, "change extension executable");
Console.WriteLine ($"{DateTime.Now} **** CHANGE EXTENSION EXECUTABLE DONE ****");
+ DumpFileStats (mtouch);
mtouch.AssertNoneModified (timestamp, name);
extension.AssertNoneModified (timestamp, name, "testServiceExtension", "testServiceExtension.aotdata.armv7", "testServiceExtension.aotdata.arm64", "testServiceExtension.dll");
@@ -450,6 +471,7 @@ public void RebuildTest_WithExtensions (string name, string abi, bool debug, str
mtouch.CreateTemporaryApp (extraCode: codeB);
mtouch.AssertExecute (MTouchAction.BuildDev, "change app executable");
Console.WriteLine ($"{DateTime.Now} **** CHANGE APP EXECUTABLE DONE ****");
+ DumpFileStats (mtouch);
mtouch.AssertNoneModified (timestamp, name, "testApp", "testApp.aotdata.armv7", "testApp.aotdata.arm64", "testApp.exe");
extension.AssertNoneModified (timestamp, name);
@@ -460,6 +482,7 @@ public void RebuildTest_WithExtensions (string name, string abi, bool debug, str
File.WriteAllText (extension.RootAssembly + ".config", "");
mtouch.AssertExecute (MTouchAction.BuildDev, "add config to extension dll");
Console.WriteLine ($"{DateTime.Now} **** ADD CONFIG TO EXTENSION DONE ****");
+ DumpFileStats (mtouch);
mtouch.AssertNoneModified (timestamp, name);
extension.AssertNoneModified (timestamp, name, "testServiceExtension.dll.config", "testServiceExtension", "testServiceExtension.aotdata.armv7", "testServiceExtension.aotdata.arm64");
CollectionAssert.Contains (Directory.EnumerateFiles (extension.AppPath, "*", SearchOption.AllDirectories).Select ((v) => Path.GetFileName (v)), "testServiceExtension.dll.config", "extension config added");
@@ -471,18 +494,19 @@ public void RebuildTest_WithExtensions (string name, string abi, bool debug, str
File.WriteAllText (mtouch.RootAssembly + ".config", "");
mtouch.AssertExecute (MTouchAction.BuildDev, "add config to container exe");
Console.WriteLine ($"{DateTime.Now} **** ADD CONFIG TO CONTAINER DONE ****");
+ DumpFileStats (mtouch);
mtouch.AssertNoneModified (timestamp, name, "testApp.exe.config", "testApp", "testApp.aotdata.armv7", "testApp.aotdata.arm64");
extension.AssertNoneModified (timestamp, name);
CollectionAssert.Contains (Directory.EnumerateFiles (mtouch.AppPath, "*", SearchOption.AllDirectories).Select ((v) => Path.GetFileName (v)), "testApp.exe.config", "container config added");
timestamp = DateTime.Now;
System.Threading.Thread.Sleep (1000); // make sure all new timestamps are at least a second older. HFS+ has a 1s timestamp resolution :(
-
{
// Add a satellite to the extension.
var satellite = extension.CreateTemporarySatelliteAssembly ();
mtouch.AssertExecute (MTouchAction.BuildDev, "add satellite to extension");
Console.WriteLine ($"{DateTime.Now} **** ADD SATELLITE TO EXTENSION DONE ****");
+ DumpFileStats (mtouch);
mtouch.AssertNoneModified (timestamp, name, Path.GetFileName (satellite));
extension.AssertNoneModified (timestamp, name, Path.GetFileName (satellite));
extension.AssertModified (timestamp, name, Path.GetFileName (satellite));
@@ -497,6 +521,7 @@ public void RebuildTest_WithExtensions (string name, string abi, bool debug, str
var satellite = mtouch.CreateTemporarySatelliteAssembly ();
mtouch.AssertExecute (MTouchAction.BuildDev, "add satellite to container");
Console.WriteLine ($"{DateTime.Now} **** ADD SATELLITE TO CONTAINER DONE ****");
+ DumpFileStats (mtouch);
mtouch.AssertNoneModified (timestamp, name, Path.GetFileName (satellite));
extension.AssertNoneModified (timestamp, name, Path.GetFileName (satellite));
mtouch.AssertModified (timestamp, name, Path.GetFileName (satellite));
diff --git a/tools/common/Application.cs b/tools/common/Application.cs
index 58930c5a6015..603b8984483c 100644
--- a/tools/common/Application.cs
+++ b/tools/common/Application.cs
@@ -126,7 +126,13 @@ public string PlatformName {
}
}
- public static bool IsUptodate (string source, string target, bool check_contents = false)
+ // Checks if the source file has a time stamp later than the target file.
+ //
+ // Optionally check if the contents of the files are different after checking the timestamp.
+ //
+ // If check_stamp is true, the function will use the timestamp of a "target".stamp file
+ // if it's later than the timestamp of the "target" file itself.
+ public static bool IsUptodate (string source, string target, bool check_contents = false, bool check_stamp = true)
{
if (Driver.Force)
return false;
@@ -138,6 +144,14 @@ public static bool IsUptodate (string source, string target, bool check_contents
return false;
}
+ if (check_stamp) {
+ var tfi_stamp = new FileInfo (target + ".stamp");
+ if (tfi_stamp.Exists && tfi_stamp.LastWriteTimeUtc > tfi.LastWriteTimeUtc) {
+ Driver.Log (3, "Target '{0}' has a stamp file with newer timestamp ({1} > {2}), using the stamp file's timestamp", target, tfi_stamp.LastWriteTimeUtc, tfi.LastWriteTimeUtc);
+ tfi = tfi_stamp;
+ }
+ }
+
var sfi = new FileInfo (source);
if (sfi.LastWriteTimeUtc <= tfi.LastWriteTimeUtc) {
@@ -323,7 +337,10 @@ public static void UpdateFile (string source, string target, bool check_contents
}
// Checks if any of the source files have a time stamp later than any of the target files.
- public static bool IsUptodate (IEnumerable sources, IEnumerable targets)
+ //
+ // If check_stamp is true, the function will use the timestamp of a "target".stamp file
+ // if it's later than the timestamp of the "target" file itself.
+ public static bool IsUptodate (IEnumerable sources, IEnumerable targets, bool check_stamp = true)
{
if (Driver.Force)
return false;
@@ -356,6 +373,14 @@ public static bool IsUptodate (IEnumerable sources, IEnumerable
return false;
}
+ if (check_stamp) {
+ var tfi_stamp = new FileInfo (t + ".stamp");
+ if (tfi_stamp.Exists && tfi_stamp.LastWriteTimeUtc > tfi.LastWriteTimeUtc) {
+ Driver.Log (3, "Target '{0}' has a stamp file with newer timestamp ({1} > {2}), using the stamp file's timestamp", t, tfi_stamp.LastWriteTimeUtc, tfi.LastWriteTimeUtc);
+ tfi = tfi_stamp;
+ }
+ }
+
var lwt = tfi.LastWriteTimeUtc;
if (max_source > lwt) {
Driver.Log (3, "Prerequisite '{0}' is newer than target '{1}' ({2} vs {3}).", max_s, t, max_source, lwt);
diff --git a/tools/common/Driver.cs b/tools/common/Driver.cs
index 16ad89de8c1b..866bd4b6bad0 100644
--- a/tools/common/Driver.cs
+++ b/tools/common/Driver.cs
@@ -7,6 +7,7 @@
*/
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
@@ -304,7 +305,7 @@ static void FileMove (string source, string target)
File.Move (source, target);
}
- static void MoveIfDifferent (string path, string tmp)
+ static void MoveIfDifferent (string path, string tmp, bool use_stamp = false)
{
// Don't read the entire file into memory, it can be quite big in certain cases.
@@ -325,10 +326,12 @@ static void MoveIfDifferent (string path, string tmp)
FileMove (tmp, path);
} else {
Log (3, "Target {0} is up-to-date.", path);
+ if (use_stamp)
+ Driver.Touch (path + ".stamp");
}
}
- public static void WriteIfDifferent (string path, string contents)
+ public static void WriteIfDifferent (string path, string contents, bool use_stamp = false)
{
var tmp = path + ".tmp";
@@ -341,7 +344,7 @@ public static void WriteIfDifferent (string path, string contents)
}
File.WriteAllText (tmp, contents);
- MoveIfDifferent (path, tmp);
+ MoveIfDifferent (path, tmp, use_stamp);
} catch (Exception e) {
File.WriteAllText (path, contents);
ErrorHelper.Warning (1014, e, "Failed to re-use cached version of '{0}': {1}.", path, e.Message);
@@ -350,7 +353,7 @@ public static void WriteIfDifferent (string path, string contents)
}
}
- public static void WriteIfDifferent (string path, byte[] contents)
+ public static void WriteIfDifferent (string path, byte[] contents, bool use_stamp = false)
{
var tmp = path + ".tmp";
@@ -362,7 +365,7 @@ public static void WriteIfDifferent (string path, byte[] contents)
}
File.WriteAllBytes (tmp, contents);
- MoveIfDifferent (path, tmp);
+ MoveIfDifferent (path, tmp, use_stamp);
} catch (Exception e) {
File.WriteAllBytes (path, contents);
ErrorHelper.Warning (1014, e, "Failed to re-use cached version of '{0}': {1}.", path, e.Message);
@@ -438,5 +441,30 @@ static void LogArguments (string [] arguments, int indentation)
}
}
}
+
+ public static void Touch (IEnumerable filenames, DateTime? timestamp = null)
+ {
+ if (timestamp == null)
+ timestamp = DateTime.Now;
+ foreach (var filename in filenames) {
+ try {
+ var fi = new FileInfo (filename);
+ if (!fi.Exists) {
+ using (var fo = fi.OpenWrite ()) {
+ // Create an empty file.
+ }
+ }
+ fi.LastWriteTime = timestamp.Value;
+ } catch (Exception e) {
+ ErrorHelper.Warning (128, "Could not touch the file '{0}': {1}", filename, e.Message);
+ }
+ }
+ }
+
+ public static void Touch (params string [] filenames)
+ {
+ Touch ((IEnumerable) filenames);
+ }
+
}
}
diff --git a/tools/common/PInvokeWrapperGenerator.cs b/tools/common/PInvokeWrapperGenerator.cs
index 0b65acaf0fdf..424cb17587a1 100644
--- a/tools/common/PInvokeWrapperGenerator.cs
+++ b/tools/common/PInvokeWrapperGenerator.cs
@@ -63,8 +63,8 @@ public void End ()
Registrar.GeneratePInvokeWrappersEnd ();
- Driver.WriteIfDifferent (HeaderPath, hdr.ToString () + "\n" + decls.ToString () + "\n" + ifaces.ToString () + "\n") ;
- Driver.WriteIfDifferent (SourcePath, mthds.ToString () + "\n" + sb.ToString () + "\n");
+ Driver.WriteIfDifferent (HeaderPath, hdr.ToString () + "\n" + decls.ToString () + "\n" + ifaces.ToString () + "\n", true);
+ Driver.WriteIfDifferent (SourcePath, mthds.ToString () + "\n" + sb.ToString () + "\n", true);
}
public void ProcessMethod (MethodDefinition method)
diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs
index 7ebff19fed0a..80bdad790ea3 100644
--- a/tools/common/StaticRegistrar.cs
+++ b/tools/common/StaticRegistrar.cs
@@ -4508,6 +4508,9 @@ public void GenerateSingleAssembly (IEnumerable assemblies,
public void Generate (IEnumerable assemblies, string header_path, string source_path)
{
+ if (Target?.CachedLink == true)
+ throw ErrorHelper.CreateError (99, "Internal error: the static registrar should not execute unless the linker also executed (or was disabled). A potential workaround is to pass '-f' as an additional " + Driver.NAME + " argument to force a full build. Please file a bug report with a test case (https://bugzilla.xamarin.com).");
+
this.input_assemblies = assemblies;
foreach (var assembly in assemblies) {
@@ -4561,12 +4564,12 @@ void Generate (string header_path, string source_path)
FlushTrace ();
- Driver.WriteIfDifferent (source_path, methods.ToString ());
+ Driver.WriteIfDifferent (source_path, methods.ToString (), true);
header.AppendLine ();
header.AppendLine (declarations);
header.AppendLine (interfaces);
- Driver.WriteIfDifferent (header_path, header.ToString ());
+ Driver.WriteIfDifferent (header_path, header.ToString (), true);
header.Dispose ();
header = null;
diff --git a/tools/common/Target.cs b/tools/common/Target.cs
index 24f1aa08282f..a19367c7b9d1 100644
--- a/tools/common/Target.cs
+++ b/tools/common/Target.cs
@@ -62,6 +62,12 @@ public Target (Application app)
this.StaticRegistrar = new StaticRegistrar (this);
}
+ public bool CachedLink {
+ get {
+ return cached_link;
+ }
+ }
+
public void ExtractNativeLinkInfo (List exceptions)
{
foreach (var a in Assemblies) {
@@ -396,7 +402,7 @@ internal string GenerateReferencingSource (string reference_m, IEnumerable references = new List ();
diff --git a/tools/mtouch/.gitignore b/tools/mtouch/.gitignore
index 2673ba7899f7..f691ab227bad 100644
--- a/tools/mtouch/.gitignore
+++ b/tools/mtouch/.gitignore
@@ -7,3 +7,5 @@ SdkVersions.cs
simlauncher-sgen
simlauncher32-sgen
simlauncher64-sgen
+*.stamp
+
diff --git a/tools/mtouch/Target.cs b/tools/mtouch/Target.cs
index a53fbe31b23c..50ec3a2739ac 100644
--- a/tools/mtouch/Target.cs
+++ b/tools/mtouch/Target.cs
@@ -537,17 +537,38 @@ public void ManagedLink ()
if (!Driver.Force) {
if (File.Exists (cache_path)) {
using (var reader = new StreamReader (cache_path)) {
- string line;
+ string line = null;
+
while ((line = reader.ReadLine ()) != null) {
var colon = line.IndexOf (':');
if (colon == -1)
continue;
- var appex = line.Substring (0, colon);
- var asm = line.Substring (colon + 1);
- List asms;
- if (!cached_output.TryGetValue (appex, out asms))
- cached_output [appex] = asms = new List ();
- asms.Add (asm);
+ var key = line.Substring (0, colon);
+ var value = line.Substring (colon + 1);
+ switch (key) {
+ case "RemoveDynamicRegistrar":
+ switch (value) {
+ case "true":
+ App.Optimizations.RemoveDynamicRegistrar = true;
+ break;
+ case "false":
+ App.Optimizations.RemoveDynamicRegistrar = false;
+ break;
+ default:
+ App.Optimizations.RemoveDynamicRegistrar = null;
+ break;
+ }
+ Driver.Log (1, $"Optimization dynamic registrar removal loaded from cached results: {(App.Optimizations.RemoveDynamicRegistrar.HasValue ? (App.Optimizations.RemoveUIThreadChecks.Value ? "enabled" : "disabled") : "not set")}");
+ break;
+ default:
+ // key: app(ex)
+ // value: assembly
+ List asms;
+ if (!cached_output.TryGetValue (key, out asms))
+ cached_output [key] = asms = new List ();
+ asms.Add (value);
+ break;
+ }
}
}
@@ -683,6 +704,8 @@ public void ManagedLink ()
// Write the input files to the cache
using (var writer = new StreamWriter (cache_path, false)) {
+ var opt = App.Optimizations.RemoveDynamicRegistrar;
+ writer.WriteLine ($"RemoveDynamicRegistrar:{(opt.HasValue ? (opt.Value ? "true" : "false") : string.Empty)}");
foreach (var target in allTargets) {
foreach (var asm in target.Assemblies) {
writer.WriteLine ($"{target.App.AppDirectory}:{asm.FullPath}");
diff --git a/tools/mtouch/mtouch.cs b/tools/mtouch/mtouch.cs
index c7b80164a518..f81124892f03 100644
--- a/tools/mtouch/mtouch.cs
+++ b/tools/mtouch/mtouch.cs
@@ -79,6 +79,8 @@ public enum OutputFormat {
namespace Xamarin.Bundler
{
partial class Driver {
+ internal const string NAME = "mtouch";
+
public static void ShowHelp (OptionSet os)
{
Console.WriteLine ("mtouch - Mono Compiler for iOS");
@@ -698,7 +700,7 @@ public static string GenerateMain (Application app, IEnumerable assemb
sw.WriteLine ("}");
}
- WriteIfDifferent (main_source, sb.ToString ());
+ WriteIfDifferent (main_source, sb.ToString (), true);
} catch (MonoTouchException) {
throw;
} catch (Exception e) {
@@ -799,24 +801,6 @@ public static bool SymlinkAssembly (Application app, string source, string targe
return true;
}
- public static void Touch (IEnumerable filenames, DateTime? timestamp = null)
- {
- if (timestamp == null)
- timestamp = DateTime.Now;
- foreach (var filename in filenames) {
- try {
- new FileInfo (filename).LastWriteTime = timestamp.Value;
- } catch (Exception e) {
- ErrorHelper.Warning (128, "Could not touch the file '{0}': {1}", filename, e.Message);
- }
- }
- }
-
- public static void Touch (params string [] filenames)
- {
- Touch ((IEnumerable) filenames);
- }
-
public static bool CanWeSymlinkTheApplication (Application app)
{
if (app.Platform != ApplePlatform.iOS)