Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@
connectors are automatically included in the VSIX without any Extension-side changes.
AppContext.BaseDirectory at runtime is {vsix-install}\LSPServer\, and
OutProcReqnrollConnector.GetConnectorsFolder() appends "Connectors\", so the
expected runtime path is LSPServer\Connectors\Reqnroll-Generic-{tfm}\. -->
<_OutputCopyLocation>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\Reqnroll.IdeSupport.LSP.Server\bin\$(Configuration)\net10.0\win-x64\Connectors\Reqnroll-Generic-$(TargetFramework)'))</_OutputCopyLocation>
expected runtime path is LSPServer\Connectors\Reqnroll-Generic-{tfm}\.
_ConnectorServerRid is passed in by Reqnroll.IdeSupport.LSP.Server.csproj's
BuildConnector target so the staged output lands next to whichever RID the
server itself is being built/published for (win-x64/linux-x64/osx-x64/osx-arm64);
it defaults to win-x64 for plain VS/VSIX builds that don't set it. -->
<_ConnectorServerRid Condition="'$(_ConnectorServerRid)' == ''">win-x64</_ConnectorServerRid>
<_OutputCopyLocation>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\Reqnroll.IdeSupport.LSP.Server\bin\$(Configuration)\net10.0\$(_ConnectorServerRid)\Connectors\Reqnroll-Generic-$(TargetFramework)'))</_OutputCopyLocation>
<NoWarn>NETSDK1138;NETSDK1233;MSB3277</NoWarn>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<!--
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,21 @@ protected string GetDotNetExecCommand(List<string> arguments, string executableF
return GetDotNetCommand();
}

private string GetDotNetCommand() => Path.Combine(GetDotNetInstallLocation(), "dotnet.exe");
private string GetDotNetCommand()
{
if (!OperatingSystem.IsWindows())
return ResolveNonWindowsDotNetCommand(Environment.GetEnvironmentVariable("DOTNET_ROOT"));

return Path.Combine(GetDotNetInstallLocation(), "dotnet.exe");
}

// No Windows-style Program Files layout on Linux/macOS. Prefer an explicit DOTNET_ROOT
// (set by the .NET install scripts / CI images); otherwise rely on "dotnet" being
// resolvable via PATH, which is the standard install on Linux/macOS.
// Extracted as a pure function (taking the env var value as a parameter) so it can be
// unit-tested without depending on the host OS actually being non-Windows.
internal static string ResolveNonWindowsDotNetCommand(string dotNetRoot) =>
string.IsNullOrEmpty(dotNetRoot) ? "dotnet" : Path.Combine(dotNetRoot, "dotnet");

protected string GetConnectorsFolder()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,17 @@
pattern: it triggers the connector's outer build, which dispatches each TFM inner
build and runs CustomAfterBuild (which stages the connector outputs into this
project's Connectors\ output folder) before this project begins compiling. -->
<!-- RuntimeIdentifier/SelfContained are global properties: when the server itself is
published self-contained for a specific RID, MSBuild would otherwise leak those
into the connector's nested build below. The connector is always a
framework-dependent, per-TFM build regardless of which RID the server targets
(see docs on _ConnectorServerRid in Connector.csproj), so they're cleared here
and the RID is instead threaded through via _ConnectorServerRid, which only
affects where the build output is staged. -->
<Target Name="BuildConnector" BeforeTargets="Build">
<MSBuild Projects="$(MSBuildThisFileDirectory)..\Reqnroll.IdeSupport.LSP.Connector\Connector\Connector.csproj"
Targets="Build"
Properties="Configuration=$(Configuration)" />
Properties="Configuration=$(Configuration);_ConnectorServerRid=$(RuntimeIdentifier);RuntimeIdentifier=;SelfContained=false" />
</Target>

<!-- Copy Connector binaries into the publish output so reflection-based binding
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ public void GetConnectorPath_for_netcore_uses_dotnet_exec_with_the_matching_dll(
var (path, args) = Resolve(tfm);

// Launcher is the dotnet host; the connector dll is passed as an "exec" argument.
path.Should().EndWith("dotnet.exe");
// The host binary name is OS-dependent ("dotnet.exe" on Windows, "dotnet" elsewhere) -
// see OutProcReqnrollConnector.GetDotNetCommand / ResolveNonWindowsDotNetCommand.
path.Should().EndWith(OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet");
args.Should().HaveCount(2);
args[0].Should().Be("exec");
args[1].Should().Be(Path.Combine(_extensionFolder, expectedRelative));
Expand All @@ -80,7 +82,7 @@ public void GetConnectorPath_defaults_to_net8_when_target_framework_has_no_versi
// net8.0 default seeded at the top of GetConnectorPath.
var (path, args) = Resolve(".NETCoreApp");

path.Should().EndWith("dotnet.exe");
path.Should().EndWith(OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet");
args[1].Should().Be(Path.Combine(_extensionFolder, @"Reqnroll-Generic-net8.0\reqnroll-ide-connector.dll"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,34 @@ public void RunDiscovery_stamps_the_connector_type_on_the_result()
// GetConnectorType() strips the "OutProcReqnrollConnector" suffix from the type name.
result.ConnectorType.Should().Be("Fake");
}

// ── Non-Windows dotnet-host resolution (see GetDotNetCommand) ──────────────
// Exercised directly against the extracted pure function so both branches are
// covered regardless of which OS actually runs the test.

[Fact]
public void ResolveNonWindowsDotNetCommand_falls_back_to_bare_dotnet_when_DOTNET_ROOT_is_null()
{
var command = OutProcReqnrollConnector.ResolveNonWindowsDotNetCommand(null);

command.Should().Be("dotnet", "PATH resolution is the standard install on Linux/macOS");
}

[Fact]
public void ResolveNonWindowsDotNetCommand_falls_back_to_bare_dotnet_when_DOTNET_ROOT_is_empty()
{
var command = OutProcReqnrollConnector.ResolveNonWindowsDotNetCommand("");

command.Should().Be("dotnet", "PATH resolution is the standard install on Linux/macOS");
}

[Fact]
public void ResolveNonWindowsDotNetCommand_prefers_DOTNET_ROOT_when_set()
{
var dotNetRoot = Path.Combine(Path.GetTempPath(), "dotnet-root");

var command = OutProcReqnrollConnector.ResolveNonWindowsDotNetCommand(dotNetRoot);

command.Should().Be(Path.Combine(dotNetRoot, "dotnet"));
}
}
Loading