diff --git a/.vsts-ci.yml b/.vsts-ci.yml
index 1b2696c845e3..fc175297e72c 100644
--- a/.vsts-ci.yml
+++ b/.vsts-ci.yml
@@ -291,6 +291,19 @@ extends:
publishArgument: $(_publishArgument)
officialBuildProperties: $(_officialBuildProperties)
runTests: false
+ ### ARM64 TESTBUILD ###
+ - ${{ if or(eq(parameters.runTestBuild, true), eq(variables['Build.Reason'], 'PullRequest')) }}:
+ - template: /eng/pipelines/templates/jobs/sdk-job-matrix.yml@self
+ parameters:
+ pool:
+ name: Azure Pipelines
+ vmImage: macOS-latest
+ os: macOS
+ helixTargetQueue: osx.13.arm64
+ macOSJobParameterSets:
+ - categoryName: TestBuild
+ buildArchitecture: arm64
+ runtimeIdentifier: osx-arm64
############### SOURCE BUILD ###############
- template: /eng/common/templates-official/job/source-build.yml@self
diff --git a/.vsts-pr.yml b/.vsts-pr.yml
index 838aa699d3f3..26169a0071f0 100644
--- a/.vsts-pr.yml
+++ b/.vsts-pr.yml
@@ -63,6 +63,18 @@ stages:
vmImage: macOS-latest
os: macOS
helixTargetQueue: osx.13.amd64.open
+ ### ARM64 ###
+ - template: /eng/pipelines/templates/jobs/sdk-job-matrix.yml
+ parameters:
+ pool:
+ name: Azure Pipelines
+ vmImage: macOS-latest
+ os: macOS
+ helixTargetQueue: osx.13.arm64.open
+ macOSJobParameterSets:
+ - categoryName: TestBuild
+ buildArchitecture: arm64
+ runtimeIdentifier: osx-arm64
############### SOURCE BUILD ###############
- template: /eng/common/templates/job/source-build.yml@self
diff --git a/eng/ManualVersions.props b/eng/ManualVersions.props
index 2b773d3a8285..e0f285d96bdc 100644
--- a/eng/ManualVersions.props
+++ b/eng/ManualVersions.props
@@ -9,20 +9,20 @@
Basically: In this file, choose the highest version when resolving merge conflicts.
-->
- 10.0.17763.45
- 10.0.18362.45
- 10.0.19041.45
- 10.0.20348.45
- 10.0.22000.45
- 10.0.22621.45
- 10.0.26100.45
- 10.0.17763.43
- 10.0.18362.43
- 10.0.19041.43
- 10.0.20348.43
- 10.0.22000.43
- 10.0.22621.43
- 10.0.26100.43
+ 10.0.17763.54
+ 10.0.18362.54
+ 10.0.19041.54
+ 10.0.20348.54
+ 10.0.22000.54
+ 10.0.22621.54
+ 10.0.26100.54
+ 10.0.17763.52
+ 10.0.18362.52
+ 10.0.19041.52
+ 10.0.20348.52
+ 10.0.22000.52
+ 10.0.22621.52
+ 10.0.26100.52
diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml
index 818cae010e57..6eb5b3de6f7e 100644
--- a/eng/SourceBuildPrebuiltBaseline.xml
+++ b/eng/SourceBuildPrebuiltBaseline.xml
@@ -37,6 +37,9 @@
+
+
+
diff --git a/eng/Versions.props b/eng/Versions.props
index 400fa11fe0ca..087dd733528c 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -14,7 +14,6 @@
$(VersionMajor).$(VersionMinor).$(VersionSDKMinor)$(VersionFeature)$(VersionMajor).$(VersionMinor)$(MajorMinorVersion).$(VersionSDKMinor)
- 8.0.100falserelease
@@ -193,7 +192,9 @@
Some .NET Framework tasks and the resolver will need to run in a VS/MSBuild that is older
than the very latest, based on what we want the SDK to support. So use a version that matches the version
- in minimumMSBuildVersion. In these cases, we don't want to use MicrosoftBuildVersion and other
+ in minimumMSBuildVersion. Note that MSBuild has started versioning before release so the version we use as the Minimum should be .0
+ to ensure we load in VS but the version we build against should be the version of MSBuild that ships in the .0 VS release.
+ In these cases, we don't want to use MicrosoftBuildVersion and other
associated properties that are updated by the VMR infrastructure. So, we read this version
from the 'minimumMSBuildVersion' file in non-source-only cases into MicrosoftBuildMinimumVersion,
then use that in Directory.Packages.props.
@@ -201,7 +202,8 @@
At usage sites, either we use MicrosoftBuildMinimumVersion, or MicrosoftBuildVersion in source-only modes. -->
17.13.0-preview-24604-0417.13.0-preview-24604-04
- $([System.IO.File]::ReadAllText('$(RepoRoot)src\Layout\redist\minimumMSBuildVersion').Trim())
+ 17.11.4
+ 17.12
diff --git a/eng/common/core-templates/steps/get-delegation-sas.yml b/eng/common/core-templates/steps/get-delegation-sas.yml
index d2901470a7f0..9db5617ea7de 100644
--- a/eng/common/core-templates/steps/get-delegation-sas.yml
+++ b/eng/common/core-templates/steps/get-delegation-sas.yml
@@ -31,7 +31,16 @@ steps:
# Calculate the expiration of the SAS token and convert to UTC
$expiry = (Get-Date).AddHours(${{ parameters.expiryInHours }}).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
- $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv
+ # Temporarily work around a helix issue where SAS tokens with / in them will cause incorrect downloads
+ # of correlation payloads. https://github.com/dotnet/dnceng/issues/3484
+ $sas = ""
+ do {
+ $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv
+ if ($LASTEXITCODE -ne 0) {
+ Write-Error "Failed to generate SAS token."
+ exit 1
+ }
+ } while($sas.IndexOf('/') -ne -1)
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to generate SAS token."
diff --git a/sdk.sln b/sdk.sln
index b8659201cec7..c040f0225773 100644
--- a/sdk.sln
+++ b/sdk.sln
@@ -1150,6 +1150,7 @@ Global
src\Compatibility\ApiCompat\Microsoft.DotNet.ApiCompat.Shared\Microsoft.DotNet.ApiCompat.Shared.projitems*{03c5a84a-982b-4f38-ac73-ab832c645c4a}*SharedItemsImports = 5
src\Compatibility\ApiCompat\Microsoft.DotNet.ApiCompat.Shared\Microsoft.DotNet.ApiCompat.Shared.projitems*{0a3c9afd-f6e6-4a5d-83fb-93bf66732696}*SharedItemsImports = 5
src\BuiltInTools\AspireService\Microsoft.WebTools.AspireService.projitems*{1f0b4b3c-dc88-4740-b04f-1707102e9930}*SharedItemsImports = 5
+ src\BuiltInTools\AspireService\Microsoft.WebTools.AspireService.projitems*{445efbd5-6730-4f09-943d-278e77501ffd}*SharedItemsImports = 5
src\BuiltInTools\AspireService\Microsoft.WebTools.AspireService.projitems*{94c8526e-dcc2-442f-9868-3dd0ba2688be}*SharedItemsImports = 13
src\Compatibility\ApiCompat\Microsoft.DotNet.ApiCompat.Shared\Microsoft.DotNet.ApiCompat.Shared.projitems*{9d36039f-d0a1-462f-85b4-81763c6b02cb}*SharedItemsImports = 13
src\Compatibility\ApiCompat\Microsoft.DotNet.ApiCompat.Shared\Microsoft.DotNet.ApiCompat.Shared.projitems*{a9103b98-d888-4260-8a05-fa36f640698a}*SharedItemsImports = 5
diff --git a/src/BuiltInTools/AspireService/AspireServerService.cs b/src/BuiltInTools/AspireService/AspireServerService.cs
index 3b620131f73f..d3450d65ad18 100644
--- a/src/BuiltInTools/AspireService/AspireServerService.cs
+++ b/src/BuiltInTools/AspireService/AspireServerService.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Collections.Immutable;
using System.Net;
using System.Net.WebSockets;
using System.Security.Cryptography;
@@ -11,9 +12,11 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
using Microsoft.WebTools.AspireServer.Contracts;
using Microsoft.WebTools.AspireServer.Helpers;
using Microsoft.WebTools.AspireServer.Models;
+using Microsoft.WebTools.AspireService.Helpers;
using IAsyncDisposable = System.IAsyncDisposable;
namespace Microsoft.WebTools.AspireServer;
@@ -32,7 +35,7 @@ internal partial class AspireServerService : IAsyncDisposable
private readonly IAspireServerEvents _aspireServerEvents;
- private readonly Action? _tracer;
+ private readonly Action? _reporter;
private readonly string _currentSecret;
private readonly string _displayName;
@@ -46,8 +49,11 @@ internal partial class AspireServerService : IAsyncDisposable
private readonly SocketConnectionManager _socketConnectionManager = new();
+ private volatile bool _isDisposed;
+
private static readonly char[] s_charSeparator = { ' ' };
- private int _isListening;
+
+ private readonly Task _requestListener;
public static readonly JsonSerializerOptions JsonSerializerOptions = new()
{
@@ -59,10 +65,10 @@ internal partial class AspireServerService : IAsyncDisposable
}
};
- public AspireServerService(IAspireServerEvents aspireServerEvents, string displayName, Action? tracer)
+ public AspireServerService(IAspireServerEvents aspireServerEvents, string displayName, Action? reporter)
{
_aspireServerEvents = aspireServerEvents;
- _tracer = tracer;
+ _reporter = reporter;
_displayName = displayName;
_port = SocketUtilities.GetNextAvailablePort();
@@ -79,83 +85,97 @@ public AspireServerService(IAspireServerEvents aspireServerEvents, string displa
var certBytes = _certificate.Export(X509ContentType.Cert);
_certificateEncodedBytes = Convert.ToBase64String(certBytes);
- // Start the server
- Initialize();
+ // Kick of the web server.
+ _requestListener = StartListening();
}
- ///
- public ValueTask>> GetServerConnectionEnvironmentAsync(CancellationToken cancelToken)
+ public async ValueTask DisposeAsync()
{
- return new ValueTask>>(new List>
- {
- new KeyValuePair(DebugSessionPortEnvVar,$"localhost:{_port}"),
- new KeyValuePair(DebugSessionTokenEnvVar, _currentSecret),
- new KeyValuePair(DebugSessionServerCertEnvVar, _certificateEncodedBytes),
- });
- }
+ // Shutdown the service:
+ _shutdownCancellationTokenSource.Cancel();
- public async ValueTask SessionEndedAsync(string dcpId, string sessionId, int processId, int? exitCode, CancellationToken cancelToken)
- {
- var payload = new SessionChangeNotification()
- {
- NotificationType = NotificationType.SessionTerminated,
- SessionId = sessionId,
- PID = processId,
- ExitCode = exitCode
- };
+ Log("Waiting for server to shutdown ...");
try
{
- LogTrace($"Sending SessionEndedAsync for session {sessionId}");
- var jsonSerialized = JsonSerializer.SerializeToUtf8Bytes(payload, JsonSerializerOptions);
- await SendMessageAsync(dcpId, jsonSerialized, cancelToken);
+ await _requestListener;
}
- catch (Exception ex)
+ catch (OperationCanceledException)
{
- // Send messageAsync can fail if the connection is lost
- LogTrace($"Sending session ended failed: {ex}");
+ // nop
}
+
+ _isDisposed = true;
+
+ _socketConnectionManager.Dispose();
+ _certificate.Dispose();
+ _shutdownCancellationTokenSource.Dispose();
}
- public async ValueTask SessionStartedAsync(string dcpId, string sessionId, int processId, CancellationToken cancelToken)
+ ///
+ public List> GetServerConnectionEnvironment()
+ =>
+ [
+ new(DebugSessionPortEnvVar, $"localhost:{_port}"),
+ new(DebugSessionTokenEnvVar, _currentSecret),
+ new(DebugSessionServerCertEnvVar, _certificateEncodedBytes),
+ ];
+
+ public ValueTask NotifySessionEndedAsync(string dcpId, string sessionId, int processId, int? exitCode, CancellationToken cancelationToken)
+ => SendNotificationAsync(
+ new SessionTerminatedNotification()
+ {
+ NotificationType = NotificationType.SessionTerminated,
+ SessionId = sessionId,
+ Pid = processId,
+ ExitCode = exitCode
+ },
+ dcpId,
+ sessionId,
+ cancelationToken);
+
+ public ValueTask NotifySessionStartedAsync(string dcpId, string sessionId, int processId, CancellationToken cancelationToken)
+ => SendNotificationAsync(
+ new ProcessRestartedNotification()
+ {
+ NotificationType = NotificationType.ProcessRestarted,
+ SessionId = sessionId,
+ PID = processId
+ },
+ dcpId,
+ sessionId,
+ cancelationToken);
+
+ public ValueTask NotifyLogMessageAsync(string dcpId, string sessionId, bool isStdErr, string data, CancellationToken cancelationToken)
+ => SendNotificationAsync(
+ new ServiceLogsNotification()
+ {
+ NotificationType = NotificationType.ServiceLogs,
+ SessionId = sessionId,
+ IsStdErr = isStdErr,
+ LogMessage = data
+ },
+ dcpId,
+ sessionId,
+ cancelationToken);
+
+ private async ValueTask SendNotificationAsync(TNotification notification, string dcpId, string sessionId, CancellationToken cancelationToken)
+ where TNotification : SessionNotification
{
- var payload = new SessionChangeNotification()
- {
- NotificationType = NotificationType.ProcessRestarted,
- SessionId = sessionId,
- PID = processId
- };
-
try
{
- LogTrace($"Sending SessionStartedAsync for session {sessionId}");
- var jsonSerialized = JsonSerializer.SerializeToUtf8Bytes(payload, JsonSerializerOptions);
- await SendMessageAsync(dcpId, jsonSerialized, cancelToken);
+ Log($"[#{sessionId}] Sending '{notification.NotificationType}'");
+ var jsonSerialized = JsonSerializer.SerializeToUtf8Bytes(notification, JsonSerializerOptions);
+ await SendMessageAsync(dcpId, jsonSerialized, cancelationToken);
}
- catch (Exception ex)
+ catch (Exception e) when (LogAndPropagate(e))
{
- LogTrace($"Sending session started failed: {ex}");
}
- }
-
- public async ValueTask SendLogMessageAsync(string dcpId, string sessionID, bool isStdErr, string data, CancellationToken cancelToken)
- {
- var payload = new SessionLogsNotification()
- {
- NotificationType = NotificationType.ServiceLogs,
- SessionId = sessionID,
- IsStdErr = isStdErr,
- LogMessage = data
- };
- try
+ bool LogAndPropagate(Exception e)
{
- var jsonSerialized = JsonSerializer.SerializeToUtf8Bytes(payload, JsonSerializerOptions);
- await SendMessageAsync(dcpId, jsonSerialized, cancelToken);
- }
- catch (Exception ex)
- {
- LogTrace($"Sending service logs failed {ex}");
+ Log($"[#{sessionId}] Sending '{notification.NotificationType}' failed: {e.Message}");
+ return false;
}
}
@@ -163,7 +183,7 @@ public async ValueTask SendLogMessageAsync(string dcpId, string sessionID, bool
/// Waits for a connection so that it can get the WebSocket that will be used to send messages tio the client. It accepts messages via Restful http
/// calls.
///
- private void StartListening()
+ private Task StartListening()
{
var builder = WebApplication.CreateSlimBuilder();
@@ -175,6 +195,12 @@ private void StartListening()
});
});
+ if (_reporter != null)
+ {
+ builder.Logging.ClearProviders();
+ builder.Logging.AddProvider(new LoggerProvider(_reporter));
+ }
+
var app = builder.Build();
app.MapGet("/", () => _displayName);
@@ -185,7 +211,7 @@ private void StartListening()
runSessionApi.MapPut("/", RunSessionPutAsync);
runSessionApi.MapDelete("/{sessionId}", RunSessionDeleteAsync);
- runSessionApi.Map(SessionNotificationBase.Url, RunSessionNotifyAsync);
+ runSessionApi.Map(SessionNotification.Url, RunSessionNotifyAsync);
app.UseWebSockets(new WebSocketOptions
{
@@ -193,7 +219,7 @@ private void StartListening()
});
// Run the application async. It will shutdown when the cancel token is signaled
- _ = app.RunAsync(_shutdownCancellationTokenSource.Token);
+ return app.RunAsync(_shutdownCancellationTokenSource.Token);
}
private async Task RunSessionPutAsync(HttpContext context)
@@ -201,12 +227,12 @@ private async Task RunSessionPutAsync(HttpContext context)
// Check the authentication header
if (!IsValidAuthentication(context))
{
- LogTrace("Authorization failure");
+ Log("Authorization failure");
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
- await ProcessStartSessionRequestAsync(context);
+ await HandleStartSessionRequestAsync(context);
}
}
@@ -215,12 +241,12 @@ private async Task RunSessionDeleteAsync(HttpContext context, string sessionId)
// Check the authentication header
if (!IsValidAuthentication(context))
{
- LogTrace("Authorization failure");
+ Log("Authorization failure");
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
- context.Response.StatusCode = await HandleStopSessionRequestAsync(context.GetDcpId(), sessionId);
+ await HandleStopSessionRequestAsync(context, sessionId);
}
}
@@ -229,7 +255,7 @@ private async Task GetInfoAsync(HttpContext context)
// Check the authentication header
if (!IsValidAuthentication(context))
{
- LogTrace("Authorization failure");
+ Log("Authorization failure");
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
@@ -244,7 +270,7 @@ private async Task RunSessionNotifyAsync(HttpContext context)
// Check the authentication header
if (!IsValidAuthentication(context))
{
- LogTrace("Authorization failure");
+ Log("Authorization failure");
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
return;
}
@@ -264,35 +290,9 @@ private async Task RunSessionNotifyAsync(HttpContext context)
await socketTcs.Task;
}
- private void LogTrace(string traceMsg)
- {
- _tracer?.Invoke($"AspireServer - {traceMsg}");
- }
-
- ///
- /// starts the web server running
- ///
- private void Initialize()
- {
- if (Interlocked.CompareExchange(ref _isListening, 1, 0) == 1)
- {
- return;
- }
-
- // Kick of the web server.
- StartListening();
- }
-
- public ValueTask DisposeAsync()
+ private void Log(string message)
{
- _socketConnectionManager.Dispose();
-
- _certificate.Dispose();
-
- // Shutdown the app
- _shutdownCancellationTokenSource.Cancel();
- _shutdownCancellationTokenSource.Dispose();
- return ValueTask.CompletedTask;
+ _reporter?.Invoke(message);
}
private bool IsValidAuthentication(HttpContext context)
@@ -311,29 +311,39 @@ private bool IsValidAuthentication(HttpContext context)
return false;
}
- private async Task ProcessStartSessionRequestAsync(HttpContext context)
+ private async Task HandleStartSessionRequestAsync(HttpContext context)
{
- // Get the project launch request data
- var projectLaunchRequest = await context.GetProjectLaunchInformationAsync(_shutdownCancellationTokenSource.Token);
- if (projectLaunchRequest is not null)
+ string? projectPath = null;
+
+ try
{
- try
+ if (_isDisposed)
{
- var sessionId = await LaunchProjectAsync(context.GetDcpId(), projectLaunchRequest);
- context.Response.StatusCode = (int)HttpStatusCode.Created;
- context.Response.Headers.Location = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}/{sessionId}";
+ throw new ObjectDisposedException(nameof(AspireServerService), "Received 'PUT /run_session' request after the service has been disposed.");
}
- catch (Exception ex)
+
+ // Get the project launch request data
+ var projectLaunchRequest = await context.GetProjectLaunchInformationAsync(_shutdownCancellationTokenSource.Token);
+ if (projectLaunchRequest == null)
{
- LogTrace($"Exception thrown starting project {projectLaunchRequest.ProjectPath}: {ex}");
- context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
- await WriteResponseTextAsync(context.Response, ex, context.GetApiVersion() is not null);
+ // Unknown or unsupported version
+ context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
+ return;
}
+
+ projectPath = projectLaunchRequest.ProjectPath;
+
+ var sessionId = await _aspireServerEvents.StartProjectAsync(context.GetDcpId(), projectLaunchRequest, _shutdownCancellationTokenSource.Token);
+
+ context.Response.StatusCode = (int)HttpStatusCode.Created;
+ context.Response.Headers.Location = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}/{sessionId}";
}
- else
+ catch (Exception e)
{
- // Unknown or unsupported version
- context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
+ Log($"Failed to start project{(projectPath == null ? "" : $" '{projectPath}'")}: {e}");
+
+ context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
+ await WriteResponseTextAsync(context.Response, e, context.GetApiVersion() is not null);
}
}
@@ -361,48 +371,58 @@ private async Task WriteResponseTextAsync(HttpResponse response, Exception ex, b
}
}
- private async Task SendMessageAsync(string dcpId,byte[] messageBytes, CancellationToken cancellationToken)
+ private async Task SendMessageAsync(string dcpId, byte[] messageBytes, CancellationToken cancellationToken)
{
// Find the connection for the passed in dcpId
WebSocketConnection? connection = _socketConnectionManager.GetSocketConnection(dcpId);
if (connection is null)
{
// Most likely the connection has already gone away
- LogTrace($"Send message failure: Connection with the following dcpId was not found {dcpId}");
+ Log($"Send message failure: Connection with the following dcpId was not found {dcpId}");
return;
}
+ var success = false;
try
{
- using var cancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _shutdownCancellationTokenSource.Token,
- connection.HttpRequestAborted);
+ using var cancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
+ cancellationToken, _shutdownCancellationTokenSource.Token, connection.HttpRequestAborted);
+
await _webSocketAccess.WaitAsync(cancelTokenSource.Token);
await connection.Socket.SendAsync(new ArraySegment(messageBytes), WebSocketMessageType.Text, endOfMessage: true, cancelTokenSource.Token);
- }
- catch (Exception ex)
- {
- // If the connection throws it almost certainly means the client has gone away, so clean up that connection
- _socketConnectionManager.RemoveSocketConnection(connection);
- LogTrace($"Send message failure: {ex.GetMessageFromException()}");
- throw;
+
+ success = true;
}
finally
{
+ if (!success)
+ {
+ // If the connection throws it almost certainly means the client has gone away, so clean up that connection
+ _socketConnectionManager.RemoveSocketConnection(connection);
+ }
+
_webSocketAccess.Release();
}
}
- private async Task HandleStopSessionRequestAsync(string dcpId, string sessionId)
+ private async ValueTask HandleStopSessionRequestAsync(HttpContext context, string sessionId)
{
- bool sessionExists = await _aspireServerEvents.StopSessionAsync(dcpId, sessionId, _shutdownCancellationTokenSource.Token);
+ try
+ {
+ if (_isDisposed)
+ {
+ throw new ObjectDisposedException(nameof(AspireServerService), "Received 'DELETE /run_session' request after the service has been disposed.");
+ }
- return (int)(sessionExists ? HttpStatusCode.OK : HttpStatusCode.NoContent);
- }
+ var sessionExists = await _aspireServerEvents.StopSessionAsync(context.GetDcpId(), sessionId, _shutdownCancellationTokenSource.Token);
+ context.Response.StatusCode = (int)(sessionExists ? HttpStatusCode.OK : HttpStatusCode.NoContent);
+ }
+ catch (Exception e)
+ {
+ Log($"[#{sessionId}] Failed to stop: {e}");
- ///
- /// Called to launch the project after first creating a LaunchProfile from the sessionRequest object. Returns the sessionId
- /// for the launched process. If it throws an exception most likely the project couldn't be launched
- ///
- private Task LaunchProjectAsync(string dcpId, ProjectLaunchRequest projectLaunchInfo)
- => _aspireServerEvents.StartProjectAsync(dcpId, projectLaunchInfo, _shutdownCancellationTokenSource.Token).AsTask();
+ context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
+ await WriteResponseTextAsync(context.Response, e, context.GetApiVersion() is not null);
+ }
+ }
}
diff --git a/src/BuiltInTools/AspireService/Contracts/IAspireServerEvents.cs b/src/BuiltInTools/AspireService/Contracts/IAspireServerEvents.cs
index d4d30c20663f..1a4b5b6ddee2 100644
--- a/src/BuiltInTools/AspireService/Contracts/IAspireServerEvents.cs
+++ b/src/BuiltInTools/AspireService/Contracts/IAspireServerEvents.cs
@@ -1,30 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
namespace Microsoft.WebTools.AspireServer.Contracts;
-///
-/// Interface implemented on the VS side and pass
-///
internal interface IAspireServerEvents
{
///
- /// Called when a request to stop a session is received. Returns false if the session does not exist. Note that the dcpId identifies
- /// which DCP/AppHost is making the request.
+ /// Called when a request to stop a session is received.
///
- ValueTask StopSessionAsync(string dcpId, string sessionId, CancellationToken cancelToken);
+ /// The id of the session to terminate. The session might have been stopped already.
+ /// DCP/AppHost making the request. May be empty for older DCP versions.
+ /// Returns false if the session is not active.
+ ValueTask StopSessionAsync(string dcpId, string sessionId, CancellationToken cancellationToken);
///
- /// Called when a request to start a project is received. Returns the sessionId of the started project. Note that the dcpId identifies
- /// which DCP/AppHost is making the request. The format of this string is ;. The first token can
- /// be used to identify the AppHost project in the solution. The 2nd is just a unique string so that running the same project multiple times
- /// generates a unique dcpId. Note that for older DCP's the dcpId will be the empty string
+ /// Called when a request to start a project is received. Returns the session id of the started project.
///
- ValueTask StartProjectAsync(string dcpId, ProjectLaunchRequest projectLaunchInfo, CancellationToken cancelToken);
+ /// DCP/AppHost making the request. May be empty for older DCP versions.
+ /// New unique session id.
+ ValueTask StartProjectAsync(string dcpId, ProjectLaunchRequest projectLaunchInfo, CancellationToken cancellationToken);
}
internal class ProjectLaunchRequest
diff --git a/src/BuiltInTools/AspireService/Helpers/LoggerProvider.cs b/src/BuiltInTools/AspireService/Helpers/LoggerProvider.cs
new file mode 100644
index 000000000000..52953e211022
--- /dev/null
+++ b/src/BuiltInTools/AspireService/Helpers/LoggerProvider.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.Logging;
+
+namespace Microsoft.WebTools.AspireService.Helpers;
+
+internal sealed class LoggerProvider(Action reporter) : ILoggerProvider
+{
+ private sealed class Logger(Action reporter) : ILogger
+ {
+ public IDisposable? BeginScope(TState state) where TState : notnull
+ => null;
+
+ public bool IsEnabled(LogLevel logLevel) => true;
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter)
+ {
+ var message = formatter(state, exception);
+ if (!string.IsNullOrEmpty(message))
+ {
+ reporter(message);
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ }
+
+ public Action Reporter
+ => reporter;
+
+ public ILogger CreateLogger(string categoryName)
+ => new Logger(reporter);
+}
diff --git a/src/BuiltInTools/AspireService/Microsoft.WebTools.AspireService.projitems b/src/BuiltInTools/AspireService/Microsoft.WebTools.AspireService.projitems
index 7092eff2441a..2fa482c32f7e 100644
--- a/src/BuiltInTools/AspireService/Microsoft.WebTools.AspireService.projitems
+++ b/src/BuiltInTools/AspireService/Microsoft.WebTools.AspireService.projitems
@@ -14,6 +14,7 @@
+
diff --git a/src/BuiltInTools/AspireService/Models/SessionChangeNotification.cs b/src/BuiltInTools/AspireService/Models/SessionChangeNotification.cs
index 49ad7e790858..d20e38f3360f 100644
--- a/src/BuiltInTools/AspireService/Models/SessionChangeNotification.cs
+++ b/src/BuiltInTools/AspireService/Models/SessionChangeNotification.cs
@@ -13,32 +13,80 @@ internal static class NotificationType
public const string ServiceLogs = "serviceLogs";
}
-internal class SessionNotificationBase
+///
+/// Implements https://github.com/dotnet/aspire/blob/445d2fc8a6a0b7ce3d8cc42def4d37b02709043b/docs/specs/IDE-execution.md#common-notification-properties.
+///
+internal class SessionNotification
{
public const string Url = "/notify";
+ ///
+ /// One of .
+ ///
[Required]
[JsonPropertyName("notification_type")]
- public string NotificationType { get; set; } = string.Empty;
+ public required string NotificationType { get; init; }
+ ///
+ /// The id of the run session that the notification is related to.
+ ///
+ [Required]
[JsonPropertyName("session_id")]
- public string SessionId { get; set; } = string.Empty;
+ public required string SessionId { get; init; }
}
-internal class SessionChangeNotification : SessionNotificationBase
+///
+/// Implements https://github.com/dotnet/aspire/blob/445d2fc8a6a0b7ce3d8cc42def4d37b02709043b/docs/specs/IDE-execution.md#session-terminated-notification.
+/// is .
+///
+internal sealed class SessionTerminatedNotification : SessionNotification
{
+ ///
+ /// The process id of the service process associated with the run session.
+ ///
+ [Required]
[JsonPropertyName("pid")]
- public int PID { get; set; }
+ public required int Pid { get; init; }
+ ///
+ /// The exit code of the process associated with the run session.
+ ///
+ [Required]
[JsonPropertyName("exit_code")]
- public int? ExitCode { get; set; }
+ public required int? ExitCode { get; init; }
+}
+
+///
+/// Implements https://github.com/dotnet/aspire/blob/445d2fc8a6a0b7ce3d8cc42def4d37b02709043b/docs/specs/IDE-execution.md#process-restarted-notification.
+/// is .
+///
+internal sealed class ProcessRestartedNotification : SessionNotification
+{
+ ///
+ /// The process id of the service process associated with the run session.
+ ///
+ [Required]
+ [JsonPropertyName("pid")]
+ public required int PID { get; init; }
}
-internal class SessionLogsNotification : SessionNotificationBase
+///
+/// Implements https://github.com/dotnet/aspire/blob/445d2fc8a6a0b7ce3d8cc42def4d37b02709043b/docs/specs/IDE-execution.md#log-notification
+/// is .
+///
+internal sealed class ServiceLogsNotification : SessionNotification
{
+ ///
+ /// True if the output comes from standard error stream, otherwise false (implying standard output stream).
+ ///
+ [Required]
[JsonPropertyName("is_std_err")]
- public bool IsStdErr { get; set; }
+ public required bool IsStdErr { get; init; }
+ ///
+ /// The text written by the service program.
+ ///
+ [Required]
[JsonPropertyName("log_message")]
- public string LogMessage { get; set; } = string.Empty;
+ public required string LogMessage { get; init; }
}
diff --git a/src/BuiltInTools/dotnet-watch.slnf b/src/BuiltInTools/dotnet-watch.slnf
index 94e287feeca5..5baaf69115c2 100644
--- a/src/BuiltInTools/dotnet-watch.slnf
+++ b/src/BuiltInTools/dotnet-watch.slnf
@@ -11,7 +11,8 @@
"test\\Microsoft.AspNetCore.Watch.BrowserRefresh.Tests\\Microsoft.AspNetCore.Watch.BrowserRefresh.Tests.csproj",
"test\\Microsoft.Extensions.DotNetDeltaApplier.Tests\\Microsoft.Extensions.DotNetDeltaApplier.Tests.csproj",
"test\\Microsoft.NET.TestFramework\\Microsoft.NET.TestFramework.csproj",
- "test\\dotnet-watch.Tests\\dotnet-watch.Tests.csproj"
+ "test\\dotnet-watch.Tests\\dotnet-watch.Tests.csproj",
+ "test\\Microsoft.WebTools.AspireService.Tests\\Microsoft.WebTools.AspireService.Tests.csproj"
]
}
}
\ No newline at end of file
diff --git a/src/BuiltInTools/dotnet-watch/Aspire/AspireServiceFactory.cs b/src/BuiltInTools/dotnet-watch/Aspire/AspireServiceFactory.cs
new file mode 100644
index 000000000000..753d6dd66cfb
--- /dev/null
+++ b/src/BuiltInTools/dotnet-watch/Aspire/AspireServiceFactory.cs
@@ -0,0 +1,262 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Globalization;
+using System.Threading.Channels;
+using Microsoft.Build.Graph;
+using Microsoft.DotNet.Watcher.Internal;
+using Microsoft.DotNet.Watcher.Tools;
+using Microsoft.Extensions.Tools.Internal;
+using Microsoft.WebTools.AspireServer;
+using Microsoft.WebTools.AspireServer.Contracts;
+
+namespace Microsoft.DotNet.Watcher;
+
+internal class AspireServiceFactory : IRuntimeProcessLauncherFactory
+{
+ private sealed class SessionManager : IAspireServerEvents, IRuntimeProcessLauncher
+ {
+ private readonly struct Session(string dcpId, string sessionId, RunningProject runningProject, Task outputReader)
+ {
+ public string DcpId { get; } = dcpId;
+ public string Id { get; } = sessionId;
+ public RunningProject RunningProject { get; } = runningProject;
+ public Task OutputReader { get; } = outputReader;
+ }
+
+ private static readonly UnboundedChannelOptions s_outputChannelOptions = new()
+ {
+ SingleReader = true,
+ SingleWriter = true
+ };
+
+ private readonly ProjectLauncher _projectLauncher;
+ private readonly AspireServerService _service;
+ private readonly IReadOnlyList<(string name, string value)> _buildProperties;
+
+ ///
+ /// Lock to access:
+ ///
+ ///
+ ///
+ private readonly object _guard = new();
+
+ private readonly Dictionary _sessions = [];
+ private int _sessionIdDispenser;
+ private volatile bool _isDisposed;
+
+ public SessionManager(ProjectLauncher projectLauncher, IReadOnlyList<(string name, string value)> buildProperties)
+ {
+ _projectLauncher = projectLauncher;
+ _buildProperties = buildProperties;
+
+ _service = new AspireServerService(
+ this,
+ displayName: ".NET Watch Aspire Server",
+ m => projectLauncher.Reporter.Verbose(m, MessageEmoji));
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+#if DEBUG
+ lock (_guard)
+ {
+ Debug.Assert(_sessions.Count == 0);
+ }
+#endif
+ _isDisposed = true;
+
+ await _service.DisposeAsync();
+ }
+
+ public async ValueTask TerminateLaunchedProcessesAsync(CancellationToken cancellationToken)
+ {
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+
+ ImmutableArray sessions;
+ lock (_guard)
+ {
+ // caller guarantees the session is active
+ sessions = [.. _sessions.Values];
+ _sessions.Clear();
+ }
+
+ foreach (var session in sessions)
+ {
+ await TerminateSessionAsync(session, cancellationToken);
+ }
+ }
+
+ public IEnumerable<(string name, string value)> GetEnvironmentVariables()
+ => _service.GetServerConnectionEnvironment().Select(kvp => (kvp.Key, kvp.Value));
+
+ private IReporter Reporter
+ => _projectLauncher.Reporter;
+
+ ///
+ /// Implements https://github.com/dotnet/aspire/blob/445d2fc8a6a0b7ce3d8cc42def4d37b02709043b/docs/specs/IDE-execution.md#create-session-request.
+ ///
+ async ValueTask IAspireServerEvents.StartProjectAsync(string dcpId, ProjectLaunchRequest projectLaunchInfo, CancellationToken cancellationToken)
+ {
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+
+ var projectOptions = GetProjectOptions(projectLaunchInfo);
+ var sessionId = Interlocked.Increment(ref _sessionIdDispenser).ToString(CultureInfo.InvariantCulture);
+ await StartProjectAsync(dcpId, sessionId, projectOptions, build: false, isRestart: false, cancellationToken);
+ return sessionId;
+ }
+
+ public async ValueTask StartProjectAsync(string dcpId, string sessionId, ProjectOptions projectOptions, bool build, bool isRestart, CancellationToken cancellationToken)
+ {
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+
+ Reporter.Verbose($"Starting project: {projectOptions.ProjectPath}", MessageEmoji);
+
+ var processTerminationSource = new CancellationTokenSource();
+ var outputChannel = Channel.CreateUnbounded(s_outputChannelOptions);
+
+ var runningProject = await _projectLauncher.TryLaunchProcessAsync(
+ projectOptions,
+ processTerminationSource,
+ onOutput: line =>
+ {
+ var writeResult = outputChannel.Writer.TryWrite(line);
+ Debug.Assert(writeResult);
+ },
+ restartOperation: (build, cancellationToken) =>
+ StartProjectAsync(dcpId, sessionId, projectOptions, build, isRestart: true, cancellationToken),
+ build: build,
+ cancellationToken);
+
+ if (runningProject == null)
+ {
+ // detailed error already reported:
+ throw new ApplicationException($"Failed to launch project '{projectOptions.ProjectPath}'.");
+ }
+
+ await _service.NotifySessionStartedAsync(dcpId, sessionId, runningProject.ProcessId, cancellationToken);
+
+ // cancel reading output when the process terminates:
+ var outputReader = StartChannelReader(processTerminationSource.Token);
+
+ lock (_guard)
+ {
+ // When process is restarted we reuse the session id.
+ // The session already exists, it needs to be updated with new info.
+ Debug.Assert(_sessions.ContainsKey(sessionId) == isRestart);
+
+ _sessions[sessionId] = new Session(dcpId, sessionId, runningProject, outputReader);
+ }
+
+ Reporter.Verbose($"Session started: #{sessionId}", MessageEmoji);
+ return runningProject;
+
+ async Task StartChannelReader(CancellationToken cancellationToken)
+ {
+ try
+ {
+ await foreach (var line in outputChannel.Reader.ReadAllAsync(cancellationToken))
+ {
+ await _service.NotifyLogMessageAsync(dcpId, sessionId, isStdErr: line.IsError, data: line.Content, cancellationToken);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // nop
+ }
+ catch (Exception e)
+ {
+ Reporter.Error($"Unexpected error reading output of session '{sessionId}': {e}");
+ }
+ }
+ }
+
+ ///
+ /// Implements https://github.com/dotnet/aspire/blob/445d2fc8a6a0b7ce3d8cc42def4d37b02709043b/docs/specs/IDE-execution.md#stop-session-request.
+ ///
+ async ValueTask IAspireServerEvents.StopSessionAsync(string dcpId, string sessionId, CancellationToken cancellationToken)
+ {
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+
+ Session session;
+ lock (_guard)
+ {
+ if (!_sessions.TryGetValue(sessionId, out session))
+ {
+ return false;
+ }
+
+ _sessions.Remove(sessionId);
+ }
+
+ await TerminateSessionAsync(session, cancellationToken);
+ return true;
+ }
+
+ private async ValueTask TerminateSessionAsync(Session session, CancellationToken cancellationToken)
+ {
+ Reporter.Verbose($"Stop session #{session.Id}", MessageEmoji);
+
+ var exitCode = await _projectLauncher.TerminateProcessAsync(session.RunningProject, cancellationToken);
+
+ // Wait until the started notification has been sent so that we don't send out of order notifications:
+ await _service.NotifySessionEndedAsync(session.DcpId, session.Id, session.RunningProject.ProcessId, exitCode, cancellationToken);
+
+ // process termination should cancel output reader task:
+ await session.OutputReader;
+ }
+
+ private ProjectOptions GetProjectOptions(ProjectLaunchRequest projectLaunchInfo)
+ {
+ var arguments = new List
+ {
+ "--project",
+ projectLaunchInfo.ProjectPath,
+ // TODO: https://github.com/dotnet/sdk/issues/43946
+ // Need to suppress launch profile for now, otherwise it would override the port set via env variable.
+ "--no-launch-profile",
+ };
+
+ //if (projectLaunchInfo.DisableLaunchProfile)
+ //{
+ // arguments.Add("--no-launch-profile");
+ //}
+ //else if (!string.IsNullOrEmpty(projectLaunchInfo.LaunchProfile))
+ //{
+ // arguments.Add("--launch-profile");
+ // arguments.Add(projectLaunchInfo.LaunchProfile);
+ //}
+
+ if (projectLaunchInfo.Arguments != null)
+ {
+ arguments.AddRange(projectLaunchInfo.Arguments);
+ }
+
+ return new()
+ {
+ IsRootProject = false,
+ ProjectPath = projectLaunchInfo.ProjectPath,
+ WorkingDirectory = _projectLauncher.EnvironmentOptions.WorkingDirectory, // TODO: Should DCP protocol specify?
+ BuildProperties = _buildProperties, // TODO: Should DCP protocol specify?
+ Command = "run",
+ CommandArguments = arguments,
+ LaunchEnvironmentVariables = projectLaunchInfo.Environment?.Select(kvp => (kvp.Key, kvp.Value)).ToArray() ?? [],
+ LaunchProfileName = projectLaunchInfo.LaunchProfile,
+ NoLaunchProfile = projectLaunchInfo.DisableLaunchProfile,
+ TargetFramework = null, // TODO: Should DCP protocol specify?
+ };
+ }
+ }
+
+ public const string MessageEmoji = "⭐";
+
+ public static readonly AspireServiceFactory Instance = new();
+ public const string AppHostProjectCapability = "Aspire";
+
+ public IRuntimeProcessLauncher? TryCreate(ProjectGraphNode projectNode, ProjectLauncher projectLauncher, IReadOnlyList<(string name, string value)> buildProperties)
+ => projectNode.GetCapabilities().Contains(AppHostProjectCapability)
+ ? new SessionManager(projectLauncher, buildProperties)
+ : null;
+}
diff --git a/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs b/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs
index e5e90a0bf361..21d2f2cfce13 100644
--- a/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs
+++ b/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs
@@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using Microsoft.Build.Graph;
+using Microsoft.DotNet.Watcher.Internal;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.DotNet.Watcher.Tools
@@ -65,8 +66,12 @@ await Task.WhenAll(serversToDispose.Select(async server =>
}
}
- // Attach trigger to the process that launches browser on URL found in the process output:
- processSpec.OnOutput += GetBrowserLaunchTrigger(projectNode, projectOptions, server, cancellationToken);
+ // Attach trigger to the process that launches browser on URL found in the process output.
+ // Only do so for root projects, not for child processes.
+ if (projectOptions.IsRootProject)
+ {
+ processSpec.OnOutput += GetBrowserLaunchTrigger(projectNode, projectOptions, server, cancellationToken);
+ }
if (server == null)
{
@@ -100,7 +105,7 @@ public bool TryGetRefreshServer(ProjectGraphNode projectNode, [NotNullWhen(true)
///
/// Get process output handler that will be subscribed to the process output event every time the process is launched.
///
- public DataReceivedEventHandler? GetBrowserLaunchTrigger(ProjectGraphNode projectNode, ProjectOptions projectOptions, BrowserRefreshServer? server, CancellationToken cancellationToken)
+ public Action? GetBrowserLaunchTrigger(ProjectGraphNode projectNode, ProjectOptions projectOptions, BrowserRefreshServer? server, CancellationToken cancellationToken)
{
if (!CanLaunchBrowser(context, projectNode, projectOptions, out var launchProfile))
{
@@ -112,20 +117,27 @@ public bool TryGetRefreshServer(ProjectGraphNode projectNode, [NotNullWhen(true)
return null;
}
+ bool matchFound = false;
+
return handler;
- void handler(object sender, DataReceivedEventArgs eventArgs)
+ void handler(OutputLine line)
{
// We've redirected the output, but want to ensure that it continues to appear in the user's console.
- Console.WriteLine(eventArgs.Data);
+ (line.IsError ? Console.Error : Console.Out).WriteLine(line.Content);
+
+ if (matchFound)
+ {
+ return;
+ }
- var match = s_nowListeningRegex.Match(eventArgs.Data ?? "");
+ var match = s_nowListeningRegex.Match(line.Content);
if (!match.Success)
{
return;
}
- ((Process)sender).OutputDataReceived -= handler;
+ matchFound = true;
var projectAddedToAttemptedSet = ImmutableInterlocked.Update(ref _browserLaunchAttempted, static (set, projectNode) => set.Add(projectNode), projectNode);
if (projectAddedToAttemptedSet)
diff --git a/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs b/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs
index 52fbc003ed1f..c3547bd5b146 100644
--- a/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs
+++ b/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs
@@ -209,6 +209,8 @@ public async ValueTask SendJsonWithSecret(Func valueFac
{
try
{
+ bool messageSent = false;
+
for (var i = 0; i < _clientSockets.Count; i++)
{
var (clientSocket, secret) = _clientSockets[i];
@@ -221,7 +223,10 @@ public async ValueTask SendJsonWithSecret(Func valueFac
var messageBytes = JsonSerializer.SerializeToUtf8Bytes(value, _jsonSerializerOptions);
await clientSocket.SendAsync(messageBytes, WebSocketMessageType.Text, endOfMessage: true, cancellationToken);
+ messageSent = true;
}
+
+ _reporter.Verbose(messageSent ? "Browser message sent." : "Unable to send message to browser, no socket is open.");
}
catch (TaskCanceledException)
{
@@ -237,6 +242,8 @@ public async ValueTask SendMessage(ReadOnlyMemory messageBytes, Cancellati
{
try
{
+ bool messageSent = false;
+
for (var i = 0; i < _clientSockets.Count; i++)
{
var (clientSocket, _) = _clientSockets[i];
@@ -244,8 +251,12 @@ public async ValueTask SendMessage(ReadOnlyMemory messageBytes, Cancellati
{
continue;
}
+
await clientSocket.SendAsync(messageBytes, WebSocketMessageType.Text, endOfMessage: true, cancellationToken);
+ messageSent = true;
}
+
+ _reporter.Verbose(messageSent ? "Browser message sent." : "Unable to send message to browser, no socket is open.");
}
catch (TaskCanceledException)
{
diff --git a/src/BuiltInTools/dotnet-watch/DotNetWatch.targets b/src/BuiltInTools/dotnet-watch/DotNetWatch.targets
index 799c29ad1f55..c0ef2c555789 100644
--- a/src/BuiltInTools/dotnet-watch/DotNetWatch.targets
+++ b/src/BuiltInTools/dotnet-watch/DotNetWatch.targets
@@ -72,7 +72,7 @@ Returns: @(Watch)
Condition="'$(UsingMicrosoftNETSdkRazor)'=='true' AND '$(DotNetWatchContentFiles)'!='false' AND '%(Content.Watch)' != 'false' AND $([System.String]::Copy('%(Identity)').Replace('\','/').StartsWith('wwwroot/'))"
StaticWebAssetPath="$(_DotNetWatchStaticWebAssetBasePath)$([System.String]::Copy('%(Identity)').Replace('\','/').Substring(8))" />
- <_WatchProjects Include="%(ProjectReference.Identity)" Condition="'%(ProjectReference.Watch)' != 'false'" />
+ <_WatchProjects Include="%(ProjectReference.Identity)" Condition="'%(ProjectReference.Watch)' != 'false' and Exists('%(Identity)')" />
((TaskCompletionSource)state!).TrySetResult(),
+ shutdownCancellationToken.Register(state => ((TaskCompletionSource)state!).TrySetResult(),
cancelledTaskSource);
if (Context.EnvironmentOptions.SuppressMSBuildIncrementalism)
@@ -31,29 +29,29 @@ public override async Task WatchAsync(CancellationToken cancellationToken)
var buildEvaluator = new BuildEvaluator(Context, RootFileSetFactory);
await using var browserConnector = new BrowserConnector(Context);
- StaticFileHandler? staticFileHandler;
- ProjectGraphNode? projectRootNode;
- if (Context.ProjectGraph != null)
- {
- projectRootNode = Context.ProjectGraph.GraphRoots.Single();
- var projectMap = new ProjectNodeMap(Context.ProjectGraph, Context.Reporter);
- staticFileHandler = new StaticFileHandler(Context.Reporter, projectMap, browserConnector);
- }
- else
- {
- Context.Reporter.Verbose("Unable to determine if this project is a webapp.");
- projectRootNode = null;
- staticFileHandler = null;
- }
-
for (var iteration = 0;;iteration++)
{
- if (await buildEvaluator.EvaluateAsync(changedFile, cancellationToken) is not { } evaluationResult)
+ if (await buildEvaluator.EvaluateAsync(changedFile, shutdownCancellationToken) is not { } evaluationResult)
{
Context.Reporter.Error("Failed to find a list of files to watch");
return;
}
+ StaticFileHandler? staticFileHandler;
+ ProjectGraphNode? projectRootNode;
+ if (evaluationResult.ProjectGraph != null)
+ {
+ projectRootNode = evaluationResult.ProjectGraph.GraphRoots.Single();
+ var projectMap = new ProjectNodeMap(evaluationResult.ProjectGraph, Context.Reporter);
+ staticFileHandler = new StaticFileHandler(Context.Reporter, projectMap, browserConnector);
+ }
+ else
+ {
+ Context.Reporter.Verbose("Unable to determine if this project is a webapp.");
+ projectRootNode = null;
+ staticFileHandler = null;
+ }
+
var processSpec = new ProcessSpec
{
Executable = Context.EnvironmentOptions.MuxerPath,
@@ -67,7 +65,7 @@ public override async Task WatchAsync(CancellationToken cancellationToken)
};
var browserRefreshServer = (projectRootNode != null)
- ? await browserConnector.LaunchOrRefreshBrowserAsync(projectRootNode, processSpec, environmentBuilder, Context.RootProjectOptions, cancellationToken)
+ ? await browserConnector.LaunchOrRefreshBrowserAsync(projectRootNode, processSpec, environmentBuilder, Context.RootProjectOptions, shutdownCancellationToken)
: null;
environmentBuilder.ConfigureProcess(processSpec);
@@ -75,16 +73,16 @@ public override async Task WatchAsync(CancellationToken cancellationToken)
// Reset for next run
buildEvaluator.RequiresRevaluation = false;
- if (cancellationToken.IsCancellationRequested)
+ if (shutdownCancellationToken.IsCancellationRequested)
{
return;
}
using var currentRunCancellationSource = new CancellationTokenSource();
- using var combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, currentRunCancellationSource.Token);
+ using var combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(shutdownCancellationToken, currentRunCancellationSource.Token);
using var fileSetWatcher = new FileWatcher(evaluationResult.Files, Context.Reporter);
- var processTask = ProcessRunner.RunAsync(processSpec, Context.Reporter, isUserApplication: true, processExitedSource: null, combinedCancellationSource.Token);
+ var processTask = ProcessRunner.RunAsync(processSpec, Context.Reporter, isUserApplication: true, launchResult: null, combinedCancellationSource.Token);
Task fileSetTask;
Task finishedTask;
@@ -112,7 +110,7 @@ public override async Task WatchAsync(CancellationToken cancellationToken)
await Task.WhenAll(processTask, fileSetTask);
- if (finishedTask == cancelledTaskSource.Task || cancellationToken.IsCancellationRequested)
+ if (finishedTask == cancelledTaskSource.Task || shutdownCancellationToken.IsCancellationRequested)
{
return;
}
@@ -124,7 +122,7 @@ public override async Task WatchAsync(CancellationToken cancellationToken)
// Now wait for a file to change before restarting process
changedFile = await fileSetWatcher.GetChangedFileAsync(
() => Context.Reporter.Report(MessageDescriptor.WaitingForFileChangeBeforeRestarting),
- cancellationToken);
+ shutdownCancellationToken);
}
else
{
diff --git a/src/BuiltInTools/dotnet-watch/EvaluationResult.cs b/src/BuiltInTools/dotnet-watch/EvaluationResult.cs
index b796976b84be..48ca7f6902af 100644
--- a/src/BuiltInTools/dotnet-watch/EvaluationResult.cs
+++ b/src/BuiltInTools/dotnet-watch/EvaluationResult.cs
@@ -1,9 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.Build.Graph;
+
namespace Microsoft.DotNet.Watcher;
-internal sealed class EvaluationResult(IReadOnlyDictionary files)
+internal sealed class EvaluationResult(IReadOnlyDictionary files, ProjectGraph? projectGraph)
{
public readonly IReadOnlyDictionary Files = files;
+ public readonly ProjectGraph? ProjectGraph = projectGraph;
}
diff --git a/src/BuiltInTools/dotnet-watch/Filters/BuildEvaluator.cs b/src/BuiltInTools/dotnet-watch/Filters/BuildEvaluator.cs
index 0025fbbbcbf5..3221f60278f2 100644
--- a/src/BuiltInTools/dotnet-watch/Filters/BuildEvaluator.cs
+++ b/src/BuiltInTools/dotnet-watch/Filters/BuildEvaluator.cs
@@ -78,7 +78,7 @@ private async ValueTask CreateEvaluationResult(CancellationTok
{
cancellationToken.ThrowIfCancellationRequested();
- var result = await rootProjectFileSetFactory.TryCreateAsync(cancellationToken);
+ var result = await rootProjectFileSetFactory.TryCreateAsync(requireProjectGraph: true, cancellationToken);
if (result != null)
{
return result;
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
index cdfc4cf3f9c2..73b3329f8fdb 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
@@ -9,13 +9,12 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.ExternalAccess.Watch.Api;
-using Microsoft.CodeAnalysis.Text;
using Microsoft.DotNet.Watcher.Internal;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.DotNet.Watcher.Tools
{
- internal sealed class CompilationHandler : IAsyncDisposable
+ internal sealed class CompilationHandler : IDisposable
{
public readonly IncrementalMSBuildWorkspace Workspace;
@@ -55,38 +54,24 @@ public CompilationHandler(IReporter reporter)
_hotReloadService = new WatchHotReloadService(Workspace.CurrentSolution.Services, GetAggregateCapabilitiesAsync);
}
- public async ValueTask DisposeAsync()
+ public void Dispose()
{
_isDisposed = true;
-
Workspace?.Dispose();
-
- IEnumerable projects;
- lock (_runningProjectsAndUpdatesGuard)
- {
- projects = _runningProjects.SelectMany(entry => entry.Value).Where(p => !p.Options.IsRootProject);
- _runningProjects = _runningProjects.Clear();
- }
-
- await TerminateAndDisposeRunningProjects(projects);
}
- private static async ValueTask TerminateAndDisposeRunningProjects(IEnumerable projects)
+ public async ValueTask TerminateNonRootProcessesAndDispose(CancellationToken cancellationToken)
{
- // cancel first, this will cause the process tasks to complete:
- foreach (var project in projects)
- {
- project.ProcessTerminationSource.Cancel();
- }
+ _reporter.Verbose("Disposing remaining child processes.");
- // wait for all tasks to complete:
- await Task.WhenAll(projects.Select(p => p.RunningProcess)).WaitAsync(CancellationToken.None);
+ var projectsToDispose = await TerminateNonRootProcessesAsync(projectPaths: null, cancellationToken);
- // dispose only after all tasks have completed to prevent the tasks from accessing disposed resources:
- foreach (var project in projects)
+ foreach (var project in projectsToDispose)
{
project.Dispose();
}
+
+ Dispose();
}
public ValueTask RestartSessionAsync(IReadOnlySet projectsToBeRebuilt, CancellationToken cancellationToken)
@@ -124,12 +109,13 @@ private DeltaApplier CreateDeltaApplier(ProjectGraphNode projectNode, BrowserRef
_ => new DefaultDeltaApplier(processReporter),
};
- public async Task TrackRunningProjectAsync(
+ public async Task TrackRunningProjectAsync(
ProjectGraphNode projectNode,
ProjectOptions projectOptions,
string namedPipeName,
BrowserRefreshServer? browserRefreshServer,
ProcessSpec processSpec,
+ RestartOperation restartOperation,
IReporter processReporter,
CancellationTokenSource processTerminationSource,
CancellationToken cancellationToken)
@@ -146,7 +132,20 @@ public async Task TrackRunningProjectAsync(
// It is important to first create the named pipe connection (delta applier is the server)
// and then start the process (named pipe client). Otherwise, the connection would fail.
deltaApplier.CreateConnection(namedPipeName, processCommunicationCancellationSource.Token);
- var runningProcess = ProcessRunner.RunAsync(processSpec, processReporter, isUserApplication: true, processExitedSource, processTerminationSource.Token);
+
+ processSpec.OnExit += (_, _) =>
+ {
+ processExitedSource.Cancel();
+ return ValueTask.CompletedTask;
+ };
+
+ var launchResult = new ProcessLaunchResult();
+ var runningProcess = ProcessRunner.RunAsync(processSpec, processReporter, isUserApplication: true, launchResult, processTerminationSource.Token);
+ if (launchResult.ProcessId == null)
+ {
+ // error already reported
+ return null;
+ }
var capabilityProvider = deltaApplier.GetApplyUpdateCapabilitiesAsync(processCommunicationCancellationSource.Token);
var runningProject = new RunningProject(
@@ -156,8 +155,10 @@ public async Task TrackRunningProjectAsync(
processReporter,
browserRefreshServer,
runningProcess,
+ launchResult.ProcessId.Value,
processExitedSource: processExitedSource,
processTerminationSource: processTerminationSource,
+ restartOperation: restartOperation,
disposables: [processCommunicationCancellationSource],
capabilityProvider);
@@ -281,6 +282,8 @@ private static void PrepareCompilations(Solution solution, string projectPath, C
var runningProjects = _runningProjects;
var updates = await _hotReloadService.GetUpdatesAsync(currentSolution, isRunningProject: p => runningProjects.ContainsKey(p.FilePath!), cancellationToken);
+ var anyProcessNeedsRestart = updates.ProjectsToRestart.Count > 0;
+
await DisplayResultsAsync(updates, cancellationToken);
if (updates.Status is ModuleUpdateStatus.None or ModuleUpdateStatus.Blocked)
@@ -292,7 +295,10 @@ private static void PrepareCompilations(Solution solution, string projectPath, C
if (updates.Status == ModuleUpdateStatus.RestartRequired)
{
- Debug.Assert(updates.ProjectsToRestart.Count > 0);
+ if (!anyProcessNeedsRestart)
+ {
+ return (ImmutableHashSet.Empty, []);
+ }
await restartPrompt.Invoke(updates.ProjectsToRestart, cancellationToken);
@@ -345,6 +351,8 @@ await ForEachProjectAsync(projectsToUpdate, async (runningProject, cancellationT
private async ValueTask DisplayResultsAsync(WatchHotReloadService.Updates updates, CancellationToken cancellationToken)
{
+ var anyProcessNeedsRestart = updates.ProjectsToRestart.Count > 0;
+
switch (updates.Status)
{
case ModuleUpdateStatus.None:
@@ -355,7 +363,15 @@ private async ValueTask DisplayResultsAsync(WatchHotReloadService.Updates update
break;
case ModuleUpdateStatus.RestartRequired:
- _reporter.Output("Unable to apply hot reload, restart is needed to apply the changes.");
+ if (anyProcessNeedsRestart)
+ {
+ _reporter.Output("Unable to apply hot reload, restart is needed to apply the changes.");
+ }
+ else
+ {
+ _reporter.Verbose("Rude edits detected but do not affect any running process");
+ }
+
break;
case ModuleUpdateStatus.Blocked:
@@ -418,6 +434,12 @@ void Display(MessageSeverity severity)
continue;
}
+ // Do not report rude edits as errors/warnings if no running process is affected.
+ if (!anyProcessNeedsRestart && diagnostic.Id is ['E', 'N', 'C', >= '0' and <= '9', ..])
+ {
+ descriptor = descriptor with { Severity = MessageSeverity.Verbose };
+ }
+
var display = CSharpDiagnosticFormatter.Instance.Format(diagnostic);
_reporter.Report(descriptor, display);
@@ -436,36 +458,94 @@ await ForEachProjectAsync(
}
///
- /// Terminates all processes launched for projects with .
+ /// Terminates all processes launched for projects with ,
+ /// or all running non-root project processes if is null.
+ ///
/// Removes corresponding entries from .
///
- /// May terminate the root project process as well.
+ /// Does not terminate the root project.
///
- internal async ValueTask> TerminateNonRootProcessesAsync(IEnumerable projectPaths, CancellationToken cancellationToken)
+ internal async ValueTask> TerminateNonRootProcessesAsync(
+ IEnumerable? projectPaths, CancellationToken cancellationToken)
{
- IEnumerable projectsToRestart;
- lock (_runningProjectsAndUpdatesGuard)
- {
- // capture snapshot of running processes that can be enumerated outside of the lock:
- var runningProjects = _runningProjects;
- projectsToRestart = projectPaths.SelectMany(path => runningProjects[path]);
+ ImmutableArray projectsToRestart = [];
- _runningProjects = runningProjects.RemoveRange(projectPaths);
+ UpdateRunningProjects(runningProjectsByPath =>
+ {
+ if (projectPaths == null)
+ {
+ projectsToRestart = _runningProjects.SelectMany(entry => entry.Value).Where(p => !p.Options.IsRootProject).ToImmutableArray();
+ return _runningProjects.Clear();
+ }
- // reset capabilities:
- _currentAggregateCapabilities = default;
- }
+ projectsToRestart = projectPaths.SelectMany(path => _runningProjects.TryGetValue(path, out var array) ? array : []).ToImmutableArray();
+ return runningProjectsByPath.RemoveRange(projectPaths);
+ });
// Do not terminate root process at this time - it would signal the cancellation token we are currently using.
// The process will be restarted later on.
var projectsToTerminate = projectsToRestart.Where(p => !p.Options.IsRootProject);
// wait for all processes to exit to release their resources, so we can rebuild:
- await TerminateAndDisposeRunningProjects(projectsToTerminate);
+ _ = await TerminateRunningProjects(projectsToTerminate, cancellationToken);
return projectsToRestart;
}
+ ///
+ /// Terminates process of the given .
+ /// Removes corresponding entries from .
+ ///
+ /// Should not be called with the root project.
+ ///
+ /// Exit code of the terminated process.
+ internal async ValueTask TerminateNonRootProcessAsync(RunningProject project, CancellationToken cancellationToken)
+ {
+ Debug.Assert(!project.Options.IsRootProject);
+
+ var projectPath = project.ProjectNode.ProjectInstance.FullPath;
+
+ UpdateRunningProjects(runningProjectsByPath =>
+ {
+ if (!runningProjectsByPath.TryGetValue(projectPath, out var runningProjects) ||
+ runningProjects.Remove(project) is var updatedRunningProjects && runningProjects == updatedRunningProjects)
+ {
+ _reporter.Verbose($"Ignoring an attempt to terminate process {project.ProcessId} of project '{projectPath}' that has no associated running processes.");
+ return runningProjectsByPath;
+ }
+
+ return updatedRunningProjects is []
+ ? runningProjectsByPath.Remove(projectPath)
+ : runningProjectsByPath.SetItem(projectPath, updatedRunningProjects);
+ });
+
+ // wait for all processes to exit to release their resources:
+ return (await TerminateRunningProjects([project], cancellationToken)).Single();
+ }
+
+ private void UpdateRunningProjects(Func>, ImmutableDictionary>> updater)
+ {
+ lock (_runningProjectsAndUpdatesGuard)
+ {
+ _runningProjects = updater(_runningProjects);
+
+ // reset capabilities:
+ _currentAggregateCapabilities = default;
+ }
+ }
+
+ private static async ValueTask> TerminateRunningProjects(IEnumerable projects, CancellationToken cancellationToken)
+ {
+ // cancel first, this will cause the process tasks to complete:
+ foreach (var project in projects)
+ {
+ project.ProcessTerminationSource.Cancel();
+ }
+
+ // wait for all tasks to complete:
+ return await Task.WhenAll(projects.Select(p => p.RunningProcess)).WaitAsync(cancellationToken);
+ }
+
private static Task ForEachProjectAsync(ImmutableDictionary> projects, Func action, CancellationToken cancellationToken)
=> Task.WhenAll(projects.SelectMany(entry => entry.Value).Select(project => action(project, cancellationToken))).WaitAsync(cancellationToken);
}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs
index ca0fe1297410..0bdceee705ca 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs
@@ -175,7 +175,7 @@ private async Task ReceiveApplyUpdateResult(CancellationToken cancellation
private void DisposePipe()
{
- Reporter.Verbose("Disposing pipe");
+ Reporter.Verbose("Disposing agent communication pipe");
_pipe?.Dispose();
_pipe = null;
}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncher.cs b/src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncher.cs
index 08bdf0eee6d7..375e2a9b3248 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncher.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncher.cs
@@ -9,5 +9,10 @@ namespace Microsoft.DotNet.Watcher;
///
internal interface IRuntimeProcessLauncher : IAsyncDisposable
{
- ValueTask> GetEnvironmentVariablesAsync(CancellationToken cancelationToken);
+ IEnumerable<(string name, string value)> GetEnvironmentVariables();
+
+ ///
+ /// Initiates shutdown. Terminates all created processes.
+ ///
+ ValueTask TerminateLaunchedProcessesAsync(CancellationToken cancellationToken);
}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs b/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
index 5020cbb03c69..9185f32044c2 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
@@ -3,11 +3,14 @@
using System.Globalization;
using Microsoft.Build.Graph;
+using Microsoft.DotNet.Watcher.Internal;
using Microsoft.DotNet.Watcher.Tools;
using Microsoft.Extensions.Tools.Internal;
namespace Microsoft.DotNet.Watcher;
+internal delegate ValueTask ProcessExitAction(int processId, int? exitCode);
+
internal sealed class ProjectLauncher(
DotNetWatchContext context,
ProjectNodeMap projectMap,
@@ -23,7 +26,13 @@ public IReporter Reporter
public EnvironmentOptions EnvironmentOptions
=> context.EnvironmentOptions;
- public async ValueTask TryLaunchProcessAsync(ProjectOptions projectOptions, CancellationTokenSource processTerminationSource, bool build, CancellationToken cancellationToken)
+ public async ValueTask TryLaunchProcessAsync(
+ ProjectOptions projectOptions,
+ CancellationTokenSource processTerminationSource,
+ Action? onOutput,
+ RestartOperation restartOperation,
+ bool build,
+ CancellationToken cancellationToken)
{
var projectNode = projectMap.TryGetProjectNode(projectOptions.ProjectPath, projectOptions.TargetFramework);
if (projectNode == null)
@@ -34,45 +43,20 @@ public EnvironmentOptions EnvironmentOptions
if (!projectNode.IsNetCoreApp(Versions.Version6_0))
{
- Reporter.Error($"Hot Reload based watching is only supported in .NET 6.0 or newer apps. Update the project's launchSettings.json to disable this feature.");
- return null;
- }
-
- try
- {
- return await LaunchProcessAsync(projectOptions, projectNode, processTerminationSource, build, cancellationToken);
- }
- catch (ObjectDisposedException e) when (e.ObjectName == typeof(HotReloadDotNetWatcher).FullName)
- {
- Reporter.Verbose("Unable to launch project, watcher has been disposed");
+ Reporter.Error($"Hot Reload based watching is only supported in .NET 6.0 or newer apps. Use --no-hot-reload switch or update the project's launchSettings.json to disable this feature.");
return null;
}
- }
- public async Task LaunchProcessAsync(ProjectOptions projectOptions, ProjectGraphNode projectNode, CancellationTokenSource processTerminationSource, bool build, CancellationToken cancellationToken)
- {
var processSpec = new ProcessSpec
{
Executable = EnvironmentOptions.MuxerPath,
WorkingDirectory = projectOptions.WorkingDirectory,
+ OnOutput = onOutput,
Arguments = build || projectOptions.Command is not ("run" or "test")
? [projectOptions.Command, .. projectOptions.CommandArguments]
: [projectOptions.Command, "--no-build", .. projectOptions.CommandArguments]
};
- // allow tests to watch for application output:
- if (Reporter.ReportProcessOutput)
- {
- var projectPath = projectNode.ProjectInstance.FullPath;
- processSpec.OnOutput += (sender, args) =>
- {
- if (args.Data != null)
- {
- Reporter.ProcessOutput(projectPath, args.Data);
- }
- };
- }
-
var environmentBuilder = EnvironmentVariablesBuilder.FromCurrentEnvironment();
var namedPipeName = Guid.NewGuid().ToString();
@@ -100,10 +84,14 @@ public async Task LaunchProcessAsync(ProjectOptions projectOptio
environmentBuilder.SetVariable(EnvironmentVariables.Names.DotnetWatch, "1");
environmentBuilder.SetVariable(EnvironmentVariables.Names.DotnetWatchIteration, (Iteration + 1).ToString(CultureInfo.InvariantCulture));
- if (context.Options.Verbose)
- {
- environmentBuilder.SetVariable(EnvironmentVariables.Names.HotReloadDeltaClientLogMessages, "1");
- }
+ // Do not ask agent to log to stdout until https://github.com/dotnet/sdk/issues/40484 is fixed.
+ // For now we need to set the env variable explicitly when we need to diagnose issue with the agent.
+ // Build targets might launch a process and read it's stdout. If the agent is loaded into such process and starts logging
+ // to stdout it might interfere with the expected output.
+ //if (context.Options.Verbose)
+ //{
+ // environmentBuilder.SetVariable(EnvironmentVariables.Names.HotReloadDeltaClientLogMessages, "1");
+ //}
// TODO: workaround for https://github.com/dotnet/sdk/issues/40484
var targetPath = projectNode.ProjectInstance.GetPropertyValue("RunCommand");
@@ -113,7 +101,7 @@ public async Task LaunchProcessAsync(ProjectOptions projectOptio
var browserRefreshServer = await browserConnector.LaunchOrRefreshBrowserAsync(projectNode, processSpec, environmentBuilder, projectOptions, cancellationToken);
environmentBuilder.ConfigureProcess(processSpec);
- var processReporter = new MessagePrefixingReporter($"[{projectNode.GetDisplayName()}] ", Reporter);
+ var processReporter = new ProjectSpecificReporter(projectNode, Reporter);
return await compilationHandler.TrackRunningProjectAsync(
projectNode,
@@ -121,11 +109,12 @@ public async Task LaunchProcessAsync(ProjectOptions projectOptio
namedPipeName,
browserRefreshServer,
processSpec,
+ restartOperation,
processReporter,
processTerminationSource,
cancellationToken);
}
- public ValueTask> TerminateProcessesAsync(IReadOnlyList projectPaths, CancellationToken cancellationToken)
- => compilationHandler.TerminateNonRootProcessesAsync(projectPaths, cancellationToken);
+ public ValueTask TerminateProcessAsync(RunningProject project, CancellationToken cancellationToken)
+ => compilationHandler.TerminateNonRootProcessAsync(project, cancellationToken);
}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/RunningProject.cs b/src/BuiltInTools/dotnet-watch/HotReload/RunningProject.cs
index 6b4cad493400..99b04b828a63 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/RunningProject.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/RunningProject.cs
@@ -8,15 +8,19 @@
namespace Microsoft.DotNet.Watcher.Tools
{
+ internal delegate ValueTask RestartOperation(bool build, CancellationToken cancellationToken);
+
internal sealed class RunningProject(
ProjectGraphNode projectNode,
ProjectOptions options,
DeltaApplier deltaApplier,
IReporter reporter,
BrowserRefreshServer? browserRefreshServer,
- Task runningProcess,
+ Task runningProcess,
+ int processId,
CancellationTokenSource processExitedSource,
CancellationTokenSource processTerminationSource,
+ RestartOperation restartOperation,
IReadOnlyList disposables,
Task> capabilityProvider) : IDisposable
{
@@ -26,7 +30,9 @@ internal sealed class RunningProject(
public readonly DeltaApplier DeltaApplier = deltaApplier;
public readonly Task> CapabilityProvider = capabilityProvider;
public readonly IReporter Reporter = reporter;
- public readonly Task RunningProcess = runningProcess;
+ public readonly Task RunningProcess = runningProcess;
+ public readonly int ProcessId = processId;
+ public readonly RestartOperation RestartOperation = restartOperation;
///
/// Cancellation source triggered when the process exits.
diff --git a/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs b/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs
index da4f09764958..3ca446c43b30 100644
--- a/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs
@@ -35,8 +35,6 @@ public HotReloadDotNetWatcher(DotNetWatchContext context, IConsole console, MSBu
public override async Task WatchAsync(CancellationToken shutdownCancellationToken)
{
- Debug.Assert(Context.ProjectGraph != null);
-
CancellationTokenSource? forceRestartCancellationSource = null;
var hotReloadEnabledMessage = "Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload.";
@@ -75,37 +73,61 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
EvaluationResult? evaluationResult = null;
RunningProject? rootRunningProject = null;
Task? fileSetWatcherTask = null;
+ IRuntimeProcessLauncher? runtimeProcessLauncher = null;
+ CompilationHandler? compilationHandler = null;
try
{
+ var rootProjectOptions = Context.RootProjectOptions;
+ var runtimeProcessLauncherFactory = _runtimeProcessLauncherFactory;
+
// Evaluate the target to find out the set of files to watch.
// In case the app fails to start due to build or other error we can wait for these files to change.
evaluationResult = await EvaluateRootProjectAsync(iterationCancellationToken);
+ Debug.Assert(evaluationResult.ProjectGraph != null);
+
+ var rootProject = evaluationResult.ProjectGraph.GraphRoots.Single();
+
+ // use normalized MSBuild path so that we can index into the ProjectGraph
+ rootProjectOptions = rootProjectOptions with { ProjectPath = rootProject.ProjectInstance.FullPath };
+
+ if (rootProject.GetCapabilities().Contains(AspireServiceFactory.AppHostProjectCapability))
+ {
+ runtimeProcessLauncherFactory ??= AspireServiceFactory.Instance;
+ Context.Reporter.Verbose("Using Aspire process launcher.");
+ }
await using var browserConnector = new BrowserConnector(Context);
- var projectMap = new ProjectNodeMap(Context.ProjectGraph, Context.Reporter);
- await using var compilationHandler = new CompilationHandler(Context.Reporter);
+ var projectMap = new ProjectNodeMap(evaluationResult.ProjectGraph, Context.Reporter);
+ compilationHandler = new CompilationHandler(Context.Reporter);
var staticFileHandler = new StaticFileHandler(Context.Reporter, projectMap, browserConnector);
var scopedCssFileHandler = new ScopedCssFileHandler(Context.Reporter, projectMap, browserConnector);
var projectLauncher = new ProjectLauncher(Context, projectMap, browserConnector, compilationHandler, iteration);
- var rootProjectOptions = Context.RootProjectOptions;
- var rootProjectNode = Context.ProjectGraph.GraphRoots.Single();
+ var rootProjectNode = evaluationResult.ProjectGraph.GraphRoots.Single();
- await using var runtimeProcessLauncher = _runtimeProcessLauncherFactory?.TryCreate(rootProjectNode, projectLauncher, rootProjectOptions.BuildProperties);
+ runtimeProcessLauncher = runtimeProcessLauncherFactory?.TryCreate(rootProjectNode, projectLauncher, rootProjectOptions.BuildProperties);
if (runtimeProcessLauncher != null)
{
- var launcherEnvironment = await runtimeProcessLauncher.GetEnvironmentVariablesAsync(iterationCancellationToken);
+ var launcherEnvironment = runtimeProcessLauncher.GetEnvironmentVariables();
rootProjectOptions = rootProjectOptions with
{
LaunchEnvironmentVariables = [.. rootProjectOptions.LaunchEnvironmentVariables, .. launcherEnvironment]
};
}
- rootRunningProject = await projectLauncher.TryLaunchProcessAsync(rootProjectOptions, rootProcessTerminationSource, build: true, iterationCancellationToken);
+ rootRunningProject = await projectLauncher.TryLaunchProcessAsync(
+ rootProjectOptions,
+ rootProcessTerminationSource,
+ onOutput: null,
+ restartOperation: new RestartOperation((_, _) => throw new InvalidOperationException("Root project shouldn't be restarted")),
+ build: true,
+ iterationCancellationToken);
+
if (rootRunningProject == null)
{
// error has been reported:
+ waitForFileChangeBeforeRestarting = false;
return;
}
@@ -257,6 +279,11 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
else
{
Context.Reporter.Verbose("Restarting without prompt since dotnet-watch is running in non-interactive mode.");
+
+ foreach (var project in projects)
+ {
+ Context.Reporter.Verbose($" Project to restart: '{project.Name}'");
+ }
}
}, iterationCancellationToken);
HotReloadEventSource.Log.HotReloadEnd(HotReloadEventSource.StartType.CompilationHandler);
@@ -280,7 +307,8 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
await Task.WhenAll(
projectsToRestart.Select(async runningProject =>
{
- var newRunningProject = await projectLauncher.LaunchProcessAsync(runningProject.Options, runningProject.ProjectNode, new CancellationTokenSource(), build: true, shutdownCancellationToken);
+ var newRunningProject = await runningProject.RestartOperation(build: true, shutdownCancellationToken);
+ runningProject.Dispose();
await newRunningProject.WaitForProcessRunningAsync(shutdownCancellationToken);
}))
.WaitAsync(shutdownCancellationToken);
@@ -304,10 +332,23 @@ await Task.WhenAll(
rootProcessTerminationSource.Cancel();
}
+ if (runtimeProcessLauncher != null)
+ {
+ // Request cleanup of all processes created by the launcher before we terminate the root process.
+ // Non-cancellable - can only be aborted by forced Ctrl+C, which immediately kills the dotnet-watch process.
+ await runtimeProcessLauncher.TerminateLaunchedProcessesAsync(CancellationToken.None);
+ }
+
+ if (compilationHandler != null)
+ {
+ // Non-cancellable - can only be aborted by forced Ctrl+C, which immediately kills the dotnet-watch process.
+ await compilationHandler.TerminateNonRootProcessesAndDispose(CancellationToken.None);
+ }
+
try
{
- // Wait for the root process to exit. Child processes will be terminated upon CompilationHandler disposal.
- await Task.WhenAll(new[] { rootRunningProject?.RunningProcess, fileSetWatcherTask }.Where(t => t != null)!);
+ // Wait for the root process to exit.
+ await Task.WhenAll(new[] { (Task?)rootRunningProject?.RunningProcess, fileSetWatcherTask }.Where(t => t != null)!);
}
catch (OperationCanceledException) when (!shutdownCancellationToken.IsCancellationRequested)
{
@@ -316,6 +357,12 @@ await Task.WhenAll(
finally
{
fileSetWatcherTask = null;
+
+ if (runtimeProcessLauncher != null)
+ {
+ await runtimeProcessLauncher.DisposeAsync();
+ }
+
rootRunningProject?.Dispose();
if (evaluationResult != null &&
@@ -381,9 +428,10 @@ private async ValueTask EvaluateRootProjectAsync(CancellationT
{
cancellationToken.ThrowIfCancellationRequested();
- var result = await RootFileSetFactory.TryCreateAsync(cancellationToken);
+ var result = await RootFileSetFactory.TryCreateAsync(requireProjectGraph: true, cancellationToken);
if (result != null)
{
+ Debug.Assert(result.ProjectGraph != null);
return result;
}
diff --git a/src/BuiltInTools/dotnet-watch/Internal/ConsoleReporter.cs b/src/BuiltInTools/dotnet-watch/Internal/ConsoleReporter.cs
index 61beec777935..0d1b8be10797 100644
--- a/src/BuiltInTools/dotnet-watch/Internal/ConsoleReporter.cs
+++ b/src/BuiltInTools/dotnet-watch/Internal/ConsoleReporter.cs
@@ -1,8 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Diagnostics;
-using Microsoft.Build.Tasks;
+using Microsoft.Build.Graph;
+using Microsoft.DotNet.Watcher.Internal;
namespace Microsoft.Extensions.Tools.Internal
{
@@ -18,10 +18,13 @@ internal sealed class ConsoleReporter(IConsole console, bool verbose, bool quiet
private readonly object _writeLock = new();
- public bool ReportProcessOutput
+ public bool EnableProcessOutputReporting
=> false;
- public void ProcessOutput(string projectPath, string data)
+ public void ReportProcessOutput(OutputLine line)
+ => throw new InvalidOperationException();
+
+ public void ReportProcessOutput(ProjectGraphNode project, OutputLine line)
=> throw new InvalidOperationException();
private void WriteLine(TextWriter writer, string message, ConsoleColor? color, string emoji)
diff --git a/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs b/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs
index 68bd365e4502..892d21e5b2e0 100644
--- a/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs
+++ b/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs
@@ -3,8 +3,10 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using Microsoft.Build.Graph;
using Microsoft.Build.Tasks;
using Microsoft.DotNet.Watcher;
+using Microsoft.DotNet.Watcher.Internal;
namespace Microsoft.Extensions.Tools.Internal
{
@@ -28,12 +30,24 @@ public bool HasMessage
[MemberNotNullWhen(true, nameof(Format), nameof(Emoji))]
public bool TryGetMessage(string? prefix, object?[] args, [NotNullWhen(true)] out string? message)
{
+ // Messages without Id are created by IReporter.Verbose|Output|Warn|Error helpers.
+ // They do not have arguments and we shouldn't interpret Format as a string with holes.
+ // Eventually, all messages should have a descriptor (so we can localize them) and this can be removed.
+ if (Id == null)
+ {
+ Debug.Assert(args is null or []);
+ Debug.Assert(HasMessage);
+ message = prefix + Format;
+ return true;
+ }
+
if (!HasMessage)
{
message = null;
return false;
}
+
message = prefix + string.Format(Format, args);
return true;
}
@@ -67,16 +81,18 @@ public bool TryGetMessage(string? prefix, object?[] args, [NotNullWhen(true)] ou
internal interface IReporter
{
void Report(MessageDescriptor descriptor, string prefix, object?[] args);
- void ProcessOutput(string projectPath, string data);
public bool IsVerbose
=> false;
///
- /// True to call when launched process writes to standard output.
+ /// True to call when launched process writes to standard output.
/// Used for testing.
///
- bool ReportProcessOutput { get; }
+ bool EnableProcessOutputReporting { get; }
+
+ void ReportProcessOutput(OutputLine line);
+ void ReportProcessOutput(ProjectGraphNode project, OutputLine line);
void Report(MessageDescriptor descriptor, params object?[] args)
=> Report(descriptor, prefix: "", args);
diff --git a/src/BuiltInTools/dotnet-watch/Internal/MessagePrefixingReporter.cs b/src/BuiltInTools/dotnet-watch/Internal/MessagePrefixingReporter.cs
deleted file mode 100644
index 5aa93ba59f34..000000000000
--- a/src/BuiltInTools/dotnet-watch/Internal/MessagePrefixingReporter.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Microsoft.Extensions.Tools.Internal;
-
-namespace Microsoft.DotNet.Watcher;
-
-internal sealed class MessagePrefixingReporter(string additionalPrefix, IReporter underlyingReporter) : IReporter
-{
- public bool IsVerbose
- => underlyingReporter.IsVerbose;
-
- public bool ReportProcessOutput
- => underlyingReporter.ReportProcessOutput;
-
- public void ProcessOutput(string projectPath, string data)
- => underlyingReporter.ProcessOutput(projectPath, data);
-
- public void Report(MessageDescriptor descriptor, string prefix, object?[] args)
- => underlyingReporter.Report(descriptor, additionalPrefix + prefix, args);
-}
diff --git a/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs b/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs
index 2e294b0b62f5..85405a76f4a7 100644
--- a/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs
+++ b/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs
@@ -1,9 +1,9 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-
using System.Diagnostics;
using System.Text.Json;
+using Microsoft.Build.Graph;
using Microsoft.DotNet.Watcher.Internal;
using Microsoft.Extensions.Tools.Internal;
@@ -21,62 +21,42 @@ namespace Microsoft.DotNet.Watcher.Tools
internal class MSBuildFileSetFactory(
string rootProjectFile,
string? targetFramework,
- IReadOnlyList<(string, string)>? buildProperties,
+ IReadOnlyList<(string name, string value)> buildProperties,
EnvironmentOptions environmentOptions,
- IReporter reporter,
- OutputSink? outputSink,
- bool trace)
+ IReporter reporter)
{
private const string TargetName = "GenerateWatchList";
private const string WatchTargetsFileName = "DotNetWatch.targets";
- private readonly OutputSink _outputSink = outputSink ?? new OutputSink();
- private readonly IReadOnlyList _buildFlags = InitializeArgs(FindTargetsFile(), targetFramework, buildProperties, trace);
-
public string RootProjectFile => rootProjectFile;
// Virtual for testing.
- public virtual async ValueTask TryCreateAsync(CancellationToken cancellationToken)
+ public virtual async ValueTask TryCreateAsync(bool? requireProjectGraph, CancellationToken cancellationToken)
{
var watchList = Path.GetTempFileName();
try
{
var projectDir = Path.GetDirectoryName(rootProjectFile);
-
- var capture = _outputSink.StartCapture();
- var arguments = new List
- {
- "msbuild",
- "/nologo",
- rootProjectFile,
- $"/p:_DotNetWatchListFile={watchList}",
- };
-
-#if !DEBUG
- if (environmentOptions.TestFlags.HasFlag(TestFlags.RunningAsTest))
-#endif
- {
- arguments.Add("/bl");
- }
-
- if (environmentOptions.SuppressHandlingStaticContentFiles)
- {
- arguments.Add("/p:DotNetWatchContentFiles=false");
- }
-
- arguments.AddRange(_buildFlags);
+ var arguments = GetMSBuildArguments(watchList);
+ var capturedOutput = new List();
var processSpec = new ProcessSpec
{
Executable = environmentOptions.MuxerPath,
WorkingDirectory = projectDir,
Arguments = arguments,
- OutputCapture = capture
+ OnOutput = line =>
+ {
+ lock (capturedOutput)
+ {
+ capturedOutput.Add(line);
+ }
+ }
};
reporter.Verbose($"Running MSBuild target '{TargetName}' on '{rootProjectFile}'");
- var exitCode = await ProcessRunner.RunAsync(processSpec, reporter, isUserApplication: false, processExitedSource: null, cancellationToken);
+ var exitCode = await ProcessRunner.RunAsync(processSpec, reporter, isUserApplication: false, launchResult: null, cancellationToken);
if (exitCode != 0 || !File.Exists(watchList))
{
@@ -85,9 +65,17 @@ internal class MSBuildFileSetFactory(
reporter.Output($"MSBuild output from target '{TargetName}':");
reporter.Output(string.Empty);
- foreach (var line in capture.Lines)
+ foreach (var (line, isError) in capturedOutput)
{
- reporter.Output($" {line}");
+ var message = " " + line;
+ if (isError)
+ {
+ reporter.Error(message);
+ }
+ else
+ {
+ reporter.Output(message);
+ }
}
reporter.Output(string.Empty);
@@ -142,7 +130,18 @@ void AddFile(string filePath, string? staticWebAssetPath)
Debug.Assert(fileItems.Values.All(f => Path.IsPathRooted(f.FilePath)), "All files should be rooted paths");
#endif
- return new EvaluationResult(fileItems);
+ // Load the project graph after the project has been restored:
+ ProjectGraph? projectGraph = null;
+ if (requireProjectGraph != null)
+ {
+ projectGraph = TryLoadProjectGraph(requireProjectGraph.Value);
+ if (projectGraph == null && requireProjectGraph == true)
+ {
+ return null;
+ }
+ }
+
+ return new EvaluationResult(fileItems, projectGraph);
}
finally
{
@@ -150,36 +149,49 @@ void AddFile(string filePath, string? staticWebAssetPath)
}
}
- private static IReadOnlyList InitializeArgs(string watchTargetsFile, string? targetFramework, IReadOnlyList<(string name, string value)>? buildProperties, bool trace)
+ private IReadOnlyList GetMSBuildArguments(string watchListFilePath)
{
- var args = new List
+ var watchTargetsFile = FindTargetsFile();
+
+ var arguments = new List
{
+ "msbuild",
+ "/restore",
"/nologo",
- "/v:n",
- "/t:" + TargetName,
- "/p:DotNetWatchBuild=true", // extensibility point for users
- "/p:DesignTimeBuild=true", // don't do expensive things
- "/p:CustomAfterMicrosoftCommonTargets=" + watchTargetsFile,
- "/p:CustomAfterMicrosoftCommonCrossTargetingTargets=" + watchTargetsFile,
+ "/v:m",
+ rootProjectFile,
+ "/t:" + TargetName
};
- if (targetFramework != null)
+#if !DEBUG
+ if (environmentOptions.TestFlags.HasFlag(TestFlags.RunningAsTest))
+#endif
{
- args.Add("/p:TargetFramework=" + targetFramework);
+ arguments.Add("/bl:DotnetWatch.GenerateWatchList.binlog");
}
- if (buildProperties != null)
+ arguments.AddRange(buildProperties.Select(p => $"/p:{p.name}={p.value}"));
+
+ // Set dotnet-watch reserved properties after the user specified propeties,
+ // so that the former take precedence.
+
+ if (environmentOptions.SuppressHandlingStaticContentFiles)
{
- args.AddRange(buildProperties.Select(p => $"/p:{p.name}={p.value}"));
+ arguments.Add("/p:DotNetWatchContentFiles=false");
}
- if (trace)
+ if (targetFramework != null)
{
- // enables capturing markers to know which projects have been visited
- args.Add("/p:_DotNetWatchTraceOutput=true");
+ arguments.Add("/p:TargetFramework=" + targetFramework);
}
- return args;
+ arguments.Add("/p:_DotNetWatchListFile=" + watchListFilePath);
+ arguments.Add("/p:DotNetWatchBuild=true"); // extensibility point for users
+ arguments.Add("/p:DesignTimeBuild=true"); // don't do expensive things
+ arguments.Add("/p:CustomAfterMicrosoftCommonTargets=" + watchTargetsFile);
+ arguments.Add("/p:CustomAfterMicrosoftCommonCrossTargetingTargets=" + watchTargetsFile);
+
+ return arguments;
}
private static string FindTargetsFile()
@@ -198,5 +210,55 @@ private static string FindTargetsFile()
var targetPath = searchPaths.Select(p => Path.Combine(p, WatchTargetsFileName)).FirstOrDefault(File.Exists);
return targetPath ?? throw new FileNotFoundException("Fatal error: could not find DotNetWatch.targets");
}
+
+ // internal for testing
+ internal ProjectGraph? TryLoadProjectGraph(bool projectGraphRequired)
+ {
+ var globalOptions = new Dictionary();
+ if (targetFramework != null)
+ {
+ globalOptions.Add("TargetFramework", targetFramework);
+ }
+
+ foreach (var (name, value) in buildProperties)
+ {
+ globalOptions[name] = value;
+ }
+
+ try
+ {
+ return new ProjectGraph(rootProjectFile, globalOptions);
+ }
+ catch (Exception e)
+ {
+ reporter.Verbose("Failed to load project graph.");
+
+ if (e is AggregateException { InnerExceptions: var innerExceptions })
+ {
+ foreach (var inner in innerExceptions)
+ {
+ Report(inner);
+ }
+ }
+ else
+ {
+ Report(e);
+ }
+
+ void Report(Exception e)
+ {
+ if (projectGraphRequired)
+ {
+ reporter.Error(e.Message);
+ }
+ else
+ {
+ reporter.Warn(e.Message);
+ }
+ }
+ }
+
+ return null;
+ }
}
}
diff --git a/src/BuiltInTools/dotnet-watch/Internal/NullReporter.cs b/src/BuiltInTools/dotnet-watch/Internal/NullReporter.cs
index fc5f95a7d3c8..6812973c3b80 100644
--- a/src/BuiltInTools/dotnet-watch/Internal/NullReporter.cs
+++ b/src/BuiltInTools/dotnet-watch/Internal/NullReporter.cs
@@ -1,6 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.Build.Graph;
+using Microsoft.DotNet.Watcher.Internal;
+
namespace Microsoft.Extensions.Tools.Internal
{
///
@@ -14,9 +17,15 @@ private NullReporter()
public static IReporter Singleton { get; } = new NullReporter();
- public bool ReportProcessOutput => false;
+ public bool EnableProcessOutputReporting
+ => false;
+
+ public void ReportProcessOutput(OutputLine line)
+ => throw new InvalidOperationException();
+
+ public void ReportProcessOutput(ProjectGraphNode project, OutputLine line)
+ => throw new InvalidOperationException();
- public void ProcessOutput(string projectPath, string data) => throw new InvalidOperationException();
public void Report(MessageDescriptor descriptor, string prefix, object?[] args)
{
diff --git a/src/BuiltInTools/dotnet-watch/Internal/OutputCapture.cs b/src/BuiltInTools/dotnet-watch/Internal/OutputCapture.cs
deleted file mode 100644
index 73b1995bc736..000000000000
--- a/src/BuiltInTools/dotnet-watch/Internal/OutputCapture.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.DotNet.Watcher.Internal
-{
- internal sealed class OutputCapture
- {
- private readonly List _lines = new();
- public IEnumerable Lines => _lines;
- public void AddLine(string line) => _lines.Add(line);
- }
-}
diff --git a/src/BuiltInTools/dotnet-watch/Internal/OutputLine.cs b/src/BuiltInTools/dotnet-watch/Internal/OutputLine.cs
new file mode 100644
index 000000000000..f80037321819
--- /dev/null
+++ b/src/BuiltInTools/dotnet-watch/Internal/OutputLine.cs
@@ -0,0 +1,6 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.DotNet.Watcher.Internal;
+
+internal readonly record struct OutputLine(string Content, bool IsError);
diff --git a/src/BuiltInTools/dotnet-watch/Internal/OutputSink.cs b/src/BuiltInTools/dotnet-watch/Internal/OutputSink.cs
deleted file mode 100644
index d625dbb6899f..000000000000
--- a/src/BuiltInTools/dotnet-watch/Internal/OutputSink.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.DotNet.Watcher.Internal
-{
- internal sealed class OutputSink
- {
- public OutputCapture? Current { get; private set; }
- public OutputCapture StartCapture()
- {
- return (Current = new OutputCapture());
- }
- }
-}
diff --git a/src/BuiltInTools/dotnet-watch/Internal/ProcessRunner.cs b/src/BuiltInTools/dotnet-watch/Internal/ProcessRunner.cs
index d3e07e8bd62e..06d9fd194327 100644
--- a/src/BuiltInTools/dotnet-watch/Internal/ProcessRunner.cs
+++ b/src/BuiltInTools/dotnet-watch/Internal/ProcessRunner.cs
@@ -9,141 +9,166 @@ namespace Microsoft.DotNet.Watcher.Internal
{
internal sealed class ProcessRunner
{
+ private const int SIGKILL = 9;
+ private const int SIGTERM = 15;
+
+ private sealed class ProcessState
+ {
+ public int ProcessId;
+ public bool HasExited;
+ public bool ForceExit;
+ }
+
///
/// Launches a process.
///
/// True if the process is a user application, false if it is a helper process (e.g. msbuild).
- public static async Task RunAsync(ProcessSpec processSpec, IReporter reporter, bool isUserApplication, CancellationTokenSource? processExitedSource, CancellationToken processTerminationToken)
+ public static async Task RunAsync(ProcessSpec processSpec, IReporter reporter, bool isUserApplication, ProcessLaunchResult? launchResult, CancellationToken processTerminationToken)
{
Ensure.NotNull(processSpec, nameof(processSpec));
+ var state = new ProcessState();
var stopwatch = new Stopwatch();
- using var process = CreateProcess(processSpec);
- using var processState = new ProcessState(process, reporter);
+ var onOutput = processSpec.OnOutput;
- processTerminationToken.Register(() => processState.TryKill());
-
- var readOutput = false;
- var readError = false;
- if (processSpec.IsOutputCaptured)
+ // allow tests to watch for application output:
+ if (reporter.EnableProcessOutputReporting)
{
- readOutput = true;
- readError = true;
+ onOutput += line => reporter.ReportProcessOutput(line);
+ }
- process.OutputDataReceived += (_, a) =>
- {
- if (!string.IsNullOrEmpty(a.Data))
- {
- processSpec.OutputCapture.AddLine(a.Data);
- }
- };
+ using var process = CreateProcess(processSpec, onOutput, state, reporter);
- process.ErrorDataReceived += (_, a) =>
- {
- if (!string.IsNullOrEmpty(a.Data))
- {
- processSpec.OutputCapture.AddLine(a.Data);
- }
- };
- }
- else if (processSpec.OnOutput != null)
- {
- readOutput = true;
- process.OutputDataReceived += processSpec.OnOutput;
- }
+ processTerminationToken.Register(() => TerminateProcess(process, state, reporter));
stopwatch.Start();
- int? processId = null;
+ Exception? launchException = null;
try
{
- if (process.Start())
+ if (!process.Start())
{
- processId = process.Id;
+ throw new InvalidOperationException("Process can't be started.");
}
- }
- finally
- {
- var argsDisplay = processSpec.GetArgumentsDisplay();
- if (processId.HasValue)
- {
- reporter.Report(MessageDescriptor.LaunchedProcess, processSpec.Executable, argsDisplay, processId.Value);
- }
- else
+ state.ProcessId = process.Id;
+
+ if (onOutput != null)
{
- reporter.Error($"Failed to launch '{processSpec.Executable}' with arguments '{argsDisplay}'");
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
}
}
+ catch (Exception e)
+ {
+ launchException = e;
+ }
- if (readOutput)
+ var argsDisplay = processSpec.GetArgumentsDisplay();
+ if (launchException == null)
+ {
+ reporter.Report(MessageDescriptor.LaunchedProcess, processSpec.Executable, argsDisplay, state.ProcessId);
+ }
+ else
{
- process.BeginOutputReadLine();
+ reporter.Error($"Failed to launch '{processSpec.Executable}' with arguments '{argsDisplay}': {launchException.Message}");
+ return int.MinValue;
}
- if (readError)
+ if (launchResult != null)
{
- process.BeginErrorReadLine();
+ launchResult.ProcessId = process.Id;
}
int? exitCode = null;
- var failed = false;
try
{
- await processState.Task;
+ try
+ {
+ await process.WaitForExitAsync(processTerminationToken);
+ }
+ catch (OperationCanceledException)
+ {
+ // Process termination requested via cancellation token.
+ // Wait for the actual process exit.
+ while (true)
+ {
+ try
+ {
+ // non-cancellable to not leave orphaned processes around blocking resources:
+ await process.WaitForExitAsync(CancellationToken.None).WaitAsync(TimeSpan.FromSeconds(5), CancellationToken.None);
+ break;
+ }
+ catch (TimeoutException)
+ {
+ // nop
+ }
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || state.ForceExit)
+ {
+ reporter.Output($"Waiting for process {state.ProcessId} to exit ...");
+ }
+ else
+ {
+ reporter.Output($"Forcing process {state.ProcessId} to exit ...");
+ }
+
+ state.ForceExit = true;
+ }
+ }
}
- catch (Exception e) when (e is not OperationCanceledException)
+ catch (Exception e)
{
- failed = true;
-
if (isUserApplication)
{
- reporter.Error($"Application failed to launch: {e.Message}");
+ reporter.Error($"Application failed: {e.Message}");
}
}
finally
{
stopwatch.Stop();
- if (!failed && !processTerminationToken.IsCancellationRequested)
+ state.HasExited = true;
+
+ try
{
- try
+ exitCode = process.ExitCode;
+ }
+ catch
+ {
+ exitCode = null;
+ }
+
+ reporter.Verbose($"Process id {process.Id} ran for {stopwatch.ElapsedMilliseconds}ms and exited with exit code {exitCode}.");
+
+ if (isUserApplication)
+ {
+ if (exitCode == 0)
{
- exitCode = process.ExitCode;
+ reporter.Output("Exited");
}
- catch
+ else if (exitCode == null)
{
- exitCode = null;
+ reporter.Error("Exited with unknown error code");
}
-
- reporter.Verbose($"Process id {process.Id} ran for {stopwatch.ElapsedMilliseconds}ms.");
-
- if (isUserApplication)
+ else
{
- if (exitCode == 0)
- {
- reporter.Output("Exited");
- }
- else if (exitCode == null)
- {
- reporter.Error("Exited with unknown error code");
- }
- else
- {
- reporter.Error($"Exited with error code {exitCode}");
- }
+ reporter.Error($"Exited with error code {exitCode}");
}
}
- processExitedSource?.Cancel();
+ if (processSpec.OnExit != null)
+ {
+ await processSpec.OnExit(state.ProcessId, exitCode);
+ }
}
return exitCode ?? int.MinValue;
}
- private static Process CreateProcess(ProcessSpec processSpec)
+ private static Process CreateProcess(ProcessSpec processSpec, Action? onOutput, ProcessState state, IReporter reporter)
{
var process = new Process
{
@@ -153,8 +178,8 @@ private static Process CreateProcess(ProcessSpec processSpec)
FileName = processSpec.Executable,
UseShellExecute = false,
WorkingDirectory = processSpec.WorkingDirectory,
- RedirectStandardOutput = processSpec.IsOutputCaptured || (processSpec.OnOutput != null),
- RedirectStandardError = processSpec.IsOutputCaptured,
+ RedirectStandardOutput = onOutput != null,
+ RedirectStandardError = onOutput != null,
}
};
@@ -175,83 +200,77 @@ private static Process CreateProcess(ProcessSpec processSpec)
process.StartInfo.Environment.Add(env.Key, env.Value);
}
- return process;
- }
-
- private sealed class ProcessState : IDisposable
- {
- private readonly IReporter _reporter;
- private readonly Process _process;
- private readonly TaskCompletionSource _processExitedCompletionSource = new();
- private volatile bool _disposed;
-
- public readonly Task Task;
-
- public ProcessState(Process process, IReporter reporter)
+ if (onOutput != null)
{
- _reporter = reporter;
- _process = process;
- _process.Exited += OnExited;
- Task = _processExitedCompletionSource.Task.ContinueWith(_ =>
+ process.OutputDataReceived += (_, args) =>
{
try
{
- // We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously
- // this code used Process.Exited, which could result in us missing some output due to the ordering of
- // events.
- //
- // See the remarks here: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit#System_Diagnostics_Process_WaitForExit_System_Int32_
- if (!_process.WaitForExit(int.MaxValue))
+ if (args.Data != null)
{
- throw new TimeoutException();
+ onOutput(new OutputLine(args.Data, IsError: false));
}
+ }
+ catch (Exception e)
+ {
+ reporter.Verbose($"Error reading stdout of process {state.ProcessId}: {e}");
+ }
+ };
- _process.WaitForExit();
+ process.ErrorDataReceived += (_, args) =>
+ {
+ try
+ {
+ if (args.Data != null)
+ {
+ onOutput(new OutputLine(args.Data, IsError: true));
+ }
}
- catch (InvalidOperationException)
+ catch (Exception e)
{
- // suppress if this throws if no process is associated with this object anymore.
+ reporter.Verbose($"Error reading stderr of process {state.ProcessId}: {e}");
}
- });
+ };
}
- public void TryKill()
+ return process;
+ }
+
+ private static void TerminateProcess(Process process, ProcessState state, IReporter reporter)
+ {
+ try
{
- if (_disposed)
+ if (!state.HasExited && !process.HasExited)
{
- return;
- }
+ reporter.Report(MessageDescriptor.KillingProcess, state.ProcessId.ToString());
- try
- {
- if (!_process.HasExited)
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- _reporter.Report(MessageDescriptor.KillingProcess, _process.Id);
- _process.Kill(entireProcessTree: true);
+ process.Kill();
}
- }
- catch (Exception ex)
- {
- _reporter.Verbose($"Error while killing process '{_process.StartInfo.FileName} {_process.StartInfo.Arguments}': {ex.Message}");
-#if DEBUG
- _reporter.Verbose(ex.ToString());
-#endif
- }
- }
+ else
+ {
+ [DllImport("libc", SetLastError = true, EntryPoint = "kill")]
+ static extern int sys_kill(int pid, int sig);
- private void OnExited(object? sender, EventArgs args)
- => _processExitedCompletionSource.TrySetResult();
+ var result = sys_kill(state.ProcessId, state.ForceExit ? SIGKILL : SIGTERM);
+ if (result != 0)
+ {
+ var error = Marshal.GetLastPInvokeError();
+ reporter.Verbose($"Error while sending SIGTERM to process {state.ProcessId}: {Marshal.GetPInvokeErrorMessage(error)} (code {error}).");
+ }
+ }
- public void Dispose()
- {
- if (!_disposed)
- {
- TryKill();
- _disposed = true;
- _process.Exited -= OnExited;
- _process.Dispose();
+ reporter.Verbose($"Process {state.ProcessId} killed.");
}
}
+ catch (Exception ex)
+ {
+ reporter.Verbose($"Error while killing process {state.ProcessId}: {ex.Message}");
+#if DEBUG
+ reporter.Verbose(ex.ToString());
+#endif
+ }
}
}
}
diff --git a/src/BuiltInTools/dotnet-watch/Internal/ProjectSpecificReporter.cs b/src/BuiltInTools/dotnet-watch/Internal/ProjectSpecificReporter.cs
new file mode 100644
index 000000000000..a46b9d078904
--- /dev/null
+++ b/src/BuiltInTools/dotnet-watch/Internal/ProjectSpecificReporter.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Build.Graph;
+using Microsoft.DotNet.Watcher.Internal;
+using Microsoft.Extensions.Tools.Internal;
+
+namespace Microsoft.DotNet.Watcher;
+
+internal sealed class ProjectSpecificReporter(ProjectGraphNode node, IReporter underlyingReporter) : IReporter
+{
+ private readonly string _projectDisplayName = node.GetDisplayName();
+
+ public bool IsVerbose
+ => underlyingReporter.IsVerbose;
+
+ public bool EnableProcessOutputReporting
+ => underlyingReporter.EnableProcessOutputReporting;
+
+ public void ReportProcessOutput(ProjectGraphNode project, OutputLine line)
+ => underlyingReporter.ReportProcessOutput(project, line);
+
+ public void ReportProcessOutput(OutputLine line)
+ => ReportProcessOutput(node, line);
+
+ public void Report(MessageDescriptor descriptor, string prefix, object?[] args)
+ => underlyingReporter.Report(descriptor, $"[{_projectDisplayName}] {prefix}", args);
+}
diff --git a/src/BuiltInTools/dotnet-watch/ProcessLaunchResult.cs b/src/BuiltInTools/dotnet-watch/ProcessLaunchResult.cs
new file mode 100644
index 000000000000..3c58c69946a9
--- /dev/null
+++ b/src/BuiltInTools/dotnet-watch/ProcessLaunchResult.cs
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.DotNet.Watcher
+{
+ internal sealed class ProcessLaunchResult
+ {
+ public int? ProcessId { get; set; }
+ }
+}
diff --git a/src/BuiltInTools/dotnet-watch/ProcessSpec.cs b/src/BuiltInTools/dotnet-watch/ProcessSpec.cs
index fbf4dfa74c0e..c6b651c91b55 100644
--- a/src/BuiltInTools/dotnet-watch/ProcessSpec.cs
+++ b/src/BuiltInTools/dotnet-watch/ProcessSpec.cs
@@ -1,9 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
using Microsoft.DotNet.Watcher.Internal;
namespace Microsoft.DotNet.Watcher
@@ -15,13 +12,10 @@ internal sealed class ProcessSpec
public Dictionary EnvironmentVariables { get; } = new();
public IReadOnlyList? Arguments { get; set; }
public string? EscapedArguments { get; set; }
- public OutputCapture? OutputCapture { get; set; }
- public DataReceivedEventHandler? OnOutput { get; set; }
+ public Action? OnOutput { get; set; }
+ public ProcessExitAction? OnExit { get; set; }
public CancellationToken CancelOutputCapture { get; set; }
- [MemberNotNullWhen(true, nameof(OutputCapture))]
- public bool IsOutputCaptured => OutputCapture != null;
-
public string? ShortDisplayName()
=> Path.GetFileNameWithoutExtension(Executable);
diff --git a/src/BuiltInTools/dotnet-watch/Program.cs b/src/BuiltInTools/dotnet-watch/Program.cs
index d6cfaa5b212a..75106f6e67f8 100644
--- a/src/BuiltInTools/dotnet-watch/Program.cs
+++ b/src/BuiltInTools/dotnet-watch/Program.cs
@@ -97,27 +97,27 @@ public static async Task Main(string[] args)
// internal for testing
internal async Task RunAsync()
{
- var cancellationSource = new CancellationTokenSource();
- var cancellationToken = cancellationSource.Token;
+ var shutdownCancellationSource = new CancellationTokenSource();
+ var shutdownCancellationToken = shutdownCancellationSource.Token;
console.CancelKeyPress += OnCancelKeyPress;
try
{
- if (cancellationToken.IsCancellationRequested)
+ if (shutdownCancellationToken.IsCancellationRequested)
{
return 1;
}
if (options.List)
{
- return await ListFilesAsync(cancellationToken);
+ return await ListFilesAsync(shutdownCancellationToken);
}
var watcher = CreateWatcher(runtimeProcessLauncherFactory: null);
- await watcher.WatchAsync(cancellationToken);
+ await watcher.WatchAsync(shutdownCancellationToken);
return 0;
}
- catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
+ catch (OperationCanceledException) when (shutdownCancellationToken.IsCancellationRequested)
{
// Ctrl+C forced an exit
return 0;
@@ -131,20 +131,24 @@ internal async Task RunAsync()
finally
{
console.CancelKeyPress -= OnCancelKeyPress;
- cancellationSource.Dispose();
+ shutdownCancellationSource.Dispose();
}
void OnCancelKeyPress(object? sender, ConsoleCancelEventArgs args)
{
- // suppress CTRL+C on the first press
- args.Cancel = !cancellationSource.IsCancellationRequested;
+ // if we already canceled, we force immediate shutdown:
+ var forceShutdown = shutdownCancellationSource.IsCancellationRequested;
- if (args.Cancel)
+ if (!forceShutdown)
{
reporter.Report(MessageDescriptor.ShutdownRequested);
+ shutdownCancellationSource.Cancel();
+ args.Cancel = true;
+ }
+ else
+ {
+ Environment.Exit(0);
}
-
- cancellationSource.Cancel();
}
}
@@ -156,21 +160,12 @@ internal Watcher CreateWatcher(IRuntimeProcessLauncherFactory? runtimeProcessLau
reporter.Output("Polling file watcher is enabled");
}
- var projectGraph = TryReadProject(rootProjectOptions, reporter);
- if (projectGraph != null)
- {
- // use normalized MSBuild path so that we can index into the ProjectGraph
- rootProjectOptions = rootProjectOptions with { ProjectPath = projectGraph.GraphRoots.Single().ProjectInstance.FullPath };
- }
-
var fileSetFactory = new MSBuildFileSetFactory(
rootProjectOptions.ProjectPath,
rootProjectOptions.TargetFramework,
rootProjectOptions.BuildProperties,
environmentOptions,
- reporter,
- outputSink: null,
- trace: true);
+ reporter);
bool enableHotReload;
if (rootProjectOptions.Command != "run")
@@ -191,7 +186,6 @@ internal Watcher CreateWatcher(IRuntimeProcessLauncherFactory? runtimeProcessLau
var context = new DotNetWatchContext
{
- ProjectGraph = projectGraph,
Reporter = reporter,
Options = options.GlobalOptions,
EnvironmentOptions = environmentOptions,
@@ -203,33 +197,6 @@ internal Watcher CreateWatcher(IRuntimeProcessLauncherFactory? runtimeProcessLau
: new DotNetWatcher(context, fileSetFactory);
}
- // internal for testing
- internal static ProjectGraph? TryReadProject(ProjectOptions options, IReporter reporter)
- {
- var globalOptions = new Dictionary();
- if (options.TargetFramework != null)
- {
- globalOptions.Add("TargetFramework", options.TargetFramework);
- }
-
- foreach (var (name, value) in options.BuildProperties)
- {
- globalOptions[name] = value;
- }
-
- try
- {
- return new ProjectGraph(options.ProjectPath, globalOptions);
- }
- catch (Exception ex)
- {
- reporter.Verbose("Reading the project instance failed.");
- reporter.Verbose(ex.ToString());
- }
-
- return null;
- }
-
private async Task ListFilesAsync(CancellationToken cancellationToken)
{
var fileSetFactory = new MSBuildFileSetFactory(
@@ -237,11 +204,9 @@ private async Task ListFilesAsync(CancellationToken cancellationToken)
rootProjectOptions.TargetFramework,
rootProjectOptions.BuildProperties,
environmentOptions,
- reporter,
- outputSink: null,
- trace: false);
+ reporter);
- if (await fileSetFactory.TryCreateAsync(cancellationToken) is not { } evaluationResult)
+ if (await fileSetFactory.TryCreateAsync(requireProjectGraph: null, cancellationToken) is not { } evaluationResult)
{
return 1;
}
diff --git a/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json b/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json
index 048a4fa86026..dafd8c0ab7ef 100644
--- a/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json
+++ b/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json
@@ -2,7 +2,7 @@
"profiles": {
"dotnet-watch": {
"commandName": "Project",
- "commandLineArgs": "--verbose",
+ "commandLineArgs": "--verbose /bl:DotnetRun.binlog",
"workingDirectory": "$(RepoRoot)src\\Assets\\TestProjects\\BlazorWasmWithLibrary\\blazorwasm",
"environmentVariables": {
"DOTNET_WATCH_DEBUG_SDK_DIRECTORY": "$(RepoRoot)artifacts\\bin\\redist\\$(Configuration)\\dotnet\\sdk\\$(Version)"
diff --git a/src/BuiltInTools/dotnet-watch/Watcher.cs b/src/BuiltInTools/dotnet-watch/Watcher.cs
index 743a25b627f4..b24a93f700e5 100644
--- a/src/BuiltInTools/dotnet-watch/Watcher.cs
+++ b/src/BuiltInTools/dotnet-watch/Watcher.cs
@@ -10,6 +10,6 @@ internal abstract class Watcher(DotNetWatchContext context, MSBuildFileSetFactor
public DotNetWatchContext Context => context;
public MSBuildFileSetFactory RootFileSetFactory => rootFileSetFactory;
- public abstract Task WatchAsync(CancellationToken cancellationToken);
+ public abstract Task WatchAsync(CancellationToken shutdownCancellationToken);
}
}
diff --git a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
index f405fff96f9b..299353a7fb71 100644
--- a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
+++ b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
@@ -1,4 +1,5 @@
+
@@ -32,6 +33,7 @@
+
diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/Commands/details/DetailsCommand.cs b/src/Cli/Microsoft.TemplateEngine.Cli/Commands/details/DetailsCommand.cs
index 8a73099ea518..0f49518164f7 100644
--- a/src/Cli/Microsoft.TemplateEngine.Cli/Commands/details/DetailsCommand.cs
+++ b/src/Cli/Microsoft.TemplateEngine.Cli/Commands/details/DetailsCommand.cs
@@ -10,8 +10,6 @@ namespace Microsoft.TemplateEngine.Cli.Commands
{
internal class DetailsCommand : BaseCommand
{
- private static NugetApiManager _nugetApiManager = new();
-
internal DetailsCommand(
Func hostBuilder)
: base(hostBuilder, "details", SymbolStrings.Command_Details_Description)
@@ -52,7 +50,7 @@ protected override async Task ExecuteAsync(
args.VersionCriteria,
args.Interactive,
args.AdditionalSources,
- _nugetApiManager,
+ new NugetApiManager(),
cancellationToken).ConfigureAwait(false);
await CheckTemplatesWithSubCommandName(args, templatePackageManager, cancellationToken).ConfigureAwait(false);
diff --git a/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs b/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs
index 018f4718ae9f..1f2342e78bdd 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs
+++ b/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs
@@ -348,7 +348,7 @@ private IEnumerable LoadOverrideSources(PackageSourceLocation pac
private List LoadDefaultSources(PackageId packageId, PackageSourceLocation packageSourceLocation = null, PackageSourceMapping packageSourceMapping = null)
{
List defaultSources = new();
- string currentDirectory = Directory.GetCurrentDirectory();
+ string currentDirectory = _currentWorkingDirectory ?? Directory.GetCurrentDirectory();
ISettings settings;
if (packageSourceLocation?.NugetConfig != null)
{
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/1028/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/1028/bundle.wxl
index 9d5177d0e99b..fc6e58cd73c5 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/1028/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/1028/bundle.wxl
@@ -4,7 +4,7 @@
確定要取消嗎?前一版安裝說明
- /install | /repair | /uninstall | /layout [\[]"directory"[\]] - 安裝、修復、解除安裝
+ /install | /repair | /uninstall | /layout [\[]"directory"[\]] - 安裝、修復、解除安裝
或在目錄中建立套件組合的完整本機複本。'/install' 是預設值。
/passive | /quiet - 顯示最基本的 UI 且不出現提示,或是不顯示任何 UI 且
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/1029/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/1029/bundle.wxl
index c381ddff74d5..6189bb220753 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/1029/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/1029/bundle.wxl
@@ -4,7 +4,7 @@
Opravdu chcete akci zrušit?Předchozí verzeNápověda nastavení
- /install | /repair | /uninstall | /layout [\[]"adresář"[\]] – Nainstaluje, opraví, odinstaluje
+ /install | /repair | /uninstall | /layout [\[]"adresář"[\]] – Nainstaluje, opraví, odinstaluje
nebo vytvoří úplnou místní kopii sady v adresáři. Výchozí nastavení je /install.
/passive | /quiet – Zobrazí minimální uživatelské rozhraní bez jakýchkoli výzev nebo nezobrazí žádné uživatelské rozhraní ani
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/1031/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/1031/bundle.wxl
index 7de6e9ba6052..feb24bba7898 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/1031/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/1031/bundle.wxl
@@ -4,7 +4,7 @@
Möchten Sie den Vorgang abbrechen?Vorherige VersionSetup-Hilfe
- /install | /repair | /uninstall | /layout [\[]„Verzeichnis“[\]] – installiert, repariert, deinstalliert
+ /install | /repair | /uninstall | /layout [\[]„Verzeichnis“[\]] – installiert, repariert, deinstalliert
oder erstellt eine vollständige lokale Kopie des Bündels im Verzeichnis. „/install“ ist die Standardeinstellung.
/passive | /quiet – Zeigt eine minimale Benutzeroberfläche ohne Eingabeaufforderungen an oder zeigt weder eine Benutzeroberfläche noch
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/1036/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/1036/bundle.wxl
index f75f57e6aa2b..667dddd946d5 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/1036/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/1036/bundle.wxl
@@ -4,7 +4,7 @@
Voulez-vous vraiment annuler ?Version précédenteAide du programme d'installation
- /install | /repair | /uninstall | /layout [\[]"directory"[\]] : installe, répare, désinstalle
+ /install | /repair | /uninstall | /layout [\[]"directory"[\]] : installe, répare, désinstalle
ou crée une copie locale complète de l’offre groupée dans le répertoire. '/install' est la valeur par défaut.
/passive | /quiet – affiche une interface utilisateur minimale sans invites ou n’affiche ni interface utilisateur et
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/1040/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/1040/bundle.wxl
index bb100ae4aea8..14fd33a2e7cd 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/1040/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/1040/bundle.wxl
@@ -4,7 +4,7 @@
Annullare?Versione precedenteGuida all'installazione
- /install | /repair | /uninstall | /layout [\[]"directory"[\]] - installa, ripristina, disinstalla
+ /install | /repair | /uninstall | /layout [\[]"directory"[\]] - installa, ripristina, disinstalla
oppure crea una copia locale completa del pacchetto nella directory. '/install' è l'impostazione predefinita.
/passive | /quiet - consente di visualizzare un'interfaccia utente minima senza messaggi o di non visualizzare alcuna interfaccia utente
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/1041/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/1041/bundle.wxl
index 059f00ef8344..cb009f405560 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/1041/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/1041/bundle.wxl
@@ -4,7 +4,7 @@
取り消しますか?以前のバージョンセットアップのヘルプ
- /install | /repair | /uninstall | /layout [\[]"directory"[\]] - インストール、修復、アンインストール
+ /install | /repair | /uninstall | /layout [\[]"directory"[\]] - インストール、修復、アンインストール
または、バンドルの完全なローカル コピーをディレクトリに作成します。'/install' が既定値です。
/passive | /quiet - 最小限の UI をプロンプトなしで表示するか、UI と
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/1042/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/1042/bundle.wxl
index 3835ff78b512..89c481c3f602 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/1042/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/1042/bundle.wxl
@@ -4,7 +4,7 @@
취소하시겠습니까?이전 버전설치 도움말
- /install | /repair | /uninstall | /layout [\[]"directory"[\]] - 설치, 복구, 제거
+ /install | /repair | /uninstall | /layout [\[]"directory"[\]] - 설치, 복구, 제거
또는 디렉터리에 번들의 전체 로컬 복사본을 만듭니다. '/install'이 기본값입니다.
/passive | /quiet - 프롬프트 없이 최소 UI를 표시하거나 UI 및
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/1045/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/1045/bundle.wxl
index bbc01b58d900..2cb469ae7d21 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/1045/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/1045/bundle.wxl
@@ -4,7 +4,7 @@
Czy na pewno chcesz anulować?Poprzednia wersjaInstalator — Pomoc
- /install | /repair | /uninstall | /layout [\[]directory[\]] — instaluje, naprawia, odinstalowuje
+ /install | /repair | /uninstall | /layout [\[]directory[\]] — instaluje, naprawia, odinstalowuje
lub tworzy pełną lokalną kopię pakietu w katalogu. „/install” jest wartością domyślną.
/passive | /quiet — wyświetla minimalny interfejs użytkownika bez monitów lub nie wyświetla interfejsu użytkownika
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/1046/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/1046/bundle.wxl
index 20dd08ef43b8..bdbb05f0fd48 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/1046/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/1046/bundle.wxl
@@ -4,7 +4,7 @@
Tem certeza de que deseja cancelar?Versão anteriorAjuda da Instalação
- /install | /repair | /uninstall | /layout [\[]"directory"[\]] – instala, repara, desinstala
+ /install | /repair | /uninstall | /layout [\[]"directory"[\]] – instala, repara, desinstala
ou cria uma cópia local completa do pacote no diretório. "/install" é o padrão.
/passive | /quiet – exibe a interface do usuário mínima sem prompts ou não exibe nenhuma interface do usuário e
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/1049/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/1049/bundle.wxl
index 1ba8b4521428..18402ec4826b 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/1049/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/1049/bundle.wxl
@@ -4,7 +4,7 @@
Отменить?Предыдущая версияСправка по установке
- /install | /repair | /uninstall | /layout [\[]"directory"[\]] - устанавливает, исправляет, удаляет
+ /install | /repair | /uninstall | /layout [\[]"directory"[\]] - устанавливает, исправляет, удаляет
или создает полную локальную копию пакета в каталоге. '/install' - значение по умолчанию.
/passive | /quiet — отображает минимальный пользовательский интерфейс без запросов или не отображает пользовательский интерфейс и
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/1055/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/1055/bundle.wxl
index 5f78c53ce22c..e42f5bcdbbe5 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/1055/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/1055/bundle.wxl
@@ -4,7 +4,7 @@
İptal etmek istediğinizden emin misiniz?Önceki sürümKurulum Yardımı
- /install | /repair | /uninstall | /layout [\[]"dizin"[\]] - yüklemeler, onarımlar, kaldırmalar
+ /install | /repair | /uninstall | /layout [\[]"dizin"[\]] - yüklemeler, onarımlar, kaldırmalar
veya paketin tam bir yerel kopyasını dizinde oluşturur. '/install' varsayılandır.
/passive | /quiet - kullanıcı arabirimini istem olmadan minimum düzeyde görüntüler veya kullanıcı arabirimi ve
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/2052/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/2052/bundle.wxl
index 66d3d768f2ce..3cd07d2fac77 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/2052/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/2052/bundle.wxl
@@ -4,7 +4,7 @@
是否确实要取消?上一版本安装程序帮助
- /install | /repair | /uninstall | /layout [\[]"directory"[\]] - 安装、修复、卸载
+ /install | /repair | /uninstall | /layout [\[]"directory"[\]] - 安装、修复、卸载
或在目录中创建捆绑包的完整本地副本。'/install' 是默认值。
/passive | /quiet - 显示最小 UI 且无提示,或不显示 UI 和
diff --git a/src/Installer/redist-installer/packaging/windows/LCID/3082/bundle.wxl b/src/Installer/redist-installer/packaging/windows/LCID/3082/bundle.wxl
index 966c9d647199..0bd39310edc7 100644
--- a/src/Installer/redist-installer/packaging/windows/LCID/3082/bundle.wxl
+++ b/src/Installer/redist-installer/packaging/windows/LCID/3082/bundle.wxl
@@ -4,7 +4,7 @@
¿Está seguro de que desea cancelar la operación?Versión anteriorAyuda del programa de instalación
- /install | /repair | /uninstall | /layout [\[]"directory"[\]]: instala, repara, desinstala
+ /install | /repair | /uninstall | /layout [\[]"directory"[\]]: instala, repara, desinstala
o crea una copia local completa de la agrupación en el directorio. "/install" es el valor predeterminado.
/passive | /quiet: muestra una interfaz de usuario mínima sin avisos o no muestra ninguna interfaz de usuario ni
diff --git a/src/Installer/redist-installer/redist-installer.csproj b/src/Installer/redist-installer/redist-installer.csproj
index 75de618eabe1..077ff2bf69cc 100644
--- a/src/Installer/redist-installer/redist-installer.csproj
+++ b/src/Installer/redist-installer/redist-installer.csproj
@@ -10,6 +10,7 @@
truetrue
+ true
diff --git a/src/Installer/redist-installer/targets/GenerateLayout.targets b/src/Installer/redist-installer/targets/GenerateLayout.targets
index 609731a3ec8f..bb05abdd8e56 100644
--- a/src/Installer/redist-installer/targets/GenerateLayout.targets
+++ b/src/Installer/redist-installer/targets/GenerateLayout.targets
@@ -9,11 +9,9 @@
- $(VSRedistCommonAspNetCoreSharedFrameworkx64100PackageVersion)
- $(MicrosoftAspNetCoreAppRuntimePackageVersion)
+ $(MicrosoftAspNetCoreAppRefInternalPackageVersion)
- $(VSRedistCommonNetCoreSharedFrameworkx64100PackageVersion)
- $(MicrosoftNETCoreAppRuntimePackageVersion)
+ $(MicrosoftNETCorePlatformsPackageVersion)$(VSRedistCommonWindowsDesktopSharedFrameworkx64100PackageVersion)$(MicrosoftWindowsDesktopAppRuntimePackageVersion)
diff --git a/src/Installer/redist-installer/targets/GenerateMSIs.targets b/src/Installer/redist-installer/targets/GenerateMSIs.targets
index bb9d9f4b157f..49c8e1f86f48 100644
--- a/src/Installer/redist-installer/targets/GenerateMSIs.targets
+++ b/src/Installer/redist-installer/targets/GenerateMSIs.targets
@@ -275,24 +275,6 @@
Overwrite="true" />
-
-
- %(SDKInternalFiles.Identity)
-
-
-
-
-
-
-
- $(MinimumVSVersion.Substring(0,$(MinimumVSVersion.LastIndexOf('.'))))
- $([MSBuild]::Add($(MinimumVSVersion), .1))
-
-
-
+
+
diff --git a/src/Tasks/Common/Resources/Strings.resx b/src/Tasks/Common/Resources/Strings.resx
index f7fa72d965ab..3bd60a9c395b 100644
--- a/src/Tasks/Common/Resources/Strings.resx
+++ b/src/Tasks/Common/Resources/Strings.resx
@@ -940,14 +940,7 @@ You may need to build the project on another operating system or architecture, o
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '{0}'))">true</IsTrimmable>
{StrBegins="NETSDK1212: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- {StrBegins="NETSDK1213: "}
-
-
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- {StrBegins="NETSDK1214: "}
-
+
NETSDK1215: Targeting .NET Standard prior to 2.0 is no longer recommended. See {0} for more details.{StrBegins="NETSDK1215: "}
@@ -976,9 +969,18 @@ You may need to build the project on another operating system or architecture, o
NETSDK1221: NuGetPackageRoot property is empty so package Microsoft.Net.Sdk.Compilers.Toolset cannot be used but it is recommended because your MSBuild and SDK versions are mismatched. Ensure you are building with '/restore /t:Build' and not '/t:Restore;Build'.{StrBegins="NETSDK1221: "}{Locked="NuGetPackageRoot"}{Locked="Microsoft.Net.Sdk.Compilers.Toolset"}{Locked="'/restore /t:Build'"}{Locked="'/t:Restore;Build'"}
-
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
+
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.{StrBegins="NETSDK1222: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}
+
+
+
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}
+
diff --git a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf
index 71b7502373bc..8fcea06dac8a 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: Aktuální sada .NET SDK nepodporuje .NET Framework bez použití výchozích nastavení .NET SDK. Pravděpodobně došlo k neshodě mezi vlastnostmi CLRSupport projektu C++/CLI a TargetFramework.{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: Cílení na .NET 8.0 nebo vyšší se ve Visual Studiu 2022 17.7 nepodporuje.
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64 se vztahuje pouze na cíle .NET Framework. Nepodporuje se a nemá žádný vliv při cílení na .NET Core.
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64 se vztahuje pouze na cíle .NET Framework. Nepodporuje se a nemá žádný vliv při cílení na .NET Core.
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.de.xlf b/src/Tasks/Common/Resources/xlf/Strings.de.xlf
index da282ea1e8e7..2fa09f8d44b9 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.de.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.de.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: Das aktuelle .NET SDK unterstützt das .NET Framework nur, wenn .NET SDK-Standardwerte verwendet werden. Wahrscheinlich liegt ein Konflikt zwischen der CLRSupport-Eigenschaft des C++-/CLI-Projekts und TargetFramework vor.{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: Die Ausrichtung auf .NET 8.0 oder höher in Visual Studio 2022 17.7 wird nicht unterstützt.
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64 gilt nur für .NET Framework Ziele. Dies wird nicht unterstützt und hat keine Auswirkungen auf .NET Core.
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64 gilt nur für .NET Framework Ziele. Dies wird nicht unterstützt und hat keine Auswirkungen auf .NET Core.
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.es.xlf b/src/Tasks/Common/Resources/xlf/Strings.es.xlf
index 32eff79b1fb6..a7fb5d086be1 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.es.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.es.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: El SDK de .NET actual no admite .NET Framework sin usar los valores predeterminados de dicho SDK. Posiblemente se deba a la falta de coincidencia entre la propiedad CLRSupport del proyecto de C++/CLI y TargetFramework.{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: no se admite el destino de .NET 8.0 o posterior en Visual Studio 2022 17.7.
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64 solo se aplica a destinos de .NET Framework. No se admite y no tiene efecto cuando el destino es .NET Core.
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64 solo se aplica a destinos de .NET Framework. No se admite y no tiene efecto cuando el destino es .NET Core.
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.fr.xlf b/src/Tasks/Common/Resources/xlf/Strings.fr.xlf
index f27b89db963c..d5012be61705 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.fr.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.fr.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: Le SDK .NET actuel ne prend pas en charge le .NET Framework avec des valeurs du SDK .NET autres que celles par défaut. Cela est probablement dû à une incompatibilité entre la propriété CLRSupport du projet C++/CLI et TargetFramework.{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: le ciblage de .NET 8.0 ou plus dans Visual Studio 2022 17.7 n’est pas pris en charge.
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64 s’applique uniquement aux cibles .NET Framework. Il n’est pas pris en charge et n’a aucun effet lors du ciblage de .NET Core.
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64 s’applique uniquement aux cibles .NET Framework. Il n’est pas pris en charge et n’a aucun effet lors du ciblage de .NET Core.
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.it.xlf b/src/Tasks/Common/Resources/xlf/Strings.it.xlf
index b64fc1d54718..8c8f32239c75 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.it.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.it.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: l'istanza corrente di .NET SDK non supporta .NET Framework senza usare le impostazioni predefinite di .NET SDK. Il problema dipende probabilmente da una mancata corrispondenza tra la proprietà CLRSupport del progetto C++/CLI e TargetFramework.{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: la destinazione .NET 8.0 o versione successiva in Visual Studio 2022 17.7 non è supportata.
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64 si applica solo alle destinazioni .NET Framework. Non è supportato e non ha alcun effetto quando si usa .NET Core come destinazione.
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64 si applica solo alle destinazioni .NET Framework. Non è supportato e non ha alcun effetto quando si usa .NET Core come destinazione.
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf
index cbf1a9fc7304..7dcc1a1c66ae 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: 現在の .NET SDK では、.NET SDK の既定値を使用せずに .NET Framework をサポートすることはできません。これは、C++/CLI プロジェクトの CLRSupport プロパティと TargetFramework の間の不一致が原因と考えられます。{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: Visual Studio 2022 17.7 では .NET 8.0 以上をターゲットにすることはできません。
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64 は、.NET Framework ターゲットにのみ適用されます。これはサポートされておらず、.NET Core をターゲットにする場合には効果がありません。
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64 は、.NET Framework ターゲットにのみ適用されます。これはサポートされておらず、.NET Core をターゲットにする場合には効果がありません。
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.ko.xlf b/src/Tasks/Common/Resources/xlf/Strings.ko.xlf
index 55dede12ac9b..b17c776ceeec 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.ko.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.ko.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: 현재 .NET SDK는 .NET SDK 기본값을 사용하지 않는 .NET Framework를 지원하지 않습니다. C++/CLI 프로젝트 CLRSupport 속성과 TargetFramework 사이의 불일치 때문일 수 있습니다.{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: Visual Studio 2022 17.7에서는 .NET 8.0 이상을 대상으로 지정하는 것이 지원되지 않습니다.
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64는 .NET Framework 대상에만 적용됩니다. .NET Core를 대상으로 하는 경우 지원되지 않으며 영향을 주지 않습니다.
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64는 .NET Framework 대상에만 적용됩니다. .NET Core를 대상으로 하는 경우 지원되지 않으며 영향을 주지 않습니다.
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf
index ee25744e373b..963bbc6f47fc 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: Bieżący zestaw .NET SDK nie obsługuje programu .NET Framework bez użycia wartości domyślnych zestawu .NET SDK. Prawdopodobna przyczyna to niezgodność między właściwością CLRSupport projektu C++/CLI i elementu TargetFramework.{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: platforma docelowa .NET 8.0 lub nowsza w programie Visual Studio 2022 17.7 nie jest obsługiwana.
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: element PreferNativeArm64 ma zastosowanie tylko do elementów docelowych programu .NET Framework. Nie jest ona obsługiwana i nie ma żadnego efektu w przypadku określania wartości docelowej platformy .NET Core.
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: element PreferNativeArm64 ma zastosowanie tylko do elementów docelowych programu .NET Framework. Nie jest ona obsługiwana i nie ma żadnego efektu w przypadku określania wartości docelowej platformy .NET Core.
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf
index b985dd5c1351..be20384f1f48 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: o SDK do .NET atual não dá suporte ao .NET Framework sem o uso de Padrões do SDK do .NET. O motivo é provavelmente uma incompatibilidade entre a propriedade CLRSupport do projeto C++/CLI e a TargetFramework.{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: não há suporte para o direcionamento do .NET 8.0 ou superior no Visual Studio 2022 17.7.
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64 se aplica somente a destinos .NET Framework. Não há suporte para ele e não tem efeito ao direcionar o .NET Core.
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64 se aplica apenas a destinos do .NET Framework. Não há suporte e não tem efeito ao direcionar para o .NET Core.
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf
index b2c499b154f1..71069896f43b 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: The current .NET SDK does not support .NET Framework without using .NET SDK Defaults. It is likely due to a mismatch between C++/CLI project CLRSupport property and TargetFramework.{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64 применяется только к целевым объектам .NET Framework. Для целевых объектов .NET Core он не поддерживается и не оказывает влияния.
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64 применяется только к целевым объектам .NET Framework. Он не поддерживается и не работает для целевых объектов .NET Core.
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf
index fd5d62d087bc..6a70211cb13e 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: Geçerli .NET SDK, .NET SDK Varsayılanlarını kullanmadan .NET Framework'ü desteklemiyor. C++/CLI projesi CLRSupport özelliği ve TargetFramework arasındaki uyuşmazlık bu duruma neden olabilir.{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: Visual Studio 2022 17.7'da .NET 8.0 veya daha üst sürümünü hedefleme desteklenmiyor.
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64 yalnızca .NET Framework hedefleri için geçerlidir. .NET Core hedeflenirken desteklenmez ve herhangi bir etkisi yoktur.
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64 yalnızca .NET Framework hedefleri için geçerlidir. .NET Core hedeflenirken desteklenmez ve herhangi bir etkisi yoktur.
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf
index ae8198fa5a2b..ed429333a887 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: 未使用 .NET SDK 默认设置的情况下,当前 .NET SDK 不支持 .NET Framework。很可能是因为 C++/CLI 项目的 CLRSupport 属性和 TargetFramework 之间存在不匹配情况。{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: 不支持在 Visual Studio 2022 17.7 中以 .NET 8.0 或更高版本为目标。
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64 仅适用于 .NET Framework 目标。它不受支持,并且在面向 .NET Core 时不起作用。
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64 仅适用于 .NET Framework 目标。它不受支持,并且在以 .NET Core 为目标时不起作用。
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf
index c1a3e77b9493..9de539545262 100644
--- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf
+++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf
@@ -73,9 +73,9 @@
{StrBegins="NETSDK1079: "}
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- NETSDK1222: ASP.NET Core framework assets are not supported for the target framework.
- {StrBegins="NETSDK1222: "}
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ NETSDK1224: ASP.NET Core framework assets are not supported for the target framework.
+ {StrBegins="NETSDK1224: "}NETSDK1080: A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically. Otherwise, the PackageReference should be replaced with a FrameworkReference.
@@ -646,10 +646,10 @@ The following are names of parameters or literal values and should not be transl
NETSDK1115: 目前的 .NET SDK 不支援在不使用 .NET SDK 預設的情形下使用 .NET Framework。這可能是因為 C++/CLI 專案 CLRSupport 屬性與 TargetFramework 不相符所致。{StrBegins="NETSDK1115: "}
-
- NETSDK1213: Targeting .NET 8.0 or higher in Visual Studio 2022 17.7 is not supported.
- NETSDK1213: 不支援在 Visual Studio 2022 17.7 中以 .NET 8.0 或更高版本為目標。
- {StrBegins="NETSDK1213: "}
+
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ NETSDK1223: Targeting .NET 9.0 or higher in Visual Studio 2022 17.11 is not supported.
+ {StrBegins="NETSDK1223: "}NETSDK1084: There is no application host available for the specified RuntimeIdentifier '{0}'.
@@ -767,9 +767,9 @@ The following are names of parameters or literal values and should not be transl
{StrBegins="NETSDK1189: "}
- NETSDK1214: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
- NETSDK1214: PreferNativeArm64 僅適用於 .NET Framework 目標。其不受支援,且在以 .NET Core 為目標時沒有作用。
- {StrBegins="NETSDK1214: "}
+ NETSDK1222: PreferNativeArm64 applies only to .NET Framework targets. It is not supported and has no effect for when targeting .NET Core.
+ NETSDK1222: PreferNativeArm64 僅適用於 .NET Framework 目標。其不受支援,且在以 .NET Core 為目標時沒有作用。
+ {StrBegins="NETSDK1222: "}NETSDK1011: Assets are consumed from project '{0}', but no corresponding MSBuild project path was found in '{1}'.
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenThatWeHaveErrorCodes.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenThatWeHaveErrorCodes.cs
index 847efc3165dd..248a1470b7b7 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenThatWeHaveErrorCodes.cs
+++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenThatWeHaveErrorCodes.cs
@@ -35,7 +35,9 @@ public class GivenThatWeHaveErrorCodes
1182,
1183,
1190,
- 1192
+ 1192,
+ 1213,
+ 1214
};
//ILLink lives in other repos and violated the _info requirement for no error code
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.DefaultItems.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.DefaultItems.targets
index eb16ada800c1..6c54a6af19a1 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.DefaultItems.targets
+++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.DefaultItems.targets
@@ -129,10 +129,10 @@ Copyright (c) .NET Foundation. All rights reserved.
FormatArguments="$(SdkResolverGlobalJsonPath)" />
-
-
+ Condition="$([MSBuild]::VersionLessThan($(MSBuildVersion), '17.12.0')) and '$(TargetFrameworkIdentifier)' == '.NETCoreApp' and $([MSBuild]::VersionGreaterThanOrEquals($(_TargetFrameworkVersionWithoutV), '9.0'))">
+
-
-
+
+
+
11.0
- 17.12
+ 17.16
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.cs.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.cs.xlf
index 56dc331335f9..0397e58fd693 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.cs.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.cs.xlf
@@ -226,22 +226,22 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ Stav nasazení je {0}.{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ Stav nasazení je {0}: {1}{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ Adresa URL stavu nasazení {0} chybí nebo je neplatná.{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ Cyklické dotazování na stav nasazení...
@@ -356,47 +356,47 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ Pokus OneDeploy o publikování souboru prostřednictvím {0} se nezdařil. Stavový kód HTTP: {1}.{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ Pokus OneDeploy o publikování souboru prostřednictvím {0} se nezdařil. Stavový kód HTTP {1}: {2}.{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ Nepovedlo se načíst přihlašovací údaje pro publikování.OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ Pokus OneDeploy o publikování souboru {0} prostřednictvím {1} se nezdařil. Stavový kód: {2}. Projděte si protokoly na {3}.{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ Soubor pro publikování {0} nebyl nalezen nebo není přístupný.{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ PublishUrl {0} chybí nebo má neplatnou hodnotu.{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ {0} se publikuje do umístění {1}...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ Nasazení OneDeploy bylo úspěšné.File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ Soubor {0} byl nahrán do cílové instance.{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.de.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.de.xlf
index c4413d928128..207e2a510830 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.de.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.de.xlf
@@ -226,22 +226,22 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ Der Bereitstellungsstatus ist „{0}“.{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ Bereitstellungsstatus: „{0}“: {1}{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ Die Bereitstellungsstatus-URL „{0}“ fehlt oder ist ungültig.{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ Der Bereitstellungsstatus wird abgerufen...
@@ -356,47 +356,47 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ OneDeploy-Versuch, die Datei über „{0}“ zu veröffentlichen, ist fehlgeschlagen. HTTP-Statuscode: „{1}“.{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ OneDeploy-Versuch, die Datei über „{0}“ zu veröffentlichen, ist fehlgeschlagen. HTTP-Statuscode: „{1}“: {2}.{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ Fehler beim Abrufen der Anmeldeinformationen für die Veröffentlichung.OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ Fehler beim OneDeploy-Versuch, die Datei „{0}“ bis „{1}“ zu veröffentlichen. Statuscode: „{2}“. Sehen Sie sich die Protokolle unter „{3}“ an.{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ Die zu veröffentlichende Datei „{0}“ wurde nicht gefunden oder ist nicht zugänglich.{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ PublishUrl „{0}“ fehlt oder weist einen ungültigen Wert auf.{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ „{0}“ wird in „{1}“ veröffentlicht...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ OneDeploy-Bereitstellung erfolgreich.File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ Die Datei „{0}“ wurde in die Zielinstanz hochgeladen.{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.es.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.es.xlf
index ae528678ccea..02cfc569ec6d 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.es.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.es.xlf
@@ -226,22 +226,22 @@ Vínculo redireccionable: http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ El estado de implementación es "{0}".{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ El estado de implementación es "{0}": {1}.{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ Falta la dirección URL de estado de implementación "{0}" o no es válida{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ Sondeando el estado de implementación...
@@ -356,47 +356,47 @@ Vínculo redireccionable: http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ Error en el intento de OneDeploy de publicar el archivo a través de "{0}" con el código de estado HTTP "{1}".{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ Error en el intento de OneDeploy de publicar el archivo a través de "{0}" con el código de estado HTTP "{1}": {2}.{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ No se pudieron recuperar las credenciales de publicación.OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ Error en el intento de OneDeploy de publicar el archivo "{0}" a "{1}" con el código de estado "{2}". Vea los registros en "{3}".{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ No se encontró el archivo para publicar "{0}" o no se puede obtener acceso a él.{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ Falta PublishUrl "{0}" o tiene un valor no válido.{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ Publicando "{0}" en "{1}"...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ Implementación de OneDeploy correcta.File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ Archivo "{0}" cargado en la instancia de destino.{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.fr.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.fr.xlf
index fea3cb7722af..009d934fc24a 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.fr.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.fr.xlf
@@ -226,22 +226,22 @@ FWLink : http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ État du déploiement : « {0} ».{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ État du déploiement : « {0} » : {1}.{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ Le « {0} » de l’URL de statut de déploiement est manquant ou non valide{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ Interrogation de l'état du déploiement...
@@ -356,47 +356,47 @@ FWLink : http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ La tentative de OneDeploy de publier un fichier via « {0} » a échoué avec le code d’état HTTP « {1} ».{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ La tentative de OneDeploy de publier un fichier via « {0} » a échoué avec le code d’état HTTP « {1} » : {2}.{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ Échec de la récupération des informations d’identification de publication.OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ La tentative de OneDeploy de publier le fichier « {0} » par « {1} » a échoué avec le code de statut « {2} ». Consultez les journaux sur « {3} ».{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ Le fichier à publier « {0} » est introuvable ou n’est pas accessible.{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ Le « {0} » PublishUrl est manquant ou a une valeur non valide.{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ Publication de « {0} » sur « {1} »...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ Déploiement de OneDeploy réussi.File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ Le fichier « {0} » chargé vers le instance cible.{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.it.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.it.xlf
index c31079265702..d8e737cd9a8f 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.it.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.it.xlf
@@ -226,22 +226,22 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ Lo stato della distribuzione è {0}.{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ Lo stato della distribuzione è '{0}': {1}{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ L'URL dello stato della distribuzione '{0}' è mancante o non valido{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ Polling dello stato della distribuzione...
@@ -356,47 +356,47 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ Il tentativo OneDeploy di pubblicazione del file tramite '{0}' non è riuscito con il codice di stato HTTP: '{1}'.{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ Il tentativo OneDeploy di pubblicazione del file tramite '{0}' non è riuscito con il codice di stato HTTP '{1}': {2}.{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ Non è stato possibile recuperare le credenziali di pubblicazione.OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ Il tentativo OneDeploy di pubblicazione del file '{0}' tramite '{1}' non è riuscito con il codice di stato '{2}'. Vedere i log in '{3}'.{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ Il file da pubblicare '{0}' non è stato trovato o non è accessibile.{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ Il '{0}' PublishUrl è mancante o ha un valore non valido.{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ Pubblicazione di '{0}' in '{1}'...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ La distribuzione di OneDeploy è riuscita.File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ File '{0}' caricato nell'istanza di destinazione.{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ja.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ja.xlf
index 5dca3cb12919..9fc69730136c 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ja.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ja.xlf
@@ -226,22 +226,22 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ デプロイ状態は '{0}' です。{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ デプロイ状態は '{0}': {1}{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ デプロイ状態 URL '{0}' が見つからないか無効です{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ 配置状態をポーリングしています...
@@ -356,47 +356,47 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ OneDeploy が '{0}' を介してファイルを発行しようとしましたが失敗しました、HTTP 状態コード '{1}'。{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ OneDeploy が '{0}' を介してファイルを発行しようとしましたが失敗しました、HTTP 状態コード '{1}': {2}。{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ 発行資格情報を取得できませんでした。OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ OneDeploy でファイル '{1}' から '{0}' を発行しようとしましたが、状態コード '{2}' で失敗しました。'{3}' のログを参照してください。{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ '{0}' を発行するファイルが見つからないか、アクセスできません。{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ PublishUrl '{0}' がないか、無効な値が含まれています。{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ '{0}' を {1} に発行しています...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ OneDeploy のデプロイに成功しました。File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ ファイル '{0}' はターゲット インスタンスにアップロードされました。{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ko.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ko.xlf
index ce92d6066e47..58dc4f44b270 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ko.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ko.xlf
@@ -226,22 +226,22 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ 배포 상태는 '{0}'입니다.{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ 배포 상태는 '{0}'({1})입니다.{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ '{0}' 배포 상태 URL이 없거나 잘못되었습니다.{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ 배포 상태에 대한 폴링...
@@ -356,47 +356,47 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ '{0}'을(를) 통해 파일을 게시하려는 OneDeploy 시도가 실패했습니다('{1}' HTTP 상태 코드).{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ '{0}'을(를) 통해 파일을 게시하려는 OneDeploy 시도가 실패했습니다('{1}' HTTP 상태 코드: {2}).{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ 게시 자격 증명을 찾아오지 못했습니다.OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ OneDeploy에서 '{1}'을(를) 통해 '{0}' 파일을 '(으)로 게시하려는 시도가 실패했습니다(상태 코드: '{2}'). '{3}'의 로그를 참조하세요.{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ '{0}'을(를) 게시할 파일을 찾을 수 없거나 액세스할 수 없습니다.{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ '{0}' PublishUrl이 없거나 값이 잘못되었습니다.{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ '{0}'을(를) '{1}에 게시하는 중...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ OneDeploy를 배포했습니다.File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ 대상 인스턴스에 '{0}' 파일을 업로드했습니다.{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.pl.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.pl.xlf
index 8218f58524a3..65a2ef1b201a 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.pl.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.pl.xlf
@@ -226,22 +226,22 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ Stan wdrożenia to „{0}”.{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ Stan wdrożenia to „{0}”: {1}{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ Brak adresu URL stanu wdrożenia „{0}” lub jest on nieprawidłowy{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ Sondowanie stanu wdrożenia...
@@ -356,47 +356,47 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ Próba opublikowania pliku przez rozwiązanie OneDeploy za pomocą "{0}" nie powiodła się. Kod stanu HTTP"{1}".{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ Próba opublikowania pliku przez rozwiązanie OneDeploy za pomocą „{0}” nie powiodła się. Kod stanu HTTP „{1}”: {2}.{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ Nie można pobrać poświadczenia publikowania.OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ Próba opublikowania pliku „{0}” za pomocą „{1}” przez rozwiązanie OneDeploy nie powiodło się. Kod stanu: „{2}”. Zobacz dzienniki w lokalizacji „{3}”.{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ Nie znaleziono pliku do opublikowania „{0}” lub jest on niedostępny.{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ Brak elementu PublishUrl „{0}” lub ma on nieprawidłową wartość.{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ Trwa publikowanie „{0}” do „{1}”...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ Wdrożenie OneDeploy powiodło się.File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ Plik „{0}” został przekazany do wystąpienia miejsce docelowego.{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.pt-BR.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.pt-BR.xlf
index 8a7ffbfd0bf3..e7b7f2f264dd 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.pt-BR.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.pt-BR.xlf
@@ -226,22 +226,22 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ O status da implantação é '{0}'.{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ O status da implantação é '{0}': {1}{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ A URL de status da implantação '{0}' está ausente ou é inválida{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ Sondando o status da implantação...
@@ -356,47 +356,47 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ A tentativa do OneDeploy de publicar o arquivo através de '{0}' falhou com o código de status HTTP '{1}'.{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ A tentativa do OneDeploy de publicar o arquivo através de '{0}' falhou com o código de status HTTP '{1}': {2}.{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ Falha ao recuperar as credenciais de publicação.OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ A tentativa do OneDeploy de publicar o arquivo '{0}' através de '{1}' falhou com o código de status '{2}'. Veja os logs em '{3}'.{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ O arquivo para publicar '{0}' não foi encontrado ou não está acessível.{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ A PublishUrl '{0}' está ausente ou tem um valor inválido.{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ Publicação de '{0}' para '{1}'...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ A implantação do OneDeploy foi bem-sucedida.File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ O arquivo '{0}' foi enviado para a instância de meta.{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ru.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ru.xlf
index 8c22509aa93f..7ac0ba67693b 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ru.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.ru.xlf
@@ -226,22 +226,22 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068.
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ Состояние развертывания: {0}.{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ Состояние развертывания: {0}: {1}{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ URL-адрес состояния развертывания '{0}' отсутствует или недопустим{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ Опрос состояния развертывания…
@@ -356,47 +356,47 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068.
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ Попытка OneDeploy опубликовать файл с помощью '{0}' не удалась, код состояния HTTP '{1}':{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ Попытка OneDeploy опубликовать файл с помощью '{0}' не удалась, код состояния HTTP '{1}': {2}.{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ Не удалось получить учетные данные публикации.OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ Попытка OneDeploy опубликовать файл с помощью '{0}' не удалась, код состояния '{1}': {2}. Просмотреть журналы в '{3}'.{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ Файл для публикации {0} не найден или недоступен.{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ PublishUrl {0} отсутствует или имеет недопустимое значение.{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ Публикация ZIP-файла {0} в {1}…{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ Развертывание OneDeploy выполнено успешно.File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ Файл '{0}' отправлен в целевой экземпляр.{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.tr.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.tr.xlf
index 0d6c30f5602a..d5e5e7715b62 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.tr.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.tr.xlf
@@ -226,22 +226,22 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ Dağıtım durumu '{0}'.{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ Dağıtım durumu '{0}': {1}{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ Dağıtım durumu URL'si '{0}' eksik veya geçersiz{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ Dağıtım durumu yoklanıyor...
@@ -356,47 +356,47 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ Dosyayı '{0}' aracılığıyla yayımlamaya yönelik OneDeploy girişimi '{1}' HTTP durum koduyla başarısız oldu.{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ Dosyayı '{0}' aracılığıyla yayımlamaya yönelik OneDeploy girişimi '{1}' HTTP durum koduyla başarısız oldu: {2}.{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ Yayımlama kimlik bilgileri alınamadı.OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ '{0}' dosyasını '{1}' aracılığıyla yayımlamaya yönelik OneDeploy girişimi '{2}' durum koduyla başarısız oldu. '{3}' konumundaki günlüklere bakın.{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ '{0}' öğesini yayımlamak için dosya bulunamadı veya erişilebilir değil.{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ PublishUrl '{0}' eksik veya geçersiz bir değere sahip.{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ '{0}', '{1}' konumunda yayımlanıyor...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ OneDeploy dağıtımı başarılı oldu.File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ Dosya '{0}' hedef örneğe yüklendi.{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.zh-Hans.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.zh-Hans.xlf
index 6eb0a8098a06..dd1824cb2ffd 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.zh-Hans.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.zh-Hans.xlf
@@ -226,22 +226,22 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ 部署状态为 ‘{0}’。{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ 部署状态为 ‘{0}’: {1}{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ 部署状态 URL ‘{0}’ 缺失或无效{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ 正在轮询部署状态…
@@ -356,47 +356,47 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ OneDeploy 尝试通过 ‘{0}’ 发布文件失败,HTTP 状态代码为 ‘{1}’。{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ OneDeploy 尝试通过 ‘{0}’ 发布文件失败,HTTP 状态代码 ‘{1}’: {2}。{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ 无法检索发布凭据。OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ OneDeploy 尝试通过 ‘{1}’ 发布文件 ‘{0}’ 失败,状态代码为 ‘{2}’。请参阅位于 ‘{3}’ 的日志。{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ 找不到或无法访问要发布 ‘{0}’ 的文件。{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ PublishUrl ‘{0}’ 缺失或具有无效值。{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ 正在将 ‘{0}’ 发布到 {1}...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ OneDeploy 部署成功。File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ 文件 ‘{0}’ 已上传到目标实例。{0} - file to publish.
diff --git a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.zh-Hant.xlf b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.zh-Hant.xlf
index 96c6046aa817..a2f48027a55b 100644
--- a/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.zh-Hant.xlf
+++ b/src/WebSdk/Publish/Tasks/Properties/xlf/Resources.zh-Hant.xlf
@@ -226,22 +226,22 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
Deployment status is '{0}'.
- Deployment status is '{0}'.
+ 部署狀態為 '{0}'。{0} - deployment status nameDeployment status is '{0}': {1}
- Deployment status is '{0}': {1}
+ 部署狀態為 '{0}': {1}{0} - deployment status name, {1} - deployment status textDeployment status URL '{0}' is missing or invalid
- Deployment status URL '{0}' is missing or invalid
+ 缺少部署狀態 URL '{0}',或其無效{0} - deployment polling URLPolling for deployment status...
- Polling for deployment status...
+ 正在輪詢部署狀態...
@@ -356,47 +356,47 @@ FWLink: http://go.microsoft.com/fwlink/?LinkId=246068
OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}'.
+ OneDeploy 嘗試透過 '{0}' 發佈檔案時失敗,HTTP 狀態代碼為 '{1}'。{0} - publish URL, {1} - HTTP response status codeOneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
- OneDeploy attempt to publish file through '{0}' failed with HTTP status code '{1}': {2}.
+ OneDeploy 嘗試透過 '{0}' 發佈檔案時失敗,HTTP 狀態代碼 '{1}': {2}。{0} - publish URL, {1} - HTTP response status code, {2} - response body as textFailed to retrieve publish credentials.
- Failed to retrieve publish credentials.
+ 無法擷取發佈認證。OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
- OneDeploy attempt to publish file '{0}' through '{1}' failed with status code '{2}'. See the logs at '{3}'.
+ OneDeploy 嘗試透過 '{1}' 發佈檔案 '{0}' 時失敗,狀態代碼為 '{2}'。請前往 '{3}' 查看記錄。{0} - file to publish, {1} - publish URL, {2} - deployment response status code, {3} - deployment logs URLFile to publish '{0}' was not found or is not accessible.
- File to publish '{0}' was not found or is not accessible.
+ 找不到或無法存取要發佈的檔案 '{0}'。{0} - file to publishPublishUrl '{0}' is missing or has an invalid value.
- PublishUrl '{0}' is missing or has an invalid value.
+ 缺少 PublishUrl '{0}',或值無效。{0} - publish URLPublishing '{0}' to '{1}'...
- Publishing '{0}' to '{1}'...
+ 正在將 '{0}' 發佈至 '{1}'...{0} - file to publish, {1} - publish URLOneDeploy deployment succeeded.
- OneDeploy deployment succeeded.
+ 成功部署 OneDeploy。File '{0}' uploaded to target instance.
- File '{0}' uploaded to target instance.
+ 已將檔案 '{0}' 上傳至目標執行個體。{0} - file to publish.
diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeManifestSupportedFrameworks.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeManifestSupportedFrameworks.cs
index be9ba6ab5988..4445fcaa465f 100644
--- a/test/Microsoft.NET.Build.Tests/GivenThatWeManifestSupportedFrameworks.cs
+++ b/test/Microsoft.NET.Build.Tests/GivenThatWeManifestSupportedFrameworks.cs
@@ -9,7 +9,7 @@ public GivenThatWeManifestSupportedFrameworks(ITestOutputHelper log) : base(log)
{
}
- [RequiresMSBuildVersionTheory("17.8.0")]
+ [RequiresMSBuildVersionTheory("17.12.0")]
[InlineData(".NETCoreApp")]
[InlineData(".NETStandard")]
public void TheMaximumVersionsAreSupported(string targetFrameworkIdentifier)
diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildALibrary.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildALibrary.cs
index 31167bf1de5e..0a701c97c8cd 100644
--- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildALibrary.cs
+++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildALibrary.cs
@@ -472,7 +472,7 @@ private void AssertDefinedConstantsOutput(TestAsset testAsset, string targetFram
definedConstants.Should().BeEquivalentTo(new[] { "DEBUG", "TRACE" }.Concat(expectedDefines).ToArray());
}
- [WindowsOnlyTheory]
+ [WindowsOnlyRequiresMSBuildVersionTheory("17.12.0")]
[InlineData("net8.0", new[] { "NETCOREAPP", "NET", "NET8_0", "NET8_0_OR_GREATER" })]
[InlineData("net9.0", new[] { "NETCOREAPP", "NET", "NET8_0_OR_GREATER", "NET9_0_OR_GREATER", "NET9_0", "WINDOWS", "WINDOWS7_0", "WINDOWS7_0_OR_GREATER" }, "windows", "7.0")]
public void It_can_use_implicitly_defined_compilation_constants(string targetFramework, string[] expectedOutput, string targetPlatformIdentifier = null, string targetPlatformVersion = null)
diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToFloatWarningLevels.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToFloatWarningLevels.cs
index fafc1185848a..f09c80a99314 100644
--- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToFloatWarningLevels.cs
+++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToFloatWarningLevels.cs
@@ -375,7 +375,7 @@ static void Main()
[InlineData("recommended", "true", new string[] { "CA1310", "CA1068", "CA2200" })]
[InlineData("all", "false", new string[] { "CA1031", "CA1310", "CA1068", "CA2200" })]
[InlineData("all", "true", new string[] { "CA1031", "CA1310", "CA1068", "CA2200" })]
- [RequiresMSBuildVersionTheory("17.8.0")]
+ [RequiresMSBuildVersionTheory("17.12.0")]
public void It_bulk_configures_rules_with_different_analysis_modes(string analysisMode, string codeAnalysisTreatWarningsAsErrors, string[] expectedViolations)
{
var testProject = new TestProject
diff --git a/test/Microsoft.NET.Build.Tests/SourceLinkTests.cs b/test/Microsoft.NET.Build.Tests/SourceLinkTests.cs
index 109a9cfc2c9e..ed364c84e408 100644
--- a/test/Microsoft.NET.Build.Tests/SourceLinkTests.cs
+++ b/test/Microsoft.NET.Build.Tests/SourceLinkTests.cs
@@ -112,7 +112,7 @@ public void WithNoGitMetadata()
///
/// When creating a new repository locally we want the build to work and not report warnings even before the remote is set.
///
- [RequiresMSBuildVersionFact("17.8.0")]
+ [RequiresMSBuildVersionFact("17.12.0")]
public void WithNoRemoteNoCommit()
{
var testAsset = _testAssetsManager
@@ -131,7 +131,7 @@ public void WithNoRemoteNoCommit()
///
/// When creating a new repository locally we want the build to work and not report warnings even before the remote is set.
///
- [RequiresMSBuildVersionFact("17.8.0")]
+ [RequiresMSBuildVersionFact("17.12.0")]
public void WithNoRemote()
{
var testAsset = _testAssetsManager
diff --git a/test/Microsoft.NET.Clean.Tests/GivenThatWeWantToCleanAProject.cs b/test/Microsoft.NET.Clean.Tests/GivenThatWeWantToCleanAProject.cs
index c00d909febcd..2e50ad96a554 100644
--- a/test/Microsoft.NET.Clean.Tests/GivenThatWeWantToCleanAProject.cs
+++ b/test/Microsoft.NET.Clean.Tests/GivenThatWeWantToCleanAProject.cs
@@ -12,7 +12,7 @@ public GivenThatWeWantToCleanAHelloWorldProject(ITestOutputHelper log) : base(lo
{
}
- [RequiresMSBuildVersionFact("17.8.0")]
+ [RequiresMSBuildVersionFact("17.12.0")]
public void It_cleans_without_logging_assets_message()
{
var testAsset = _testAssetsManager
diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs
index 891b538f2478..44679e8ded2e 100644
--- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs
+++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs
@@ -19,7 +19,7 @@ public GivenThatWeWantToPublishAnAotApp(ITestOutputHelper log) : base(log)
{
}
- [RequiresMSBuildVersionTheory("17.8.0")]
+ [RequiresMSBuildVersionTheory("17.12.0")]
[MemberData(nameof(Net7Plus), MemberType = typeof(PublishTestUtils))]
public void NativeAot_hw_runs_with_no_warnings_when_PublishAot_is_enabled(string targetFramework)
{
@@ -91,7 +91,7 @@ public void NativeAot_hw_runs_with_no_warnings_when_PublishAot_is_enabled(string
.And.HaveStdOutContaining("Hello World");
}
- [RequiresMSBuildVersionTheory("17.8.0")]
+ [RequiresMSBuildVersionTheory("17.12.0")]
[MemberData(nameof(Net7Plus), MemberType = typeof(PublishTestUtils))]
public void NativeAot_hw_runs_with_no_warnings_when_PublishAot_is_false(string targetFramework)
{
diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishReadyToRun.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishReadyToRun.cs
index 06259ae936d6..e17ce09acbff 100644
--- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishReadyToRun.cs
+++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishReadyToRun.cs
@@ -45,8 +45,6 @@ public void It_only_runs_readytorun_compiler_when_switch_is_enabled(string targe
}
[RequiresMSBuildVersionTheory("17.0.0.32901")]
- [InlineData("netcoreapp3.0")]
- [InlineData("net5.0")]
[InlineData(ToolsetInfo.CurrentTargetFramework)]
public void It_creates_readytorun_images_for_all_assemblies_except_excluded_ones(string targetFramework)
{
@@ -91,8 +89,6 @@ public void It_creates_readytorun_images_for_all_assemblies_except_excluded_ones
}
[RequiresMSBuildVersionTheory("17.0.0.32901")]
- [InlineData("netcoreapp3.0")]
- [InlineData("net5.0")]
[InlineData(ToolsetInfo.CurrentTargetFramework)]
public void It_creates_readytorun_symbols_when_switch_is_used(string targetFramework)
{
@@ -100,8 +96,6 @@ public void It_creates_readytorun_symbols_when_switch_is_used(string targetFrame
}
[RequiresMSBuildVersionTheory("17.0.0.32901")]
- [InlineData("netcoreapp3.0")]
- [InlineData("net5.0")]
[InlineData(ToolsetInfo.CurrentTargetFramework)]
public void It_supports_framework_dependent_publishing(string targetFramework)
{
@@ -187,8 +181,6 @@ public void It_warns_when_targetting_netcoreapp_2_x_readytorun()
}
[RequiresMSBuildVersionTheory("17.0.0.32901")]
- [InlineData("netcoreapp3.0")]
- [InlineData("net5.0")]
[InlineData(ToolsetInfo.CurrentTargetFramework)]
public void It_can_publish_readytorun_for_library_projects(string targetFramework)
{
@@ -196,8 +188,6 @@ public void It_can_publish_readytorun_for_library_projects(string targetFramewor
}
[RequiresMSBuildVersionTheory("17.0.0.32901")]
- [InlineData("netcoreapp3.0")]
- [InlineData("net5.0")]
[InlineData(ToolsetInfo.CurrentTargetFramework)]
public void It_can_publish_readytorun_for_selfcontained_library_projects(string targetFramework)
{
diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs
index 03e3171e3c00..d77c46042580 100644
--- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs
+++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs
@@ -1662,49 +1662,6 @@ public void ILLink_dont_display_time_awareness_message_on_incremental_build(stri
.Should().Pass().And.NotHaveStdErrContaining("This process might take a while");
}
- [Fact()]
- public void ILLink_and_crossgen_process_razor_assembly()
- {
- var targetFramework = "netcoreapp3.0";
- var rid = EnvironmentInfo.GetCompatibleRid(targetFramework);
-
- var testProject = new TestProject
- {
- Name = "TestWeb",
- IsExe = true,
- ProjectSdk = "Microsoft.NET.Sdk.Web",
- TargetFrameworks = targetFramework,
- SourceFiles =
- {
- ["Program.cs"] = @"
- class Program
- {
- static void Main() {}
- }",
- ["Test.cshtml"] = @"
- @page
- @{
- System.IO.Compression.ZipFile.OpenRead(""test.zip"");
- }
- ",
- },
- AdditionalProperties =
- {
- ["RuntimeIdentifier"] = rid,
- ["PublishTrimmed"] = "true",
- ["PublishReadyToRun"] = "true",
- }
- };
-
- var testAsset = _testAssetsManager.CreateTestProject(testProject);
- var publishCommand = new PublishCommand(testAsset);
- publishCommand.Execute().Should().Pass();
-
- var publishDir = publishCommand.GetOutputDirectory(targetFramework, runtimeIdentifier: rid);
- publishDir.Should().HaveFile("System.IO.Compression.ZipFile.dll");
- GivenThatWeWantToPublishReadyToRun.DoesImageHaveR2RInfo(publishDir.File("TestWeb.Views.dll").FullName);
- }
-
[RequiresMSBuildVersionTheory("17.0.0.32901")]
[InlineData(ToolsetInfo.CurrentTargetFramework, true)]
[InlineData(ToolsetInfo.CurrentTargetFramework, false)]
diff --git a/test/Microsoft.NET.Publish.Tests/PublishTestUtils.cs b/test/Microsoft.NET.Publish.Tests/PublishTestUtils.cs
index 7307d22114de..0ac32c34ca6e 100644
--- a/test/Microsoft.NET.Publish.Tests/PublishTestUtils.cs
+++ b/test/Microsoft.NET.Publish.Tests/PublishTestUtils.cs
@@ -9,7 +9,8 @@ internal static class PublishTestUtils
public static IEnumerable