Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Harness] Make sure we do not copy/generate the app sources more than once. #8243

Merged
merged 2 commits into from
Mar 31, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ public class XamariniOSTemplate : ITemplatedProject {
string WatchAppTemplatePath => Path.Combine (OutputDirectoryPath, "templates", "watchOS", "App").Replace ("/", "\\");
string WatchExtensionTemplatePath => Path.Combine (OutputDirectoryPath, "templates", "watchOS", "Extension").Replace ("/", "\\");

bool srcGenerated = false;
object srcGeneratedLock = new object ();

Stream GetTemplateStream (string templateName)
{
var resources = GetType ().Assembly.GetManifestResourceNames ();
Expand Down Expand Up @@ -229,24 +232,39 @@ internal static string GetPListPath (string rootDir, WatchAppType appType)
}
}

public async Task GenerateSource (string srcOuputPath)
// this method could be async, since we could be using async IO. The problem is that it might be called
// several times. The File and the Directory classes are not smart, and there is a possibility that we
// stop the thread between the Directory.Exist and the Directory.Delete. A nice way to solve this would be
// to set the src to get a AsyncLazy<bool> and generate the source only once on the first run, but AsyncLazy
// got move to target .netcore 5.0 (https://github.com/dotnet/runtime/issues/27510) yet we want to fix issue
// https://github.com/xamarin/xamarin-macios/issues/8240 so for the time being we lock, check, and generate
// if needed, which is better than:
//
// * Have a thread issue.
// * Generate the src EVERY SINGLE TIME for a list of projects.
public void GenerateSource (string srcOuputPath)
{
// mk the expected directories
if (Directory.Exists (srcOuputPath))
Directory.Delete (srcOuputPath, true); // delete, we always want to add the embeded src
BuildSrcTree (srcOuputPath);
// the code is simple, we are going to look for all the resources that we know are src and will write a
// copy of the stream in the designated output path
var resources = GetType ().Assembly.GetManifestResourceNames ().Where (a => a.StartsWith (srcResourcePrefix));

// we need to be smart, since the resource name != the path
foreach (var r in resources) {
var path = CalculateDestinationPath (srcOuputPath, r);
// copy the stream
using (var sourceReader = GetType ().Assembly.GetManifestResourceStream (r))
using (var destination = File.Create (path)) {
await sourceReader.CopyToAsync (destination);
lock (srcGeneratedLock) {
if (srcGenerated)
return;
// mk the expected directories
if (Directory.Exists (srcOuputPath))
Directory.Delete (srcOuputPath, true); // delete, we always want to add the embedded src
BuildSrcTree (srcOuputPath);
// the code is simple, we are going to look for all the resources that we know are src and will write a
// copy of the stream in the designated output path
var resources = GetType ().Assembly.GetManifestResourceNames ().Where (a => a.StartsWith (srcResourcePrefix));

// we need to be smart, since the resource name != the path
foreach (var r in resources) {
var path = CalculateDestinationPath (srcOuputPath, r);
// copy the stream
using (var sourceReader = GetType ().Assembly.GetManifestResourceStream (r))
using (var destination = File.Create (path)) {
sourceReader.CopyTo (destination);
}
}
srcGenerated = true;
}
}

Expand Down Expand Up @@ -314,7 +332,7 @@ string GenerateIncludeFilesNode (string projectName, (string FailureMessage, Lis
public async Task<GeneratedProjects> GenerateTestProjectsAsync (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform)
{
// generate the template c# code before we create the diff projects
await GenerateSource (Path.Combine (OutputDirectoryPath, "templates"));
GenerateSource (Path.Combine (OutputDirectoryPath, "templates"));
var result = new GeneratedProjects ();
switch (platform) {
case Platform.WatchOS:
Expand Down