Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[automated] Merge branch 'release/9.0.1xx' => 'release/9.0.2xx' #45636

Open
wants to merge 6 commits into
base: release/9.0.2xx
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/pipelines/templates/jobs/vmr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ jobs:

if [[ $archiveVersionLine =~ $versionPattern ]]; then
archiveVersion="${BASH_REMATCH[1]}"
archiveUrl="https://dotnetcli.azureedge.net/source-built-artifacts/sdks/dotnet-sdk-$archiveVersion-${{ parameters.artifactsRid }}.tar.gz"
archiveUrl="https://builds.dotnet.microsoft.com/source-built-artifacts/sdks/dotnet-sdk-$archiveVersion-${{ parameters.artifactsRid }}.tar.gz"
downloadDir="$(sourcesPath)/prereqs/packages/archive/"

echo "Downloading source-built SDK from $archiveUrl..."
Expand Down
12 changes: 6 additions & 6 deletions eng/pipelines/templates/variables/vmr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ variables:
- name: almaLinuxContainer
value: mcr.microsoft.com/dotnet-buildtools/prereqs:almalinux-8-source-build
- name: alpineLatestContainer
value: mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.20-withnode
value: mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.21-amd64
- name: alpinePreviousContainer
value: mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.19-WithNode
value: mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.20-withnode
- name: centOSStreamContainer
value: mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream9
- name: fedoraContainer
Expand Down Expand Up @@ -50,9 +50,9 @@ variables:
- name: almaLinuxName
value: AlmaLinux8
- name: alpineLatestName
value: Alpine320
value: Alpine321
- name: alpinePreviousName
value: Alpine319
value: Alpine320
- name: centOSStreamName
value: CentOSStream9
- name: fedoraName
Expand All @@ -69,9 +69,9 @@ variables:
- name: linuxMuslArm64Rid
value: linux-musl-arm64
- name: alpineLatestX64Rid
value: alpine.3.20-x64
value: alpine.3.21-x64
- name: alpinePreviousX64Rid
value: alpine.3.19-x64
value: alpine.3.20-x64
- name: centOSStreamX64Rid
value: centos.9-x64
- name: fedoraX64Rid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void SetVariable(string name, string value)

public void ConfigureProcess(ProcessSpec processSpec)
{
processSpec.Arguments = [.. GetCommandLineDirectives(), .. processSpec.Arguments];
processSpec.Arguments = [.. GetCommandLineDirectives(), .. processSpec.Arguments ?? []];
AddToEnvironment(processSpec.EnvironmentVariables);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ private async ValueTask DisplayResultsAsync(WatchHotReloadService.Updates update
switch (updates.Status)
{
case ModuleUpdateStatus.None:
_reporter.Output("No hot reload changes to apply.");
_reporter.Output("No C# changes to apply.");
break;

case ModuleUpdateStatus.Ready:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,11 @@ private async Task<bool> ReceiveApplyUpdateResult(CancellationToken cancellation
var status = ArrayPool<byte>.Shared.Rent(1);
try
{
var statusBytesRead = await _pipe.ReadAsync(status, cancellationToken);
var statusBytesRead = await _pipe.ReadAsync(status, offset: 0, count: 1, cancellationToken);
if (statusBytesRead != 1 || status[0] != UpdatePayload.ApplySuccessValue)
{
Reporter.Error($"Change failed to apply (error code: '{BitConverter.ToString(status, 0, statusBytesRead)}'). Further changes won't be applied to this process.");
var message = (statusBytesRead == 0) ? "received no data" : $"received status 0x{status[0]:x2}";
Reporter.Error($"Change failed to apply ({message}). Further changes won't be applied to this process.");
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,17 @@ public async Task UpdateProjectConeAsync(string rootProjectPath, CancellationTok

var loader = new MSBuildProjectLoader(this);
var projectMap = ProjectMap.Create();
var projectInfos = await loader.LoadProjectInfoAsync(rootProjectPath, projectMap, progress: null, msbuildLogger: null, cancellationToken).ConfigureAwait(false);

ImmutableArray<ProjectInfo> projectInfos;
try
{
projectInfos = await loader.LoadProjectInfoAsync(rootProjectPath, projectMap, progress: null, msbuildLogger: null, cancellationToken).ConfigureAwait(false);
}
catch (InvalidOperationException)
{
// TODO: workaround for https://github.com/dotnet/roslyn/issues/75956
projectInfos = [];
}

var oldProjectIdsByPath = oldSolution.Projects.ToDictionary(keySelector: static p => p.FilePath!, elementSelector: static p => p.Id);

Expand Down
61 changes: 41 additions & 20 deletions src/BuiltInTools/dotnet-watch/HotReload/ScopedCssFileHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections;
using System.Diagnostics;
using Microsoft.Build.Graph;
using Microsoft.Build.Framework;
using Microsoft.DotNet.Watcher.Internal;
using Microsoft.Extensions.Tools.Internal;

Expand Down Expand Up @@ -52,41 +53,61 @@ public async ValueTask HandleFileChangesAsync(IReadOnlyList<ChangedFile> files,
}
}

var logger = reporter.IsVerbose ? new[] { new Build.Logging.ConsoleLogger() } : null;
if (!hasApplicableFiles)
{
return;
}

var logger = reporter.IsVerbose ? new[] { new Build.Logging.ConsoleLogger(LoggerVerbosity.Minimal) } : null;

var tasks = projectsToRefresh.Select(async projectNode =>
var buildTasks = projectsToRefresh.Select(projectNode => Task.Run(() =>
{
if (!projectNode.ProjectInstance.DeepCopy().Build(BuildTargetName, logger))
try
{
return false;
if (!projectNode.ProjectInstance.DeepCopy().Build(BuildTargetName, logger))
{
return null;
}
}

if (browserConnector.TryGetRefreshServer(projectNode, out var browserRefreshServer))
catch (Exception e)
{
await HandleBrowserRefresh(browserRefreshServer, projectNode.ProjectInstance.FullPath, cancellationToken);
reporter.Error($"[{projectNode.GetDisplayName()}] Target {BuildTargetName} failed to build: {e}");
return null;
}

return true;
});
return projectNode;
}));

var results = await Task.WhenAll(tasks).WaitAsync(cancellationToken);
var buildResults = await Task.WhenAll(buildTasks).WaitAsync(cancellationToken);

if (hasApplicableFiles)
var browserRefreshTasks = buildResults.Where(p => p != null)!.GetTransitivelyReferencingProjects().Select(async projectNode =>
{
var successfulCount = results.Sum(r => r ? 1 : 0);

if (successfulCount == results.Length)
{
reporter.Output("Hot reload of scoped css succeeded.", emoji: "🔥");
}
else if (successfulCount > 0)
if (browserConnector.TryGetRefreshServer(projectNode, out var browserRefreshServer))
{
reporter.Output($"Hot reload of scoped css partially succeeded: {successfulCount} project(s) out of {results.Length} were updated.", emoji: "🔥");
reporter.Verbose($"[{projectNode.GetDisplayName()}] Refreshing browser.");
await HandleBrowserRefresh(browserRefreshServer, projectNode.ProjectInstance.FullPath, cancellationToken);
}
else
{
reporter.Output("Hot reload of scoped css failed.", emoji: "🔥");
reporter.Verbose($"[{projectNode.GetDisplayName()}] No refresh server.");
}
});

await Task.WhenAll(browserRefreshTasks).WaitAsync(cancellationToken);

var successfulCount = buildResults.Sum(r => r != null ? 1 : 0);

if (successfulCount == buildResults.Length)
{
reporter.Output("Hot reload of scoped css succeeded.", emoji: "🔥");
}
else if (successfulCount > 0)
{
reporter.Output($"Hot reload of scoped css partially succeeded: {successfulCount} project(s) out of {buildResults.Length} were updated.", emoji: "🔥");
}
else
{
reporter.Output("Hot reload of scoped css failed.", emoji: "🔥");
}
}

Expand Down
11 changes: 11 additions & 0 deletions src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke

var rootProjectOptions = Context.RootProjectOptions;
var rootProjectNode = Context.ProjectGraph.GraphRoots.Single();
var rootProjectCapabilities = rootProjectNode.GetCapabilities();

await using var runtimeProcessLauncher = _runtimeProcessLauncherFactory?.TryCreate(rootProjectNode, projectLauncher, rootProjectOptions.BuildProperties);
if (runtimeProcessLauncher != null)
Expand Down Expand Up @@ -211,6 +212,16 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
await compilationHandler.Workspace.UpdateFileContentAsync(changedFiles, iterationCancellationToken);
}

if (!rootProjectCapabilities.Contains("SupportsHotReload"))
{
Context.Reporter.Warn($"Project '{rootProjectNode.GetDisplayName()}' does not support Hot Reload and must be rebuilt.");

// file change already detected
waitForFileChangeBeforeRestarting = false;
iterationCancellationSource.Cancel();
break;
}

HotReloadEventSource.Log.HotReloadStart(HotReloadEventSource.StartType.Main);
var stopwatch = Stopwatch.StartNew();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,28 @@ public static bool IsNetCoreApp(this ProjectGraphNode projectNode, Version minVe

public static IEnumerable<string> GetCapabilities(this ProjectGraphNode projectNode)
=> projectNode.ProjectInstance.GetItems("ProjectCapability").Select(item => item.EvaluatedInclude);

public static IEnumerable<ProjectGraphNode> GetTransitivelyReferencingProjects(this IEnumerable<ProjectGraphNode> projects)
{
var visited = new HashSet<ProjectGraphNode>();
var queue = new Queue<ProjectGraphNode>();
foreach (var project in projects)
{
queue.Enqueue(project);
}

while (queue.Count > 0)
{
var project = queue.Dequeue();
if (visited.Add(project))
{
foreach (var referencingProject in project.ReferencingProjects)
{
queue.Enqueue(referencingProject);
}
}
}

return visited;
}
}
2 changes: 1 addition & 1 deletion src/SourceBuild/content/prep-source-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ function DownloadArchive {
archiveRid=$artifactsRid
fi

archiveUrl="https://dotnetcli.azureedge.net/source-built-artifacts/assets/Private.SourceBuilt.$archiveType.$archiveVersion.$archiveRid.tar.gz"
archiveUrl="https://builds.dotnet.microsoft.com/source-built-artifacts/assets/Private.SourceBuilt.$archiveType.$archiveVersion.$archiveRid.tar.gz"

echo " Downloading source-built $archiveType from $archiveUrl..."
(cd "$packagesArchiveDir" && curl -f --retry 5 -O "$archiveUrl")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -790,9 +790,9 @@ Copyright (c) .NET Foundation. All rights reserved.

<!--
============================================================
_GetAppHostPaths
Gets the path to apphost (restored via packages or in an apphost pack),
and computes the path for the destination apphost.
_GetAppHostCreationConfiguration
Computes the properties for configuration of apphost creation
during either build or publish
============================================================
-->
<Target Name="_GetAppHostCreationConfiguration"
Expand Down Expand Up @@ -981,7 +981,7 @@ Copyright (c) .NET Foundation. All rights reserved.
============================================================
-->
<Target Name="_ComputeNETCoreBuildOutputFiles"
DependsOnTargets="_GetAppHostPaths;_GetComHostPaths;_GetIjwHostPaths"
DependsOnTargets="_GetAppHostPaths;_GetAppHostCreationConfiguration;_GetComHostPaths;_GetIjwHostPaths"
BeforeTargets="AssignTargetPaths"
Condition="'$(ComputeNETCoreBuildOutputFiles)' == 'true'">

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ private async Task<List<ITaskItem>> PrepareWorkItem(ITaskItem xunitProject)

var testFilter = string.IsNullOrEmpty(assemblyPartitionInfo.ClassListArgumentString) ? "" : $"--filter \"{assemblyPartitionInfo.ClassListArgumentString}\"";
command = $"{driver} test {assemblyName} -e HELIX_WORK_ITEM_TIMEOUT={timeout} {testExecutionDirectory} {msbuildAdditionalSdkResolverFolder} " +
$"{(XUnitArguments != null ? " " + XUnitArguments : "")} --results-directory .{Path.DirectorySeparatorChar} --logger trx --blame-hang --blame-hang-timeout 30m {testFilter} -- {arguments}";
$"{(XUnitArguments != null ? " " + XUnitArguments : "")} --results-directory .{Path.DirectorySeparatorChar} --logger trx --logger \"console;verbosity=detailed\" --blame-hang --blame-hang-timeout 30m {testFilter} -- {arguments}";

Log.LogMessage($"Creating work item with properties Identity: {assemblyName}, PayloadDirectory: {publishDirectory}, Command: {command}");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,44 @@ public void It_does_not_rewrite_the_single_file_unnecessarily()
fileWriteTimeAfterSecondRun.Should().Be(fileWriteTimeAfterFirstRun);
}

[RequiresMSBuildVersionFact("16.8.0")]
public void It_uses_appropriate_host_on_selfcontained_publish_with_no_build()
{
var testProject = new TestProject()
{
Name = "SingleFileTest",
TargetFrameworks = ToolsetInfo.CurrentTargetFramework,
RuntimeIdentifier = RuntimeInformation.RuntimeIdentifier,
IsExe = true,
};
testProject.AdditionalProperties.Add("SelfContained", "true");
TestAsset testAsset = _testAssetsManager.CreateTestProject(testProject);

// Build will create app using apphost
var buildCommand = new BuildCommand(testAsset);
buildCommand
.Execute()
.Should()
.Pass();

// Publish without build should create app using singlefilehost
var publishCommand = new PublishCommand(testAsset);
publishCommand
.Execute(PublishSingleFile, "/p:NoBuild=true")
.Should()
.Pass();
string singleFilePath = Path.Combine(
GetPublishDirectory(publishCommand).FullName,
$"{testProject.Name}{Constants.ExeSuffix}");

// Make sure published app runs correctly
var command = new RunExeCommand(Log, singleFilePath);
command.Execute()
.Should()
.Pass()
.And.HaveStdOutContaining("Hello World");
}

[RequiresMSBuildVersionFact("16.8.0")]
public void It_rewrites_the_apphost_for_single_file_publish()
{
Expand Down
54 changes: 54 additions & 0 deletions test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,60 @@ public static void Print()
await App.AssertOutputLineStartsWith("Changed!");
}

[Fact(Skip = "https://github.com/dotnet/sdk/issues/45457")]
public async Task ChangeFileInFSharpProject()
{
var testAsset = TestAssets.CopyTestAsset("FSharpTestAppSimple")
.WithSource();

App.Start(testAsset, []);

await App.AssertOutputLineStartsWith(MessageDescriptor.WaitingForFileChangeBeforeRestarting);

UpdateSourceFile(Path.Combine(testAsset.Path, "Program.fs"), content => content.Replace("Hello World!", "<Updated>"));

await App.AssertOutputLineStartsWith("<Updated>");
}

[Fact]
public async Task ChangeFileInFSharpProjectWithLoop()
{
var testAsset = TestAssets.CopyTestAsset("FSharpTestAppSimple")
.WithSource();

var source = """
module ConsoleApplication.Program

open System
open System.Threading

[<EntryPoint>]
let main argv =
while true do
printfn "Waiting"
Thread.Sleep(200)
0
""";

var sourcePath = Path.Combine(testAsset.Path, "Program.fs");

File.WriteAllText(sourcePath, source);

App.Start(testAsset, []);

await App.AssertOutputLineStartsWith(MessageDescriptor.WaitingForChanges);

UpdateSourceFile(sourcePath, content => content.Replace("Waiting", "<Updated>"));

await App.AssertOutputLineStartsWith(MessageDescriptor.WaitingForChanges, failure: _ => false);
await App.AssertOutputLineStartsWith("<Updated>");

UpdateSourceFile(sourcePath, content => content.Replace("<Updated>", "<Updated2>"));

await App.AssertOutputLineStartsWith(MessageDescriptor.WaitingForChanges, failure: _ => false);
await App.AssertOutputLineStartsWith("<Updated2>");
}

// Test is timing out on .NET Framework: https://github.com/dotnet/sdk/issues/41669
[CoreMSBuildOnlyFact]
public async Task HandleTypeLoadFailure()
Expand Down