Skip to content

Commit 602a481

Browse files
committed
chore(app-launch): Apply PR review suggestions
1 parent e58847c commit 602a481

File tree

8 files changed

+114
-160
lines changed

8 files changed

+114
-160
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net;
4+
using System.Net.Http;
5+
using Microsoft.Build.Framework;
6+
using Microsoft.Build.Utilities;
7+
8+
namespace Uno.Sdk.Tasks;
9+
10+
public sealed class UnoNotifyAppLaunchToDevServer_v0 : Task
11+
{
12+
[Required] public string Port { get; set; } = string.Empty;
13+
[Required] public string TargetPath { get; set; } = string.Empty;
14+
public string IsDebug { get; set; } = string.Empty;
15+
public string Ide { get; set; } = string.Empty;
16+
public string Plugin { get; set; } = string.Empty;
17+
18+
[Output] public bool Success { get; set; }
19+
[Output] public string ResponseContent { get; set; } = string.Empty;
20+
21+
public override bool Execute()
22+
{
23+
// Validate
24+
if (string.IsNullOrWhiteSpace(Port) || !ushort.TryParse(Port, out var portNum) || portNum == 0)
25+
{
26+
Log.LogError("UnoRemoteControlPort must be a valid port number between 1 and 65535.");
27+
return Success = false;
28+
}
29+
if (string.IsNullOrWhiteSpace(TargetPath))
30+
{
31+
Log.LogError("TargetPath is required.");
32+
return Success = false;
33+
}
34+
35+
var encodedPath = WebUtility.UrlEncode(TargetPath);
36+
37+
var parts = new List<string>();
38+
void Add(string name, string value)
39+
{
40+
if (!string.IsNullOrEmpty(value))
41+
parts.Add($"{name}={Uri.EscapeDataString(value)}");
42+
}
43+
44+
Add("ide", Ide);
45+
Add("plugin", Plugin);
46+
47+
// IsDebug is optional: treat empty/null as "false"
48+
var isDebugValue = false;
49+
if (!string.IsNullOrEmpty(IsDebug) && bool.TryParse(IsDebug, out var parsed))
50+
{
51+
isDebugValue = parsed;
52+
}
53+
Add("isDebug", isDebugValue ? "true" : "false");
54+
55+
var qs = parts.Count > 0 ? "?" + string.Join("&", parts) : string.Empty;
56+
var url = $"http://localhost:{portNum}/applaunch/asm/{encodedPath}{qs}";
57+
58+
try
59+
{
60+
using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
61+
var response = client.GetAsync(url).GetAwaiter().GetResult();
62+
63+
Log.LogMessage(MessageImportance.High,
64+
$"[NotifyDevServer] GET {url} -> {(int)response.StatusCode} {response.ReasonPhrase}");
65+
66+
if (response.IsSuccessStatusCode)
67+
{
68+
ResponseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
69+
Success = true;
70+
return true;
71+
}
72+
else
73+
{
74+
Success = false;
75+
ResponseContent = string.Empty;
76+
return false;
77+
}
78+
}
79+
catch (Exception ex)
80+
{
81+
Log.LogWarning($"[NotifyDevServer] GET {url} failed: {ex.GetType().Name}: {ex.Message}");
82+
Success = false;
83+
ResponseContent = string.Empty;
84+
return false;
85+
}
86+
}
87+
}

src/Uno.Sdk/targets/Uno.AppLaunch.targets

Lines changed: 4 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -31,109 +31,8 @@
3131
<UnoNotifyAppLaunchDependsOn>$(UnoNotifyAppLaunchDependsOn);GetTargetPath</UnoNotifyAppLaunchDependsOn>
3232
</PropertyGroup>
3333

34-
<UsingTask TaskName="UnoNotifyAppLaunchToDevServer"
35-
TaskFactory="RoslynCodeTaskFactory"
36-
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
37-
<ParameterGroup>
38-
<Port ParameterType="System.String" Required="true"/>
39-
<TargetPath ParameterType="System.String" Required="true"/>
40-
<Ide ParameterType="System.String" Required="false"/>
41-
<Plugin ParameterType="System.String" Required="false"/>
42-
<IsDebug ParameterType="System.String" Required="false"/>
43-
<Success ParameterType="System.Boolean" Output="true"/>
44-
<ResponseContent ParameterType="System.String" Output="true"/>
45-
</ParameterGroup>
46-
<Task>
47-
<Code Type="Class" Language="cs"><![CDATA[
48-
using System;
49-
using System.Collections.Generic;
50-
using System.Net;
51-
using System.Net.Http;
52-
using Microsoft.Build.Framework;
53-
using Microsoft.Build.Utilities;
54-
55-
public sealed class UnoNotifyAppLaunchToDevServer : Task
56-
{
57-
[Required] public string Port { get; set; } = string.Empty;
58-
[Required] public string TargetPath { get; set; } = string.Empty;
59-
public string IsDebug { get; set; } = string.Empty;
60-
public string Ide { get; set; } = string.Empty;
61-
public string Plugin { get; set; } = string.Empty;
62-
63-
[Output] public bool Success { get; set; }
64-
[Output] public string ResponseContent { get; set; } = string.Empty;
65-
66-
public override bool Execute()
67-
{
68-
// Validate
69-
if (string.IsNullOrWhiteSpace(Port) || !ushort.TryParse(Port, out var portNum) || portNum == 0)
70-
{
71-
Log.LogError("UnoRemoteControlPort must be a valid port number between 1 and 65535.");
72-
return Success = false;
73-
}
74-
if (string.IsNullOrWhiteSpace(TargetPath))
75-
{
76-
Log.LogError("TargetPath is required.");
77-
return Success = false;
78-
}
79-
80-
var encodedPath = WebUtility.UrlEncode(TargetPath);
81-
82-
var parts = new List<string>();
83-
void Add(string name, string value)
84-
{
85-
if (!string.IsNullOrEmpty(value))
86-
parts.Add($"{name}={Uri.EscapeDataString(value)}");
87-
}
88-
89-
Add("ide", Ide);
90-
Add("plugin", Plugin);
91-
92-
// IsDebug is optional: treat empty/null as "false"
93-
var isDebugValue = false;
94-
if (!string.IsNullOrEmpty(IsDebug) && bool.TryParse(IsDebug, out var parsed))
95-
{
96-
isDebugValue = parsed;
97-
}
98-
Add("isDebug", isDebugValue ? "true" : "false");
99-
100-
var qs = parts.Count > 0 ? "?" + string.Join("&", parts) : string.Empty;
101-
var url = $"http://localhost:{portNum}/applaunch/asm/{encodedPath}{qs}";
102-
103-
try
104-
{
105-
using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
106-
var response = client.GetAsync(url).GetAwaiter().GetResult();
107-
108-
Log.LogMessage(MessageImportance.High,
109-
$"[NotifyDevServer] GET {url} -> {(int)response.StatusCode} {response.ReasonPhrase}");
110-
111-
if (response.IsSuccessStatusCode)
112-
{
113-
ResponseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
114-
Success = true;
115-
return true;
116-
}
117-
else
118-
{
119-
Success = false;
120-
ResponseContent = string.Empty;
121-
return false;
122-
}
123-
}
124-
catch (Exception ex)
125-
{
126-
Log.LogWarning($"[NotifyDevServer] GET {url} failed: {ex.GetType().Name}: {ex.Message}");
127-
Success = false;
128-
ResponseContent = string.Empty;
129-
return false;
130-
}
131-
}
132-
}
133-
]]></Code>
134-
</Task>
135-
</UsingTask>
136-
34+
<UsingTask TaskName="Uno.Sdk.Tasks.UnoNotifyAppLaunchToDevServer_v0"
35+
AssemblyFile="$(MSBuildThisFileDirectory)netstandard2.0\Uno.Sdk_v0.dll" />
13736
<Target Name="UnoNotifyAppLaunch"
13837
DependsOnTargets="$(UnoNotifyAppLaunchDependsOn)"
13938
Condition="'$(TargetFramework)'!=''">
@@ -151,15 +50,15 @@
15150
Condition="!Exists('$(TargetPath)')"/>
15251

15352
<!-- Pass the new properties -->
154-
<UnoNotifyAppLaunchToDevServer
53+
<UnoNotifyAppLaunchToDevServer_v0
15554
Port="$(UnoRemoteControlPort)"
15655
TargetPath="$(TargetPath)"
15756
Ide="$(Ide)"
15857
Plugin="$(Plugin)"
15958
IsDebug="$(IsDebug)">
16059
<Output TaskParameter="Success" PropertyName="_UnoLaunchSuccess"/>
16160
<Output TaskParameter="ResponseContent" PropertyName="UnoNotifyAppLaunchHttpResponse"/>
162-
</UnoNotifyAppLaunchToDevServer>
61+
</UnoNotifyAppLaunchToDevServer_v0>
16362

16463
<Error Text="Failed to notify devserver for assembly: $(TargetPath)" Importance="High" Condition="'$(_UnoLaunchSuccess)' != 'True'"/>
16564

src/Uno.UI.RemoteControl.DevServer.Tests/Helpers/DevServerTestHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public Guid? IdeChannelId
6262
{
6363
get
6464
{
65-
if (_environmentVariables?.TryGetValue("UNO_PLATFORM_DEVSERVER_ideChannel", out var v) is true)
65+
if (_environmentVariables?.TryGetValue("UNO_DEVSERVER_ideChannel", out var v) is true)
6666
{
6767
return Guid.TryParse(v, out var g) ? g : null;
6868
}

src/Uno.UI.RemoteControl.DevServer.Tests/Telemetry/TelemetryTestBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ protected DevServerTestHelper CreateTelemetryHelperWithExactPath(string exactFil
9494
if (enableIdeChannel)
9595
{
9696
// Create an IDE channel GUID so the dev-server will initialize the named-pipe IDE channel
97-
envVars["UNO_PLATFORM_DEVSERVER_ideChannel"] = Guid.NewGuid().ToString();
97+
envVars["UNO_DEVSERVER_ideChannel"] = Guid.NewGuid().ToString();
9898
}
9999

100100
return new DevServerTestHelper(

src/Uno.UI.RemoteControl.Host/Extensibility/AddIns.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,11 @@ public static AddInsDiscoveryResult Discover(string solutionFile, ITelemetry? te
5050
else
5151
{
5252
var binlog = Path.GetTempFileName();
53+
// Important: the output file extension must be .binlog, otherwise the build will fail.
5354
result = ProcessHelper.RunProcess("dotnet", DumpTFM($"\"-bl:{binlog}.binlog\""), wd);
5455

5556
_log.Log(LogLevel.Warning, msg);
56-
if (result.error is { Length: > 0 })
57-
{
58-
_log.Log(LogLevel.Debug, $"Error details: {result.error}");
59-
}
57+
_log.Log(LogLevel.Debug, $"Error details: {result.output}");
6058
}
6159
}
6260

src/Uno.UI.RemoteControl.Host/Extensibility/AddInsExtensions.cs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,10 @@ namespace Uno.UI.RemoteControl.Host.Extensibility;
1212

1313
public static class AddInsExtensions
1414
{
15-
public static IWebHostBuilder ConfigureAddIns(this IWebHostBuilder builder, string solutionFile, ITelemetry? telemetry = null)
16-
{
17-
return builder.ConfigureServices(services =>
18-
{
19-
var discovery = AddIns.Discover(solutionFile, telemetry);
20-
var loadResults = AssemblyHelper.Load(discovery.AddIns, telemetry, throwIfLoadFailed: false);
21-
22-
var assemblies = loadResults
23-
.Where(result => result.Assembly is not null)
24-
.Select(result => result.Assembly)
25-
.ToImmutableArray();
26-
27-
services.AddFromAttributes(assemblies);
28-
services.AddSingleton(new AddInsStatus(discovery, loadResults));
29-
});
30-
}
31-
3215
public static WebApplicationBuilder ConfigureAddIns(this WebApplicationBuilder builder, string solutionFile, ITelemetry? telemetry = null)
3316
{
17+
// TODO: Move this to new pattern with a .AddAddIns() method.
18+
3419
var discovery = AddIns.Discover(solutionFile, telemetry);
3520
var loadResults = AssemblyHelper.Load(discovery.AddIns, telemetry, throwIfLoadFailed: false);
3621

src/Uno.UI.RemoteControl.Host/IDEChannel/IdeChannelServer.cs

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,24 @@ internal class IdeChannelServer : IIdeChannel, IDisposable
2424
private NamedPipeServerStream? _pipeServer;
2525
private JsonRpc? _rpcServer;
2626
private Proxy? _proxy;
27+
private readonly Timer _keepAliveTimer;
2728

2829
public IdeChannelServer(ILogger<IdeChannelServer> logger, IOptionsMonitor<IdeChannelServerOptions> config)
2930
{
3031
_logger = logger;
3132

33+
_keepAliveTimer = new Timer(_ =>
34+
{
35+
if (_pipeServer?.IsConnected ?? false)
36+
{
37+
SendKeepAlive();
38+
}
39+
else
40+
{
41+
_keepAliveTimer!.Change(Timeout.Infinite, Timeout.Infinite);
42+
}
43+
});
44+
3245
_initializeTask = Task.Run(() => InitializeServer(config.CurrentValue.ChannelId));
3346
_configSubscription = config.OnChange(opts => _initializeTask = InitializeServer(opts.ChannelId));
3447
}
@@ -131,44 +144,18 @@ private async Task<bool> InitializeServer(Guid channelId)
131144
}
132145

133146
private const int KeepAliveDelay = 10000; // 10 seconds in milliseconds
134-
private Timer? _keepAliveTimer;
135-
136-
private void ScheduleKeepAlive()
137-
{
138-
// Ensure a single periodic timer is running; dispose any previous instance safely
139-
var oldTimer = Interlocked.Exchange(ref _keepAliveTimer, null);
140-
oldTimer?.Dispose();
141-
142-
// Start a periodic keep-alive timer. If the pipe disconnects, stop the timer.
143-
var timer = new Timer(_ =>
144-
{
145-
if (_pipeServer?.IsConnected ?? false)
146-
{
147-
SendKeepAlive();
148-
}
149-
else
150-
{
151-
Interlocked.Exchange(ref _keepAliveTimer, null)?.Dispose();
152-
}
153-
}, null, KeepAliveDelay, KeepAliveDelay);
154147

155-
// Publish the new timer instance; if another thread already set one, dispose this one
156-
if (Interlocked.CompareExchange(ref _keepAliveTimer, timer, null) is not null)
157-
{
158-
timer.Dispose();
159-
}
160-
}
148+
private void ScheduleKeepAlive() => _keepAliveTimer.Change(KeepAliveDelay, KeepAliveDelay);
161149

162150
private void SendKeepAlive() => _proxy?.SendToIde(new KeepAliveIdeMessage("dev-server"));
163151

164-
165152
/// <inheritdoc />
166153
public void Dispose()
167154
{
155+
_keepAliveTimer.Dispose();
168156
_configSubscription?.Dispose();
169157
_rpcServer?.Dispose();
170158
_pipeServer?.Dispose();
171-
Interlocked.Exchange(ref _keepAliveTimer, null)?.Dispose();
172159
}
173160

174161
private class Proxy(IdeChannelServer Owner) : IIdeChannelServer

src/Uno.UI.RemoteControl.Host/Program.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,15 @@ static async Task Main(string[] args)
184184
.ConfigureAppConfiguration((hostingContext, config) =>
185185
{
186186
config.AddCommandLine(args);
187-
config.AddEnvironmentVariables("UNO_PLATFORM_DEVSERVER_");
187+
config.AddEnvironmentVariables("UNO_DEVSERVER_");
188188
})
189189
.ConfigureServices(services =>
190190
{
191-
services.AddSingleton<IIdeChannel, IdeChannelServer>();
192-
services.AddSingleton<UnoDevEnvironmentService>();
193191
services.AddRouting();
194192
services.Configure<RemoteControlOptions>(builder.Configuration);
195193
});
196194

197-
builder.Services.AddSingleton<IIdeChannel, IdeChannelServer>();
195+
builder.Services.AddSingleton<IIdeChannel>(_ => globalServiceProvider.GetRequiredService<IIdeChannel>());
198196
builder.Services.AddSingleton<UnoDevEnvironmentService>();
199197

200198
builder.Services.AddSingleton<ApplicationLaunchMonitor>(

0 commit comments

Comments
 (0)