Skip to content
Draft
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
1 change: 1 addition & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Testing:
- Create a dogfood environment: `source eng/dogfood.sh`
- Test commands in the dogfood shell (e.g., `dnx --help`, `dotnet tool install --help`)
- The dogfood script sets up PATH and environment to use the newly built SDK
- For fast incremental iteration on specific projects (e.g., `dotnet`, `Microsoft.DotNet.ProjectTools`), use `eng/deploy-dogfood.ps1` instead of a full `build.cmd`. It builds only the specified projects and copies DLLs into the redist layout (~30s vs ~5min). Usage: `.\eng\deploy-dogfood.ps1 -Projects dotnet,Microsoft.DotNet.ProjectTools`

Output Considerations:
- When considering how output should look, solicit advice from baronfel.
Expand Down
97 changes: 97 additions & 0 deletions eng/deploy-dogfood.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<#
.SYNOPSIS
Incrementally builds changed SDK projects and deploys DLLs into the dogfood redist layout.
Much faster than running build.cmd (~seconds vs ~minutes).

.PARAMETER Projects
Names of projects to build and deploy (without .csproj extension).
Defaults to the common file-based-programs projects.

.EXAMPLE
.\eng\deploy-dogfood.ps1
.\eng\deploy-dogfood.ps1 -Projects dotnet,Microsoft.DotNet.ProjectTools
#>
param(
[string[]]$Projects = @(
"dotnet",
"Microsoft.DotNet.ProjectTools"
)
)

$ErrorActionPreference = "Stop"
$repoRoot = (Split-Path $PSScriptRoot -Parent)

$dotnet = Join-Path $repoRoot ".dotnet\dotnet"
$sdkVersion = (Get-ChildItem (Join-Path $repoRoot "artifacts\bin\redist\Debug\dotnet\sdk") -Directory | Select-Object -First 1).Name
if (-not $sdkVersion) {
Write-Error "No SDK version found in redist layout. Run build.cmd first."
exit 1
}

$redistTargets = @(
"artifacts\bin\redist\Debug\dotnet\sdk\$sdkVersion",
"artifacts\bin\redist\Debug\dotnet-installer\sdk\$sdkVersion"
)

foreach($proj in $Projects) {
# Find the .csproj file.
$csprojFiles = Get-ChildItem $repoRoot -Recurse -Filter "$proj.csproj" |
Where-Object { $_.FullName -notmatch "\\test\\" -and $_.FullName -notmatch "\\artifacts\\" }

if ($csprojFiles.Count -eq 0) {
Write-Warning "Project '$proj' not found, skipping."
continue
}

$csproj = $csprojFiles[0].FullName
Write-Host "Building $proj..." -ForegroundColor Cyan

& $dotnet build $csproj --no-restore -nologo -consoleLoggerParameters:NoSummary 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Error "Build failed for $proj"
exit 1
}

# Find the built DLL by discovering the TFM folder dynamically.
$binDir = Join-Path $repoRoot "artifacts\bin\$proj\Debug"
$builtDll = $null
if (Test-Path $binDir) {
$builtDll = Get-ChildItem $binDir -Recurse -Filter "$proj.dll" |
Sort-Object LastWriteTime -Descending | Select-Object -First 1
}
if (-not $builtDll) {
Write-Warning "Built DLL not found for $proj under $binDir, skipping deploy."
continue
}
$builtDll = $builtDll.FullName

# Copy to all redist targets.
foreach ($target in $redistTargets) {
$targetPath = Join-Path $repoRoot $target
$destDll = Join-Path $targetPath "$proj.dll"
if (Test-Path $destDll) {
Copy-Item $builtDll $destDll -Force
# Also copy PDB if available.
$builtPdb = [IO.Path]::ChangeExtension($builtDll, ".pdb")
$destPdb = [IO.Path]::ChangeExtension($destDll, ".pdb")
if (Test-Path $builtPdb) {
Copy-Item $builtPdb $destPdb -Force
}
Write-Host " Deployed to $target" -ForegroundColor Green
}
}

# Also check dotnet-watch tool location.
foreach ($target in $redistTargets) {
$watchToolDir = Join-Path $repoRoot "$target\DotnetTools\dotnet-watch\$sdkVersion\tools"
if (Test-Path $watchToolDir) {
$watchDll = Get-ChildItem $watchToolDir -Recurse -Filter "$proj.dll" | Select-Object -First 1
if ($watchDll) {
Copy-Item $builtDll $watchDll.FullName -Force
Write-Host " Deployed to dotnet-watch in $target" -ForegroundColor Green
}
}
}
}

Write-Host "`nDone! Dogfood SDK updated." -ForegroundColor Green
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@
<value>The '#:project' directive is invalid: {0}</value>
<comment>{0} is the inner error message.</comment>
</data>
<data name="InvalidRefDirective" xml:space="preserve">
<value>The '#:ref' directive is invalid: {0}</value>
<comment>{Locked="#:ref"}{0} is the inner error message.</comment>
</data>
<data name="CouldNotFindRefFile" xml:space="preserve">
<value>Could not find file `{0}`.</value>
<comment>{0} is the file path.</comment>
</data>
<data name="MissingDirectiveName" xml:space="preserve">
<value>Missing name of '{0}'.</value>
<comment>{0} is the directive name like 'package' or 'sdk'.</comment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ public void ReportError(TextSpan span, string message)
case "property": return Property.Parse(context);
case "package": return Package.Parse(context);
case "project": return Project.Parse(context);
case "ref": return Ref.Parse(context);
case "include" or "exclude": return IncludeOrExclude.Parse(context);
default:
context.ReportError(string.Format(FileBasedProgramsResources.UnrecognizedDirective, context.DirectiveKind));
Expand Down Expand Up @@ -587,6 +588,105 @@ void ReportError(string message)
public override string ToString() => $"#:project {Name}";
}

/// <summary>
/// <c>#:ref</c> directive. References another file-based app.
/// </summary>
public sealed class Ref : Named
{
[SetsRequiredMembers]
public Ref(in ParseInfo info, string name) : base(info)
{
Name = name;
OriginalName = name;
}

/// <summary>
/// Preserved across <see cref="WithName"/> calls, i.e.,
/// this is the original directive text as entered by the user.
/// </summary>
public string OriginalName { get; init; }

/// <summary>
/// This is the <see cref="OriginalName"/> with MSBuild <c>$(..)</c> vars expanded.
/// </summary>
public string? ExpandedName { get; init; }

/// <summary>
/// The resolved full path to the referenced <c>.cs</c> file.
/// </summary>
public string? ResolvedPath { get; init; }

public static new Ref? Parse(in ParseContext context)
{
var directiveText = context.DirectiveText;
if (directiveText.IsWhiteSpace())
{
context.ReportError(string.Format(FileBasedProgramsResources.MissingDirectiveName, context.DirectiveKind));
return null;
}

return new Ref(context.Info, directiveText);
}

public enum NameKind
{
/// <summary>
/// Change <see cref="Named.Name"/> and <see cref="ExpandedName"/>.
/// </summary>
Expanded = 1,

/// <summary>
/// Change <see cref="Named.Name"/> and <see cref="ResolvedPath"/>.
/// </summary>
Resolved = 2,

/// <summary>
/// Change only <see cref="Named.Name"/>.
/// </summary>
Final = 3,
}

public Ref WithName(string name, NameKind kind)
{
return new Ref(Info, name)
{
OriginalName = OriginalName,
ExpandedName = kind == NameKind.Expanded ? name : ExpandedName,
ResolvedPath = kind == NameKind.Resolved ? name : ResolvedPath,
};
}

/// <summary>
/// Resolves the directive path to a full file path and validates the file exists.
/// </summary>
public Ref EnsureResolvedPath(ErrorReporter errorReporter)
{
var sourcePath = Info.SourceFile.Path;

var sourceDirectory = Path.GetDirectoryName(sourcePath)
?? throw new InvalidOperationException($"Source file path '{sourcePath}' does not have a containing directory.");

var resolvedFilePath = Path.GetFullPath(Path.Combine(sourceDirectory, Name.Replace('\\', '/')));

if (!File.Exists(resolvedFilePath))
{
errorReporter(Info.SourceFile.Text, sourcePath, Info.Span,
string.Format(FileBasedProgramsResources.InvalidRefDirective,
string.Format(FileBasedProgramsResources.CouldNotFindRefFile, resolvedFilePath)));
}

// Name keeps the (possibly relative) display path; ResolvedPath stores the full path.
return new Ref(Info, Name)
{
OriginalName = OriginalName,
ExpandedName = ExpandedName,
ResolvedPath = resolvedFilePath,
};
}

public override string ToString() => $"#:ref {Name}";
}

public enum IncludeOrExcludeKind
{
Include,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ 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.Ref
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.EnsureResolvedPath(Microsoft.DotNet.FileBasedPrograms.ErrorReporter! errorReporter) -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref!
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.ExpandedName.get -> string?
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.ExpandedName.init -> void
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.NameKind
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.NameKind.Expanded = 1 -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.NameKind
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.NameKind.Final = 3 -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.NameKind
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.NameKind.Resolved = 2 -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.NameKind
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.OriginalName.get -> string!
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.OriginalName.init -> void
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.Ref(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo info, string! name) -> void
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.ResolvedPath.get -> string?
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.ResolvedPath.init -> void
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.WithName(string! name, Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.NameKind kind) -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref!
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?
Expand Down Expand Up @@ -123,6 +137,7 @@ override Microsoft.DotNet.FileBasedPrograms.CSharpDirective.IncludeOrExclude.ToS
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.Ref.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
Expand All @@ -134,6 +149,7 @@ static Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Package.Parse(in Micro
static Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Parse(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext context) -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Named?
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.Ref.Parse(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext context) -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref?
static Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Sdk.Parse(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseContext context) -> Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Sdk?
static Microsoft.DotNet.FileBasedPrograms.ErrorReporters.CreateCollectingReporter(out System.Collections.Immutable.ImmutableArray<Microsoft.DotNet.FileBasedPrograms.SimpleDiagnostic!>.Builder! builder) -> Microsoft.DotNet.FileBasedPrograms.ErrorReporter!
static Microsoft.DotNet.FileBasedPrograms.ExternalHelpers.CombineHashCodes(int value1, int value2) -> int
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading