Skip to content

Commit aa4b11c

Browse files
committed
Implement go as a runner for .NET file-based apps with fast incremental rebuilds
This change turns the `go` global tool into a first-class launcher for .NET file-based applications (the new .cs-only project style using # directives in .NET 10+). Behavior: - Usage: go <file.cs> [extra args] - The first argument is the path to the .cs entry point. - Any arguments before a '--' separator are passed through to 'dotnet publish'. - Arguments after '--' (or all arguments if no separator) are forwarded to the published application at runtime. - Fast path / caching: - A <file>.stamp file is maintained next to the source .cs file. - On first run (or when inputs change), the tool invokes 'dotnet publish <file.cs>' while injecting custom MSBuild logic from go.targets. - go.targets hooks CoreCompile to record every real input source file (as 'input = ...') and hooks Publish to record the final published app executable path (as 'app = ...') into the stamp file (controlled via the GoConfig environment variable). - Before publishing, BuildState.TryRead + BuildManager.IsUpToDate checks whether the recorded app binary exists and has a newer last-write time than all the recorded input files. If so, publish is completely skipped. - The app is then launched directly via ProcessRunner. - Implementation details: - Uses ConsoleAppFramework for argument parsing and help. - DotnetMuxer (previous change) locates the real dotnet executable. - ProcessRunner handles both the publish invocation (with env) and final app exec. - GoArgs.Split cleanly separates publish vs. app arguments at '--'. - Stamp parsing is robust (handles UTF8 BOM, forward vs backslashes, comments). - BuildManager performs mtime-based freshness checks. - Tests: - GoArgsTests: separator cases (no sep, leading, trailing, normal). - GoBuildCacheTests: up-to-date / stale / missing app scenarios. - GoConfigReaderTests (BuildStateTests): parsing of multi-input stamps, BOM, paths. - Samples: - samples/minimal: trivial hello.cs - samples/file-based-apps: rich showcase using #:property, #:package (Spectre), #:include, #:ref, Directory.Build.*, config files, etc. Includes launch profile friendly files. - Other: - go.csproj updated to include and publish go.targets. - help.md regenerated from the new CLI. - launchSettings.json updated for convenient debugging against the showcase sample. This delivers a snappy 'go file.cs' experience for modern file-based .NET programs while correctly handling incremental compilation and argument passthrough.
1 parent 9c6c75e commit aa4b11c

26 files changed

Lines changed: 587 additions & 19 deletions
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<Project>
2+
<!-- Implicit build file: inherited by all file-based apps in this folder (.NET 10) -->
3+
<PropertyGroup>
4+
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
5+
</PropertyGroup>
6+
</Project>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-tl:off
2+
-bl
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"app": "showcase",
3+
"features": ["#:include", "#:ref", "#:package", "#:property"]
4+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Showcase.Extensions;
2+
3+
public static class StringExtensions
4+
{
5+
public static string Emphasize(this string value) => $"[bold]{value}[/]";
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// This file is excluded via #:exclude includes/**/internal-only.cs
2+
namespace Showcase.Internal;
3+
4+
public static class InternalOnly
5+
{
6+
public static string Secret => "should not compile";
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace Showcase.Models;
2+
3+
public readonly record struct TaskItem(int Id, string Title, bool IsDone);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#:property OutputType=Library
2+
#:package Spectre.Console@*
3+
4+
using Spectre.Console;
5+
6+
namespace ShowcaseLib;
7+
8+
public static class Greeter
9+
{
10+
public static string Greet(string name) =>
11+
$"[green]{Markup.Escape($"Hello from #:ref greeter.cs, {name}!")}[/]";
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#:property OutputType=Library
2+
#:property ExperimentalFileBasedProgramEnableRefDirective=true
3+
#:ref utils.cs
4+
5+
namespace ShowcaseLib;
6+
7+
public static class MathLib
8+
{
9+
public static int Add(int a, int b) => Utils.Sum(a, b);
10+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#:property OutputType=Library
2+
3+
namespace ShowcaseLib;
4+
5+
public static class Utils
6+
{
7+
public static int Sum(int a, int b) => a + b;
8+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
input = %(Compile.FullPath.Replace('\', '/'))
2+
input = %(Compile.FullPath.Replace('\', '/'))
3+
input = %(Compile.FullPath.Replace('\', '/'))
4+
input = %(Compile.FullPath.Replace('\', '/'))
5+
input = %(Compile.FullPath.Replace('\', '/'))
6+
input = %(Compile.FullPath.Replace('\', '/'))
7+
input = %(Compile.FullPath.Replace('\', '/'))
8+
input = %(Compile.FullPath.Replace('\', '/'))
9+
input = %(Compile.FullPath.Replace('\', '/'))
10+
input = %(Compile.FullPath.Replace('\', '/'))
11+
input = %(Compile.FullPath.Replace('\', '/'))
12+
input = %(Compile.FullPath.Replace('\', '/'))
13+
input = %(Compile.FullPath.Replace('\', '/'))
14+
input = %(Compile.FullPath.Replace('\', '/'))
15+
input = %(Compile.FullPath.Replace('\', '/'))
16+
input = %(Compile.FullPath.Replace('\', '/'))

0 commit comments

Comments
 (0)