Skip to content

Commit

Permalink
[tools] Add helper tool to create launch.json for dotnet-linker. (#18294
Browse files Browse the repository at this point in the history
)

This tool takes a binlog as input, and creates a launch.json for dotnet-linker
that will execute the linker as was done in the binlog.

This is very useful for debugging custom linker steps, since they can be
debugged in VSCode now.
  • Loading branch information
rolfbjarne authored May 19, 2023
1 parent cdbf30c commit 464cf69
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 0 deletions.
136 changes: 136 additions & 0 deletions tools/create-dotnet-linker-launch-json/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using System.IO;
using System.Text;

using Microsoft.Build.Framework;
using Microsoft.Build.Logging;
using Microsoft.Build.Logging.StructuredLogger;

using Mono.Options;

class Program {
static int Main (string [] args)
{
var printHelp = false;
var binlog = string.Empty;
var rootDirectory = string.Empty;
var skippedLinkerCommands = 0;
var options = new OptionSet {
{ "h|?|help", "Print this help message", (v) => printHelp = true },
{ "r|root=", "The root directory", (v) => rootDirectory = v },
{ "bl|binlog=", "The binlog", (v) => binlog = v },
{ "s|skip", "Task invocations to skip", (v) => skippedLinkerCommands++ },
};

if (printHelp) {
options.WriteOptionDescriptions (Console.Out);
return 0;
}

var others = options.Parse (args);
if (others.Any ()) {
Console.WriteLine ("Unexpected arguments:");
foreach (var arg in others)
Console.WriteLine ("\t{0}", arg);
Console.WriteLine ("Expected arguments are:");
options.WriteOptionDescriptions (Console.Out);
return 1;
}

if (string.IsNullOrEmpty (binlog)) {
Console.Error.WriteLine ("A binlog is required");
Console.WriteLine ("Expected arguments are:");
options.WriteOptionDescriptions (Console.Out);
return 1;
}

var path = Path.GetFullPath (binlog);

if (string.IsNullOrEmpty (rootDirectory))
rootDirectory = Path.GetDirectoryName (path)!;

Console.WriteLine ($"Processing {path} with root directory {rootDirectory}...");


var reader = new BinLogReader ();
var records = reader.ReadRecords (path).ToArray ();
foreach (var record in records) {
if (record is null)
continue;

if (record.Args is null)
continue;

if (record.Args is TaskStartedEventArgs tsea && tsea.TaskName == "ILLink") {
if (skippedLinkerCommands > 0) {
Console.WriteLine ($"Skipped an ILLink task invocation, {skippedLinkerCommands} left to skip...");
skippedLinkerCommands--;
continue;
}


var relevantRecords = records.Where (v => v?.Args?.BuildEventContext?.TaskId == tsea.BuildEventContext.TaskId).Select (v => v.Args).ToArray ();
var cla = relevantRecords.Where (v => v is BuildMessageEventArgs).Cast<BuildMessageEventArgs> ().Where (v => v?.ToString ()?.Contains ("CommandLineArguments") == true).ToArray ();
foreach (var rr in relevantRecords) {
if (rr is TaskCommandLineEventArgs tclea) {
if (!Xamarin.Utils.StringUtils.TryParseArguments (tclea.CommandLine.Replace ('\n', ' '), out var arguments, out var ex)) {
Console.WriteLine ($"Failed to parse command line arguments: {ex.Message}");
return 1;
}

WriteLaunchJson (CreateLaunchJson (rootDirectory, arguments));
return 0;
}
}
}
}

Console.Error.WriteLine ($"Unable to find command line arguments for ILLink in {path}");
return 1;
}

static void WriteLaunchJson (string contents)
{
var dir = Environment.CurrentDirectory!;
while (!Directory.Exists (Path.Combine (dir, "tools", "dotnet-linker")))
dir = Path.GetDirectoryName (dir)!;
var path = Path.Combine (dir, "tools", "dotnet-linker", ".vscode", "launch.json");
File.WriteAllText (path, contents);
Console.WriteLine ($"Created {path}");
}

static string CreateLaunchJson (string workingDirectory, string [] arguments)
{
var dotnet = arguments [0];
var sb = new StringBuilder ();
sb.AppendLine ("{");
sb.AppendLine (" // Use IntelliSense to learn about possible attributes.");
sb.AppendLine (" // Hover to view descriptions of existing attributes.");
sb.AppendLine (" // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387");
sb.AppendLine (" \"version\": \"0.2.0\",");
sb.AppendLine (" \"configurations\": [");
sb.AppendLine (" {");
sb.AppendLine (" \"justMyCode\": false,");
sb.AppendLine (" \"preLaunchTask\": \"make\",");
sb.AppendLine (" \"name\": \".NET Core Launch (console)\",");
sb.AppendLine (" \"type\": \"coreclr\",");
sb.AppendLine (" \"request\": \"launch\",");
sb.AppendLine ($" \"program\": \"{dotnet}\",");
sb.AppendLine (" \"args\": [");
for (var i = 1; i < arguments.Length; i++) {
sb.AppendLine ($" \"{arguments [i]}\"{(i < arguments.Length - 1 ? "," : "")}");
}
sb.AppendLine (" ],");
sb.AppendLine ($" \"cwd\": \"{Path.GetFullPath (workingDirectory)}\",");
sb.AppendLine (" \"console\": \"internalConsole\",");
sb.AppendLine (" \"stopAtEntry\": false");
sb.AppendLine (" },");
sb.AppendLine (" {");
sb.AppendLine (" \"name\": \".NET Core Attach\",");
sb.AppendLine (" \"type\": \"coreclr\",");
sb.AppendLine (" \"request\": \"attach\"");
sb.AppendLine (" }");
sb.AppendLine (" ]");
sb.AppendLine ("}");
return sb.ToString ();
}
}
3 changes: 3 additions & 0 deletions tools/create-dotnet-linker-launch-json/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This tool takes a binlog that executes dotnet-linker, and creates a
launch.json file for the dotnet-linker directory so that the steps can be
debugged in VSCode.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace>create_dotnet_linker_launch_json</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MSBuild.StructuredLogger" Version="2.1.758" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\common\StringUtils.cs">
<Link>StringUtils.cs</Link>
</Compile>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 25.0.1706.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "create-dotnet-linker-launch-json", "create-dotnet-linker-launch-json.csproj", "{9D626D84-80BA-43E8-ADDF-EAE8944F73D8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9D626D84-80BA-43E8-ADDF-EAE8944F73D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D626D84-80BA-43E8-ADDF-EAE8944F73D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D626D84-80BA-43E8-ADDF-EAE8944F73D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D626D84-80BA-43E8-ADDF-EAE8944F73D8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B197B398-E8C7-444F-B2E2-1F479AE20CA2}
EndGlobalSection
EndGlobal

5 comments on commit 464cf69

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

Please sign in to comment.