Skip to content

Commit a806ddf

Browse files
committed
Logging and Progress Update
Added per job file logs fixed up the progress bar system to be cleaner
1 parent 54dd8dd commit a806ddf

18 files changed

+388
-183
lines changed

sources/SilkTouch/SilkTouch/Clang/ClangScraper.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using Microsoft.Extensions.Logging;
2323
using Microsoft.Extensions.Options;
2424
using Silk.NET.SilkTouch.Caching;
25+
using Silk.NET.SilkTouch.Logging;
2526
using Silk.NET.SilkTouch.Mods;
2627
using Silk.NET.SilkTouch.Sources;
2728
using Silk.NET.SilkTouch.Utility;
@@ -41,12 +42,14 @@ namespace Silk.NET.SilkTouch.Clang;
4142
/// <param name="inputResolver">The input resolver to use.</param>
4243
/// <param name="cacheProvider">The cache provider into which ClangSharp outputs are cached.</param>
4344
/// <param name="responseFileMods">The mods that modify response files before they are fed to ClangSharp.</param>
45+
/// <param name="progressService">the progress service to use</param>
4446
[ModConfiguration<Configuration>]
4547
public sealed class ClangScraper(
4648
ResponseFileHandler rspHandler,
4749
IOptionsSnapshot<ClangScraper.Configuration> config,
4850
ILogger<ClangScraper> logger,
4951
IInputResolver inputResolver,
52+
IProgressService progressService,
5053
ICacheProvider? cacheProvider = null,
5154
IEnumerable<IJobDependency<IResponseFileMod>>? responseFileMods = null
5255
) : IMod
@@ -549,7 +552,7 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default)
549552
);
550553

551554
// Resolve any foreign paths referenced in the response files
552-
await inputResolver.ResolveInPlace(rsps);
555+
await inputResolver.ResolveInPlace(rsps, progressService);
553556

554557
// Should we completely skip running ClangSharp (e.g. we can't get Windows SDK bindings on macOS)
555558
var skip = (cfg.SkipScrapeIf?.Any(applicableSkipIfs.Contains)).GetValueOrDefault();

sources/SilkTouch/SilkTouch/Clang/ResponseFileHandler.cs

+4-7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using ClangSharp;
1212
using Microsoft.Extensions.FileSystemGlobbing;
1313
using Microsoft.Extensions.Logging;
14+
using Silk.NET.SilkTouch.Logging;
1415
using Silk.NET.SilkTouch.Utility;
1516
using static ClangSharp.Interop.CXTranslationUnit_Flags;
1617

@@ -20,7 +21,7 @@ namespace Silk.NET.SilkTouch.Clang;
2021
/// Reads a response file (rsp) containing ClangSharpPInvokeGenerator command line arguments.
2122
/// </summary>
2223
[SuppressMessage("ReSharper", "InconsistentNaming")]
23-
public class ResponseFileHandler(ILogger<ResponseFileHandler> logger)
24+
public class ResponseFileHandler(ILogger<ResponseFileHandler> logger, IProgressService progressService)
2425
{
2526
// Begin verbatim ClangSharp code
2627
private static readonly string[] s_additionalOptionAliases = ["--additional", "-a"];
@@ -1356,7 +1357,6 @@ out Dictionary<string, string> withPackings
13561357
{
13571358
foreach (var error in errorList)
13581359
{
1359-
ProgressBarUtility.Hide(LogLevel.Information);
13601360
logger.LogError($"Error in args for '{files.FirstOrDefault()}': {error}");
13611361
}
13621362
}
@@ -1497,8 +1497,7 @@ IReadOnlyList<string> globs
14971497
IEnumerable<string> rsps = Glob(globs);
14981498
int index = 0;
14991499
int count = rsps.Count();
1500-
ProgressBarUtility.SetPercentage(0);
1501-
ProgressBarUtility.Show(LogLevel.Information);
1500+
progressService.SetTask("Reading ResponseFiles");
15021501
foreach (var rsp in rsps)
15031502
{
15041503
index++;
@@ -1508,14 +1507,12 @@ IReadOnlyList<string> globs
15081507
?? throw new InvalidOperationException("Couldn't get directory name of path");
15091508
var read = ReadResponseFile(RspRelativeTo(dir, rsp).ToArray(), dir, rsp);
15101509

1511-
ProgressBarUtility.SetPercentage(index / (float)count);
1510+
progressService.SetProgress(index / (float)count);
15121511
yield return read with
15131512
{
15141513
FileDirectory = dir,
15151514
};
15161515
}
1517-
1518-
ProgressBarUtility.Hide(LogLevel.Information);
15191516
}
15201517

15211518
private IEnumerable<string> RspRelativeTo(string directory, string fullPath)
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Concurrent;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Text;
9+
using System.Threading.Tasks;
10+
11+
namespace Silk.NET.SilkTouch
12+
{
13+
/// <summary>
14+
/// Information on the current Job
15+
/// </summary>
16+
public class JobContext
17+
{
18+
private readonly AsyncLocal<string?> _key = new AsyncLocal<string?>();
19+
20+
/// <summary>
21+
/// The key for the relevant job
22+
/// </summary>
23+
public string? JobKey
24+
{
25+
get => _key.Value;
26+
set => _key.Value = value;
27+
}
28+
29+
/// <summary>
30+
/// A dictionary of all current open log file writers
31+
/// </summary>
32+
public readonly ConcurrentDictionary<string, StreamWriter> LogWriters = new ConcurrentDictionary<string, StreamWriter>();
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace Silk.NET.SilkTouch.Logging
11+
{
12+
/// <summary>
13+
/// A console renderer which handles progress bar writing
14+
/// </summary>
15+
public class ConsoleRenderer
16+
{
17+
private readonly IProgressService _progressService;
18+
private readonly TextWriter _originalOut;
19+
private readonly StringWriter _consoleOutput;
20+
private readonly Timer _timer;
21+
private bool _isRunning = true;
22+
private int _progressBarCount;
23+
private readonly object _lockObject = new object();
24+
25+
/// <summary>
26+
/// Creates and instance of the <see cref="ConsoleRenderer"/>
27+
/// </summary>
28+
/// <param name="progressService">the progress service</param>
29+
public ConsoleRenderer(IProgressService progressService)
30+
{
31+
_progressService = progressService;
32+
_originalOut = Console.Out;
33+
_consoleOutput = new StringWriter();
34+
Console.SetOut(_consoleOutput); // Redirect console output
35+
36+
_timer = new Timer(Render, null, 0, 500);
37+
}
38+
39+
/// <summary>
40+
/// Stops the console renderer processing and restores console to original state
41+
/// </summary>
42+
public void Stop()
43+
{
44+
_isRunning = false;
45+
_timer.Dispose();
46+
Console.SetOut(_originalOut); // Restore original output
47+
}
48+
49+
private void Render(object? state)
50+
{
51+
if (!_isRunning && _consoleOutput.GetStringBuilder().Length > 0)
52+
return;
53+
54+
lock (_lockObject)
55+
{
56+
// Clear the progress bars by moving the cursor and overwriting with spaces
57+
if (_progressBarCount > 0)
58+
{
59+
int currentTop = Console.CursorTop;
60+
Console.SetCursorPosition(0, currentTop - _progressBarCount);
61+
for (int i = 0; i < _progressBarCount; i++)
62+
{
63+
_originalOut.Write(new string(' ', Console.WindowWidth));
64+
}
65+
Console.SetCursorPosition(0, currentTop - _progressBarCount);
66+
}
67+
68+
var progressDictionary = _progressService.GetAllProgress();
69+
_progressBarCount = progressDictionary.Count();
70+
71+
// Write all console output captured since the last render
72+
var output = _consoleOutput.GetStringBuilder().ToString();
73+
if (!string.IsNullOrWhiteSpace(output))
74+
{
75+
_originalOut.Write(output);
76+
_consoleOutput.GetStringBuilder().Clear();
77+
}
78+
79+
foreach (var kvp in progressDictionary)
80+
{
81+
string task = string.IsNullOrWhiteSpace(kvp.Value.Item1) ? string.Empty : $" - {kvp.Value.Item1}";
82+
_originalOut.WriteLine($"{kvp.Key}{task}: ({new string('|', (int)(kvp.Value.Item2 * 20))}{new string('-', 20 - (int)(kvp.Value.Item2 * 20))}) ({(kvp.Value.Item2 * 100):F2}%)");
83+
}
84+
}
85+
}
86+
}
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace Silk.NET.SilkTouch.Logging
11+
{
12+
/// <summary>
13+
/// An interface for handling current task progress in a job
14+
/// </summary>
15+
public interface IProgressService
16+
{
17+
/// <summary>
18+
/// Returns an enumerable of all progress values with job as the key, and then the task and progress in a tuple
19+
/// </summary>
20+
/// <returns></returns>
21+
IEnumerable<KeyValuePair<string, (string, float)>> GetAllProgress();
22+
/// <summary>
23+
/// Get the current task and progress for this job
24+
/// </summary>
25+
/// <returns>current task and progress for this job</returns>
26+
(string, float) GetCurrentTaskAndProgress();
27+
/// <summary>
28+
/// Set the name of the task for this job
29+
/// Resets the progress to 0
30+
/// </summary>
31+
/// <param name="task">name of the current task</param>
32+
void SetTask(string task);
33+
/// <summary>
34+
/// Sets the progress of the current job's task
35+
/// Values are expected between 0 and 1
36+
/// </summary>
37+
/// <param name="progress">current progress value</param>
38+
void SetProgress(float progress);
39+
/// <summary>
40+
/// Remove the current job from the progress service
41+
/// </summary>
42+
void RemoveProgress();
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Concurrent;
6+
using System.Collections.Generic;
7+
using System.Diagnostics;
8+
using System.Linq;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
using Newtonsoft.Json.Linq;
12+
13+
namespace Silk.NET.SilkTouch.Logging
14+
{
15+
/// <summary>
16+
/// Default Implementation for ProgressService
17+
/// </summary>
18+
public class ProgressService : IProgressService
19+
{
20+
private ConcurrentDictionary<string, (string, float)> Progress = new ConcurrentDictionary<string, (string, float)>();
21+
private JobContext _jobContext;
22+
23+
/// <summary>
24+
/// creates an instance of <see cref="ProgressService"/>
25+
/// </summary>
26+
/// <param name="context">the current job context</param>
27+
public ProgressService(JobContext context)
28+
{
29+
_jobContext = context;
30+
}
31+
32+
/// <inheritdoc/>
33+
public IEnumerable<KeyValuePair<string, (string, float)>> GetAllProgress() => Progress;
34+
/// <inheritdoc/>
35+
public (string, float) GetCurrentTaskAndProgress() => Progress.TryGetValue(_jobContext.JobKey ?? string.Empty, out var value) ? value : (string.Empty, 0);
36+
/// <inheritdoc/>
37+
public void RemoveProgress() => Progress.TryRemove(_jobContext.JobKey ?? string.Empty, out _);
38+
/// <inheritdoc/>
39+
public void SetProgress(float progress)
40+
{
41+
if (_jobContext.JobKey is null)
42+
{
43+
return;
44+
}
45+
46+
if (!Progress.TryGetValue(_jobContext.JobKey, out var value))
47+
{
48+
value = (string.Empty, 0);
49+
}
50+
51+
Progress[_jobContext.JobKey] = (value.Item1, progress);
52+
}
53+
/// <inheritdoc/>
54+
public void SetTask(string task)
55+
{
56+
if (_jobContext.JobKey is null)
57+
{
58+
return;
59+
}
60+
61+
Progress[_jobContext.JobKey] = (task, 0);
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)