-
Notifications
You must be signed in to change notification settings - Fork 31
[BaseTasks] Import Xamarin.Build.AsyncTask #237
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
f57b9cf
[BaseTasks] Import Xamarin.Build.AsyncTask
pjcollins 02e2827
Add LogErrorFromException From android-sdk-installer
pjcollins 4cf94c3
Formatting
pjcollins f305cf1
Feedback
pjcollins 68db73b
Fix SO
pjcollins 04b2873
Merge AndroidAsyncTask -> AsyncTask
pjcollins ffc4ed4
Remove custom exception logging, use LogUnhandledException
pjcollins 8b8375a
[ci] install .NET 8
pjcollins 883809f
Merge remote-tracking branch 'origin/main' into dev/pjc/import-asynctask
pjcollins File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next
Next commit
[BaseTasks] Import Xamarin.Build.AsyncTask
We've decided to move https://github.com/xamarin/Xamarin.Build.AsyncTask into this repository as part of dotnet org migration efforts. Source and test content from xamarin/Xamarin.Build.AsyncTask@db4ce14dac has been added to Microsoft.Android.Build.BaseTasks.
- Loading branch information
commit f57b9cfec90769cb05cd4e789f5e235b261a5aff
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,390 @@ | ||
| // https://github.com/xamarin/Xamarin.Build.AsyncTask/blob/db4ce14dacfef47435c238b1b681c124e60ea1a0/Xamarin.Build.AsyncTask/AsyncTask.cs | ||
|
|
||
| using System; | ||
| using System.IO; | ||
| using Microsoft.Build.Utilities; | ||
| using Microsoft.Build.Framework; | ||
| using System.Threading; | ||
| using System.Collections; | ||
| using System.Collections.Generic; | ||
|
|
||
| namespace Microsoft.Android.Build.Tasks | ||
| { | ||
| /// <summary> | ||
| /// Base class for tasks that need long-running cancellable asynchronous tasks | ||
| /// that don't block the UI thread in the IDE. | ||
| /// </summary> | ||
| public class AsyncTask : Task, ICancelableTask | ||
| { | ||
| readonly CancellationTokenSource cts = new CancellationTokenSource(); | ||
| readonly Queue logMessageQueue = new Queue(); | ||
| readonly Queue warningMessageQueue = new Queue(); | ||
| readonly Queue errorMessageQueue = new Queue(); | ||
| readonly Queue customMessageQueue = new Queue(); | ||
| readonly Queue telemetryMessageQueue = new Queue (); | ||
| readonly ManualResetEvent logDataAvailable = new ManualResetEvent(false); | ||
| readonly ManualResetEvent errorDataAvailable = new ManualResetEvent(false); | ||
| readonly ManualResetEvent warningDataAvailable = new ManualResetEvent(false); | ||
| readonly ManualResetEvent customDataAvailable = new ManualResetEvent(false); | ||
| readonly ManualResetEvent telemetryDataAvailable = new ManualResetEvent(false); | ||
| readonly ManualResetEvent taskCancelled = new ManualResetEvent(false); | ||
| readonly ManualResetEvent completed = new ManualResetEvent(false); | ||
| bool isRunning = true; | ||
| object eventlock = new object(); | ||
| int uiThreadId = 0; | ||
|
|
||
| /// <summary> | ||
| /// Indicates if the task will yield the node during tool execution. | ||
| /// </summary> | ||
| public bool YieldDuringToolExecution { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// The cancellation token to notify the cancellation requests | ||
| /// </summary> | ||
| public CancellationToken CancellationToken => cts.Token; | ||
|
|
||
| /// <summary> | ||
| /// Gets the current working directory for the task, which is captured at task | ||
| /// creation time from <see cref="Directory.GetCurrentDirectory"/>. | ||
| /// </summary> | ||
| protected string WorkingDirectory { get; private set; } | ||
|
|
||
| [Obsolete("Do not use the Log.LogXXXX from within your Async task as it will Lock the Visual Studio UI. Use the this.LogXXXX methods instead.")] | ||
| private new TaskLoggingHelper Log => base.Log; | ||
|
|
||
| /// <summary> | ||
| /// Initializes the task. | ||
| /// </summary> | ||
| public AsyncTask() | ||
| { | ||
| YieldDuringToolExecution = false; | ||
| WorkingDirectory = Directory.GetCurrentDirectory(); | ||
| uiThreadId = Thread.CurrentThread.ManagedThreadId; | ||
| } | ||
|
|
||
| public void Cancel() => taskCancelled.Set(); | ||
|
|
||
| protected void Complete(System.Threading.Tasks.Task task) | ||
| { | ||
| if (task.Exception != null) | ||
| { | ||
| var ex = task.Exception.GetBaseException(); | ||
| LogError(ex.Message + Environment.NewLine + ex.StackTrace); | ||
| } | ||
| Complete(); | ||
| } | ||
|
|
||
| public void Complete() => completed.Set(); | ||
|
|
||
| public void LogDebugTaskItems(string message, string[] items) | ||
| { | ||
| LogDebugMessage(message); | ||
|
|
||
| if (items == null) | ||
| return; | ||
|
|
||
| foreach (var item in items) | ||
| LogDebugMessage(" {0}", item); | ||
| } | ||
|
|
||
| public void LogDebugTaskItems(string message, ITaskItem[] items) | ||
| { | ||
| LogDebugMessage(message); | ||
|
|
||
| if (items == null) | ||
| return; | ||
|
|
||
| foreach (var item in items) | ||
| LogDebugMessage(" {0}", item.ItemSpec); | ||
| } | ||
|
|
||
| public void LogTelemetry(string eventName, IDictionary<string, string> properties) | ||
| { | ||
| if (uiThreadId == Thread.CurrentThread.ManagedThreadId) | ||
| { | ||
| #pragma warning disable 618 | ||
| Log.LogTelemetry(eventName, properties); | ||
| return; | ||
| #pragma warning restore 618 | ||
| } | ||
|
|
||
| var data = new TelemetryEventArgs() { | ||
| EventName = eventName, | ||
| Properties = properties | ||
| }; | ||
| EnqueueMessage(telemetryMessageQueue, data, telemetryDataAvailable); | ||
| } | ||
|
|
||
| public void LogMessage(string message) => LogMessage(message, importance: MessageImportance.Normal); | ||
|
|
||
| public void LogMessage(string message, params object[] messageArgs) => LogMessage(string.Format(message, messageArgs)); | ||
|
|
||
| public void LogDebugMessage(string message) => LogMessage(message, importance: MessageImportance.Low); | ||
|
|
||
| public void LogDebugMessage(string message, params object[] messageArgs) => LogMessage(string.Format(message, messageArgs), importance: MessageImportance.Low); | ||
|
|
||
| public void LogMessage(string message, MessageImportance importance = MessageImportance.Normal) | ||
| { | ||
| if (uiThreadId == Thread.CurrentThread.ManagedThreadId) | ||
| { | ||
| #pragma warning disable 618 | ||
| Log.LogMessage(importance, message); | ||
| return; | ||
| #pragma warning restore 618 | ||
| } | ||
|
|
||
| var data = new BuildMessageEventArgs( | ||
| message: message, | ||
| helpKeyword: null, | ||
| senderName: null, | ||
| importance: importance | ||
| ); | ||
| EnqueueMessage(logMessageQueue, data, logDataAvailable); | ||
| } | ||
|
|
||
| public void LogError(string message) => LogCodedError(code: null, message: message, file: null, lineNumber: 0); | ||
|
|
||
| public void LogError(string message, params object[] messageArgs) => LogCodedError(code: null, message: string.Format(message, messageArgs)); | ||
|
|
||
| public void LogCodedError(string code, string message) => LogCodedError(code: code, message: message, file: null, lineNumber: 0); | ||
|
|
||
| public void LogCodedError(string code, string message, params object[] messageArgs) => LogCodedError(code: code, message: string.Format(message, messageArgs), file: null, lineNumber: 0); | ||
|
|
||
| public void LogCodedError(string code, string file, int lineNumber, string message, params object[] messageArgs) => LogCodedError(code: code, message: string.Format(message, messageArgs), file: file, lineNumber: lineNumber); | ||
|
|
||
| public void LogCodedError(string code, string message, string file, int lineNumber) | ||
| { | ||
| if (uiThreadId == Thread.CurrentThread.ManagedThreadId) | ||
| { | ||
| #pragma warning disable 618 | ||
| Log.LogError( | ||
| subcategory: null, | ||
| errorCode: code, | ||
| helpKeyword: null, | ||
| file: file, | ||
| lineNumber: lineNumber, | ||
| columnNumber: 0, | ||
| endLineNumber: 0, | ||
| endColumnNumber: 0, | ||
| message: message | ||
| ); | ||
| return; | ||
| #pragma warning restore 618 | ||
| } | ||
|
|
||
| var data = new BuildErrorEventArgs( | ||
| subcategory: null, | ||
| code: code, | ||
| file: file, | ||
| lineNumber: lineNumber, | ||
| columnNumber: 0, | ||
| endLineNumber: 0, | ||
| endColumnNumber: 0, | ||
| message: message, | ||
| helpKeyword: null, | ||
| senderName: null | ||
| ); | ||
| EnqueueMessage(errorMessageQueue, data, errorDataAvailable); | ||
| } | ||
|
|
||
| public void LogWarning(string message) => LogCodedWarning(code: null, message: message, file: null, lineNumber: 0); | ||
|
|
||
| public void LogWarning(string message, params object[] messageArgs) => LogCodedWarning(code: null, message: string.Format(message, messageArgs)); | ||
|
|
||
| public void LogCodedWarning(string code, string message) => LogCodedWarning(code: code, message: message, file: null, lineNumber: 0); | ||
|
|
||
| public void LogCodedWarning(string code, string message, params object[] messageArgs) => LogCodedWarning(code: code, message: string.Format(message, messageArgs), file: null, lineNumber: 0); | ||
|
|
||
| public void LogCodedWarning(string code, string file, int lineNumber, string message, params object[] messageArgs) => LogCodedWarning(code: code, message: string.Format(message, messageArgs), file: file, lineNumber: lineNumber); | ||
|
|
||
| public void LogCodedWarning(string code, string message, string file, int lineNumber) | ||
| { | ||
| if (uiThreadId == Thread.CurrentThread.ManagedThreadId) | ||
| { | ||
| #pragma warning disable 618 | ||
| Log.LogWarning( | ||
| subcategory: null, | ||
| warningCode: code, | ||
| helpKeyword: null, | ||
| file: file, | ||
| lineNumber: lineNumber, | ||
| columnNumber: 0, | ||
| endLineNumber: 0, | ||
| endColumnNumber: 0, | ||
| message: message | ||
| ); | ||
| return; | ||
| #pragma warning restore 618 | ||
| } | ||
| var data = new BuildWarningEventArgs( | ||
| subcategory: null, | ||
| code: code, | ||
| file: file, | ||
| lineNumber: lineNumber, | ||
| columnNumber: 0, | ||
| endLineNumber: 0, | ||
| endColumnNumber: 0, | ||
| message: message, | ||
| helpKeyword: null, | ||
| senderName: null | ||
| ); | ||
| EnqueueMessage(warningMessageQueue, data, warningDataAvailable); | ||
| } | ||
|
|
||
| public void LogCustomBuildEvent(CustomBuildEventArgs e) | ||
| { | ||
| if (uiThreadId == Thread.CurrentThread.ManagedThreadId) | ||
| { | ||
| #pragma warning disable 618 | ||
| BuildEngine.LogCustomEvent(e); | ||
| return; | ||
| #pragma warning restore 618 | ||
| } | ||
| EnqueueMessage(customMessageQueue, e, customDataAvailable); | ||
| } | ||
|
|
||
| public override bool Execute() | ||
| { | ||
| WaitForCompletion(); | ||
| #pragma warning disable 618 | ||
| return !Log.HasLoggedErrors; | ||
| #pragma warning restore 618 | ||
| } | ||
|
|
||
| private void EnqueueMessage(Queue queue, object item, ManualResetEvent resetEvent) | ||
| { | ||
| lock (queue.SyncRoot) | ||
| { | ||
| queue.Enqueue(item); | ||
| lock (eventlock) | ||
| { | ||
| if (isRunning) | ||
| resetEvent.Set(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private void LogInternal<T>(Queue queue, Action<T> action, ManualResetEvent resetEvent) | ||
| { | ||
| lock (queue.SyncRoot) | ||
| { | ||
| while (queue.Count > 0) | ||
| { | ||
| var args = (T)queue.Dequeue(); | ||
| action(args); | ||
| } | ||
| resetEvent.Reset(); | ||
| } | ||
| } | ||
|
|
||
| protected void Yield() | ||
| { | ||
| if (YieldDuringToolExecution && BuildEngine is IBuildEngine3) | ||
| ((IBuildEngine3)BuildEngine).Yield(); | ||
| } | ||
|
|
||
| protected void Reacquire() | ||
| { | ||
| if (YieldDuringToolExecution && BuildEngine is IBuildEngine3) | ||
| ((IBuildEngine3)BuildEngine).Reacquire(); | ||
| } | ||
|
|
||
| protected void WaitForCompletion() | ||
| { | ||
| WaitHandle[] handles = new WaitHandle[] { | ||
| logDataAvailable, | ||
| errorDataAvailable, | ||
| warningDataAvailable, | ||
| customDataAvailable, | ||
| telemetryDataAvailable, | ||
| taskCancelled, | ||
| completed, | ||
| }; | ||
| try | ||
| { | ||
| while (isRunning) | ||
| { | ||
| var index = (WaitHandleIndex)System.Threading.WaitHandle.WaitAny(handles, TimeSpan.FromMilliseconds(10)); | ||
| switch (index) | ||
| { | ||
| case WaitHandleIndex.LogDataAvailable: | ||
| LogInternal<BuildMessageEventArgs>(logMessageQueue, (e) => | ||
| { | ||
| #pragma warning disable 618 | ||
| Log.LogMessage(e.Importance, e.Message); | ||
| #pragma warning restore 618 | ||
| }, logDataAvailable); | ||
| break; | ||
| case WaitHandleIndex.ErrorDataAvailable: | ||
| LogInternal<BuildErrorEventArgs>(errorMessageQueue, (e) => | ||
| { | ||
| #pragma warning disable 618 | ||
| Log.LogError(subcategory: null, | ||
| errorCode: e.Code, | ||
| helpKeyword: null, | ||
| file: e.File, | ||
| lineNumber: e.LineNumber, | ||
| columnNumber: e.ColumnNumber, | ||
| endLineNumber: e.EndLineNumber, | ||
| endColumnNumber: e.EndColumnNumber, | ||
| message: e.Message); | ||
| #pragma warning restore 618 | ||
| }, errorDataAvailable); | ||
| break; | ||
| case WaitHandleIndex.WarningDataAvailable: | ||
| LogInternal<BuildWarningEventArgs>(warningMessageQueue, (e) => | ||
| { | ||
| #pragma warning disable 618 | ||
| Log.LogWarning(subcategory: null, | ||
| warningCode: e.Code, | ||
| helpKeyword: null, | ||
| file: e.File, | ||
| lineNumber: e.LineNumber, | ||
| columnNumber: e.ColumnNumber, | ||
| endLineNumber: e.EndLineNumber, | ||
| endColumnNumber: e.EndColumnNumber, | ||
| message: e.Message); | ||
| #pragma warning restore 618 | ||
| }, warningDataAvailable); | ||
| break; | ||
| case WaitHandleIndex.CustomDataAvailable: | ||
| LogInternal<CustomBuildEventArgs>(customMessageQueue, (e) => | ||
| { | ||
| BuildEngine.LogCustomEvent(e); | ||
| }, customDataAvailable); | ||
| break; | ||
| case WaitHandleIndex.TelemetryDataAvailable: | ||
| LogInternal<TelemetryEventArgs>(telemetryMessageQueue, (e) => { | ||
| BuildEngine5.LogTelemetry(e.EventName, e.Properties); | ||
| }, telemetryDataAvailable); | ||
| break; | ||
| case WaitHandleIndex.TaskCancelled: | ||
| Cancel(); | ||
| cts.Cancel(); | ||
| isRunning = false; | ||
| break; | ||
| case WaitHandleIndex.Completed: | ||
| isRunning = false; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| } | ||
| finally | ||
| { | ||
|
|
||
| } | ||
| } | ||
|
|
||
| private enum WaitHandleIndex | ||
| { | ||
| LogDataAvailable, | ||
| ErrorDataAvailable, | ||
| WarningDataAvailable, | ||
| CustomDataAvailable, | ||
| TelemetryDataAvailable, | ||
| TaskCancelled, | ||
| Completed, | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.