diff --git a/.editorconfig b/.editorconfig
index ec6569278190..34450257dc72 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -515,11 +515,11 @@ dotnet_diagnostic.IDE0040.severity = warning
insert_final_newline = false
# Verify settings
-[*.{received,verified}.{txt,xml,json}]
+[*.{received,verified}.{txt,xml,json,sh,zsh,nu,fish,ps1}]
charset = "utf-8-bom"
end_of_line = lf
indent_size = unset
indent_style = unset
insert_final_newline = false
tab_width = unset
-trim_trailing_whitespace = false
\ No newline at end of file
+trim_trailing_whitespace = false
diff --git a/.gitattributes b/.gitattributes
index 2f46e347155a..a7c35ea1b75a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -67,3 +67,7 @@
*.verified.txt text eol=lf working-tree-encoding=UTF-8
*.verified.xml text eol=lf working-tree-encoding=UTF-8
*.verified.json text eol=lf working-tree-encoding=UTF-8
+*.verified.sh text eol=lf working-tree-encoding=UTF-8
+*.verified.zsh text eol=lf working-tree-encoding=UTF-8
+*.verified.nu text eol=lf working-tree-encoding=UTF-8
+*.verified.fish text eol=lf working-tree-encoding=UTF-8
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 1cdcc7c1869e..212ebd955c26 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,11 @@
{
"dotnet.testWindow.disableAutoDiscovery": true,
- "dotnet.defaultSolution": "sdk.slnx"
+ "dotnet.testWindow.disableBuildOnRun": true,
+ "dotnet.defaultSolution": "cli.slnf",
+ "files.associations": {
+ "*.slnf": "json",
+ "*.props": "xml",
+ "*.targets": "xml",
+ "*.*proj": "xml"
+ }
}
diff --git a/cli.slnf b/cli.slnf
new file mode 100644
index 000000000000..a300800156bf
--- /dev/null
+++ b/cli.slnf
@@ -0,0 +1,15 @@
+{
+ "solution": {
+ "path": "sdk.slnx",
+ "projects": [
+ "src\\BuiltInTools\\dotnet-watch\\dotnet-watch.csproj",
+ "src\\Cli\\dotnet\\dotnet.csproj",
+ "src\\Cli\\Microsoft.DotNet.Cli.Utils\\Microsoft.DotNet.Cli.Utils.csproj",
+ "test\\dotnet-new.IntegrationTests\\dotnet-new.IntegrationTests.csproj",
+ "test\\dotnet-watch.Tests\\dotnet-watch.Tests.csproj",
+ "test\\dotnet.Tests\\dotnet.Tests.csproj",
+ "test\\Microsoft.DotNet.Cli.Utils.Tests\\Microsoft.DotNet.Cli.Utils.Tests.csproj",
+ "test\\Microsoft.NET.TestFramework\\Microsoft.NET.TestFramework.csproj"
+ ]
+ }
+}
diff --git a/documentation/general/analyzer-redirecting.md b/documentation/general/analyzer-redirecting.md
index e999b98d76fe..993445af4ce6 100644
--- a/documentation/general/analyzer-redirecting.md
+++ b/documentation/general/analyzer-redirecting.md
@@ -19,11 +19,12 @@ Targeting an SDK (and hence also loading analyzers) with newer major version in
## Overview
-- The SDK will contain a VSIX with the analyzer DLLs and an MEF-exported implementation of `IAnalyzerAssemblyRedirector`.
+- The SDK will deploy analyzer DLLs and Roslyn will deploy an implementation of `IAnalyzerAssemblyRedirector`
+ (could be deployed by SDK but we are saving on analyzer loads and Roslyn already has a VSIX with a DLL).
Implementations of this interface are imported by Roslyn and can redirect analyzer DLL loading.
-- The SDK's implementation of `IAnalyzerAssemblyRedirector` will redirect any analyzer DLL matching some pattern
- to the corresponding DLL deployed via the VSIX.
+- That implementation of `IAnalyzerAssemblyRedirector` will redirect any analyzer DLL matching some pattern
+ to the corresponding DLL deployed with VS.
Details of this process are described below.
- Note that when `IAnalyzerAssemblyRedirector` is involved, Roslyn is free to not use shadow copy loading and instead load the DLLs directly.
@@ -33,7 +34,7 @@ Targeting an SDK (and hence also loading analyzers) with newer major version in
## Details
-The VSIX contains some analyzers, for example:
+The VS deployment contains some analyzers, for example:
```
AspNetCoreAnalyzers\analyzers\dotnet\cs\Microsoft.AspNetCore.App.Analyzers.dll
@@ -69,7 +70,7 @@ C:\Program Files\dotnet\sdk\9.0.100-preview.5.24307.3\Sdks\Microsoft.NET.Sdk\ana
will be redirected to
```
-{VSIX}\SDKAnalyzers\Sdks\Microsoft.NET.Sdk\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll
+{InstallDir}\SDKAnalyzers\Sdks\Microsoft.NET.Sdk\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll
```
where `metadata.json` has `"SDKAnalyzers": "9.0.100-dev"`, because
@@ -83,6 +84,5 @@ Analyzers that cannot be matched will continue to be loaded from the SDK
### Implementation
Analyzer DLLs are contained in transport package `VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers`.
-The redirecting logic lives in "system" VS extension `Microsoft.Net.Sdk.AnalyzerRedirecting`.
[torn-sdk]: https://github.com/dotnet/sdk/issues/42087
diff --git a/documentation/general/dotnet-run-file.md b/documentation/general/dotnet-run-file.md
index 41bb96b5f6a2..a14132092ee1 100644
--- a/documentation/general/dotnet-run-file.md
+++ b/documentation/general/dotnet-run-file.md
@@ -56,6 +56,9 @@ Additionally, the implicit project file has the following customizations:
in case there is a project or solution in the same directory as the file-based app.
This ensures that items from nested projects and artifacts are not included by the app.
+ - `EnableDefaultEmbeddedResourceItems` and `EnableDefaultNoneItems` properties are set to `false` if the default SDK (`Microsoft.NET.Sdk`) is being used.
+ This avoids including files like `./**/*.resx` in simple file-based apps where users usually don't expect that.
+
## Grow up
When file-based programs reach an inflection point where build customizations in a project file are needed,
diff --git a/eng/Versions.props b/eng/Versions.props
index 4fa4077784a9..9cc9ebe8ba88 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -88,6 +88,7 @@
9.0.09.0.09.0.0
+ 8.0.09.0.04.5.48.0.0
diff --git a/sdk.slnx b/sdk.slnx
index 2b7309281946..c8592eeb1f7b 100644
--- a/sdk.slnx
+++ b/sdk.slnx
@@ -24,7 +24,6 @@
-
@@ -66,6 +65,7 @@
+
@@ -338,7 +338,6 @@
-
diff --git a/src/BuiltInTools/BrowserRefresh/Microsoft.AspNetCore.Watch.BrowserRefresh.csproj b/src/BuiltInTools/BrowserRefresh/Microsoft.AspNetCore.Watch.BrowserRefresh.csproj
index cb7cec42e392..4aeac077f070 100644
--- a/src/BuiltInTools/BrowserRefresh/Microsoft.AspNetCore.Watch.BrowserRefresh.csproj
+++ b/src/BuiltInTools/BrowserRefresh/Microsoft.AspNetCore.Watch.BrowserRefresh.csproj
@@ -25,4 +25,10 @@
+
+
+
+ %(FileName)%(Extension)
+
+
diff --git a/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs b/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs
index d3608901433e..d39ea6c0f519 100644
--- a/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs
+++ b/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs
@@ -29,9 +29,6 @@ internal abstract class AbstractBrowserRefreshServer(string middlewareAssemblyPa
{
public const string ServerLogComponentName = "BrowserRefreshServer";
- private static readonly ReadOnlyMemory s_reloadMessage = Encoding.UTF8.GetBytes("Reload");
- private static readonly ReadOnlyMemory s_waitMessage = Encoding.UTF8.GetBytes("Wait");
- private static readonly ReadOnlyMemory s_pingMessage = Encoding.UTF8.GetBytes("""{ "type" : "Ping" }""");
private static readonly JsonSerializerOptions s_jsonSerializerOptions = new(JsonSerializerDefaults.Web);
private readonly List _activeConnections = [];
@@ -233,19 +230,15 @@ public ValueTask SendJsonMessageAsync(TValue value, CancellationToken ca
public ValueTask SendReloadMessageAsync(CancellationToken cancellationToken)
{
logger.Log(LogEvents.ReloadingBrowser);
- return SendAsync(s_reloadMessage, cancellationToken);
+ return SendAsync(JsonReloadRequest.Message, cancellationToken);
}
public ValueTask SendWaitMessageAsync(CancellationToken cancellationToken)
{
logger.Log(LogEvents.SendingWaitMessage);
- return SendAsync(s_waitMessage, cancellationToken);
+ return SendAsync(JsonWaitRequest.Message, cancellationToken);
}
- // obsolete: to be removed
- public ValueTask SendPingMessageAsync(CancellationToken cancellationToken)
- => SendAsync(s_pingMessage, cancellationToken);
-
private ValueTask SendAsync(ReadOnlyMemory messageBytes, CancellationToken cancellationToken)
=> SendAndReceiveAsync(request: _ => messageBytes, response: null, cancellationToken);
@@ -293,13 +286,13 @@ public async ValueTask SendAndReceiveAsync(
public ValueTask RefreshBrowserAsync(CancellationToken cancellationToken)
{
logger.Log(LogEvents.RefreshingBrowser);
- return SendJsonMessageAsync(new AspNetCoreHotReloadApplied(), cancellationToken);
+ return SendAsync(JsonRefreshBrowserRequest.Message, cancellationToken);
}
public ValueTask ReportCompilationErrorsInBrowserAsync(ImmutableArray compilationErrors, CancellationToken cancellationToken)
{
logger.Log(LogEvents.UpdatingDiagnostics);
- return SendJsonMessageAsync(new HotReloadDiagnostics { Diagnostics = compilationErrors }, cancellationToken);
+ return SendJsonMessageAsync(new JsonReportDiagnosticsRequest { Diagnostics = compilationErrors }, cancellationToken);
}
public async ValueTask UpdateStaticAssetsAsync(IEnumerable relativeUrls, CancellationToken cancellationToken)
@@ -308,24 +301,37 @@ public async ValueTask UpdateStaticAssetsAsync(IEnumerable relativeUrls,
foreach (var relativeUrl in relativeUrls)
{
logger.Log(LogEvents.SendingStaticAssetUpdateRequest, relativeUrl);
- var message = JsonSerializer.SerializeToUtf8Bytes(new UpdateStaticFileMessage { Path = relativeUrl }, s_jsonSerializerOptions);
+ var message = JsonSerializer.SerializeToUtf8Bytes(new JasonUpdateStaticFileRequest { Path = relativeUrl }, s_jsonSerializerOptions);
await SendAsync(message, cancellationToken);
}
}
- private readonly struct AspNetCoreHotReloadApplied
+ private readonly struct JsonWaitRequest
+ {
+ public string Type => "Wait";
+ public static readonly ReadOnlyMemory Message = JsonSerializer.SerializeToUtf8Bytes(new JsonWaitRequest(), s_jsonSerializerOptions);
+ }
+
+ private readonly struct JsonReloadRequest
+ {
+ public string Type => "Reload";
+ public static readonly ReadOnlyMemory Message = JsonSerializer.SerializeToUtf8Bytes(new JsonReloadRequest(), s_jsonSerializerOptions);
+ }
+
+ private readonly struct JsonRefreshBrowserRequest
{
- public string Type => "AspNetCoreHotReloadApplied";
+ public string Type => "RefreshBrowser";
+ public static readonly ReadOnlyMemory Message = JsonSerializer.SerializeToUtf8Bytes(new JsonRefreshBrowserRequest(), s_jsonSerializerOptions);
}
- private readonly struct HotReloadDiagnostics
+ private readonly struct JsonReportDiagnosticsRequest
{
- public string Type => "HotReloadDiagnosticsv1";
+ public string Type => "ReportDiagnostics";
public IEnumerable Diagnostics { get; init; }
}
- private readonly struct UpdateStaticFileMessage
+ private readonly struct JasonUpdateStaticFileRequest
{
public string Type => "UpdateStaticFile";
public string Path { get; init; }
diff --git a/src/BuiltInTools/HotReloadClient/Web/WebAssemblyHotReloadClient.cs b/src/BuiltInTools/HotReloadClient/Web/WebAssemblyHotReloadClient.cs
index 2a45bfb07370..f47d6eb3a5ba 100644
--- a/src/BuiltInTools/HotReloadClient/Web/WebAssemblyHotReloadClient.cs
+++ b/src/BuiltInTools/HotReloadClient/Web/WebAssemblyHotReloadClient.cs
@@ -133,7 +133,7 @@ public override async Task ApplyManagedCodeUpdatesAsync(ImmutableAr
var anyFailure = false;
await browserRefreshServer.SendAndReceiveAsync(
- request: sharedSecret => new JsonApplyHotReloadDeltasRequest
+ request: sharedSecret => new JsonApplyManagedCodeUpdatesRequest
{
SharedSecret = sharedSecret,
UpdateId = batchId,
@@ -178,9 +178,9 @@ private static bool ReceiveUpdateResponseAsync(ReadOnlySpan value, ILogger
public override Task InitialUpdatesAppliedAsync(CancellationToken cancellationToken)
=> Task.CompletedTask;
- private readonly struct JsonApplyHotReloadDeltasRequest
+ private readonly struct JsonApplyManagedCodeUpdatesRequest
{
- public string Type => "BlazorHotReloadDeltav3";
+ public string Type => "ApplyManagedCodeUpdates";
public string? SharedSecret { get; init; }
public int UpdateId { get; init; }
@@ -211,7 +211,7 @@ private readonly struct JsonLogEntry
private readonly struct JsonGetApplyUpdateCapabilitiesRequest
{
- public string Type => "BlazorRequestApplyUpdateCapabilities2";
+ public string Type => "GetApplyUpdateCapabilities";
}
}
}
diff --git a/src/BuiltInTools/Web.Middleware/BrowserScriptMiddleware.cs b/src/BuiltInTools/Web.Middleware/BrowserScriptMiddleware.cs
index fcc10510e5eb..bade75e4fedd 100644
--- a/src/BuiltInTools/Web.Middleware/BrowserScriptMiddleware.cs
+++ b/src/BuiltInTools/Web.Middleware/BrowserScriptMiddleware.cs
@@ -48,7 +48,7 @@ public async Task InvokeAsync(HttpContext context)
// for backwards compat only
internal static ReadOnlyMemory GetBlazorHotReloadJS()
{
- var jsFileName = "Microsoft.AspNetCore.Watch.BrowserRefresh.BlazorHotReload.js";
+ var jsFileName = "BlazorHotReload.js";
using var stream = new MemoryStream();
var manifestStream = typeof(BrowserScriptMiddleware).Assembly.GetManifestResourceStream(jsFileName)!;
manifestStream.CopyTo(stream);
@@ -66,7 +66,7 @@ internal static ReadOnlyMemory GetBrowserRefreshJS()
internal static ReadOnlyMemory GetWebSocketClientJavaScript(string hostString, string serverKey)
{
- var jsFileName = "Microsoft.AspNetCore.Watch.BrowserRefresh.WebSocketScriptInjection.js";
+ var jsFileName = "WebSocketScriptInjection.js";
using var reader = new StreamReader(typeof(BrowserScriptMiddleware).Assembly.GetManifestResourceStream(jsFileName)!);
var script = reader.ReadToEnd()
.Replace("{{hostString}}", hostString)
diff --git a/src/BuiltInTools/Web.Middleware/Microsoft.DotNet.HotReload.Web.Middleware.Package.csproj b/src/BuiltInTools/Web.Middleware/Microsoft.DotNet.HotReload.Web.Middleware.Package.csproj
index f89cbbb34070..5bdde90926bf 100644
--- a/src/BuiltInTools/Web.Middleware/Microsoft.DotNet.HotReload.Web.Middleware.Package.csproj
+++ b/src/BuiltInTools/Web.Middleware/Microsoft.DotNet.HotReload.Web.Middleware.Package.csproj
@@ -38,4 +38,19 @@
+
+
+ $(TargetsForTfmSpecificContentInPackage);_AddJSFilesToSourcePackage
+
+
+
+
+
+
+ <_File Include="$(MSBuildProjectDirectory)\**\*.js" TargetDir="contentFiles/cs/$(TargetFramework)" BuildAction="EmbeddedResource" />
+
+
+
diff --git a/src/BuiltInTools/Web.Middleware/WebSocketScriptInjection.js b/src/BuiltInTools/Web.Middleware/WebSocketScriptInjection.js
index aa8f1fd60f98..44d6ae3250ad 100644
--- a/src/BuiltInTools/Web.Middleware/WebSocketScriptInjection.js
+++ b/src/BuiltInTools/Web.Middleware/WebSocketScriptInjection.js
@@ -26,38 +26,22 @@ setTimeout(async function () {
let waiting = false;
- connection.onmessage = function (message) {
- if (message.data === 'Reload') {
- console.debug('Server is ready. Reloading...');
- location.reload();
- } else if (message.data === 'Wait') {
- if (waiting) {
- return;
- }
- waiting = true;
- console.debug('File changes detected. Waiting for application to rebuild.');
- const glyphs = ['☱', '☲', '☴'];
- const title = document.title;
- let i = 0;
- setInterval(function () { document.title = glyphs[i++ % glyphs.length] + ' ' + title; }, 240);
+ connection.onmessage = function (message) {
+ const payload = JSON.parse(message.data);
+ const action = {
+ 'Reload': () => reload(),
+ 'Wait': () => wait(),
+ 'UpdateStaticFile': () => updateStaticFile(payload.path),
+ 'ApplyManagedCodeUpdates': () => applyManagedCodeUpdates(payload.sharedSecret, payload.updateId, payload.deltas, payload.responseLoggingLevel),
+ 'ReportDiagnostics': () => reportDiagnostics(payload.diagnostics),
+ 'GetApplyUpdateCapabilities': () => getApplyUpdateCapabilities(),
+ 'RefreshBrowser': () => refreshBrowser()
+ };
+
+ if (payload.type && action.hasOwnProperty(payload.type)) {
+ action[payload.type]();
} else {
- const payload = JSON.parse(message.data);
- const action = {
- 'UpdateStaticFile': () => updateStaticFile(payload.path),
- 'BlazorHotReloadDeltav1': () => applyBlazorDeltas_legacy(payload.sharedSecret, payload.deltas, false),
- 'BlazorHotReloadDeltav2': () => applyBlazorDeltas_legacy(payload.sharedSecret, payload.deltas, true),
- 'BlazorHotReloadDeltav3': () => applyBlazorDeltas(payload.sharedSecret, payload.updateId, payload.deltas, payload.responseLoggingLevel),
- 'HotReloadDiagnosticsv1': () => displayDiagnostics(payload.diagnostics),
- 'BlazorRequestApplyUpdateCapabilities': () => getBlazorWasmApplyUpdateCapabilities(false),
- 'BlazorRequestApplyUpdateCapabilities2': () => getBlazorWasmApplyUpdateCapabilities(true),
- 'AspNetCoreHotReloadApplied': () => aspnetCoreHotReloadApplied()
- };
-
- if (payload.type && action.hasOwnProperty(payload.type)) {
- action[payload.type]();
- } else {
- console.error('Unknown payload:', message.data);
- }
+ console.error('Unknown payload:', message.data);
}
}
@@ -106,12 +90,12 @@ setTimeout(async function () {
return messageAndStack
}
- function getBlazorWasmApplyUpdateCapabilities(sendErrorToClient) {
+ function getApplyUpdateCapabilities() {
let applyUpdateCapabilities;
try {
applyUpdateCapabilities = window.Blazor._internal.getApplyUpdateCapabilities();
} catch (error) {
- applyUpdateCapabilities = sendErrorToClient ? "!" + getMessageAndStack(error) : '';
+ applyUpdateCapabilities = "!" + getMessageAndStack(error);
}
connection.send(applyUpdateCapabilities);
}
@@ -137,41 +121,6 @@ setTimeout(async function () {
styleElement.parentNode.insertBefore(newElement, styleElement.nextSibling);
}
- async function applyBlazorDeltas_legacy(serverSecret, deltas, sendErrorToClient) {
- if (sharedSecret && (serverSecret != sharedSecret.encodedSharedSecret)) {
- // Validate the shared secret if it was specified. It might be unspecified in older versions of VS
- // that do not support this feature as yet.
- throw 'Unable to validate the server. Rejecting apply-update payload.';
- }
-
- let applyError = undefined;
-
- try {
- applyDeltas_legacy(deltas)
- } catch (error) {
- console.warn(error);
- applyError = error;
- }
-
- const body = JSON.stringify({
- id: deltas[0].sequenceId,
- deltas: deltas
- });
- try {
- await fetch('/_framework/blazor-hotreload', { method: 'post', headers: { 'content-type': 'application/json' }, body: body });
- } catch (error) {
- console.warn(error);
- applyError = error;
- }
-
- if (applyError) {
- sendDeltaNotApplied(sendErrorToClient ? applyError : undefined);
- } else {
- sendDeltaApplied();
- notifyHotReloadApplied();
- }
- }
-
function applyDeltas_legacy(deltas) {
let apply = window.Blazor?._internal?.applyHotReload
@@ -190,26 +139,16 @@ setTimeout(async function () {
});
}
}
- function sendDeltaApplied() {
- connection.send(new Uint8Array([1]).buffer);
- }
-
- function sendDeltaNotApplied(error) {
- if (error) {
- let encoder = new TextEncoder()
- connection.send(encoder.encode("\0" + error.message + "\0" + error.stack));
- } else {
- connection.send(new Uint8Array([0]).buffer);
- }
- }
- async function applyBlazorDeltas(serverSecret, updateId, deltas, responseLoggingLevel) {
+ async function applyManagedCodeUpdates(serverSecret, updateId, deltas, responseLoggingLevel) {
if (sharedSecret && (serverSecret != sharedSecret.encodedSharedSecret)) {
// Validate the shared secret if it was specified. It might be unspecified in older versions of VS
// that do not support this feature as yet.
throw 'Unable to validate the server. Rejecting apply-update payload.';
}
+ console.debug('Applying managed code updates.');
+
const AgentMessageSeverity_Error = 2
let applyError = undefined;
@@ -261,11 +200,13 @@ setTimeout(async function () {
}));
if (!applyError) {
- notifyHotReloadApplied();
+ displayChangesAppliedToast();
}
}
- function displayDiagnostics(diagnostics) {
+ function reportDiagnostics(diagnostics) {
+ console.debug('Reporting Hot Reload diagnostics.');
+
document.querySelectorAll('#dotnet-compile-error').forEach(el => el.remove());
const el = document.body.appendChild(document.createElement('div'));
el.id = 'dotnet-compile-error';
@@ -280,7 +221,7 @@ setTimeout(async function () {
});
}
- function notifyHotReloadApplied() {
+ function displayChangesAppliedToast() {
document.querySelectorAll('#dotnet-compile-error').forEach(el => el.remove());
if (document.querySelector('#dotnet-hotreload-toast')) {
return;
@@ -298,7 +239,7 @@ setTimeout(async function () {
setTimeout(() => el.remove(), 2000);
}
- function aspnetCoreHotReloadApplied() {
+ function refreshBrowser() {
if (window.Blazor) {
window[hotReloadActiveKey] = true;
// hotReloadApplied triggers an enhanced navigation to
@@ -306,17 +247,39 @@ setTimeout(async function () {
// Blazor SSR.
if (window.Blazor?._internal?.hotReloadApplied)
{
+ console.debug('Refreshing browser: WASM.');
Blazor._internal.hotReloadApplied();
}
else
{
- notifyHotReloadApplied();
+ console.debug('Refreshing browser.');
+ displayChangesAppliedToast();
}
} else {
+ console.debug('Refreshing browser: Reloading.');
location.reload();
}
}
+ function reload() {
+ console.debug('Reloading.');
+ location.reload();
+ }
+
+ function wait() {
+ console.debug('Waiting for application to rebuild.');
+
+ if (waiting) {
+ return;
+ }
+
+ waiting = true;
+ const glyphs = ['☱', '☲', '☴'];
+ const title = document.title;
+ let i = 0;
+ setInterval(function () { document.title = glyphs[i++ % glyphs.length] + ' ' + title; }, 240);
+ }
+
async function getSecret(serverKeyString) {
if (!serverKeyString || !window.crypto || !window.crypto.subtle) {
return null;
@@ -382,8 +345,8 @@ setTimeout(async function () {
webSocket.addEventListener('close', onClose);
if (window.Blazor?.removeEventListener && window.Blazor?.addEventListener)
{
- webSocket.addEventListener('close', () => window.Blazor?.removeEventListener('enhancedload', notifyHotReloadApplied));
- window.Blazor?.addEventListener('enhancedload', notifyHotReloadApplied);
+ webSocket.addEventListener('close', () => window.Blazor?.removeEventListener('enhancedload', displayChangesAppliedToast));
+ window.Blazor?.addEventListener('enhancedload', displayChangesAppliedToast);
}
});
}
diff --git a/src/BuiltInTools/dotnet-watch/Build/EvaluationResult.cs b/src/BuiltInTools/dotnet-watch/Build/EvaluationResult.cs
index 966ea12c87c4..d4090ce69840 100644
--- a/src/BuiltInTools/dotnet-watch/Build/EvaluationResult.cs
+++ b/src/BuiltInTools/dotnet-watch/Build/EvaluationResult.cs
@@ -32,12 +32,26 @@ public void WatchFiles(FileWatcher fileWatcher)
fileWatcher.WatchFiles(BuildFiles);
}
+ public static ImmutableDictionary GetGlobalBuildOptions(IEnumerable buildArguments, EnvironmentOptions environmentOptions)
+ {
+ // See https://github.com/dotnet/project-system/blob/main/docs/well-known-project-properties.md
+
+ return CommandLineOptions.ParseBuildProperties(buildArguments)
+ .ToImmutableDictionary(keySelector: arg => arg.key, elementSelector: arg => arg.value)
+ .SetItem(PropertyNames.DotNetWatchBuild, "true")
+ .SetItem(PropertyNames.DesignTimeBuild, "true")
+ .SetItem(PropertyNames.SkipCompilerExecution, "true")
+ .SetItem(PropertyNames.ProvideCommandLineArgs, "true")
+ // F# targets depend on host path variable:
+ .SetItem("DOTNET_HOST_PATH", environmentOptions.MuxerPath);
+ }
+
///
/// Loads project graph and performs design-time build.
///
public static EvaluationResult? TryCreate(
+ ProjectGraphFactory factory,
string rootProjectPath,
- IEnumerable buildArguments,
ILogger logger,
GlobalOptions options,
EnvironmentOptions environmentOptions,
@@ -46,20 +60,8 @@ public void WatchFiles(FileWatcher fileWatcher)
{
var buildReporter = new BuildReporter(logger, options, environmentOptions);
- // See https://github.com/dotnet/project-system/blob/main/docs/well-known-project-properties.md
-
- var globalOptions = CommandLineOptions.ParseBuildProperties(buildArguments)
- .ToImmutableDictionary(keySelector: arg => arg.key, elementSelector: arg => arg.value)
- .SetItem(PropertyNames.DotNetWatchBuild, "true")
- .SetItem(PropertyNames.DesignTimeBuild, "true")
- .SetItem(PropertyNames.SkipCompilerExecution, "true")
- .SetItem(PropertyNames.ProvideCommandLineArgs, "true")
- // F# targets depend on host path variable:
- .SetItem("DOTNET_HOST_PATH", environmentOptions.MuxerPath);
-
- var projectGraph = ProjectGraphUtilities.TryLoadProjectGraph(
+ var projectGraph = factory.TryLoadProjectGraph(
rootProjectPath,
- globalOptions,
logger,
projectGraphRequired: true,
cancellationToken);
diff --git a/src/BuiltInTools/dotnet-watch/Build/ProjectGraphFactory.cs b/src/BuiltInTools/dotnet-watch/Build/ProjectGraphFactory.cs
new file mode 100644
index 000000000000..520bd4b8f972
--- /dev/null
+++ b/src/BuiltInTools/dotnet-watch/Build/ProjectGraphFactory.cs
@@ -0,0 +1,80 @@
+// 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 Microsoft.Build.Evaluation;
+using Microsoft.Build.Graph;
+using Microsoft.Extensions.Logging;
+using ILogger = Microsoft.Extensions.Logging.ILogger;
+
+namespace Microsoft.DotNet.Watch;
+
+internal sealed class ProjectGraphFactory(ImmutableDictionary globalOptions)
+{
+ ///
+ /// Reuse with XML element caching to improve performance.
+ ///
+ /// The cache is automatically updated when build files change.
+ /// https://github.com/dotnet/msbuild/blob/b6f853defccd64ae1e9c7cf140e7e4de68bff07c/src/Build/Definition/ProjectCollection.cs#L343-L354
+ ///
+ private readonly ProjectCollection _collection = new(
+ globalProperties: globalOptions,
+ loggers: [],
+ remoteLoggers: [],
+ ToolsetDefinitionLocations.Default,
+ maxNodeCount: 1,
+ onlyLogCriticalEvents: false,
+ loadProjectsReadOnly: false,
+ useAsynchronousLogging: false,
+ reuseProjectRootElementCache: true);
+
+ ///
+ /// Tries to create a project graph by running the build evaluation phase on the .
+ ///
+ public ProjectGraph? TryLoadProjectGraph(
+ string rootProjectFile,
+ ILogger logger,
+ bool projectGraphRequired,
+ CancellationToken cancellationToken)
+ {
+ var entryPoint = new ProjectGraphEntryPoint(rootProjectFile, globalOptions);
+ try
+ {
+ return new ProjectGraph([entryPoint], _collection, projectInstanceFactory: null, cancellationToken);
+ }
+ catch (Exception e) when (e is not OperationCanceledException)
+ {
+ // ProejctGraph aggregates OperationCanceledException exception,
+ // throw here to propagate the cancellation.
+ cancellationToken.ThrowIfCancellationRequested();
+
+ logger.LogDebug("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)
+ {
+ logger.LogError(e.Message);
+ }
+ else
+ {
+ logger.LogWarning(e.Message);
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/BuiltInTools/dotnet-watch/Build/ProjectGraphUtilities.cs b/src/BuiltInTools/dotnet-watch/Build/ProjectGraphUtilities.cs
index 56bcba3427e6..6cb4c6b57a04 100644
--- a/src/BuiltInTools/dotnet-watch/Build/ProjectGraphUtilities.cs
+++ b/src/BuiltInTools/dotnet-watch/Build/ProjectGraphUtilities.cs
@@ -1,82 +1,14 @@
// 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 Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Graph;
using Microsoft.DotNet.Cli;
-using Microsoft.Extensions.Logging;
-using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace Microsoft.DotNet.Watch;
internal static class ProjectGraphUtilities
{
- ///
- /// Tries to create a project graph by running the build evaluation phase on the .
- ///
- public static ProjectGraph? TryLoadProjectGraph(
- string rootProjectFile,
- ImmutableDictionary globalOptions,
- ILogger logger,
- bool projectGraphRequired,
- CancellationToken cancellationToken)
- {
- var entryPoint = new ProjectGraphEntryPoint(rootProjectFile, globalOptions);
- try
- {
- // Create a new project collection that does not reuse element cache
- // to work around https://github.com/dotnet/msbuild/issues/12064:
- var collection = new ProjectCollection(
- globalProperties: globalOptions,
- loggers: [],
- remoteLoggers: [],
- ToolsetDefinitionLocations.Default,
- maxNodeCount: 1,
- onlyLogCriticalEvents: false,
- loadProjectsReadOnly: false,
- useAsynchronousLogging: false,
- reuseProjectRootElementCache: false);
-
- return new ProjectGraph([entryPoint], collection, projectInstanceFactory: null, cancellationToken);
- }
- catch (Exception e) when (e is not OperationCanceledException)
- {
- // ProejctGraph aggregates OperationCanceledException exception,
- // throw here to propagate the cancellation.
- cancellationToken.ThrowIfCancellationRequested();
-
- logger.LogDebug("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)
- {
- logger.LogError(e.Message);
- }
- else
- {
- logger.LogWarning(e.Message);
- }
- }
- }
-
- return null;
- }
-
public static string GetDisplayName(this ProjectGraphNode projectNode)
=> $"{Path.GetFileNameWithoutExtension(projectNode.ProjectInstance.FullPath)} ({projectNode.GetTargetFramework()})";
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs
index 565bc7f9062b..5466d27b32c2 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs
@@ -20,6 +20,7 @@ internal sealed class HotReloadDotNetWatcher
private readonly RestartPrompt? _rudeEditRestartPrompt;
private readonly DotNetWatchContext _context;
+ private readonly ProjectGraphFactory _designTimeBuildGraphFactory;
internal Task? Test_FileChangesCompletedTask { get; set; }
@@ -40,6 +41,11 @@ public HotReloadDotNetWatcher(DotNetWatchContext context, IConsole console, IRun
_rudeEditRestartPrompt = new RestartPrompt(context.Logger, consoleInput, noPrompt ? true : null);
}
+
+ _designTimeBuildGraphFactory = new ProjectGraphFactory(
+ EvaluationResult.GetGlobalBuildOptions(
+ context.RootProjectOptions.BuildArguments,
+ context.EnvironmentOptions));
}
public async Task WatchAsync(CancellationToken shutdownCancellationToken)
@@ -824,8 +830,8 @@ private async ValueTask EvaluateRootProjectAsync(bool restore,
var stopwatch = Stopwatch.StartNew();
var result = EvaluationResult.TryCreate(
- _context.RootProjectOptions.ProjectPath,
- _context.RootProjectOptions.BuildArguments,
+ _designTimeBuildGraphFactory,
+ _context.RootProjectOptions.ProjectPath,
_context.BuildLogger,
_context.Options,
_context.EnvironmentOptions,
diff --git a/src/BuiltInTools/dotnet-watch/Watch/MsBuildFileSetFactory.cs b/src/BuiltInTools/dotnet-watch/Watch/MsBuildFileSetFactory.cs
index 6de60206c1d2..50ce65d4e9a1 100644
--- a/src/BuiltInTools/dotnet-watch/Watch/MsBuildFileSetFactory.cs
+++ b/src/BuiltInTools/dotnet-watch/Watch/MsBuildFileSetFactory.cs
@@ -30,6 +30,9 @@ internal class MSBuildFileSetFactory(
private EnvironmentOptions EnvironmentOptions => buildReporter.EnvironmentOptions;
private ILogger Logger => buildReporter.Logger;
+ private readonly ProjectGraphFactory _buildGraphFactory = new(
+ globalOptions: CommandLineOptions.ParseBuildProperties(buildArguments).ToImmutableDictionary(keySelector: arg => arg.key, elementSelector: arg => arg.value));
+
internal sealed class EvaluationResult(IReadOnlyDictionary files, ProjectGraph? projectGraph)
{
public readonly IReadOnlyDictionary Files = files;
@@ -124,10 +127,7 @@ void AddFile(string filePath, string? staticWebAssetPath)
ProjectGraph? projectGraph = null;
if (requireProjectGraph != null)
{
- var globalOptions = CommandLineOptions.ParseBuildProperties(buildArguments)
- .ToImmutableDictionary(keySelector: arg => arg.key, elementSelector: arg => arg.value);
-
- projectGraph = ProjectGraphUtilities.TryLoadProjectGraph(rootProjectFile, globalOptions, Logger, requireProjectGraph.Value, cancellationToken);
+ projectGraph = _buildGraphFactory.TryLoadProjectGraph(rootProjectFile, Logger, requireProjectGraph.Value, cancellationToken);
if (projectGraph == null && requireProjectGraph == true)
{
return null;
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Env.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Env.cs
index dbedb133bfc0..c85eaa1d0bf7 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/Env.cs
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Env.cs
@@ -1,6 +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.CodeAnalysis;
+
namespace Microsoft.DotNet.Cli.Utils;
public static class Env
@@ -21,6 +23,9 @@ public static class Env
public static bool GetEnvironmentVariableAsBool(string name, bool defaultValue = false) =>
s_environment.GetEnvironmentVariableAsBool(name, defaultValue);
+ public static bool TryGetEnvironmentVariableAsBool(string name, [NotNullWhen(true)] out bool value) =>
+ s_environment.TryGetEnvironmentVariableAsBool(name, out value);
+
public static int? GetEnvironmentVariableAsNullableInt(string name) =>
s_environment.GetEnvironmentVariableAsNullableInt(name);
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs
index 50984a692d1c..06c64b67110a 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.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.Diagnostics.CodeAnalysis;
using Microsoft.DotNet.Cli.Utils.Extensions;
namespace Microsoft.DotNet.Cli.Utils;
@@ -136,6 +137,12 @@ public bool GetEnvironmentVariableAsBool(string name, bool defaultValue)
return Environment.GetEnvironmentVariable(variable, target);
}
+ public bool TryGetEnvironmentVariable(string name, [NotNullWhen(true)] out string? value)
+ {
+ value = Environment.GetEnvironmentVariable(name);
+ return value != null;
+ }
+
public void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target)
{
Environment.SetEnvironmentVariable(variable, value, target);
@@ -150,4 +157,57 @@ public void SetEnvironmentVariable(string variable, string value, EnvironmentVar
return null;
}
+
+ public bool TryGetEnvironmentVariableAsBool(string name, [NotNullWhen(true)] out bool value)
+ {
+ if (TryGetEnvironmentVariable(name, out string? strValue) &&
+ (bool.TryParse(strValue, out bool boolValue)
+ || TryParseNonBoolConstantStringAsBool(strValue, out boolValue)))
+ {
+ value = boolValue;
+ return true;
+ }
+ else
+ {
+ value = false;
+ return false;
+ }
+ }
+
+ ///
+ /// Parses non-boolean constant strings like "1", "0", "yes", "no", "on", "off" as boolean values.
+ ///
+ private static bool TryParseNonBoolConstantStringAsBool(string? strValue, out bool value)
+ {
+ switch (strValue?.ToLowerInvariant())
+ {
+ case "1":
+ case "yes":
+ case "on":
+ value = true;
+ return true;
+ case "0":
+ case "no":
+ case "off":
+ value = false;
+ return true;
+ default:
+ value = false;
+ return false;
+ }
+ }
+
+ public bool TryGetEnvironmentVariableAsInt(string name, [NotNullWhen(true)] out int value)
+ {
+ if (TryGetEnvironmentVariable(name, out string? strValue) && int.TryParse(strValue, out int intValue))
+ {
+ value = intValue;
+ return true;
+ }
+ else
+ {
+ value = -1;
+ return false;
+ }
+ }
}
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs
index 385f989d4990..8aaacf30fbad 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs
@@ -3,6 +3,9 @@
namespace Microsoft.DotNet.Cli.Utils;
+using System.Diagnostics.CodeAnalysis;
+
+
public interface IEnvironmentProvider
{
IEnumerable ExecutableExtensions { get; }
@@ -19,6 +22,10 @@ public interface IEnvironmentProvider
string? GetEnvironmentVariable(string name);
+ bool TryGetEnvironmentVariable(string name, [NotNullWhen(true)] out string? value);
+ bool TryGetEnvironmentVariableAsBool(string name, [NotNullWhen(true)] out bool value);
+ bool TryGetEnvironmentVariableAsInt(string name, [NotNullWhen(true)] out int value);
+
string? GetEnvironmentVariable(string variable, EnvironmentVariableTarget target);
void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target);
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs
index 12226f42f0d6..7fedc17f3878 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs
@@ -21,7 +21,9 @@ private MSBuildArgs(
string[]? getTargetResult,
string[]? getResultOutputFile,
VerbosityOptions? verbosity,
- string[]? otherMSBuildArgs)
+ bool noLogo,
+ string[]? otherMSBuildArgs
+ )
{
GlobalProperties = properties;
RestoreGlobalProperties = restoreProperties;
@@ -31,6 +33,7 @@ private MSBuildArgs(
GetTargetResult = getTargetResult;
GetResultOutputFile = getResultOutputFile;
Verbosity = verbosity;
+ NoLogo = noLogo;
OtherMSBuildArgs = otherMSBuildArgs is not null
? [.. otherMSBuildArgs]
: new List();
@@ -51,16 +54,33 @@ private MSBuildArgs(
///
public string[]? RequestedTargets { get; }
+ ///
+ /// If specified, the list of MSBuild Property names to retrieve and report directly for this build of a single project.
+ ///
public string[]? GetProperty { get; }
+ ///
+ /// If specified, the list of MSBuild Item names to retrieve and report directly for this build of a single project.
+ ///
public string[]? GetItem { get; }
+ ///
+ /// If specified, the list of MSBuild Target Output/Return Items to retrieve and report directly for this build of a single project.
+ ///
public string[]? GetTargetResult { get; }
+ ///
+ /// If specified, the list of output files to which to write --getProperty, --getItem, and --getTargetResult outputs.
+ ///
public string[]? GetResultOutputFile { get; }
public VerbosityOptions? Verbosity { get; }
+ ///
+ /// Whether or not the MSBuild product header text should be emitted at the start of this build
+ ///
+ public bool NoLogo { get; }
+
///
/// All other arguments that aren't already explicitly modeled by this structure.
/// The main categories of these today are logger configurations
@@ -89,16 +109,15 @@ public static MSBuildArgs AnalyzeMSBuildArguments(IEnumerable forwardedA
}
var parseResult = fakeCommand.Parse([.. forwardedAndUserFacingArgs], _analysisParsingConfiguration);
- var globalProperties = parseResult.GetResult("--property") is OptionResult propResult ? propResult.GetValueOrDefault?>() : null;
- var restoreProperties = parseResult.GetResult("--restoreProperty") is OptionResult restoreResult ? restoreResult.GetValueOrDefault?>() : null;
- var requestedTargets = parseResult.GetResult("--target") is OptionResult targetResult ? targetResult.GetValueOrDefault() : null;
+ var globalProperties = TryGetValue?>("--property");
+ var restoreProperties = TryGetValue?>("--restoreProperty");
+ var requestedTargets = TryGetValue("--target");
var getProperty = TryGetValue("--getProperty");
var getItem = TryGetValue("--getItem");
var getTargetResult = TryGetValue("--getTargetResult");
var getResultOutputFile = TryGetValue("--getResultOutputFile");
- var verbosity = parseResult.GetResult("--verbosity") is OptionResult verbosityResult
- ? verbosityResult.GetValueOrDefault()
- : null;
+ var verbosity = TryGetValue("--verbosity");
+ var nologo = TryGetValue("--no-logo") ?? true; // Default to nologo if not specified
var otherMSBuildArgs = parseResult.UnmatchedTokens.ToArray();
return new MSBuildArgs(
properties: globalProperties,
@@ -109,8 +128,12 @@ public static MSBuildArgs AnalyzeMSBuildArguments(IEnumerable forwardedA
getTargetResult: getTargetResult,
getResultOutputFile: getResultOutputFile,
otherMSBuildArgs: otherMSBuildArgs,
- verbosity: verbosity);
+ verbosity: verbosity,
+ noLogo: nologo);
+ /// We can't use to check if the names of the options we care
+ /// about were specified, because if they weren't specified it throws.
+ /// So we first check if the option was specified, and only then get its value.
T? TryGetValue(string name)
{
return options.Any(o => o.Name == name) ? parseResult.GetValue(name) : default;
@@ -120,19 +143,19 @@ public static MSBuildArgs AnalyzeMSBuildArguments(IEnumerable forwardedA
public static MSBuildArgs FromProperties(ReadOnlyDictionary? properties)
{
- return new MSBuildArgs(properties, null, null, null, null, null, null, null, null);
+ return new MSBuildArgs(properties, null, null, null, null, null, null, null, noLogo: false, null);
}
public static MSBuildArgs FromOtherArgs(params ReadOnlySpan args)
{
- return new MSBuildArgs(null, null, null, null, null, null, null, null, args.ToArray());
+ return new MSBuildArgs(null, null, null, null, null, null, null, null, noLogo: false, args.ToArray());
}
public static MSBuildArgs FromVerbosity(VerbosityOptions verbosity)
{
- return new MSBuildArgs(null, null, null, null, null, null, null, verbosity, null);
+ return new MSBuildArgs(null, null, null, null, null, null, null, verbosity, noLogo: false, null);
}
- public static readonly MSBuildArgs ForHelp = new(null, null, null, null, null, null, null, null, ["--help"]);
+ public static readonly MSBuildArgs ForHelp = new(null, null, null, null, null, null, null, null, noLogo: true, ["--help"]);
///
/// Completely replaces the MSBuild arguments with the provided .
@@ -148,6 +171,7 @@ public MSBuildArgs CloneWithExplicitArgs(string[] newArgs)
getTargetResult: GetTargetResult,
getResultOutputFile: GetResultOutputFile,
otherMSBuildArgs: newArgs,
+ noLogo: NoLogo,
verbosity: Verbosity);
}
@@ -168,6 +192,7 @@ public MSBuildArgs CloneWithAdditionalArgs(params string[] additionalArgs)
GetTargetResult,
GetResultOutputFile,
Verbosity,
+ NoLogo,
OtherMSBuildArgs.ToArray());
}
@@ -180,6 +205,7 @@ public MSBuildArgs CloneWithAdditionalArgs(params string[] additionalArgs)
GetTargetResult,
GetResultOutputFile,
Verbosity,
+ NoLogo,
[.. OtherMSBuildArgs, .. additionalArgs]);
}
@@ -197,6 +223,7 @@ public MSBuildArgs CloneWithAdditionalRestoreProperties(ReadOnlyDictionary additi
GetTargetResult,
GetResultOutputFile,
Verbosity,
+ NoLogo,
OtherMSBuildArgs.ToArray());
}
@@ -305,6 +338,22 @@ public MSBuildArgs CloneWithVerbosity(VerbosityOptions newVerbosity)
GetTargetResult,
GetResultOutputFile,
newVerbosity,
+ NoLogo,
+ OtherMSBuildArgs.ToArray());
+ }
+
+ public MSBuildArgs CloneWithNoLogo(bool noLogo)
+ {
+ return new MSBuildArgs(
+ GlobalProperties,
+ RestoreGlobalProperties,
+ RequestedTargets,
+ GetProperty,
+ GetItem,
+ GetTargetResult,
+ GetResultOutputFile,
+ Verbosity,
+ noLogo,
OtherMSBuildArgs.ToArray());
}
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs
index e0dca709ac3c..87d021eda157 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs
@@ -44,17 +44,11 @@ public static string MSBuildVersion
private readonly List _msbuildRequiredParameters = ["-maxcpucount", $"--verbosity:{DefaultVerbosity}"];
- public MSBuildForwardingAppWithoutLogging(MSBuildArgs msbuildArgs, string? msbuildPath = null, bool includeLogo = false, bool isRestoring = true)
+ public MSBuildForwardingAppWithoutLogging(MSBuildArgs msbuildArgs, string? msbuildPath = null)
{
string defaultMSBuildPath = GetMSBuildExePath();
_msbuildArgs = msbuildArgs;
- if (!includeLogo && !msbuildArgs.OtherMSBuildArgs.Contains("-nologo", StringComparer.OrdinalIgnoreCase))
- {
- // If the user didn't explicitly ask for -nologo, we add it to avoid the MSBuild logo.
- // This is useful for scenarios like restore where we don't want to print the logo.
- // Note that this is different from the default behavior of MSBuild, which prints the logo.
- msbuildArgs.OtherMSBuildArgs.Add("-nologo");
- }
+
string? tlpDefault = TerminalLoggerDefault;
if (string.IsNullOrWhiteSpace(tlpDefault))
{
@@ -101,6 +95,7 @@ private string[] EmitMSBuildArgs(MSBuildArgs msbuildArgs) => [
.. msbuildArgs.RestoreGlobalProperties?.Select(kvp => EmitProperty(kvp, "restoreProperty")) ?? [],
.. msbuildArgs.RequestedTargets?.Select(target => $"--target:{target}") ?? [],
.. msbuildArgs.Verbosity is not null ? new string[1] { $"--verbosity:{msbuildArgs.Verbosity}" } : [],
+ .. msbuildArgs.NoLogo is true ? new string[1] { "--nologo" } : [],
.. msbuildArgs.OtherMSBuildArgs
];
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Polyfills.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Polyfills.cs
index ab674a2906ee..59668dbae394 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/Polyfills.cs
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Polyfills.cs
@@ -4,11 +4,24 @@
#if NET472
#pragma warning disable IDE0130 // Namespace does not match folder structure
-namespace System.Runtime.CompilerServices;
-#pragma warning restore IDE0130 // Namespace does not match folder structure
+namespace System.Runtime.CompilerServices {
+
+ internal static class IsExternalInit
+ {
+ }
+
+}
-internal static class IsExternalInit
+namespace System.Diagnostics.CodeAnalysis
{
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+ internal sealed class NotNullWhenAttribute : Attribute
+ {
+ public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
+
+ public bool ReturnValue { get; }
+ }
}
+#pragma warning restore IDE0130 // Namespace does not match folder structure
#endif
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/.editorconfig b/src/Cli/Microsoft.DotNet.FileBasedPrograms/.editorconfig
new file mode 100644
index 000000000000..5aebb5380316
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/.editorconfig
@@ -0,0 +1,20 @@
+# Note: this editorconfig is *only* used during local builds.
+# The actual source package will include 'eng\config\SourcePackage.editorconfig' or similar per-TFM config to control analyzer behavior at the consumption side.
+
+[*.cs]
+# IDE0240: Remove redundant nullable directive
+# The directive needs to be included since all sources in a source package are considered generated code
+# when referenced from a project via package reference.
+dotnet_diagnostic.IDE0240.severity = none
+
+dotnet_diagnostic.RS0051.severity = warning # Add internal types and members to the declared API
+dotnet_diagnostic.RS0052.severity = warning # Remove deleted types and members from the declared internal API
+dotnet_diagnostic.RS0053.severity = warning # The contents of the internal API files are invalid
+dotnet_diagnostic.RS0054.severity = warning # Do not duplicate symbols in internal API files
+dotnet_diagnostic.RS0055.severity = warning # Annotate nullability of internal types and members in the declared API
+dotnet_diagnostic.RS0056.severity = warning # Enable tracking of nullability of reference types in the declared API
+dotnet_diagnostic.RS0057.severity = warning # Internal members should not use oblivious types
+dotnet_diagnostic.RS0058.severity = warning # Missing shipped or unshipped internal API file
+dotnet_diagnostic.RS0059.severity = warning # Do not add multiple public overloads with optional parameters
+dotnet_diagnostic.RS0060.severity = warning # API with optional parameter(s) should have the most parameters amongst its public overloads
+dotnet_diagnostic.RS0061.severity = warning # Constructor make noninheritable base class inheritable
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/ExternalHelpers.cs b/src/Cli/Microsoft.DotNet.FileBasedPrograms/ExternalHelpers.cs
new file mode 100644
index 000000000000..6b554d381eed
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/ExternalHelpers.cs
@@ -0,0 +1,64 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+using System;
+using System.IO;
+
+namespace Microsoft.DotNet.FileBasedPrograms;
+
+///
+/// When targeting netstandard2.0, the user of the source package must "implement" certain methods by declaring members in this type.
+///
+internal partial class ExternalHelpers
+{
+ public static partial int CombineHashCodes(int value1, int value2);
+ public static partial string GetRelativePath(string relativeTo, string path);
+
+ public static partial bool IsPathFullyQualified(string path);
+
+#if NET
+ public static partial int CombineHashCodes(int value1, int value2)
+ => HashCode.Combine(value1, value2);
+
+ public static partial string GetRelativePath(string relativeTo, string path)
+ => Path.GetRelativePath(relativeTo, path);
+
+ public static partial bool IsPathFullyQualified(string path)
+ => Path.IsPathFullyQualified(path);
+
+#elif FILE_BASED_PROGRAMS_SOURCE_PACKAGE_BUILD
+ // This path should only be used when we are verifying that the source package itself builds under netstandard2.0.
+ public static partial int CombineHashCodes(int value1, int value2)
+ => throw new NotImplementedException();
+
+ public static partial string GetRelativePath(string relativeTo, string path)
+ => throw new NotImplementedException();
+
+ public static partial bool IsPathFullyQualified(string path)
+ => throw new NotImplementedException();
+
+#endif
+}
+
+// https://github.com/dotnet/sdk/issues/51487: Remove usage of GracefulException from the source package
+#if FILE_BASED_PROGRAMS_SOURCE_PACKAGE_GRACEFUL_EXCEPTION
+internal class GracefulException : Exception
+{
+ public GracefulException()
+ {
+ }
+
+ public GracefulException(string? message) : base(message)
+ {
+ }
+
+ public GracefulException(string format, string arg) : this(string.Format(format, arg))
+ {
+ }
+
+ public GracefulException(string? message, Exception? innerException) : base(message, innerException)
+ {
+ }
+}
+#endif
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileBasedProgramsResources.resx b/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileBasedProgramsResources.resx
new file mode 100644
index 000000000000..1161700f99d9
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileBasedProgramsResources.resx
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Could not find any project in `{0}`.
+
+
+ Could not find project or directory `{0}`.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+
+
+ Invalid property name: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ {Locked="#:property"}
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ {Locked="#:property"}
+
+
+ error
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ {Locked="--force"}
+
+
+ Duplicate directives are not supported: {0}
+ {0} is the directive type and name.
+
+
+ Directives currently cannot contain double quotes (").
+
+
+ The '#:project' directive is invalid: {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Unrecognized directive '{0}'.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs b/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs
new file mode 100644
index 000000000000..22fd4000c0ff
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs
@@ -0,0 +1,625 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.Json.Serialization;
+using System.Text.RegularExpressions;
+using System.Xml;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+// https://github.com/dotnet/sdk/issues/51487: Remove usage of GracefulException from the source package
+#if !FILE_BASED_PROGRAMS_SOURCE_PACKAGE_GRACEFUL_EXCEPTION
+using Microsoft.DotNet.Cli.Utils;
+#endif
+
+namespace Microsoft.DotNet.FileBasedPrograms;
+
+// https://github.com/dotnet/sdk/issues/51487: Use 'file class' where appropriate to reduce exposed internal API surface
+internal static class FileLevelDirectiveHelpers
+{
+ public static SyntaxTokenParser CreateTokenizer(SourceText text)
+ {
+ return SyntaxFactory.CreateTokenParser(text,
+ CSharpParseOptions.Default.WithFeatures([new("FileBasedProgram", "true")]));
+ }
+
+ ///
+ /// If , the whole is parsed to find diagnostics about every app directive.
+ /// Otherwise, only directives up to the first C# token is checked.
+ /// The former is useful for dotnet project convert where we want to report all errors because it would be difficult to fix them up after the conversion.
+ /// The latter is useful for dotnet run file.cs where if there are app directives after the first token,
+ /// compiler reports anyway, so we speed up success scenarios by not parsing the whole file up front in the SDK CLI.
+ ///
+ public static ImmutableArray FindDirectives(SourceFile sourceFile, bool reportAllErrors, DiagnosticBag diagnostics)
+ {
+ var builder = ImmutableArray.CreateBuilder();
+ var tokenizer = CreateTokenizer(sourceFile.Text);
+
+ var result = tokenizer.ParseLeadingTrivia();
+ var triviaList = result.Token.LeadingTrivia;
+
+ FindLeadingDirectives(sourceFile, triviaList, diagnostics, builder);
+
+ // In conversion mode, we want to report errors for any invalid directives in the rest of the file
+ // so users don't end up with invalid directives in the converted project.
+ if (reportAllErrors)
+ {
+ tokenizer.ResetTo(result);
+
+ do
+ {
+ result = tokenizer.ParseNextToken();
+
+ foreach (var trivia in result.Token.LeadingTrivia)
+ {
+ ReportErrorFor(trivia);
+ }
+
+ foreach (var trivia in result.Token.TrailingTrivia)
+ {
+ ReportErrorFor(trivia);
+ }
+ }
+ while (!result.Token.IsKind(SyntaxKind.EndOfFileToken));
+ }
+
+ void ReportErrorFor(SyntaxTrivia trivia)
+ {
+ if (trivia.ContainsDiagnostics && trivia.IsKind(SyntaxKind.IgnoredDirectiveTrivia))
+ {
+ diagnostics.AddError(sourceFile, trivia.Span, FileBasedProgramsResources.CannotConvertDirective);
+ }
+ }
+
+ // The result should be ordered by source location, RemoveDirectivesFromFile depends on that.
+ return builder.ToImmutable();
+ }
+
+ /// Finds file-level directives in the leading trivia list of a compilation unit and reports diagnostics on them.
+ /// The builder to store the parsed directives in, or null if the parsed directives are not needed.
+ public static void FindLeadingDirectives(
+ SourceFile sourceFile,
+ SyntaxTriviaList triviaList,
+ DiagnosticBag diagnostics,
+ ImmutableArray.Builder? builder)
+ {
+ Debug.Assert(triviaList.Span.Start == 0);
+
+ var deduplicated = new Dictionary(NamedDirectiveComparer.Instance);
+ TextSpan previousWhiteSpaceSpan = default;
+
+ for (var index = 0; index < triviaList.Count; index++)
+ {
+ var trivia = triviaList[index];
+ // Stop when the trivia contains an error (e.g., because it's after #if).
+ if (trivia.ContainsDiagnostics)
+ {
+ break;
+ }
+
+ if (trivia.IsKind(SyntaxKind.WhitespaceTrivia))
+ {
+ Debug.Assert(previousWhiteSpaceSpan.IsEmpty);
+ previousWhiteSpaceSpan = trivia.FullSpan;
+ continue;
+ }
+
+ if (trivia.IsKind(SyntaxKind.ShebangDirectiveTrivia))
+ {
+ TextSpan span = GetFullSpan(previousWhiteSpaceSpan, trivia);
+
+ var whiteSpace = GetWhiteSpaceInfo(triviaList, index);
+ var info = new CSharpDirective.ParseInfo
+ {
+ Span = span,
+ LeadingWhiteSpace = whiteSpace.Leading,
+ TrailingWhiteSpace = whiteSpace.Trailing,
+ };
+ builder?.Add(new CSharpDirective.Shebang(info));
+ }
+ else if (trivia.IsKind(SyntaxKind.IgnoredDirectiveTrivia))
+ {
+ TextSpan span = GetFullSpan(previousWhiteSpaceSpan, trivia);
+
+ var message = trivia.GetStructure() is IgnoredDirectiveTriviaSyntax { Content: { RawKind: (int)SyntaxKind.StringLiteralToken } content }
+ ? content.Text.AsSpan().Trim()
+ : "";
+ var parts = Patterns.Whitespace.Split(message.ToString(), 2);
+ var name = parts.Length > 0 ? parts[0] : "";
+ var value = parts.Length > 1 ? parts[1] : "";
+ Debug.Assert(!(parts.Length > 2));
+
+ var whiteSpace = GetWhiteSpaceInfo(triviaList, index);
+ var context = new CSharpDirective.ParseContext
+ {
+ Info = new()
+ {
+ Span = span,
+ LeadingWhiteSpace = whiteSpace.Leading,
+ TrailingWhiteSpace = whiteSpace.Trailing,
+ },
+ Diagnostics = diagnostics,
+ SourceFile = sourceFile,
+ DirectiveKind = name,
+ DirectiveText = value,
+ };
+
+ // Block quotes now so we can later support quoted values without a breaking change. https://github.com/dotnet/sdk/issues/49367
+ if (value.Contains('"'))
+ {
+ diagnostics.AddError(sourceFile, context.Info.Span, FileBasedProgramsResources.QuoteInDirective);
+ }
+
+ if (CSharpDirective.Parse(context) is { } directive)
+ {
+ // If the directive is already present, report an error.
+ if (deduplicated.ContainsKey(directive))
+ {
+ var existingDirective = deduplicated[directive];
+ var typeAndName = $"#:{existingDirective.GetType().Name.ToLowerInvariant()} {existingDirective.Name}";
+ diagnostics.AddError(sourceFile, directive.Info.Span, string.Format(FileBasedProgramsResources.DuplicateDirective, typeAndName));
+ }
+ else
+ {
+ deduplicated.Add(directive, directive);
+ }
+
+ builder?.Add(directive);
+ }
+ }
+
+ previousWhiteSpaceSpan = default;
+ }
+
+ return;
+
+ static TextSpan GetFullSpan(TextSpan previousWhiteSpaceSpan, SyntaxTrivia trivia)
+ {
+ // Include the preceding whitespace in the span, i.e., span will be the whole line.
+ return previousWhiteSpaceSpan.IsEmpty ? trivia.FullSpan : TextSpan.FromBounds(previousWhiteSpaceSpan.Start, trivia.FullSpan.End);
+ }
+
+ static (WhiteSpaceInfo Leading, WhiteSpaceInfo Trailing) GetWhiteSpaceInfo(in SyntaxTriviaList triviaList, int index)
+ {
+ (WhiteSpaceInfo Leading, WhiteSpaceInfo Trailing) result = default;
+
+ for (int i = index - 1; i >= 0; i--)
+ {
+ if (!Fill(ref result.Leading, triviaList, i)) break;
+ }
+
+ for (int i = index + 1; i < triviaList.Count; i++)
+ {
+ if (!Fill(ref result.Trailing, triviaList, i)) break;
+ }
+
+ return result;
+
+ static bool Fill(ref WhiteSpaceInfo info, in SyntaxTriviaList triviaList, int index)
+ {
+ var trivia = triviaList[index];
+ if (trivia.IsKind(SyntaxKind.EndOfLineTrivia))
+ {
+ info.LineBreaks += 1;
+ info.TotalLength += trivia.FullSpan.Length;
+ return true;
+ }
+
+ if (trivia.IsKind(SyntaxKind.WhitespaceTrivia))
+ {
+ info.TotalLength += trivia.FullSpan.Length;
+ return true;
+ }
+
+ return false;
+ }
+ }
+ }
+}
+
+internal readonly record struct SourceFile(string Path, SourceText Text)
+{
+ public static SourceFile Load(string filePath)
+ {
+ using var stream = File.OpenRead(filePath);
+ return new SourceFile(filePath, SourceText.From(stream, Encoding.UTF8));
+ }
+
+ public SourceFile WithText(SourceText newText)
+ {
+ return new SourceFile(Path, newText);
+ }
+
+ public void Save()
+ {
+ using var stream = File.Open(Path, FileMode.Create, FileAccess.Write);
+ using var writer = new StreamWriter(stream, Encoding.UTF8);
+ Text.Write(writer);
+ }
+
+ public FileLinePositionSpan GetFileLinePositionSpan(TextSpan span)
+ {
+ return new FileLinePositionSpan(Path, Text.Lines.GetLinePositionSpan(span));
+ }
+
+ public string GetLocationString(TextSpan span)
+ {
+ var positionSpan = GetFileLinePositionSpan(span);
+ return $"{positionSpan.Path}({positionSpan.StartLinePosition.Line + 1})";
+ }
+}
+
+internal static partial class Patterns
+{
+ public static Regex Whitespace { get; } = new Regex("""\s+""", RegexOptions.Compiled);
+
+ public static Regex DisallowedNameCharacters { get; } = new Regex("""[\s@=/]""", RegexOptions.Compiled);
+
+ public static Regex EscapedCompilerOption { get; } = new Regex("""^/\w+:".*"$""", RegexOptions.Compiled | RegexOptions.Singleline);
+}
+
+internal struct WhiteSpaceInfo
+{
+ public int LineBreaks;
+ public int TotalLength;
+}
+
+///
+/// Represents a C# directive starting with #: (a.k.a., "file-level directive").
+/// Those are ignored by the language but recognized by us.
+///
+internal abstract class CSharpDirective(in CSharpDirective.ParseInfo info)
+{
+ public ParseInfo Info { get; } = info;
+
+ public readonly struct ParseInfo
+ {
+ ///
+ /// Span of the full line including the trailing line break.
+ ///
+ public required TextSpan Span { get; init; }
+ public required WhiteSpaceInfo LeadingWhiteSpace { get; init; }
+ public required WhiteSpaceInfo TrailingWhiteSpace { get; init; }
+ }
+
+ public readonly struct ParseContext
+ {
+ public required ParseInfo Info { get; init; }
+ public required DiagnosticBag Diagnostics { get; init; }
+ public required SourceFile SourceFile { get; init; }
+ public required string DirectiveKind { get; init; }
+ public required string DirectiveText { get; init; }
+ }
+
+ public static Named? Parse(in ParseContext context)
+ {
+ return context.DirectiveKind switch
+ {
+ "sdk" => Sdk.Parse(context),
+ "property" => Property.Parse(context),
+ "package" => Package.Parse(context),
+ "project" => Project.Parse(context),
+ var other => context.Diagnostics.AddError(context.SourceFile, context.Info.Span, string.Format(FileBasedProgramsResources.UnrecognizedDirective, other)),
+ };
+ }
+
+ private static (string, string?)? ParseOptionalTwoParts(in ParseContext context, char separator)
+ {
+ var separatorIndex = context.DirectiveText.IndexOf(separator);
+ var firstPart = (separatorIndex < 0 ? context.DirectiveText : context.DirectiveText.AsSpan(0, separatorIndex)).TrimEnd();
+
+ string directiveKind = context.DirectiveKind;
+ if (firstPart.IsWhiteSpace())
+ {
+ return context.Diagnostics.AddError<(string, string?)?>(context.SourceFile, context.Info.Span, string.Format(FileBasedProgramsResources.MissingDirectiveName, directiveKind));
+ }
+
+ // If the name contains characters that resemble separators, report an error to avoid any confusion.
+ if (Patterns.DisallowedNameCharacters.Match(context.DirectiveText, beginning: 0, length: firstPart.Length).Success)
+ {
+ return context.Diagnostics.AddError<(string, string?)?>(context.SourceFile, context.Info.Span, string.Format(FileBasedProgramsResources.InvalidDirectiveName, directiveKind, separator));
+ }
+
+ if (separatorIndex < 0)
+ {
+ return (firstPart.ToString(), null);
+ }
+
+ var secondPart = context.DirectiveText.AsSpan(separatorIndex + 1).TrimStart();
+ if (secondPart.IsWhiteSpace())
+ {
+ Debug.Assert(secondPart.Length == 0,
+ "We have trimmed the second part, so if it's white space, it should be actually empty.");
+
+ return (firstPart.ToString(), string.Empty);
+ }
+
+ return (firstPart.ToString(), secondPart.ToString());
+ }
+
+ public abstract override string ToString();
+
+ ///
+ /// #! directive.
+ ///
+ public sealed class Shebang(in ParseInfo info) : CSharpDirective(info)
+ {
+ public override string ToString() => "#!";
+ }
+
+ public abstract class Named(in ParseInfo info) : CSharpDirective(info)
+ {
+ public required string Name { get; init; }
+ }
+
+ ///
+ /// #:sdk directive.
+ ///
+ public sealed class Sdk(in ParseInfo info) : Named(info)
+ {
+ public string? Version { get; init; }
+
+ public static new Sdk? Parse(in ParseContext context)
+ {
+ if (ParseOptionalTwoParts(context, separator: '@') is not var (sdkName, sdkVersion))
+ {
+ return null;
+ }
+
+ return new Sdk(context.Info)
+ {
+ Name = sdkName,
+ Version = sdkVersion,
+ };
+ }
+
+ public override string ToString() => Version is null ? $"#:sdk {Name}" : $"#:sdk {Name}@{Version}";
+ }
+
+ ///
+ /// #:property directive.
+ ///
+ public sealed class Property(in ParseInfo info) : Named(info)
+ {
+ public required string Value { get; init; }
+
+ public static new Property? Parse(in ParseContext context)
+ {
+ if (ParseOptionalTwoParts(context, separator: '=') is not var (propertyName, propertyValue))
+ {
+ return null;
+ }
+
+ if (propertyValue is null)
+ {
+ return context.Diagnostics.AddError(context.SourceFile, context.Info.Span, FileBasedProgramsResources.PropertyDirectiveMissingParts);
+ }
+
+ try
+ {
+ propertyName = XmlConvert.VerifyName(propertyName);
+ }
+ catch (XmlException ex)
+ {
+ return context.Diagnostics.AddError(context.SourceFile, context.Info.Span, string.Format(FileBasedProgramsResources.PropertyDirectiveInvalidName, ex.Message), ex);
+ }
+
+ if (propertyName.Equals("RestoreUseStaticGraphEvaluation", StringComparison.OrdinalIgnoreCase) &&
+ MSBuildUtilities.ConvertStringToBool(propertyValue))
+ {
+ context.Diagnostics.AddError(context.SourceFile, context.Info.Span, FileBasedProgramsResources.StaticGraphRestoreNotSupported);
+ }
+
+ return new Property(context.Info)
+ {
+ Name = propertyName,
+ Value = propertyValue,
+ };
+ }
+
+ public override string ToString() => $"#:property {Name}={Value}";
+ }
+
+ ///
+ /// #:package directive.
+ ///
+ public sealed class Package(in ParseInfo info) : Named(info)
+ {
+ public string? Version { get; init; }
+
+ public static new Package? Parse(in ParseContext context)
+ {
+ if (ParseOptionalTwoParts(context, separator: '@') is not var (packageName, packageVersion))
+ {
+ return null;
+ }
+
+ return new Package(context.Info)
+ {
+ Name = packageName,
+ Version = packageVersion,
+ };
+ }
+
+ public override string ToString() => Version is null ? $"#:package {Name}" : $"#:package {Name}@{Version}";
+ }
+
+ ///
+ /// #:project directive.
+ ///
+ public sealed class Project(in ParseInfo info) : Named(info)
+ {
+ public static new Project? Parse(in ParseContext context)
+ {
+ var directiveText = context.DirectiveText;
+ if (directiveText.IsWhiteSpace())
+ {
+ string directiveKind = context.DirectiveKind;
+ return context.Diagnostics.AddError(context.SourceFile, context.Info.Span, string.Format(FileBasedProgramsResources.MissingDirectiveName, directiveKind));
+ }
+
+ try
+ {
+ // If the path is a directory like '../lib', transform it to a project file path like '../lib/lib.csproj'.
+ // Also normalize blackslashes to forward slashes to ensure the directive works on all platforms.
+ // https://github.com/dotnet/sdk/issues/51487: Behavior should not depend on process current directory
+ var sourceDirectory = Path.GetDirectoryName(context.SourceFile.Path) ?? ".";
+ var resolvedProjectPath = Path.Combine(sourceDirectory, directiveText.Replace('\\', '/'));
+ if (Directory.Exists(resolvedProjectPath))
+ {
+ var fullFilePath = GetProjectFileFromDirectory(resolvedProjectPath).FullName;
+
+ // Keep a relative path only if the original directive was a relative path.
+ directiveText = ExternalHelpers.IsPathFullyQualified(directiveText)
+ ? fullFilePath
+ : ExternalHelpers.GetRelativePath(relativeTo: sourceDirectory, fullFilePath);
+ }
+ else if (!File.Exists(resolvedProjectPath))
+ {
+ throw new GracefulException(FileBasedProgramsResources.CouldNotFindProjectOrDirectory, resolvedProjectPath);
+ }
+ }
+ catch (GracefulException e)
+ {
+ context.Diagnostics.AddError(context.SourceFile, context.Info.Span, string.Format(FileBasedProgramsResources.InvalidProjectDirective, e.Message), e);
+ }
+
+ return new Project(context.Info)
+ {
+ Name = directiveText,
+ };
+ }
+
+ public Project WithName(string name)
+ {
+ return new Project(Info) { Name = name };
+ }
+
+ // https://github.com/dotnet/sdk/issues/51487: Delete copies of methods from MsbuildProject and MSBuildUtilities from the source package, sharing the original method(s) under src/Cli instead.
+ public static FileInfo GetProjectFileFromDirectory(string projectDirectory)
+ {
+ DirectoryInfo dir;
+ try
+ {
+ dir = new DirectoryInfo(projectDirectory);
+ }
+ catch (ArgumentException)
+ {
+ throw new GracefulException(FileBasedProgramsResources.CouldNotFindProjectOrDirectory, projectDirectory);
+ }
+
+ if (!dir.Exists)
+ {
+ throw new GracefulException(FileBasedProgramsResources.CouldNotFindProjectOrDirectory, projectDirectory);
+ }
+
+ FileInfo[] files = dir.GetFiles("*proj");
+ if (files.Length == 0)
+ {
+ throw new GracefulException(
+ FileBasedProgramsResources.CouldNotFindAnyProjectInDirectory,
+ projectDirectory);
+ }
+
+ if (files.Length > 1)
+ {
+ throw new GracefulException(FileBasedProgramsResources.MoreThanOneProjectInDirectory, projectDirectory);
+ }
+
+ return files.First();
+ }
+
+ public override string ToString() => $"#:project {Name}";
+ }
+}
+
+///
+/// Used for deduplication - compares directives by their type and name (ignoring case).
+///
+internal sealed class NamedDirectiveComparer : IEqualityComparer
+{
+ public static readonly NamedDirectiveComparer Instance = new();
+
+ private NamedDirectiveComparer() { }
+
+ public bool Equals(CSharpDirective.Named? x, CSharpDirective.Named? y)
+ {
+ if (ReferenceEquals(x, y)) return true;
+
+ if (x is null || y is null) return false;
+
+ return x.GetType() == y.GetType() &&
+ StringComparer.OrdinalIgnoreCase.Equals(x.Name, y.Name);
+ }
+
+ public int GetHashCode(CSharpDirective.Named obj)
+ {
+ return ExternalHelpers.CombineHashCodes(
+ obj.GetType().GetHashCode(),
+ StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Name));
+ }
+}
+
+internal sealed class SimpleDiagnostic
+{
+ public required Position Location { get; init; }
+ public required string Message { get; init; }
+
+ ///
+ /// An adapter of that ensures we JSON-serialize only the necessary fields.
+ ///
+ ///
+ /// note: this type is only serialized for run-api scenarios.
+ /// If/when run-api is removed, we would also want to remove the usage of System.Text.Json attributes.
+ ///
+ public readonly struct Position
+ {
+ public required string Path { get; init; }
+ public required LinePositionSpan Span { get; init; }
+ [JsonIgnore]
+ public TextSpan TextSpan { get; init; }
+ }
+}
+
+internal readonly struct DiagnosticBag
+{
+ public bool IgnoreDiagnostics { get; private init; }
+
+ ///
+ /// If and is , the first diagnostic is thrown as .
+ ///
+ public ImmutableArray.Builder? Builder { get; private init; }
+
+ public static DiagnosticBag ThrowOnFirst() => default;
+ public static DiagnosticBag Collect(out ImmutableArray.Builder builder) => new() { Builder = builder = ImmutableArray.CreateBuilder() };
+ public static DiagnosticBag Ignore() => new() { IgnoreDiagnostics = true, Builder = null };
+
+ public void AddError(SourceFile sourceFile, TextSpan textSpan, string message, Exception? inner = null)
+ {
+ if (Builder != null)
+ {
+ Debug.Assert(!IgnoreDiagnostics);
+ Builder.Add(new SimpleDiagnostic { Location = new SimpleDiagnostic.Position() { Path = sourceFile.Path, TextSpan = textSpan, Span = sourceFile.GetFileLinePositionSpan(textSpan).Span }, Message = message });
+ }
+ else if (!IgnoreDiagnostics)
+ {
+ throw new GracefulException($"{sourceFile.GetLocationString(textSpan)}: {FileBasedProgramsResources.DirectiveError}: {message}", inner);
+ }
+ }
+
+ public T? AddError(SourceFile sourceFile, TextSpan span, string message, Exception? inner = null)
+ {
+ AddError(sourceFile, span, message, inner);
+ return default;
+ }
+}
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Shipped.txt b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Shipped.txt
new file mode 100644
index 000000000000..7dc5c58110bf
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Shipped.txt
@@ -0,0 +1 @@
+#nullable enable
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt
new file mode 100644
index 000000000000..84565c88284c
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt
@@ -0,0 +1,127 @@
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.CSharpDirective(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo info) -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Info.get -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Named
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Named.Name.get -> string!
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Named.Name.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Named.Named(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo info) -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Package
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Package.Package(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo info) -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Package.Version.get -> string?
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Package.Version.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext.Diagnostics.get -> Microsoft.DotNet.FileBasedPrograms.DiagnosticBag
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext.Diagnostics.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext.DirectiveKind.get -> string!
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext.DirectiveKind.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext.DirectiveText.get -> string!
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext.DirectiveText.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext.Info.get -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext.Info.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext.ParseContext() -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext.SourceFile.get -> Microsoft.DotNet.FileBasedPrograms.SourceFile
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext.SourceFile.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo.LeadingWhiteSpace.get -> Microsoft.DotNet.FileBasedPrograms.WhiteSpaceInfo
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo.LeadingWhiteSpace.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo.ParseInfo() -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo.Span.get -> Microsoft.CodeAnalysis.Text.TextSpan
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo.Span.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo.TrailingWhiteSpace.get -> Microsoft.DotNet.FileBasedPrograms.WhiteSpaceInfo
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo.TrailingWhiteSpace.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Project
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Project.Project(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo info) -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Project.WithName(string! name) -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Project!
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Property
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Property.Property(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo info) -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Property.Value.get -> string!
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Property.Value.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Sdk
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Sdk.Sdk(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo info) -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Sdk.Version.get -> string?
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Sdk.Version.init -> void
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Shebang
+Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Shebang.Shebang(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo info) -> void
+Microsoft.DotNet.FileBasedPrograms.DiagnosticBag
+Microsoft.DotNet.FileBasedPrograms.DiagnosticBag.AddError(Microsoft.DotNet.FileBasedPrograms.SourceFile sourceFile, Microsoft.CodeAnalysis.Text.TextSpan textSpan, string! message, System.Exception? inner = null) -> void
+Microsoft.DotNet.FileBasedPrograms.DiagnosticBag.AddError(Microsoft.DotNet.FileBasedPrograms.SourceFile sourceFile, Microsoft.CodeAnalysis.Text.TextSpan span, string! message, System.Exception? inner = null) -> T?
+Microsoft.DotNet.FileBasedPrograms.DiagnosticBag.Builder.get -> System.Collections.Immutable.ImmutableArray.Builder?
+Microsoft.DotNet.FileBasedPrograms.DiagnosticBag.DiagnosticBag() -> void
+Microsoft.DotNet.FileBasedPrograms.DiagnosticBag.IgnoreDiagnostics.get -> bool
+Microsoft.DotNet.FileBasedPrograms.ExternalHelpers
+Microsoft.DotNet.FileBasedPrograms.ExternalHelpers.ExternalHelpers() -> void
+Microsoft.DotNet.FileBasedPrograms.FileLevelDirectiveHelpers
+Microsoft.DotNet.FileBasedPrograms.GracefulException
+Microsoft.DotNet.FileBasedPrograms.GracefulException.GracefulException() -> void
+Microsoft.DotNet.FileBasedPrograms.GracefulException.GracefulException(string! format, string! arg) -> void
+Microsoft.DotNet.FileBasedPrograms.GracefulException.GracefulException(string? message) -> void
+Microsoft.DotNet.FileBasedPrograms.GracefulException.GracefulException(string? message, System.Exception? innerException) -> void
+Microsoft.DotNet.FileBasedPrograms.MSBuildUtilities
+Microsoft.DotNet.FileBasedPrograms.MSBuildUtilities.MSBuildUtilities() -> void
+Microsoft.DotNet.FileBasedPrograms.NamedDirectiveComparer
+Microsoft.DotNet.FileBasedPrograms.NamedDirectiveComparer.Equals(Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Named? x, Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Named? y) -> bool
+Microsoft.DotNet.FileBasedPrograms.NamedDirectiveComparer.GetHashCode(Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Named! obj) -> int
+Microsoft.DotNet.FileBasedPrograms.Patterns
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Location.get -> Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Position
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Location.init -> void
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Message.get -> string!
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Message.init -> void
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Position
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Position.Path.get -> string!
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Position.Path.init -> void
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Position.Position() -> void
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Position.Span.get -> Microsoft.CodeAnalysis.Text.LinePositionSpan
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Position.Span.init -> void
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Position.TextSpan.get -> Microsoft.CodeAnalysis.Text.TextSpan
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.Position.TextSpan.init -> void
+Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic.SimpleDiagnostic() -> void
+Microsoft.DotNet.FileBasedPrograms.SourceFile
+Microsoft.DotNet.FileBasedPrograms.SourceFile.Deconstruct(out string! Path, out Microsoft.CodeAnalysis.Text.SourceText! Text) -> void
+Microsoft.DotNet.FileBasedPrograms.SourceFile.Equals(Microsoft.DotNet.FileBasedPrograms.SourceFile other) -> bool
+Microsoft.DotNet.FileBasedPrograms.SourceFile.GetFileLinePositionSpan(Microsoft.CodeAnalysis.Text.TextSpan span) -> Microsoft.CodeAnalysis.FileLinePositionSpan
+Microsoft.DotNet.FileBasedPrograms.SourceFile.GetLocationString(Microsoft.CodeAnalysis.Text.TextSpan span) -> string!
+Microsoft.DotNet.FileBasedPrograms.SourceFile.Path.get -> string!
+Microsoft.DotNet.FileBasedPrograms.SourceFile.Path.init -> void
+Microsoft.DotNet.FileBasedPrograms.SourceFile.Save() -> void
+Microsoft.DotNet.FileBasedPrograms.SourceFile.SourceFile() -> void
+Microsoft.DotNet.FileBasedPrograms.SourceFile.SourceFile(string! Path, Microsoft.CodeAnalysis.Text.SourceText! Text) -> void
+Microsoft.DotNet.FileBasedPrograms.SourceFile.Text.get -> Microsoft.CodeAnalysis.Text.SourceText!
+Microsoft.DotNet.FileBasedPrograms.SourceFile.Text.init -> void
+Microsoft.DotNet.FileBasedPrograms.SourceFile.WithText(Microsoft.CodeAnalysis.Text.SourceText! newText) -> Microsoft.DotNet.FileBasedPrograms.SourceFile
+Microsoft.DotNet.FileBasedPrograms.WhiteSpaceInfo
+Microsoft.DotNet.FileBasedPrograms.WhiteSpaceInfo.LineBreaks -> int
+Microsoft.DotNet.FileBasedPrograms.WhiteSpaceInfo.TotalLength -> int
+Microsoft.DotNet.FileBasedPrograms.WhiteSpaceInfo.WhiteSpaceInfo() -> void
+override abstract Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ToString() -> string!
+override Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Package.ToString() -> string!
+override Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Project.ToString() -> string!
+override Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Property.ToString() -> string!
+override Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Sdk.ToString() -> string!
+override Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Shebang.ToString() -> string!
+override Microsoft.DotNet.FileBasedPrograms.SourceFile.GetHashCode() -> int
+static Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Package.Parse(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext context) -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Package?
+static Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Parse(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext context) -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Named?
+static Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Project.GetProjectFileFromDirectory(string! projectDirectory) -> System.IO.FileInfo!
+static Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Project.Parse(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext context) -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Project?
+static Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Property.Parse(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext context) -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Property?
+static Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Sdk.Parse(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext context) -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Sdk?
+static Microsoft.DotNet.FileBasedPrograms.DiagnosticBag.Collect(out System.Collections.Immutable.ImmutableArray.Builder! builder) -> Microsoft.DotNet.FileBasedPrograms.DiagnosticBag
+static Microsoft.DotNet.FileBasedPrograms.DiagnosticBag.Ignore() -> Microsoft.DotNet.FileBasedPrograms.DiagnosticBag
+static Microsoft.DotNet.FileBasedPrograms.DiagnosticBag.ThrowOnFirst() -> Microsoft.DotNet.FileBasedPrograms.DiagnosticBag
+static Microsoft.DotNet.FileBasedPrograms.ExternalHelpers.CombineHashCodes(int value1, int value2) -> int
+static Microsoft.DotNet.FileBasedPrograms.ExternalHelpers.GetRelativePath(string! relativeTo, string! path) -> string!
+static Microsoft.DotNet.FileBasedPrograms.ExternalHelpers.IsPathFullyQualified(string! path) -> bool
+static Microsoft.DotNet.FileBasedPrograms.FileLevelDirectiveHelpers.CreateTokenizer(Microsoft.CodeAnalysis.Text.SourceText! text) -> Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser!
+static Microsoft.DotNet.FileBasedPrograms.FileLevelDirectiveHelpers.FindDirectives(Microsoft.DotNet.FileBasedPrograms.SourceFile sourceFile, bool reportAllErrors, Microsoft.DotNet.FileBasedPrograms.DiagnosticBag diagnostics) -> System.Collections.Immutable.ImmutableArray
+static Microsoft.DotNet.FileBasedPrograms.FileLevelDirectiveHelpers.FindLeadingDirectives(Microsoft.DotNet.FileBasedPrograms.SourceFile sourceFile, Microsoft.CodeAnalysis.SyntaxTriviaList triviaList, Microsoft.DotNet.FileBasedPrograms.DiagnosticBag diagnostics, System.Collections.Immutable.ImmutableArray.Builder? builder) -> void
+static Microsoft.DotNet.FileBasedPrograms.MSBuildUtilities.ConvertStringToBool(string? parameterValue, bool defaultValue = false) -> bool
+static Microsoft.DotNet.FileBasedPrograms.Patterns.DisallowedNameCharacters.get -> System.Text.RegularExpressions.Regex!
+static Microsoft.DotNet.FileBasedPrograms.Patterns.EscapedCompilerOption.get -> System.Text.RegularExpressions.Regex!
+static Microsoft.DotNet.FileBasedPrograms.Patterns.Whitespace.get -> System.Text.RegularExpressions.Regex!
+static Microsoft.DotNet.FileBasedPrograms.SourceFile.Load(string! filePath) -> Microsoft.DotNet.FileBasedPrograms.SourceFile
+static Microsoft.DotNet.FileBasedPrograms.SourceFile.operator !=(Microsoft.DotNet.FileBasedPrograms.SourceFile left, Microsoft.DotNet.FileBasedPrograms.SourceFile right) -> bool
+static Microsoft.DotNet.FileBasedPrograms.SourceFile.operator ==(Microsoft.DotNet.FileBasedPrograms.SourceFile left, Microsoft.DotNet.FileBasedPrograms.SourceFile right) -> bool
+static readonly Microsoft.DotNet.FileBasedPrograms.NamedDirectiveComparer.Instance -> Microsoft.DotNet.FileBasedPrograms.NamedDirectiveComparer!
+~override Microsoft.DotNet.FileBasedPrograms.SourceFile.Equals(object obj) -> bool
+~override Microsoft.DotNet.FileBasedPrograms.SourceFile.ToString() -> string
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/MSBuildUtilities.cs b/src/Cli/Microsoft.DotNet.FileBasedPrograms/MSBuildUtilities.cs
new file mode 100644
index 000000000000..0b556f86daee
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/MSBuildUtilities.cs
@@ -0,0 +1,71 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// https://github.com/dotnet/sdk/issues/51487: avoid this extra copy of the file.
+#nullable enable
+using System;
+
+namespace Microsoft.DotNet.FileBasedPrograms
+{
+ ///
+ /// Internal utilities copied from microsoft/MSBuild repo.
+ ///
+ class MSBuildUtilities
+ {
+ ///
+ /// Converts a string to a bool. We consider "true/false", "on/off", and
+ /// "yes/no" to be valid boolean representations in the XML.
+ /// Modified from its original version to not throw, but return a default value.
+ ///
+ /// The string to convert.
+ /// Boolean true or false, corresponding to the string.
+ internal static bool ConvertStringToBool(string? parameterValue, bool defaultValue = false)
+ {
+ if (string.IsNullOrEmpty(parameterValue))
+ {
+ return defaultValue;
+ }
+ else if (ValidBooleanTrue(parameterValue))
+ {
+ return true;
+ }
+ else if (ValidBooleanFalse(parameterValue))
+ {
+ return false;
+ }
+ else
+ {
+ // Unsupported boolean representation.
+ return defaultValue;
+ }
+ }
+
+ ///
+ /// Returns true if the string represents a valid MSBuild boolean true value,
+ /// such as "on", "!false", "yes"
+ ///
+ private static bool ValidBooleanTrue(string? parameterValue)
+ {
+ return ((string.Compare(parameterValue, "true", StringComparison.OrdinalIgnoreCase) == 0) ||
+ (string.Compare(parameterValue, "on", StringComparison.OrdinalIgnoreCase) == 0) ||
+ (string.Compare(parameterValue, "yes", StringComparison.OrdinalIgnoreCase) == 0) ||
+ (string.Compare(parameterValue, "!false", StringComparison.OrdinalIgnoreCase) == 0) ||
+ (string.Compare(parameterValue, "!off", StringComparison.OrdinalIgnoreCase) == 0) ||
+ (string.Compare(parameterValue, "!no", StringComparison.OrdinalIgnoreCase) == 0));
+ }
+
+ ///
+ /// Returns true if the string represents a valid MSBuild boolean false value,
+ /// such as "!on" "off" "no" "!true"
+ ///
+ private static bool ValidBooleanFalse(string? parameterValue)
+ {
+ return ((string.Compare(parameterValue, "false", StringComparison.OrdinalIgnoreCase) == 0) ||
+ (string.Compare(parameterValue, "off", StringComparison.OrdinalIgnoreCase) == 0) ||
+ (string.Compare(parameterValue, "no", StringComparison.OrdinalIgnoreCase) == 0) ||
+ (string.Compare(parameterValue, "!true", StringComparison.OrdinalIgnoreCase) == 0) ||
+ (string.Compare(parameterValue, "!on", StringComparison.OrdinalIgnoreCase) == 0) ||
+ (string.Compare(parameterValue, "!yes", StringComparison.OrdinalIgnoreCase) == 0));
+ }
+ }
+}
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/Microsoft.DotNet.FileBasedPrograms.Package.csproj b/src/Cli/Microsoft.DotNet.FileBasedPrograms/Microsoft.DotNet.FileBasedPrograms.Package.csproj
new file mode 100644
index 000000000000..ead3a31bed97
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/Microsoft.DotNet.FileBasedPrograms.Package.csproj
@@ -0,0 +1,80 @@
+
+
+
+
+ $(VisualStudioServiceTargetFramework);netstandard2.0
+ false
+ none
+ false
+ preview
+
+
+ true
+ true
+ true
+ Microsoft.DotNet.FileBasedPrograms
+ false
+ Package containing sources for file-based programs support.
+
+ $(NoWarn);NU5128
+ false
+ $(DefineConstants);FILE_BASED_PROGRAMS_SOURCE_PACKAGE_BUILD;FILE_BASED_PROGRAMS_SOURCE_PACKAGE_GRACEFUL_EXCEPTION
+
+ disable
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+ External\%(NuGetPackageId)\%(Link)
+
+
+
+
+
+
+
+
+ true
+ contentFiles\cs\any\FileBasedProgramsResources.resx
+
+
+ true
+ contentFiles\cs\any\xlf
+
+
+
+
+
+
+
+ <_PackageFiles Remove="@(_PackageFiles)" Condition="$([System.String]::Copy('%(_PackageFiles.Identity)').EndsWith('FileBasedProgramsResources.cs'))" />
+
+
+
+
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/Microsoft.DotNet.FileBasedPrograms.projitems b/src/Cli/Microsoft.DotNet.FileBasedPrograms/Microsoft.DotNet.FileBasedPrograms.projitems
new file mode 100644
index 000000000000..31ce6cd9eabc
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/Microsoft.DotNet.FileBasedPrograms.projitems
@@ -0,0 +1,15 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ 374C251E-BF99-45B2-A58E-40229ED8AACA
+
+
+ Microsoft.DotNet.FileBasedPrograms
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/Microsoft.DotNet.FileBasedPrograms.shproj b/src/Cli/Microsoft.DotNet.FileBasedPrograms/Microsoft.DotNet.FileBasedPrograms.shproj
new file mode 100644
index 000000000000..68cb2e509ef5
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/Microsoft.DotNet.FileBasedPrograms.shproj
@@ -0,0 +1,13 @@
+
+
+
+ 374C251E-BF99-45B2-A58E-40229ED8AACA
+ 14.0
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/README.md b/src/Cli/Microsoft.DotNet.FileBasedPrograms/README.md
new file mode 100644
index 000000000000..ddce21ddf94d
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/README.md
@@ -0,0 +1,21 @@
+# Microsoft.DotNet.FileBasedPrograms Source Package
+
+This is a source package containing shared code for [file-based programs](../../../documentation/general/dotnet-run-file.md) support. This package is intended only for internal use by .NET components.
+
+## Usage in Consuming Projects
+
+To use this package in your project, add the following to your `.csproj` file:
+
+```xml
+
+
+
+
+
+
+
+ $(DefineConstants);FILE_BASED_PROGRAMS_SOURCE_PACKAGE_GRACEFUL_EXCEPTION
+
+```
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.cs.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.cs.xlf
new file mode 100644
index 000000000000..d2dd3b59202f
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.cs.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ Některé direktivy nelze převést. Spuštěním souboru zobrazíte všechny chyby kompilace. Zadejte „--force“, pokud chcete přesto provést převod.
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ V {0} se nenašel žádný projekt.
+
+
+
+ Could not find project or directory `{0}`.
+ Nenašel se projekt ani adresář {0}.
+
+
+
+ error
+ chyba
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ Duplicitní direktivy nejsou podporovány: {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ Direktiva by měla obsahovat název bez speciálních znaků a volitelnou hodnotu oddělenou znakem {1}, například #:{0} Název{1}Hodnota.
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ Direktiva #:project je neplatná: {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ Chybí název pro: {0}.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ V {0} se našlo několik projektů. Vyberte, který z nich chcete použít.
+
+
+
+ Invalid property name: {0}
+ Neplatný název vlastnosti: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ Direktiva property musí mít dvě části oddělené znakem =, například #:property PropertyName=PropertyValue.
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ Direktivy v současné době nemůžou obsahovat dvojité uvozovky (").
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ Statické obnovení grafu se pro souborové aplikace nepodporuje. Odeberte #:property.
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ Nerozpoznaná direktiva {0}.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.de.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.de.xlf
new file mode 100644
index 000000000000..13eb91fbaee3
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.de.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ Einige Anweisungen können nicht konvertiert werden. Führen Sie die Datei aus, um alle Kompilierungsfehler anzuzeigen. Geben Sie „--force“ an, um das Umwandeln trotzdem auszuführen.
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ In "{0}" wurde kein Projekt gefunden.
+
+
+
+ Could not find project or directory `{0}`.
+ Das Projekt oder Verzeichnis "{0}" wurde nicht gefunden.
+
+
+
+ error
+ Fehler
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ Doppelte Anweisungen werden nicht unterstützt: {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ Die Anweisung sollte einen Namen ohne Sonderzeichen und einen optionalen Wert enthalten, die durch „{1}“ getrennt sind, wie „#:{0} Name{1}Wert“.
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ Die Anweisung „#:p roject“ ist ungültig: {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ Fehlender Name der Anweisung „{0}“.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ In "{0}" wurden mehrere Projekte gefunden. Geben Sie an, welches davon verwendet werden soll.
+
+
+
+ Invalid property name: {0}
+ Ungültiger Eigenschaftenname: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ Die Eigenschaftsanweisung muss zwei durch „=“ getrennte Teile aufweisen, z. B. „#:property PropertyName=PropertyValue“.
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ Direktiven dürfen derzeit keine doppelten Anführungszeichen (") enthalten.
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ Die Statische Graphwiederherstellung wird für dateibasierte Apps nicht unterstützt. Entfernen Sie '#:property'.
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ Unbekannte Anweisung „{0}“.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.es.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.es.xlf
new file mode 100644
index 000000000000..38d1daaa07ae
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.es.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ Algunas directivas no se pueden convertir. Ejecute el archivo para ver todos los errores de compilación. Especifique "--force" para convertir de todos modos.
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ No se encuentra ningún proyecto en "{0}".
+
+
+
+ Could not find project or directory `{0}`.
+ No se encuentra el proyecto o directorio "{0}".
+
+
+
+ error
+ error
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ No se admiten directivas duplicadas: {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ La directiva debe contener un nombre sin caracteres especiales y un valor opcional separado por "{1}" como "#:{0} Nombre{1}Valor".
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ La directiva "#:project" no es válida: {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ Falta el nombre de "{0}".
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ Se han encontrado varios proyectos en "{0}". Especifique el que debe usarse.
+
+
+
+ Invalid property name: {0}
+ Nombre de propiedad no válido {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ La directiva de propiedad debe tener dos partes separadas por "=", como "#:property PropertyName=PropertyValue".
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ Las directivas no pueden contener comillas dobles ("), por ahora.
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ No se admite la restauración de gráficos estáticos para aplicaciones basadas en archivos. Elimine "#:property".
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ Directiva no reconocida "{0}".
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.fr.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.fr.xlf
new file mode 100644
index 000000000000..e034912dd36e
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.fr.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ Vous ne pouvez pas convertir certaines directives. Exécutez le fichier pour voir toutes les erreurs de compilation. Spécifiez « --force » pour convertir quand même.
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ Projet introuvable dans '{0}'.
+
+
+
+ Could not find project or directory `{0}`.
+ Projet ou répertoire '{0}' introuvable.
+
+
+
+ error
+ erreur
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ Les directives dupliquées ne sont pas prises en charge : {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ La directive dans doit contenir un nom sans caractères spéciaux et une valeur facultative séparée par « {1} » comme « # :{0} Nom{1}Valeur ».
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ La directive « #:project » n’est pas valide : {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ Nom manquant pour « {0} ».
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ Plusieurs projets dans '{0}'. Spécifiez celui à utiliser.
+
+
+
+ Invalid property name: {0}
+ Nom de propriété non valide : {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ La directive de propriété doit avoir deux parties séparées par '=' comme '#:property PropertyName=PropertyValue'.
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ Les directives ne peuvent actuellement pas contenir de guillemets doubles (").
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ La restauration de graphique statique n’est pas prise en charge pour les applications basées sur des fichiers. Supprimer la « #:property ».
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ Directive « {0} » non reconnue.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.it.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.it.xlf
new file mode 100644
index 000000000000..846df0ee663f
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.it.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ Non è possibile convertire alcune direttive. Eseguire il file per visualizzare tutti gli errori di compilazione. Specificare '--force' per eseguire comunque la conversione.
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ Non è stato trovato alcun progetto in `{0}`.
+
+
+
+ Could not find project or directory `{0}`.
+ Non sono stati trovati progetti o directory `{0}`.
+
+
+
+ error
+ errore
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ Le direttive duplicate non supportate: {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ La direttiva deve contenere un nome senza caratteri speciali e un valore facoltativo delimitato da '{1}' come '#:{0}Nome {1}Valore'.
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ La direttiva '#:project' non è valida: {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ Manca il nome di '{0}'.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ Sono stati trovati più progetti in `{0}`. Specificare quello da usare.
+
+
+
+ Invalid property name: {0}
+ Nome proprietà non valido: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ La direttiva di proprietà deve avere due parti delimitate da '=', come '#:property PropertyName=PropertyValue'.
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ Le direttive attualmente non possono contenere virgolette doppie (").
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ Il ripristino statico del grafo non è supportato per le app basate su file. Rimuovere '#:property'.
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ Direttiva non riconosciuta '{0}'.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.ja.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.ja.xlf
new file mode 100644
index 000000000000..18e3732d59d1
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.ja.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ 一部のディレクティブは変換できません。ファイルを実行して、すべてのコンパイル エラーを表示します。それでも変換する場合は '--force' を指定してください。
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ `{0}` にプロジェクトが見つかりませんでした。
+
+
+
+ Could not find project or directory `{0}`.
+ プロジェクトまたはディレクトリ `{0}` が見つかりませんでした。
+
+
+
+ error
+ エラー
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ 重複するディレクティブはサポートされていません: {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ ディレクティブには、特殊文字を含まない名前と、'#:{0} Name{1}Value' などの '{1}' で区切られた省略可能な値を含める必要があります。
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ '#:p roject' ディレクティブが無効です: {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ '{0}' の名前がありません。
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ `{0}` に複数のプロジェクトが見つかりました。使用するプロジェクトを指定してください。
+
+
+
+ Invalid property name: {0}
+ 無効なプロパティ名: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ プロパティ ディレクティブには、'#:property PropertyName=PropertyValue' のように '=' で区切られた 2 つの部分が必要です。
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ ディレクティブには二重引用符 (") を含めることはできません。
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ 静的グラフの復元はファイルベースのアプリではサポートされていません。'#:property' を削除します。
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ 認識されないディレクティブ '{0}' です。
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.ko.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.ko.xlf
new file mode 100644
index 000000000000..d65519a8a4e0
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.ko.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ 일부 지시문을 변환할 수 없습니다. 파일을 실행하여 모든 컴파일 오류를 확인하세요. 변환을 강제로 진행하려면 '--force'를 지정하세요.
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ '{0}'에서 프로젝트를 찾을 수 없습니다.
+
+
+
+ Could not find project or directory `{0}`.
+ 프로젝트 또는 디렉터리 {0}을(를) 찾을 수 없습니다.
+
+
+
+ error
+ 오류
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ 중복 지시문은 지원되지 않습니다. {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ 지시문에는 특수 문자가 없는 이름과 '#:{0} 이름{1}값'과 같이 '{1}'(으)로 구분된 선택적 값이 포함되어야 합니다.
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ '#:p roject' 지시문이 잘못되었습니다. {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ '{0}' 이름이 없습니다.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ '{0}'에서 프로젝트를 두 개 이상 찾았습니다. 사용할 프로젝트를 지정하세요.
+
+
+
+ Invalid property name: {0}
+ 잘못된 속성 이름: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ property 지시문에는 '#:property PropertyName=PropertyValue'와 같이 '='로 구분된 두 부분이 있어야 합니다.
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ 지시문은 현재 큰따옴표(")를 포함할 수 없습니다.
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ 정적 그래프 복원은 파일 기반 앱에서 지원되지 않습니다. '#:property'를 제거합니다.
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ 인식할 수 없는 지시문 '{0}'입니다.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.pl.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.pl.xlf
new file mode 100644
index 000000000000..4cf66127fa52
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.pl.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ Nie można przekonwertować niektórych dyrektyw. Uruchom plik, aby wyświetlić wszystkie błędy kompilacji. Określ element „--force”, aby mimo to przekonwertować.
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ Nie można odnaleźć żadnego projektu w lokalizacji „{0}”.
+
+
+
+ Could not find project or directory `{0}`.
+ Nie można odnaleźć projektu ani katalogu „{0}”.
+
+
+
+ error
+ błąd
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ Zduplikowane dyrektywy nie są obsługiwane: {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ Dyrektywa powinna zawierać nazwę bez znaków specjalnych i opcjonalną wartość rozdzieloną znakiem "{1}#:{0} Name{1}Value".
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ Dyrektywa „#:project” jest nieprawidłowa: {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ Brak nazwy „{0}”.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ Znaleziono więcej niż jeden projekt w lokalizacji „{0}”. Określ, który ma zostać użyty.
+
+
+
+ Invalid property name: {0}
+ Nieprawidłowa nazwa właściwości: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ Dyrektywa właściwości musi mieć dwie części oddzielone znakiem „=”, na przykład „#:property PropertyName=PropertyValue”.
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ Dyrektywy nie mogą obecnie zawierać podwójnych cudzysłowów (").
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ Przywracanie statycznego grafu nie jest obsługiwane w przypadku aplikacji opartych na plikach. Usuń element „#:property”.
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ Nierozpoznana dyrektywa „{0}”.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.pt-BR.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.pt-BR.xlf
new file mode 100644
index 000000000000..732bdffaeaba
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.pt-BR.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ Algumas diretivas não podem ser convertidas. Execute o arquivo para ver todos os erros de compilação. Especifique '--force' para converter mesmo assim.
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ Não foi possível encontrar nenhum projeto em ‘{0}’.
+
+
+
+ Could not find project or directory `{0}`.
+ Não foi possível encontrar o projeto ou diretório ‘{0}’.
+
+
+
+ error
+ erro
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ Diretivas duplicadas não são suportadas:{0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ A diretiva deve conter um nome sem caracteres especiais e um valor opcional separado por '{1}' como '#:{0} Nome{1}Valor'.
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ A diretiva '#:project' é inválida:{0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ Nome de '{0}' ausente.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ Foi encontrado mais de um projeto em ‘{0}’. Especifique qual deve ser usado.
+
+
+
+ Invalid property name: {0}
+ Nome de propriedade inválido: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ A diretiva de propriedade precisa ter duas partes separadas por '=' como '#:property PropertyName=PropertyValue'.
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ No momento, as diretivas não podem conter aspas duplas (").
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ A restauração de grafo estático não é suportada para aplicativos baseados em arquivos. Remova '#:property'.
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ Diretiva não reconhecida '{0}'.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.ru.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.ru.xlf
new file mode 100644
index 000000000000..ca7d77675a4f
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.ru.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ Некоторые директивы невозможно преобразовать. Запустите файл, чтобы увидеть все ошибки компиляции. Укажите параметр "--force", чтобы выполнить преобразование, невзирая на ошибки.
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ Не удалось найти проекты в "{0}".
+
+
+
+ Could not find project or directory `{0}`.
+ Не удалось найти проект или каталог "{0}".
+
+
+
+ error
+ ошибка
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ Повторяющиеся директивы не поддерживаются: {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ Директива должна содержать имя без специальных символов и необязательное значение, разделенные символом-разделителем "{1}", например "#:{0} Имя{1}Значение".
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ Недопустимая директива "#:project": {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ Отсутствует имя "{0}".
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ Найдено несколько проектов в "{0}". Выберите один.
+
+
+
+ Invalid property name: {0}
+ Недопустимое имя свойства: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ Директива свойства должна иметь две части, разделенные символом "=", например "#:property PropertyName=PropertyValue".
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ В директивах пока нельзя использовать двойные кавычки (").
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ Восстановление статического графа не поддерживается для приложений на основе файлов. Удалите "#:property".
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ Нераспознанная директива "{0}".
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.tr.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.tr.xlf
new file mode 100644
index 000000000000..aa8bf66a3cdc
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.tr.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ Bazı yönergeler dönüştürülemez. Tüm derleme hatalarını görmek için dosyayı çalıştırın. Yine de dönüştürmek için '--force' belirtin.
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ `{0}` içinde proje bulunamadı.
+
+
+
+ Could not find project or directory `{0}`.
+ `{0}` projesi veya dizini bulunamadı.
+
+
+
+ error
+ hata
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ Yinelenen yönergeler desteklenmez: {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ Yönerge, özel karakterler içermeyen bir ad ve ‘#:{0} Ad{1}Değer’ gibi '{1}' ile ayrılmış isteğe bağlı bir değer içermelidir.
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ ‘#:project’ yönergesi geçersizdir: {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ '{0}' adı eksik.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ `{0}` içinde birden fazla proje bulundu. Hangisinin kullanılacağını belirtin.
+
+
+
+ Invalid property name: {0}
+ Geçersiz özellik adı: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ Özellik yönergesi, ‘#:property PropertyName=PropertyValue’ gibi ‘=’ ile ayrılmış iki bölümden oluşmalıdır.
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ Yönergeler şu anda çift tırnak (") içeremez.
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ Dosya tabanlı uygulamalar için statik grafik geri yükleme desteklenmemektedir. ‘#:property’i kaldırın.
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ Tanınmayan yönerge '{0}'.
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.zh-Hans.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.zh-Hans.xlf
new file mode 100644
index 000000000000..7a3b8fdc7a4e
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.zh-Hans.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ 一些指令无法转换。运行该文件以查看所有编译错误。请指定 '--force' 以进行转换。
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ “{0}”中找不到任何项目。
+
+
+
+ Could not find project or directory `{0}`.
+ 找不到项目或目录“{0}”。
+
+
+
+ error
+ 错误
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ 不支持重复指令: {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ 该指令应包含一个不带特殊字符的名称,以及一个以 '#:{0} Name{1}Value' 等 ‘{1}’ 分隔的可选值。
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ '#:project' 指令无效: {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ 缺少 '{0}' 的名称。
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ 在“{0}”中找到多个项目。请指定使用哪一个。
+
+
+
+ Invalid property name: {0}
+ 属性名无效: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ 属性指令需要包含两个由 ‘=’ 分隔的部件,例如 '#:property PropertyName=PropertyValue'。
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ 指令当前不能包含双引号(")。
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ 基于文件的应用不支持静态图形还原。移除 '#:property'。
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ 无法识别的指令 ‘{0}’。
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.zh-Hant.xlf b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.zh-Hant.xlf
new file mode 100644
index 000000000000..d865da88d7d8
--- /dev/null
+++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/xlf/FileBasedProgramsResources.zh-Hant.xlf
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
+ 無法轉換某些指示詞。執行檔案以查看所有編譯錯誤。指定 '--force' 以繼續轉換。
+ {Locked="--force"}
+
+
+ Could not find any project in `{0}`.
+ 在 `{0}` 中找不到任何專案。
+
+
+
+ Could not find project or directory `{0}`.
+ 找不到專案或目錄 `{0}`。
+
+
+
+ error
+ 錯誤
+ Used when reporting directive errors like "file(location): error: message".
+
+
+ Duplicate directives are not supported: {0}
+ 不支援重複的指示詞: {0}
+ {0} is the directive type and name.
+
+
+ The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
+ 指示詞應包含不含特殊字元的名稱,以及 '{1}' 分隔的選用值,例如 '#:{0} Name{1}Value'。
+ {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
+
+
+ The '#:project' directive is invalid: {0}
+ '#:project' 指示詞無效: {0}
+ {0} is the inner error message.
+
+
+ Missing name of '{0}'.
+ 缺少 '{0}' 的名稱。
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+ Found more than one project in `{0}`. Specify which one to use.
+ 在 `{0}` 中找到多個專案。請指定要使用的專案。
+
+
+
+ Invalid property name: {0}
+ 屬性名稱無效: {0}
+ {0} is an inner exception message.
+
+
+ The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
+ 屬性指示詞必須有兩個部分,其以 '=' 分隔,例如 '#:property PropertyName=PropertyValue'。
+ {Locked="#:property"}
+
+
+ Directives currently cannot contain double quotes (").
+ 指令目前不能包含雙引號 (")。
+
+
+
+ Static graph restore is not supported for file-based apps. Remove the '#:property'.
+ 檔案型應用程式不支援靜態圖表還原。移除 ''#:property'。
+ {Locked="#:property"}
+
+
+ Unrecognized directive '{0}'.
+ 無法識別的指示詞 '{0}'。
+ {0} is the directive name like 'package' or 'sdk'.
+
+
+
+
\ No newline at end of file
diff --git a/src/Cli/dotnet/CliStrings.resx b/src/Cli/dotnet/CliStrings.resx
index 7852c04414a0..58f8ae903efe 100644
--- a/src/Cli/dotnet/CliStrings.resx
+++ b/src/Cli/dotnet/CliStrings.resx
@@ -201,15 +201,6 @@
Required command was not provided.
-
- Could not find any project in `{0}`.
-
-
- Could not find project or directory `{0}`.
-
-
- Found more than one project in `{0}`. Specify which one to use.
-
Found a project `{0}` but it is invalid.
diff --git a/src/Cli/dotnet/CliStringsExtensions.cs b/src/Cli/dotnet/CliStringsExtensions.cs
new file mode 100644
index 000000000000..fa98cb1f1ec3
--- /dev/null
+++ b/src/Cli/dotnet/CliStringsExtensions.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.DotNet.Cli;
+using Microsoft.DotNet.FileBasedPrograms;
+
+///
+/// Allow convenient access to resources which are relevant to Cli scenarios but actually defined in other resource files.
+///
+internal static class CliStringsExtensions
+{
+ extension(CliStrings)
+ {
+ public static string CouldNotFindAnyProjectInDirectory => FileBasedProgramsResources.CouldNotFindAnyProjectInDirectory;
+ public static string CouldNotFindProjectOrDirectory => FileBasedProgramsResources.CouldNotFindProjectOrDirectory;
+ public static string MoreThanOneProjectInDirectory => FileBasedProgramsResources.MoreThanOneProjectInDirectory;
+ }
+}
diff --git a/src/Cli/dotnet/CommandFactory/CommandResolution/ProjectToolsCommandResolver.cs b/src/Cli/dotnet/CommandFactory/CommandResolution/ProjectToolsCommandResolver.cs
index 2f8bb7badd98..aa844850e316 100644
--- a/src/Cli/dotnet/CommandFactory/CommandResolution/ProjectToolsCommandResolver.cs
+++ b/src/Cli/dotnet/CommandFactory/CommandResolution/ProjectToolsCommandResolver.cs
@@ -385,7 +385,7 @@ internal void GenerateDepsJsonFile(
string? stdOut;
string? stdErr;
- var msbuildArgs = MSBuildArgs.AnalyzeMSBuildArguments([..args], CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, BuildCommandParser.TargetOption, BuildCommandParser.VerbosityOption);
+ var msbuildArgs = MSBuildArgs.AnalyzeMSBuildArguments([..args], CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, BuildCommandParser.TargetOption, BuildCommandParser.VerbosityOption, BuildCommandParser.NoLogoOption);
var forwardingAppWithoutLogging = new MSBuildForwardingAppWithoutLogging(msbuildArgs, msBuildExePath);
if (forwardingAppWithoutLogging.ExecuteMSBuildOutOfProc)
{
diff --git a/src/Cli/dotnet/Commands/Build/BuildCommand.cs b/src/Cli/dotnet/Commands/Build/BuildCommand.cs
index 3d8943315a94..264abdad7d80 100644
--- a/src/Cli/dotnet/Commands/Build/BuildCommand.cs
+++ b/src/Cli/dotnet/Commands/Build/BuildCommand.cs
@@ -43,7 +43,7 @@ public static CommandBase FromParseResult(ParseResult parseResult, string? msbui
noRestore: noRestore,
msbuildPath: msbuildPath
),
- [CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, BuildCommandParser.TargetOption, BuildCommandParser.VerbosityOption],
+ [CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, BuildCommandParser.TargetOption, BuildCommandParser.VerbosityOption, BuildCommandParser.NoLogoOption],
parseResult,
msbuildPath
);
diff --git a/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs b/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs
index 74b4efa2a712..be2e52c45263 100644
--- a/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs
+++ b/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs
@@ -36,11 +36,7 @@ internal static class BuildCommandParser
Arity = ArgumentArity.Zero
}.ForwardAs("--property:BuildProjectReferences=false");
- public static readonly Option NoLogoOption = new Option("--nologo")
- {
- Description = CliCommandStrings.BuildCmdNoLogo,
- Arity = ArgumentArity.Zero
- }.ForwardAs("-nologo");
+ public static readonly Option NoLogoOption = CommonOptions.NoLogoOption();
public static readonly Option NoRestoreOption = CommonOptions.NoRestoreOption;
diff --git a/src/Cli/dotnet/Commands/Clean/CleanCommand.cs b/src/Cli/dotnet/Commands/Clean/CleanCommand.cs
index 1290b8b68cfd..c7e48c376ee9 100644
--- a/src/Cli/dotnet/Commands/Clean/CleanCommand.cs
+++ b/src/Cli/dotnet/Commands/Clean/CleanCommand.cs
@@ -33,7 +33,7 @@ public static CommandBase FromParseResult(ParseResult result, string? msbuildPat
NoWriteBuildMarkers = true,
},
static (msbuildArgs, msbuildPath) => new CleanCommand(msbuildArgs, msbuildPath),
- [ CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, CleanCommandParser.TargetOption, CleanCommandParser.VerbosityOption ],
+ [ CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, CleanCommandParser.TargetOption, CleanCommandParser.VerbosityOption, CleanCommandParser.NoLogoOption],
result,
msbuildPath
);
diff --git a/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs b/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs
index 24e83acc8b64..117537d7fd1f 100644
--- a/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs
+++ b/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs
@@ -24,15 +24,11 @@ internal static class CleanCommandParser
HelpName = CliCommandStrings.CleanCmdOutputDir
}.ForwardAsOutputPath("OutputPath");
- public static readonly Option NoLogoOption = new Option("--nologo")
- {
- Description = CliCommandStrings.CleanCmdNoLogo,
- Arity = ArgumentArity.Zero
- }.ForwardAs("-nologo");
+ public static readonly Option NoLogoOption = CommonOptions.NoLogoOption();
- public static readonly Option FrameworkOption = CommonOptions.FrameworkOption(CliCommandStrings.CleanFrameworkOptionDescription);
+ public static readonly Option FrameworkOption = CommonOptions.FrameworkOption(CliCommandStrings.CleanFrameworkOptionDescription);
- public static readonly Option ConfigurationOption = CommonOptions.ConfigurationOption(CliCommandStrings.CleanConfigurationOptionDescription);
+ public static readonly Option ConfigurationOption = CommonOptions.ConfigurationOption(CliCommandStrings.CleanConfigurationOptionDescription);
public static readonly Option TargetOption = CommonOptions.RequiredMSBuildTargetOption("Clean");
diff --git a/src/Cli/dotnet/Commands/CliCommandStrings.resx b/src/Cli/dotnet/Commands/CliCommandStrings.resx
index d429fc7f1cac..960ea3ad5fa5 100644
--- a/src/Cli/dotnet/Commands/CliCommandStrings.resx
+++ b/src/Cli/dotnet/Commands/CliCommandStrings.resx
@@ -166,7 +166,7 @@
.NET Builder
-
+
Do not display the startup banner or the copyright message.
@@ -250,9 +250,6 @@ Paths searched: '{1}', '{2}'.
.NET Clean Command
-
- Do not display the startup banner or the copyright message.
-
OUTPUT_DIR
@@ -378,12 +375,6 @@ For MSTest before 2.2.4, the timeout is used for all testcases.
Unable to generate a temporary file for project '{0}'. Cannot add package reference. Clear the temp directory and try again.
-
- Defines the path of directory to run. If not specified, it defaults to the current directory.
-
-
- DIRECTORY_PATH
-
Prevent restoring multiple projects in parallel.
@@ -550,11 +541,13 @@ This is equivalent to deleting project.assets.json.
Consider packages with prerelease versions when searching for newer packages. Requires the '--outdated' option.
- Defines the path of the project file to {0}. Use path to the project file, or path to the directory containing the project file. If not specified, it defaults to the current directory.
- {0} is the verb (e.g., "test", "run")
+ Defines the path of the project file to run. Use path to the project file, or path to the directory containing the project file. If not specified, it defaults to the current directory.
-
- PROJECT_PATH
+
+ Defines the path of the project or solution file to test. Use path to the project file, or path to the directory containing the project file. If not specified, it defaults to the current directory.
+
+
+ PROJECT_OR_SOLUTION_PATHForces restore to reevaluate all dependencies even if a lock file already exists.
@@ -1102,14 +1095,6 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
Minimum expected tests policy violation, tests ran {0}, minimum expected {1}{0}, {1} number of tests
-
- The '#:project' directive is invalid: {0}
- {0} is the inner error message.
-
-
- Missing name of '{0}'.
- {0} is the directive name like 'package' or 'sdk'.
-
Run Microsoft Build Engine (MSBuild) commands.
@@ -1341,9 +1326,6 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
.NET Core NuGet Package Packer
-
- Do not display the startup banner or the copyright message.
-
OUTPUT_DIR
@@ -1597,37 +1579,6 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
Project(s)
-
- Invalid property name: {0}
- {0} is an inner exception message.
-
-
- The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'.
- {Locked="#:property"}
-
-
- Static graph restore is not supported for file-based apps. Remove the '#:property'.
- {Locked="#:property"}
-
-
- error
- Used when reporting directive errors like "file(location): error: message".
-
-
- The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'.
- {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='.
-
-
- Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway.
- {Locked="--force"}
-
-
- Duplicate directives are not supported: {0}
- {0} is the directive type and name.
-
-
- Directives currently cannot contain double quotes (").
-
Cannot specify option '{0}' when also using '-' to read the file from standard input.{0} is an option name like '--no-build'.
@@ -1647,9 +1598,6 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
Publisher for the .NET Platform
-
- Do not display the startup banner or the copyright message.
-
The configuration to publish for. The default is 'Release' for NET 8.0 projects and above, but 'Debug' for older projects.
@@ -2265,10 +2213,6 @@ and the corresponding package Ids for installed tools using the command
Rows with Unlogged changes represent changes from actions other than .NET CLI workload commands. Usually this represents an update to the .NET SDK or to Visual Studio.
-
- Unrecognized directive '{0}'.
- {0} is the directive name like 'package' or 'sdk'.
-
Update all tools.
@@ -2664,9 +2608,6 @@ Proceed?
'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe.
-
- 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe.
-
Method '{0}' did not exit successfully
diff --git a/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs b/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs
index d7edaea57478..7e28945067a3 100644
--- a/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs
+++ b/src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs
@@ -11,7 +11,17 @@ namespace Microsoft.DotNet.Cli.Commands.MSBuild;
public class MSBuildCommand(
IEnumerable msbuildArgs,
string? msbuildPath = null
-) : MSBuildForwardingApp(MSBuildArgs.AnalyzeMSBuildArguments([..msbuildArgs], CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, MSBuildCommandParser.TargetOption, CommonOptions.VerbosityOption()), msbuildPath, includeLogo: true)
+) : MSBuildForwardingApp(MSBuildArgs.AnalyzeMSBuildArguments(
+ [.. msbuildArgs],
+ CommonOptions.PropertiesOption,
+ CommonOptions.RestorePropertiesOption,
+ MSBuildCommandParser.TargetOption,
+ CommonOptions.VerbosityOption(),
+ // We set the no-logo option to false here to ensure that by default the logo is shown for this command.
+ // This is different from other commands that default to hiding the logo - but this command is meant to mimic
+ // the behavior of calling MSBuild directly, which shows the logo by default.
+ CommonOptions.NoLogoOption(false)
+ ), msbuildPath)
{
public static MSBuildCommand FromArgs(string[] args, string? msbuildPath = null)
{
diff --git a/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingApp.cs b/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingApp.cs
index 3055de0883f5..df0eaf4d166a 100644
--- a/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingApp.cs
+++ b/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingApp.cs
@@ -42,18 +42,17 @@ private static MSBuildArgs ConcatTelemetryLogger(MSBuildArgs msbuildArgs)
/// Mostly intended for quick/one-shot usage - most 'core' SDK commands should do more hands-on parsing.
///
public MSBuildForwardingApp(IEnumerable rawMSBuildArgs, string? msbuildPath = null) : this(
- MSBuildArgs.AnalyzeMSBuildArguments(rawMSBuildArgs.ToArray(), CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, CommonOptions.MSBuildTargetOption(), CommonOptions.VerbosityOption()),
+ MSBuildArgs.AnalyzeMSBuildArguments(rawMSBuildArgs.ToArray(), CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, CommonOptions.MSBuildTargetOption(), CommonOptions.VerbosityOption(), CommonOptions.NoLogoOption()),
msbuildPath)
{
}
- public MSBuildForwardingApp(MSBuildArgs msBuildArgs, string? msbuildPath = null, bool includeLogo = false)
+ public MSBuildForwardingApp(MSBuildArgs msBuildArgs, string? msbuildPath = null)
{
var modifiedMSBuildArgs = CommonRunHelpers.AdjustMSBuildForLLMs(ConcatTelemetryLogger(msBuildArgs));
_forwardingAppWithoutLogging = new MSBuildForwardingAppWithoutLogging(
modifiedMSBuildArgs,
- msbuildPath: msbuildPath,
- includeLogo: includeLogo);
+ msbuildPath: msbuildPath);
// Add the performance log location to the environment of the target process.
if (PerformanceLogManager.Instance != null && !string.IsNullOrEmpty(PerformanceLogManager.Instance.CurrentLogDirectory))
diff --git a/src/Cli/dotnet/Commands/New/DotnetCommandCallbacks.cs b/src/Cli/dotnet/Commands/New/DotnetCommandCallbacks.cs
index 46ddfe28c4b0..4aae2f0f037e 100644
--- a/src/Cli/dotnet/Commands/New/DotnetCommandCallbacks.cs
+++ b/src/Cli/dotnet/Commands/New/DotnetCommandCallbacks.cs
@@ -38,7 +38,7 @@ internal static bool RestoreProject(string pathToRestore)
{
PathUtility.EnsureAllPathsExist([pathToRestore], CliStrings.CommonFileNotFound, allowDirectories: true);
// for the implicit restore we do not want the terminal logger to emit any output unless there are errors
- return RestoreCommand.Run([pathToRestore, "-tlp:verbosity=quiet"]) == 0;
+ return RestoreCommand.Run([pathToRestore, "-tlp:verbosity=quiet", "--no-logo"]) == 0;
}
internal static bool AddProjectsToSolution(string solutionPath, IReadOnlyList projectsToAdd, string? solutionFolder, bool? inRoot)
diff --git a/src/Cli/dotnet/Commands/Pack/PackCommand.cs b/src/Cli/dotnet/Commands/Pack/PackCommand.cs
index 57342f67bad2..9f31bfa145fe 100644
--- a/src/Cli/dotnet/Commands/Pack/PackCommand.cs
+++ b/src/Cli/dotnet/Commands/Pack/PackCommand.cs
@@ -60,6 +60,7 @@ public static CommandBase FromParseResult(ParseResult parseResult, string? msbui
CommonOptions.RestorePropertiesOption,
PackCommandParser.TargetOption,
PackCommandParser.VerbosityOption,
+ PackCommandParser.NoLogoOption
],
parseResult,
msbuildPath,
diff --git a/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs b/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs
index a57aa2fabfcd..23fc1ef58f69 100644
--- a/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs
+++ b/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs
@@ -49,11 +49,7 @@ internal static class PackCommandParser
Arity = ArgumentArity.Zero
}.ForwardAs("-property:Serviceable=true");
- public static readonly Option NoLogoOption = new Option("--nologo")
- {
- Description = CliCommandStrings.PackCmdNoLogo,
- Arity = ArgumentArity.Zero
- }.ForwardAs("-nologo");
+ public static readonly Option NoLogoOption = CommonOptions.NoLogoOption();
public static readonly Option NoRestoreOption = CommonOptions.NoRestoreOption;
diff --git a/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs b/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs
index 8377bf6927b1..a463165828cb 100644
--- a/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs
+++ b/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs
@@ -12,6 +12,7 @@
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Extensions;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.FileBasedPrograms;
using NuGet.ProjectModel;
namespace Microsoft.DotNet.Cli.Commands.Package.Add;
@@ -90,7 +91,7 @@ private static void GetProjectDependencyGraph(string projectFilePath, string dgF
$"-property:RestoreDotnetCliToolReferences=false",
// Output should not include MSBuild version header
- "-nologo",
+ "--nologo",
// Set verbosity to quiet to avoid cluttering the output for this 'inner' build
"-v:quiet"
diff --git a/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs b/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs
index 3ed3ed573416..e90099382273 100644
--- a/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs
+++ b/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs
@@ -7,6 +7,7 @@
using Microsoft.DotNet.Cli.Commands.Run;
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.FileBasedPrograms;
namespace Microsoft.DotNet.Cli.Commands.Package.Remove;
diff --git a/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs b/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs
index f1bc3ab7d655..e41367a34fd0 100644
--- a/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs
+++ b/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs
@@ -7,6 +7,7 @@
using Microsoft.Build.Evaluation;
using Microsoft.DotNet.Cli.Commands.Run;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.FileBasedPrograms;
using Microsoft.TemplateEngine.Cli.Commands;
namespace Microsoft.DotNet.Cli.Commands.Project.Convert;
@@ -30,7 +31,7 @@ public override int Execute()
// Find directives (this can fail, so do this before creating the target directory).
var sourceFile = SourceFile.Load(file);
- var directives = VirtualProjectBuildingCommand.FindDirectives(sourceFile, reportAllErrors: !_force, DiagnosticBag.ThrowOnFirst());
+ var directives = FileLevelDirectiveHelpers.FindDirectives(sourceFile, reportAllErrors: !_force, DiagnosticBag.ThrowOnFirst());
// Create a project instance for evaluation.
var projectCollection = new ProjectCollection();
diff --git a/src/Cli/dotnet/Commands/Publish/PublishCommand.cs b/src/Cli/dotnet/Commands/Publish/PublishCommand.cs
index 2bfb835871a9..424a2250c25a 100644
--- a/src/Cli/dotnet/Commands/Publish/PublishCommand.cs
+++ b/src/Cli/dotnet/Commands/Publish/PublishCommand.cs
@@ -58,7 +58,7 @@ public static CommandBase FromParseResult(ParseResult parseResult, string? msbui
noRestore: noRestore,
msbuildPath: msbuildPath
),
- [CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, PublishCommandParser.TargetOption, PublishCommandParser.VerbosityOption],
+ [CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, PublishCommandParser.TargetOption, PublishCommandParser.VerbosityOption, PublishCommandParser.NoLogoOption],
parseResult,
msbuildPath,
(msbuildArgs) =>
diff --git a/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs b/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs
index 9fbe305d4877..2c2b1b20e36f 100644
--- a/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs
+++ b/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs
@@ -38,11 +38,7 @@ internal static class PublishCommandParser
Arity = ArgumentArity.Zero
}.ForwardAs("-property:NoBuild=true");
- public static readonly Option NoLogoOption = new Option("--nologo")
- {
- Description = CliCommandStrings.PublishCmdNoLogo,
- Arity = ArgumentArity.Zero
- }.ForwardAs("-nologo");
+ public static readonly Option NoLogoOption = CommonOptions.NoLogoOption();
public static readonly Option NoRestoreOption = CommonOptions.NoRestoreOption;
diff --git a/src/Cli/dotnet/Commands/Restore/RestoreCommand.cs b/src/Cli/dotnet/Commands/Restore/RestoreCommand.cs
index 6eb650b0e261..5a9ff6a37056 100644
--- a/src/Cli/dotnet/Commands/Restore/RestoreCommand.cs
+++ b/src/Cli/dotnet/Commands/Restore/RestoreCommand.cs
@@ -40,7 +40,7 @@ public static CommandBase FromParseResult(ParseResult result, string? msbuildPat
{
return CreateForwarding(msbuildArgs, msbuildPath);
},
- [CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, RestoreCommandParser.TargetOption, RestoreCommandParser.VerbosityOption],
+ [CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, RestoreCommandParser.TargetOption, RestoreCommandParser.VerbosityOption, RestoreCommandParser.NoLogoOption],
result,
msbuildPath
);
diff --git a/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs b/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs
index 7e1f4a48106d..f32a0b1545b7 100644
--- a/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs
+++ b/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs
@@ -27,36 +27,36 @@ internal static class RestoreCommandParser
public static readonly Option TargetOption = CommonOptions.RequiredMSBuildTargetOption("Restore");
public static readonly Option VerbosityOption = CommonOptions.VerbosityOption(Utils.VerbosityOptions.minimal);
-
- private static IEnumerable