Skip to content
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
5 changes: 5 additions & 0 deletions src/Cargo.UnitTests/CargoTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ public void ProjectsCanDependOnEachOtherProjects()
[Theory]
[InlineData("AutomaticallyUseReferenceAssemblyPackages", "true", "true")]
[InlineData("AutomaticallyUseReferenceAssemblyPackages", null, "false")]
[InlineData("CargoProfile", "release-windows", "release-windows")]
[InlineData("CargoProfile", null, "")]
[InlineData("DebugSymbols", "true", "false")]
[InlineData("DebugSymbols", null, "false")]
[InlineData("DebugType", "Full", "None")]
Expand Down Expand Up @@ -217,6 +219,9 @@ public void ProjectsCanDependOnEachOtherProjects()
[InlineData("SkipCopyFilesMarkedCopyLocal", "false", "false")]
[InlineData("SkipCopyFilesMarkedCopyLocal", "true", "true")]
[InlineData("SkipCopyFilesMarkedCopyLocal", null, "")]
[InlineData("MsRustupTargets", "aarch64-pc-windows-msvc", "aarch64-pc-windows-msvc")]
[InlineData("MsRustupTargets", "aarch64-pc-windows-msvc;x86_64-pc-windows-msvc", "aarch64-pc-windows-msvc;x86_64-pc-windows-msvc")]
[InlineData("MsRustupTargets", null, "")]
public void PropertiesHaveExpectedValues(string propertyName, string value, string expectedValue)
{
ProjectCreator.Templates.CargoProject(
Expand Down
76 changes: 67 additions & 9 deletions src/Cargo/CargoTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,19 @@ private enum ExitCode
/// </summary>
public string CargoOutputDir { get; set; } = string.Empty;

/// <summary>
/// Gets or sets an optional Cargo profile to pass to cargo as "--profile &lt;value&gt;" for MSRustup.
/// When set, this overrides the behavior of deriving the profile from Configuration.
/// </summary>
public string CargoProfile { get; set; } = string.Empty;

/// <summary>
/// Gets or sets an optional semicolon-separated list of additional target triples to install when running "msrustup toolchain install"
/// (e.g. "aarch64-pc-windows-msvc;x86_64-pc-windows-msvc"). Each value is passed to MSRustup as "--target &lt;triple&gt;".
/// Use this to enable cross-compilation.
/// </summary>
public string MsRustupTargets { get; set; } = string.Empty;

/// <inheritdoc/>
public override bool Execute()
{
Expand Down Expand Up @@ -178,7 +191,7 @@ private async Task<bool> ExecuteAsync()

foreach (var registry in GetRegistries(Path.Combine(RepoRoot, _cargoConfigFilePath)))
{
var registryName = registry.Key.Trim().ToUpper();
var registryName = registry.Key.Trim().ToUpper().Replace('-', '_');
_cargoRegistries.Add(registryName);
var tokenName = $"CARGO_REGISTRIES_{registryName}_TOKEN";
AddOrUpdateEnvVar(tokenName, $"Bearer {val}");
Expand Down Expand Up @@ -247,13 +260,7 @@ private async Task<ExitCode> CargoRunCommandAsync(string command, string args)

if (!string.IsNullOrEmpty(customCargo))
{
bool isDebugConfiguration = true;
if (!Configuration.Equals("debug", StringComparison.InvariantCultureIgnoreCase))
{
isDebugConfiguration = false;
}

return await ExecuteProcessAsync(GetCustomToolChainCargoBin() !, $"{command} {args} --offline {(isDebugConfiguration ? string.Empty : "--" + Configuration.ToLowerInvariant())} --config {Path.Combine(RepoRoot, _cargoConfigFilePath)}", ".", _envVars);
return await ExecuteProcessAsync(GetCustomToolChainCargoBin() !, $"{command} {args} --offline {GetMsRustupProfileArgument()} --config {Path.Combine(RepoRoot, _cargoConfigFilePath)}", ".", _envVars);
}

return ExitCode.Failed;
Expand All @@ -262,6 +269,28 @@ private async Task<ExitCode> CargoRunCommandAsync(string command, string args)
return await ExecuteProcessAsync(_cargoPath, $"{command} {args}", ".", _envVars);
}

/// <summary>
/// For MSRustup, determines the appropriate Cargo profile argument to pass based on the CargoProfile and Configuration properties.
/// </summary>
/// <returns>The Cargo profile argument string, if needed; else an empty string.</returns>
private string GetMsRustupProfileArgument()
{
// Explicit CargoProfile wins over the Configuration-derived value.
if (!string.IsNullOrEmpty(CargoProfile))
{
return $"--profile {CargoProfile}";
}

// No flag for the default (Debug) profile.
if (string.IsNullOrEmpty(Configuration) || Configuration.Equals("debug", StringComparison.InvariantCultureIgnoreCase))
{
return string.Empty;
}

// Use the Configuration as a Cargo profile shorthand (e.g. "--release").
return "--" + Configuration.ToLowerInvariant();
}

private async Task<bool> DownloadAndInstallRust()
{
try
Expand Down Expand Up @@ -643,7 +672,7 @@ private async Task<bool> InstallRust()
AddOrUpdateEnvVar("MSRUSTUP_PAT", val);
}

exitCodeLatest = await ExecuteProcessAsync(rustUpBinary, $"toolchain install {GetToolChainVersion()}", StartupProj, _envVars);
exitCodeLatest = await ExecuteProcessAsync(rustUpBinary, $"toolchain install {GetToolChainVersion()}{GetMsRustupTargetArgs()}", StartupProj, _envVars);

if (exitCodeLatest == ExitCode.Succeeded)
{
Expand All @@ -663,6 +692,35 @@ private async Task<bool> InstallRust()
return exitCode == 0 && exitCodeLatest == 0;
}

/// <summary>
/// Builds the target arguments to pass to "msrustup toolchain install" from the <see cref="MsRustupTargets"/> property.
/// </summary>
/// <returns>
/// Each target in the semicolon-separated list becomes its own "--target &lt;triple&gt;" argument in the returned string.
/// Returns the empty string when no targets are configured.
/// </returns>
private string GetMsRustupTargetArgs()
{
if (string.IsNullOrWhiteSpace(MsRustupTargets))
{
return string.Empty;
}

var sb = new System.Text.StringBuilder();
foreach (var target in MsRustupTargets.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
var trimmed = target.Trim();
if (trimmed.Length == 0)
{
continue;
}

sb.Append(" --target ").Append(trimmed);
}

return sb.ToString();
}

private async Task<bool> VerifyInitHashAsync()
{
using var sha256 = SHA256.Create();
Expand Down
28 changes: 25 additions & 3 deletions src/Cargo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,30 @@ msbuild /t:clearcargocache
### Using MSRustup (Microsoft internal use only)
To enable use of MSRustup, you will need to have a rust-toolchain.toml at the root of your repo. The toml file should include a channel specifier that has "ms-" as a prefix, followed by the channel version.
```toml

[toolchain]
channel - ms-<version>
channel = "ms-<version>"
```


#### Optional MSRustup configuration properties

The SDK exposes a handful of MSBuild properties for advanced scenarios.

##### `CargoProfile`

By default the SDK derives the Cargo profile from the MSBuild `Configuration`: `Debug` uses Cargo's default debug profile, and any other configuration is
passed as the `--<Configuration>` value (so `Release` becomes `--release`).

Set `CargoProfile` to override this and pass `--profile <CargoProfile>` to Cargo instead. This is useful when your `Cargo.toml` defines a custom profile
such as `release-windows`.

##### `MsRustupTargets`

A semicolon-separated list of target triples to install when running `msrustup toolchain install`.
Each value becomes a `--target <triple>` argument. Use this to enable cross-compilation.

```xml
<PropertyGroup>
<MsRustupTargets>aarch64-pc-windows-msvc;x86_64-pc-windows-msvc</MsRustupTargets>
</PropertyGroup>
```

2 changes: 1 addition & 1 deletion src/Cargo/sdk/InstallCargo.proj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
<UsingTask TaskName="Microsoft.Build.Cargo.CargoTask" AssemblyFile="$(MSBuildThisFileDirectory)..\build\net8.0\Microsoft.Build.Cargo.dll" Condition="'$(MSBuildRuntimeType)' == 'Core'" />
<UsingTask TaskName="Microsoft.Build.Cargo.CargoTask" AssemblyFile="$(MSBuildThisFileDirectory)..\build\net472\Microsoft.Build.Cargo.dll" Condition="'$(MSBuildRuntimeType)' != 'Core'" />
<Target Name="InstallCargo">
<CargoTask EnableAuth="$(AuthMode)" StartupProj="$(MSBuildStartupDirectory)" Command="installcargo" RepoRoot="$(RepoRoot)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" />
<CargoTask EnableAuth="$(AuthMode)" StartupProj="$(MSBuildStartupDirectory)" Command="installcargo" RepoRoot="$(RepoRoot)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" MsRustupTargets="$(MsRustupTargets)" />
</Target>
</Project>
12 changes: 12 additions & 0 deletions src/Cargo/sdk/Sdk.props
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@
<CargoInstallationRoot Condition="'$(CargoInstallationRoot)' == ''"></CargoInstallationRoot>
<MsRustupAuthType Condition="'$(MsRustupAuthType)' == ''">AzureAuth</MsRustupAuthType>
<CargoOutputDir Condition="'$(CargoOutputDir)' == ''">$(MSBuildProjectDirectory)\bin</CargoOutputDir>
<!--
Optional Cargo profile override, e.g. "release-windows".
When set, the SDK passes the value as the cargo "profile" argument, instead of deriving the profile from Configuration.
Only applies when the msrustup toolchain is in use.
-->
<CargoProfile Condition="'$(CargoProfile)' == ''"></CargoProfile>
<!--
Optional semicolon-separated list of additional target triples to install when running "msrustup toolchain install"
(e.g. "aarch64-pc-windows-msvc;x86_64-pc-windows-msvc"). Each value becomes a "&#45;&#45;target <triple>" argument.
Use to enable cross-compilation. Only applies when the msrustup toolchain is in use.
-->
<MsRustupTargets Condition="'$(MsRustupTargets)' == ''"></MsRustupTargets>
</PropertyGroup>
<ItemGroup>
<None Include="**\*.rs" />
Expand Down
16 changes: 8 additions & 8 deletions src/Cargo/sdk/Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -112,35 +112,35 @@
<MSBuild
Projects="$(MSBuildThisFileDirectory)InstallCargo.proj"
Targets="InstallCargo"
Properties="EnableTelemetryLoggerCopy=$(EnableTelemetryLoggerCopy);TelemetryLoggerLocation=$(TelemetryLoggerLocation);TelemetryLoggerSourcePath=$(TelemetryLoggerSourcePath);TelemetryLoggerInstallId=$(TelemetryLoggerInstallId);RepoRoot=$(RepoRoot);CargoInstallationRoot=$(CargoInstallationRoot);MsRustupAuthType=$(MsRustupAuthType);CargoOutputDir=$(CargoOutputDir)"
Properties="EnableTelemetryLoggerCopy=$(EnableTelemetryLoggerCopy);TelemetryLoggerLocation=$(TelemetryLoggerLocation);TelemetryLoggerSourcePath=$(TelemetryLoggerSourcePath);TelemetryLoggerInstallId=$(TelemetryLoggerInstallId);RepoRoot=$(RepoRoot);CargoInstallationRoot=$(CargoInstallationRoot);MsRustupAuthType=$(MsRustupAuthType);CargoOutputDir=$(CargoOutputDir);MsRustupTargets=$(MsRustupTargets)"
RemoveProperties="NuGetInteractive;MSBuildRestoreSessionId;TargetFramework;RuntimeIdentifier" />
</Target>
<Target Name="CargoFetch" AfterTargets="InstallCargo">
<CargoTask EnableAuth="$(AuthMode)" StartupProj="$(StartupProj)" Command="fetch" RepoRoot="$(RepoRoot)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" />
</Target>
<Target Name="CargoBuild" AfterTargets="CoreCompile">
<CargoTask EnableAuth="$(AuthMode)" StartupProj="$(StartupProj)" Command="build" CommandArgs="$(CargoBuildCommandArgs)" Configuration="$(Configuration)" RepoRoot="$(RepoRoot)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" />
<CargoTask EnableAuth="$(AuthMode)" StartupProj="$(StartupProj)" Command="build" CommandArgs="$(CargoBuildCommandArgs)" Configuration="$(Configuration)" RepoRoot="$(RepoRoot)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" CargoProfile="$(CargoProfile)" />
</Target>
<Target Name="CargoTest" DependsOnTargets="CargoFetch">
<CargoTask StartupProj="$(StartupProj)" Command="test" CommandArgs="$(CargoTestCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" />
<CargoTask StartupProj="$(StartupProj)" Command="test" CommandArgs="$(CargoTestCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" CargoProfile="$(CargoProfile)" />
</Target>
<Target Name="CargoRun" DependsOnTargets="CargoFetch">
<CargoTask StartupProj="$(StartupProj)" Command="run" CommandArgs="$(CargoRunCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" />
<CargoTask StartupProj="$(StartupProj)" Command="run" CommandArgs="$(CargoRunCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" CargoProfile="$(CargoProfile)" />
</Target>
<Target Name="CargoDoc" DependsOnTargets="CargoFetch">
<CargoTask StartupProj="$(StartupProj)" Command="doc" CommandArgs="$(CargoDocCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" />
<CargoTask StartupProj="$(StartupProj)" Command="doc" CommandArgs="$(CargoDocCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" CargoProfile="$(CargoProfile)" />
</Target>
<Target Name="CargoUpdate" DependsOnTargets="CargoFetch">
<CargoTask StartupProj="$(StartupProj)" Command="update" CommandArgs="$(CargoUpdateCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" />
</Target>
<Target Name="CargoInstall" DependsOnTargets="CargoFetch">
<CargoTask StartupProj="$(StartupProj)" Command="install" CommandArgs="$(CargoInstallCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" />
<CargoTask StartupProj="$(StartupProj)" Command="install" CommandArgs="$(CargoInstallCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" CargoProfile="$(CargoProfile)" />
</Target>
<Target Name="CargoCommand" DependsOnTargets="CargoFetch">
<CargoTask StartupProj="$(StartupProj)" Command="$(CargoCommand)" CommandArgs="$(CargoCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" />
<CargoTask StartupProj="$(StartupProj)" Command="$(CargoCommand)" CommandArgs="$(CargoCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" CargoProfile="$(CargoProfile)" />
</Target>
<Target Name="CargoClean">
<CargoTask StartupProj="$(StartupProj)" Command="clean" CommandArgs="$(CargoCleanCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" />
<CargoTask StartupProj="$(StartupProj)" Command="clean" CommandArgs="$(CargoCleanCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" CargoProfile="$(CargoProfile)" />
</Target>
<Target Name="ClearCargoCache">
<CargoTask StartupProj="$(StartupProj)" Command="clearcargocache" CommandArgs="$(ClearCargoCacheCommandArgs)" CargoInstallationRoot="$(CargoInstallationRoot)" MsRustupAuthType="$(MsRustupAuthType)" CargoOutputDir="$(CargoOutputDir)" />
Expand Down