Home > Back-end >  How to set Rust's cargo directory in windows PATH?
How to set Rust's cargo directory in windows PATH?

Time:08-14

I want unnatended install of rust, so I did this little script:

Write-Host "Installing Rust..." -ForegroundColor Cyan
$exePath = "$env:TEMP\rustup-init.exe"

Write-Host "Downloading..."
(New-Object Net.WebClient).DownloadFile('https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe', $exePath)

Write-Host "Installing..."
cmd /c start /wait $exePath -y
Remove-Item $exePath

$addPath = "$env:USERPROFILE\.cargo\bin"
[Environment]::SetEnvironmentVariable('PATH', $env:PATH, $addPath)

but I get

  virtualbox-iso: Cannot convert argument "target", with value: "C:\Users\vagrant\.cargo\bin", for "SetEnvironmentVariable" to type
    virtualbox-iso: "System.EnvironmentVariableTarget": "Cannot convert value "C:\Users\vagrant\.cargo\bin" to type
    virtualbox-iso: "System.EnvironmentVariableTarget". Error: "Unable to match the identifier name C:\Users\vagrant\.cargo\bin to a valid

it looks like a text cannot be converted to a PATH type? What does it mean?

CodePudding user response:

The immediate solution to your problem is that you need to call [Environment]::SetEnvironmentVariable() as follows:

# Modify the user-level PATH definition.
# To modify the machine-level definition, use 'Machine' instead of user 'User',
# but you then need to run with ELEVATION (as admin).
# !! SEE CAVEATS BELOW.
[Environment]::SetEnvironmentVariable('PATH', ($env:PATH   ";$addPath"), 'User')

That is, as Herohtar points out, the method expects only a complete, new value, as the second parameter, and its third parameter specifies the target scope for the persistent, registry-based environment-variable definition.

Caveats:

  • The process-level $env:PATH value is a composite value of the machine-level and user-level definitions in the registry, so with the command above you're in effect duplicating entries from the respective other scope.

  • Additionally, redefining the variable this way replaces any entries in the value that are defined in terms of other environment variables with the expanded, literal values.

    • $env:PATH contains expanded values to begin with, but even reading the unexpanded value directly from the registry won't work, because [Environment]::SetEnvironmentVariable() invariably writes the given string as a REG_SZ value (literal string) rather than as an REG_EXPAND_SZ value (string that may contain %FOO%-style references to other environment variables). Similarly, [Environment]::GetEnvironmentVariable() only reports expanded values.

A proper solution requires quite a bit of extra work, as discussed in this answer, which contains helper function Add-Path.

A pragmatic shortcut - if you're willing to accept that your registry definitions are converted to literal strings - is the following:

  • Read the current definition - for the target scope only - from the registry, via [Environment]::GetEnvironmentVariable()

  • Add to this scope-specific value and save it back to the same scope.

  • Additionally, you may want to add the new entry to the in-process definition of $env:PATH so that the change takes immediate effect.

Here is the solution in the context of a streamlined version of your code:

Write-Host "Installing Rust..." -ForegroundColor Cyan
$exePath = "$env:TEMP\rustup-init.exe"

Write-Host "Downloading..."
Invoke-WebRequest 'https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe' -OutFile $exePath

Write-Host "Installing..."
& $exePath -y
Remove-Item $exePath

$addPath = "$env:USERPROFILE\.cargo\bin"
$scope = 'User' # Change to 'Machine', if needed, which then requires ELEVATION.
[Environment]::SetEnvironmentVariable(
  'PATH', 
  ([Environment]::GetEnvironmentVariable('PATH', $scope)   ";$addPath"), 
  $scope
)

# Also update the current process' definition
$env:PATH  = ";$addPath"
  • Related