Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f84c283
Add Rust URL updater using rustup script
tmukh Mar 24, 2026
a5b2607
Add Rust commandlet installer with Windows MSVC setup
tmukh Mar 24, 2026
6b3dd08
#1719: Change Rust MSVC Name
tmukh Apr 7, 2026
a183880
Added Rust commandlet properties
tmukh Apr 16, 2026
539c119
Apply suggestions from code review
hohwille Apr 24, 2026
29eaf5e
fix installDependencies
hohwille Apr 24, 2026
fcbc211
do not mock commandlet
hohwille Apr 24, 2026
d76c043
#1719: Refactor LocalToolCommandlet and Rust.java
laert-ll May 19, 2026
168699a
#1719: Add getDownloadedToolFile method
laert-ll May 19, 2026
9a92b3d
#1719: More refactoring
laert-ll May 22, 2026
c6c07b4
#1719: Revert unnecessary docstring change
laert-ll May 22, 2026
f513119
#1719: Cleanup
laert-ll May 22, 2026
2843a85
#1719: Cleanup
laert-ll May 22, 2026
0aeaf68
#1719: Rename of RustGithubUrlTagUpdaterTest
laert-ll May 22, 2026
c1dd198
#1719: Revert rename of RustGithubUrlTagUpdaterTest
laert-ll May 22, 2026
582b49b
#1719: Removed installDependencies method
laert-ll May 22, 2026
f4227f8
#1719: MSVC url updater and installation with dependencies.json
laert-ll May 26, 2026
847a181
#1719: MSVC download link
laert-ll May 26, 2026
06b410b
#1719: Fixing MSVC commandlet
laert-ll May 26, 2026
1c6a285
––#1719: Fix MSVC commandlet
laert-ll May 26, 2026
7ccfb3b
#1719: Refactor MSVC commandlet
laert-ll May 27, 2026
6eb3e02
#1719: Add MSVC help message
laert-ll May 27, 2026
c116ad1
#1719: Temporary tests fix
laert-ll May 28, 2026
13ccd61
#1719: Extend RustTest.java
laert-ll May 28, 2026
04d1de8
#1719: Remove unnecessary RustGithubUrlTagUpdaterTest.java
laert-ll May 28, 2026
6032e89
#1719: Add License, tiny Msvc.java tweaks
laert-ll May 28, 2026
684727e
#1719: Cleanup
laert-ll May 28, 2026
dc473a5
#1719: Fix
laert-ll Jun 1, 2026
08ccac2
Fix CHANGELOG.adoc rebase conflict.
laert-ll Jun 1, 2026
133b2a7
#1719: Revert temporary tests bug fix
laert-ll Jun 3, 2026
acefc08
#1719: Implement review suggestions for MSVC non-Windows installation
laert-ll Jun 3, 2026
529b3ff
#1719: Implement review suggestion for unneccessary getDownloadedTool…
laert-ll Jun 3, 2026
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
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Release with new features and bugfixes:
* https://github.com/devonfw/IDEasy/issues/1849[#1849]: Add VSCodium support
* https://github.com/devonfw/IDEasy/issues/1391[#1391]: Fix bashrc messed with terraform completions
* https://github.com/devonfw/IDEasy/issues/1922[#1922]: Add Task CLI to IDEasy commandlets
* https://github.com/devonfw/IDEasy/issues/1719[#1719]: Add Rust and MSVC support

The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/45?closed=1[milestone 2026.06.001].

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.devonfw.tools.ide.tool.kubectl.KubeCtl;
import com.devonfw.tools.ide.tool.lazydocker.LazyDocker;
import com.devonfw.tools.ide.tool.mvn.Mvn;
import com.devonfw.tools.ide.tool.msvc.Msvc;
import com.devonfw.tools.ide.tool.nest.Nest;
import com.devonfw.tools.ide.tool.ng.Ng;
import com.devonfw.tools.ide.tool.node.Node;
Expand All @@ -56,6 +57,7 @@
import com.devonfw.tools.ide.tool.pycharm.Pycharm;
import com.devonfw.tools.ide.tool.python.Python;
import com.devonfw.tools.ide.tool.quarkus.Quarkus;
import com.devonfw.tools.ide.tool.rust.Rust;
import com.devonfw.tools.ide.tool.sonar.Sonar;
import com.devonfw.tools.ide.tool.spring.Spring;
import com.devonfw.tools.ide.tool.squirrelsql.SquirrelSql;
Expand Down Expand Up @@ -127,12 +129,14 @@ public CommandletManagerImpl(IdeContext context) {
add(new Node(context));
add(new Npm(context));
add(new Mvn(context));
add(new Msvc(context));
add(new GcViewer(context));
add(new Gradle(context));
add(new Eclipse(context));
add(new Terraform(context));
add(new Oc(context));
add(new Quarkus(context));
add(new Rust(context));
add(new Kotlinc(context));
add(new KotlincNative(context));
add(new KubeCtl(context));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,8 @@ public ToolInstallation installTool(ToolInstallRequest request) {
}

/**
* Performs the actual installation of the {@link #getName() tool} by downloading its binary, optionally extracting it, backing up any existing installation,
* and writing the version file.
* Performs the installation of the {@link #getName() tool} by using {@link #installDownloadedToolPayload(ToolInstallRequest, Path, Path)}
* for tool-specific logic, backing up any existing installation, and writing the version file.
* <p>
* This method assumes that the version has already been resolved and dependencies installed. It handles the final steps of placing the tool into the
* appropriate installation directory.
Expand All @@ -238,10 +238,7 @@ protected void performToolInstallation(ToolInstallRequest request, Path installa
ToolEditionAndVersion requested = request.getRequested();
VersionIdentifier resolvedVersion = requested.getResolvedVersion();
Path downloadedToolFile = downloadTool(requested.getEdition().edition(), resolvedVersion);
boolean extract = isExtract();
if (!extract) {
LOG.trace("Extraction is disabled for '{}' hence just moving the downloaded file {}.", this.tool, downloadedToolFile);
}

if (Files.isDirectory(installationPath)) {
if (this.tool.equals(IdeasyCommandlet.TOOL_NAME)) {
LOG.warn("Your IDEasy installation is missing the version file.");
Expand All @@ -250,13 +247,31 @@ protected void performToolInstallation(ToolInstallRequest request, Path installa
}
}
fileAccess.mkdirs(installationPath.getParent());
fileAccess.extract(downloadedToolFile, installationPath, this::postExtract, extract);

installDownloadedToolPayload(request, installationPath, downloadedToolFile);

this.context.writeVersionFile(resolvedVersion, installationPath);
// fix macOS Gatekeeper blocking - must run after version file is written but before any executables are launched
getMacOsHelper().removeQuarantineAttribute(installationPath);
LOG.debug("Installed {} in version {} at {}", this.tool, resolvedVersion, installationPath);
}

/**
* Performs the actual installation of the tool bits.
*
* @param request the {@link ToolInstallRequest}.
* @param installationPath the target {@link Path} where the tool should be installed.
* @param downloadedToolFile the {@link Path} to the downloaded tool file.
*/
protected void installDownloadedToolPayload(ToolInstallRequest request, Path installationPath, Path downloadedToolFile) {
Comment thread
hohwille marked this conversation as resolved.

boolean extract = isExtract();
if (!extract) {
LOG.trace("Extraction is disabled for '{}' hence just moving the downloaded file {}.", this.tool, downloadedToolFile);
}
this.context.getFileAccess().extract(downloadedToolFile, installationPath, this::postExtract, extract);
}

/**
* @param edition the {@link #getConfiguredEdition() tool edition} to download.
* @param resolvedVersion the resolved {@link VersionIdentifier version} to download.
Expand Down
52 changes: 52 additions & 0 deletions cli/src/main/java/com/devonfw/tools/ide/tool/msvc/Msvc.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.devonfw.tools.ide.tool.msvc;

import java.nio.file.Path;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.devonfw.tools.ide.common.Tag;
import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.process.ProcessErrorHandling;
import com.devonfw.tools.ide.tool.LocalToolCommandlet;
import com.devonfw.tools.ide.tool.ToolInstallRequest;
import com.devonfw.tools.ide.tool.ToolInstallation;

public class Msvc extends LocalToolCommandlet {

private static final Logger LOG = LoggerFactory.getLogger(Msvc.class);

public Msvc(IdeContext context) {
super(context, "msvc", Set.of(Tag.BUILD, Tag.CPP));
}

@Override
protected boolean isExtract() {
return false;
}

@Override
public ToolInstallation installTool(ToolInstallRequest request) {

if (this.context.getSystemInfo().isWindows()) {
return super.installTool(request);
}
LOG.trace("Skipping msvc installation that is only available on Windows.");
return null;
}

@Override
protected void installDownloadedToolPayload(ToolInstallRequest request, Path installationPath, Path installer) {

this.context.getFileAccess().mkdirs(installationPath);

this.context.newProcess().errorHandling(ProcessErrorHandling.THROW_CLI)
.executable(installer)
.withExitCodeAcceptor(code -> (code == 0) || (code == 3010))
.addArgs("--installPath", installationPath.toString(),
"--add", "Microsoft.VisualStudio.Workload.VCTools",
"--quiet", "--wait", "--norestart", "--nocache")
.run();
}
}
97 changes: 97 additions & 0 deletions cli/src/main/java/com/devonfw/tools/ide/tool/rust/Rust.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.devonfw.tools.ide.tool.rust;

import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;

import com.devonfw.tools.ide.common.Tag;
import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.io.FileAccess;
import com.devonfw.tools.ide.process.EnvironmentContext;
import com.devonfw.tools.ide.process.ProcessContext;
import com.devonfw.tools.ide.process.ProcessErrorHandling;
import com.devonfw.tools.ide.tool.LocalToolCommandlet;
import com.devonfw.tools.ide.tool.ToolInstallRequest;
import com.devonfw.tools.ide.tool.ToolInstallation;
import com.devonfw.tools.ide.version.VersionIdentifier;

/**
* {@link LocalToolCommandlet} for <a href="https://www.rust-lang.org/">Rust</a>.
*/
public class Rust extends LocalToolCommandlet {

/**
* The constructor.
*
* @param context the {@link IdeContext}.
*/
public Rust(IdeContext context) {

super(context, "rust", Set.of(Tag.RUST));
}

@Override
public String getBinaryName() {

return "rustc";
}

@Override
public String getToolHelpArguments() {

return "--help";
}

@Override
protected boolean isExtract() {

// The rustup installer script is an executable script and must not be extracted.
return false;
}

@Override
protected void installDownloadedToolPayload(ToolInstallRequest request, Path installationPath, Path installerScript) {

VersionIdentifier resolvedVersion = request.getRequested().getResolvedVersion();
FileAccess fileAccess = this.context.getFileAccess();

Path cargoHome = installationPath.resolve(".cargo");
Path rustupHome = installationPath.resolve(".rustup");
fileAccess.mkdirs(cargoHome);
fileAccess.mkdirs(rustupHome);

if (Files.isDirectory(installerScript)) {
// ToolRepositoryMock may provide an unpacked folder instead of a single download file.
installerScript = installerScript.resolve("content.sh");
}

ProcessContext process = request.getProcessContext().createChild().errorHandling(ProcessErrorHandling.THROW_CLI).directory(installationPath)
.withEnvVar("CARGO_HOME", cargoHome.toString()).withEnvVar("RUSTUP_HOME", rustupHome.toString());

List<String> installerArgs = List.of("-y", "--no-modify-path", "--profile", "default", "--default-toolchain", resolvedVersion.toString());

process.executable(this.context.findBashRequired()).addArgs(installerScript.toAbsolutePath().toString()).addArgs(installerArgs);
process.run();

Path cargoBin = cargoHome.resolve("bin");
Path toolBin = installationPath.resolve("bin");
if (Files.exists(toolBin, LinkOption.NOFOLLOW_LINKS)) {
fileAccess.delete(toolBin);
}
if (Files.isDirectory(cargoBin)) {
fileAccess.symlink(cargoBin, toolBin);
}
}

@Override
public void setEnvironment(EnvironmentContext environmentContext, ToolInstallation toolInstallation, boolean additionalInstallation) {

super.setEnvironment(environmentContext, toolInstallation, additionalInstallation);
Path rootDir = toolInstallation.rootDir();
environmentContext.withEnvVar("CARGO_HOME", rootDir.resolve(".cargo").toString());
environmentContext.withEnvVar("RUSTUP_HOME", rootDir.resolve(".rustup").toString());
}

}
4 changes: 4 additions & 0 deletions cli/src/main/resources/nls/Help.properties
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ cmd.list-versions=List the available versions of the selected tool.
cmd.list-versions.detail=To list all available versions of e.g. 'intellij' simply type: 'ide list-versions intellij'.
cmd.ln=Create a symbolic or hard link.
cmd.ln.detail=Creates a link similar to the command "ln" but working cross-platform (unlike in git-bash on Windows that typically only creates a copy).
cmd.msvc=Tool commandlet for MSVC (Microsoft Visual C++ Build Tools).
cmd.msvc.detail=MSVC provides the C++ compiler and build tools required by Rust on Windows. Detailed documentation can be found at https://visualstudio.microsoft.com/visual-cpp-build-tools/
cmd.mvn=Tool commandlet for Maven (Build-Tool).
cmd.mvn.detail=Apache Maven is a build automation and dependency management tool for Java projects. Detailed documentation can be found at https://maven.apache.org/guides/index.html
cmd.nest=Tool commandlet for Nest CLI.
Expand All @@ -112,6 +114,8 @@ cmd.quarkus.detail=Quarkus is a Kubernetes-native Java framework for building cl
cmd.repository=Set up pre-configured git repositories using 'ide repository setup <repository>'
cmd.repository.detail=Without further arguments this will set up all pre-configured git repositories.\nAlso, you can provide an explicit git repo as `<repository>` argument and IDEasy will automatically clone, build and set up your project based on the existing property file.\nRepositories are configured in 'settings/repository/<repository>.properties' and can therefore be shared with your project team for automatic or optional setup.
cmd.repository.val.repository=The name of the properties file of the pre-configured git repository to set up, omit to set up all active repositories.
cmd.rust=Tool commandlet for Rust (programming language).
cmd.rust.detail=Rust is a programming-language focused on safety and performance. Detailed documentation can be found at https://www.rust-lang.org/learn
cmd.set-edition=Set the edition of the selected tool.
cmd.set-edition.detail=This will set the according tool edition variable in your configuration file. If you want to roll out such change and share it with your team, you can commit and push your settings git repository.\nBy default these changes are saved in the project specific settings. Use --conf --home or --workspace to specify otherwise.
cmd.set-edition.opt.--cfg=Selection of the configuration file (settings | home | conf | workspace).
Expand Down
4 changes: 4 additions & 0 deletions cli/src/main/resources/nls/Help_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ cmd.list-versions=Listet die verfügbaren Versionen des selektierten Werkzeugs a
cmd.list-versions.detail=Um alle verfügbaren Versionen von z.B. 'intellij' aufzulisten, geben Sie einfach 'ide list-versions intellij' in die Konsole ein.
cmd.ln=Erstellt einen symbolischen oder harten Link
cmd.ln.detail=Erstellt einen Link wie das Kommando "ln" jedoch plattform-übergreifend (anders als in Git-Bash unter Windows wo typischerweise nur eine Kopie erstellt wird).
cmd.msvc=Werkzeug Kommando für MSVC (Microsoft Visual C++ Build-Werkzeuge).
cmd.msvc.detail=MSVC stellt den C++ Compiler und Build-Werkzeuge bereit, die von Rust unter Windows benötigt werden. Detaillierte Dokumentation ist zu finden unter https://visualstudio.microsoft.com/visual-cpp-build-tools/
cmd.mvn=Werkzeug Kommando für Maven (Build-Werkzeug).
cmd.mvn.detail=Apache Maven ist ein Build-Automatisierungs- und Abhängigkeitsverwaltungstool für Java-Projekte. Detaillierte Dokumentation ist zu finden unter https://maven.apache.org/guides/index.html
cmd.nest=Werkzeug Kommando für Nest CLI.
Expand All @@ -112,6 +114,8 @@ cmd.quarkus.detail=Quarkus ist ein Kubernetes-native Java-Framework zur Entwickl
cmd.repository=Richtet das vorkonfigurierte Git Repository ein mittels 'ide repository setup <repository>'.
cmd.repository.detail=Dies wird alle vorkonfigurierten Repositories einrichten. Rufen Sie einfach 'ide repository setup <your_project_name>' auf, ersetzen Sie <your_project_name> durch den Namen Ihrer Projektkonfigurationsdatei, die sich in 'settings/repository/your_project_name' befindet und IDEasy wird Ihr Projekt basierend auf der vorhandenen Eigenschaftsdatei automatisch klonen, bauen und einrichten.\nWenn Sie den Projektnamen weglassen, werden alle im Repository-Verzeichnis gefundenen Projekte vorkonfiguriert.
cmd.repository.val.repository=Der Name der Properties-Datei des vorkonfigurierten Git Repositories zum Einrichten. Falls nicht angegeben, werden alle aktiven Projekte eingerichtet.
cmd.rust=Werkzeug Kommando für Rust (Programmiersprache).
cmd.rust.detail=Rust ist eine Programmiersprache mit Fokus auf Sicherheit und Performance. Detaillierte Dokumentation findet sich unter https://www.rust-lang.org/learn
cmd.set-edition=Setzt die Edition des selektierten Werkzeugs.
cmd.set-edition.detail=Dies setzt die entsprechende Werkzeug-Edition Variable in der Konfigurationsdatei. Um solche Anpassungen auszurollen und mit dem Team zu teilen, kann diese im Settings git repository committed und gepushed werden.\nDiese Änderungen werden in der projektspezifischen Konfiguration gespeichert, es sei denn es wird mit --conf --home oder --workspace einen anderer Benutzer- oder Workspace-spezifischer Speicherort definiert.
cmd.set-edition.opt.--cfg=Auswahl der Konfigurationsdatei (settings | home | conf | workspace).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void testIdeCompleterBatch() {
void testIdeCompleterInstall() {

this.reader.setCompleter(newCompleter());
assertBuffer("install mvn ", new TestBuffer("install m").tab());
assertBuffer("install mvn ", new TestBuffer("install mv").tab());
Comment thread
laert-ll marked this conversation as resolved.
}

/**
Expand All @@ -59,7 +59,7 @@ void testIdeCompleterInstall() {
void testIdeCompleterHelpWithToolCompletion() {

this.reader.setCompleter(newCompleter());
assertBuffer("help mvn ", new TestBuffer("help m").tab().tab());
assertBuffer("help mvn ", new TestBuffer("help mv").tab().tab());
}

/**
Expand Down
52 changes: 52 additions & 0 deletions cli/src/test/java/com/devonfw/tools/ide/tool/rust/RustTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.devonfw.tools.ide.tool.rust;

import java.nio.file.Path;

import org.junit.jupiter.api.Test;

import com.devonfw.tools.ide.context.AbstractIdeContextTest;
import com.devonfw.tools.ide.context.IdeTestContext;

/**
* Test of {@link Rust}.
*/
class RustTest extends AbstractIdeContextTest {

private static final String PROJECT_RUST = "rust";

private static final String RUST_VERSION = "1.80.1";

@Test
void testRustInstallViaRustupScript() {

// arrange
IdeTestContext context = newContext(PROJECT_RUST);
Rust rust = new Rust(context);

// act
rust.install();

// assert
assertThat(context.getSoftwarePath().resolve("rust/.ide.software.version")).exists().hasContent(RUST_VERSION);
assertThat(context).logAtSuccess().hasMessageContaining("Successfully installed rust in version " + RUST_VERSION);
}

@Test
void testRustInstallProducesCargoLayoutAndBinLink() {

// arrange
IdeTestContext context = newContext(PROJECT_RUST);
Rust rust = new Rust(context);
String rustcName = context.getSystemInfo().isWindows() ? "rustc.cmd" : "rustc";

// act
rust.install();

// assert
Path softwareRust = context.getSoftwarePath().resolve("rust");
assertThat(softwareRust.resolve(".cargo")).exists();
assertThat(softwareRust.resolve(".rustup")).exists();
assertThat(softwareRust.resolve(".cargo/bin").resolve(rustcName)).exists();
assertThat(softwareRust.resolve("bin").resolve(rustcName)).exists();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
https://sh.rustup.rs

Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -eu

mkdir -p "${CARGO_HOME}/bin"
mkdir -p "${RUSTUP_HOME}"

cat > "${CARGO_HOME}/bin/rustc" <<'EOF'
#!/usr/bin/env bash
echo rustc "$@"
EOF
chmod +x "${CARGO_HOME}/bin/rustc"

cat > "${CARGO_HOME}/bin/rustc.cmd" <<'EOF'
@echo off
echo rustc %*
EOF

2 changes: 2 additions & 0 deletions documentation/LICENSE.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ The column `inclusion` indicates the way the component is included:
|https://docs.aws.amazon.com/cdk/v2/guide/home.html[AWS CDK CLI] |Optional|https://github.com/aws/aws-cdk-cli/blob/main/LICENSE[Apache 2.0]
|https://taskfile.dev/[Task CLI] |Optional|https://github.com/go-task/task/blob/main/LICENSE[MIT License]
|https://developer.konghq.com/inso-cli |Optional|https://github.com/Kong/insomnia/blob/develop/LICENSE[Apache 2.0]
|https://www.rust-lang.org/[Rust] |Optional|https://www.rust-lang.org/policies/licenses[MIT and Apache 2.0]
|https://visualstudio.microsoft.com/visual-cpp-build-tools/[MSVC Build Tools] |Optional|https://visualstudio.microsoft.com/license-terms/vs2022-ga-community/[Microsoft Software License Terms]
|===

== Apache Software License - Version 2.0
Expand Down
Loading