Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#556: update PATH on pc run #614

Merged
merged 11 commits into from
Sep 19, 2024
37 changes: 29 additions & 8 deletions cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
Expand Down Expand Up @@ -39,6 +40,8 @@ public class SystemPath {

private final List<Path> paths;

private final List<Path> extraPathEntries;

private final IdeContext context;

private static final List<String> EXTENSION_PRIORITY = List.of(".exe", ".cmd", ".bat", ".msi", ".ps1", "");
Expand Down Expand Up @@ -74,7 +77,7 @@ public SystemPath(IdeContext context, String envPath) {
*/
public SystemPath(IdeContext context, String envPath, Path ideRoot, Path softwarePath) {

this(context, envPath, ideRoot, softwarePath, File.pathSeparatorChar);
this(context, envPath, ideRoot, softwarePath, File.pathSeparatorChar, Collections.emptyList());
jan-vcapgemini marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand All @@ -86,14 +89,9 @@ public SystemPath(IdeContext context, String envPath, Path ideRoot, Path softwar
* @param softwarePath the {@link IdeContext#getSoftwarePath() software path}.
* @param pathSeparator the path separator char (';' for Windows and ':' otherwise).
*/
public SystemPath(IdeContext context, String envPath, Path ideRoot, Path softwarePath, char pathSeparator) {
jan-vcapgemini marked this conversation as resolved.
Show resolved Hide resolved
public SystemPath(IdeContext context, String envPath, Path ideRoot, Path softwarePath, char pathSeparator, List<Path> extraPathEntries) {

super();
this.context = context;
this.envPath = envPath;
this.pathSeparator = pathSeparator;
this.tool2pathMap = new HashMap<>();
this.paths = new ArrayList<>();
this(context, envPath, pathSeparator, extraPathEntries, new HashMap<>(), new ArrayList<>());
String[] envPaths = envPath.split(Character.toString(pathSeparator));
for (String segment : envPaths) {
Path path = Path.of(segment);
Expand All @@ -105,6 +103,17 @@ public SystemPath(IdeContext context, String envPath, Path ideRoot, Path softwar
collectToolPath(softwarePath);
}

private SystemPath(IdeContext context, String envPath, char pathSeparator, List<Path> extraPathEntries, Map<String, Path> tool2PathMap, List<Path> paths) {

super();
this.context = context;
this.envPath = envPath;
this.pathSeparator = pathSeparator;
this.extraPathEntries = extraPathEntries;
this.tool2pathMap = tool2PathMap;
this.paths = paths;
}

private void collectToolPath(Path softwarePath) {

if (softwarePath == null) {
Expand Down Expand Up @@ -237,6 +246,9 @@ public String toString(WindowsPathSyntax pathSyntax) {
separator = this.pathSeparator;
}
StringBuilder sb = new StringBuilder(this.envPath.length() + 128);
for (Path path : this.extraPathEntries) {
appendPath(path, sb, separator, pathSyntax);
}
for (Path path : this.tool2pathMap.values()) {
appendPath(path, sb, separator, pathSyntax);
}
Expand All @@ -246,6 +258,15 @@ public String toString(WindowsPathSyntax pathSyntax) {
return sb.toString();
}

public SystemPath withPath(String overriddenPath, List<Path> extraPathEntries) {
jan-vcapgemini marked this conversation as resolved.
Show resolved Hide resolved

if (overriddenPath == null) {
return new SystemPath(this.context, this.envPath, this.pathSeparator, extraPathEntries, this.tool2pathMap, this.paths);
} else {
return new SystemPath(this.context, overriddenPath, null, null, this.pathSeparator, extraPathEntries);
}
}

private static void appendPath(Path path, StringBuilder sb, char separator, WindowsPathSyntax pathSyntax) {

if (sb.length() > 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
package com.devonfw.tools.ide.process;

import java.nio.file.Path;

/**
* Interface for the environment context relatable in the case a tool needs to be run with an environment variable.
*/
public interface EnvironmentContext {

/**
* Sets or overrides the specified environment variable only for the planned process execution. Please note that the environment variables are initialized
* when the {@link EnvironmentContext} is created. This method explicitly set an additional or overrides an existing environment and will have effect for each
* process execution invoked from this {@link EnvironmentContext} instance.
* Sets or overrides the specified environment variable only in this context. Please note that the environment variables are initialized when the
* {@link EnvironmentContext} is created. This method explicitly set an additional or overrides an existing environment and will have effect only within this
* context and not change the {@link com.devonfw.tools.ide.environment.EnvironmentVariables} or {@link com.devonfw.tools.ide.common.SystemPath}.
*
* @param key the name of the environment variable (E.g. "PATH").
* @param value the value of the environment variable.
* @return this {@link EnvironmentContext} for fluent API calls.
*/
EnvironmentContext withEnvVar(String key, String value);

/**
* Extends the "PATH" variable with the given {@link Path} entry. The new entry will be added to the beginning of the "PATH" and potentially override other
* entries if it contains the same binary.
*
* @param path the {@link Path} pointing to the folder with the binaries to add to the "PATH" variable.
* @return this {@link EnvironmentContext} for fluent API calls.
*/
EnvironmentContext withPathEntry(Path path);

}
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,12 @@ default ProcessContext addArgs(List<?>... args) {
return this;
}

/**
* Sets or overrides the specified environment variable only for the planned {@link #run() process execution}. Please note that the environment variables are
* initialized when the {@link ProcessContext} is created. This method explicitly set an additional or overrides an existing environment and will have effect
* for each {@link #run() process execution} invoked from this {@link ProcessContext} instance. Be aware of such side-effects when reusing the same
* {@link ProcessContext} to {@link #run() run} multiple commands.
*
* @param key the name of the environment variable (E.g. "PATH").
* @param value the value of the environment variable.
* @return this {@link ProcessContext} for fluent API calls.
*/
@Override
ProcessContext withEnvVar(String key, String value);

@Override
ProcessContext withPathEntry(Path path);

/**
* Runs the previously configured {@link #executable(Path) command} with the configured {@link #addArgs(String...) arguments}. Will reset the
* {@link #addArgs(String...) arguments} but not the {@link #executable(Path) command} for sub-sequent calls.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.devonfw.tools.ide.os.SystemInfoImpl;
import com.devonfw.tools.ide.os.WindowsPathSyntax;
import com.devonfw.tools.ide.util.FilenameUtil;
import com.devonfw.tools.ide.variable.IdeVariables;

/**
* Implementation of {@link ProcessContext}.
Expand All @@ -40,6 +41,10 @@ public class ProcessContextImpl implements ProcessContext {

private Path executable;

private String overriddenPath;

private final List<Path> extraPathEntries;

private ProcessErrorHandling errorHandling;

/**
Expand All @@ -60,6 +65,7 @@ public ProcessContextImpl(IdeContext context) {
}
}
this.arguments = new ArrayList<>();
this.extraPathEntries = new ArrayList<>();
}

@Override
Expand Down Expand Up @@ -90,7 +96,7 @@ public ProcessContext executable(Path command) {
throw new IllegalStateException("Arguments already present - did you forget to call run for previous call?");
}

this.executable = this.context.getPath().findBinary(command);
this.executable = command;
return this;
}

Expand All @@ -104,14 +110,25 @@ public ProcessContext addArg(String arg) {
@Override
public ProcessContext withEnvVar(String key, String value) {

this.processBuilder.environment().put(key, value);
if (IdeVariables.PATH.getName().equals(key)) {
this.overriddenPath = value;
} else {
this.context.trace("Setting process environment variable {}={}", key, value);
this.processBuilder.environment().put(key, value);
}
return this;
}

@Override
public ProcessContext withPathEntry(Path path) {

this.extraPathEntries.add(path);
return this;
}

@Override
public ProcessResult run(ProcessMode processMode) {

// TODO ProcessMode needs to be configurable for GUI
if (processMode == ProcessMode.DEFAULT) {
this.processBuilder.redirectOutput(Redirect.INHERIT).redirectError(Redirect.INHERIT);
}
Expand All @@ -123,6 +140,15 @@ public ProcessResult run(ProcessMode processMode) {
if (this.executable == null) {
throw new IllegalStateException("Missing executable to run process!");
}

SystemPath systemPath = this.context.getPath();
if ((this.overriddenPath != null) || !this.extraPathEntries.isEmpty()) {
systemPath = systemPath.withPath(this.overriddenPath, this.extraPathEntries);
}
String path = systemPath.toString();
this.context.trace("Setting PATH for process execution of {} to {}", this.executable.getFileName(), path);
this.executable = systemPath.findBinary(this.executable);
this.processBuilder.environment().put(IdeVariables.PATH.getName(), path);
List<String> args = new ArrayList<>(this.arguments.size() + 4);
String interpreter = addExecutable(this.executable.toString(), args);
args.addAll(this.arguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,12 @@ public void runTool(ProcessMode processMode, VersionIdentifier toolVersion, bool

Path binaryPath;
binaryPath = Path.of(getBinaryName());
ProcessContext pc;
ProcessContext pc = createProcessContext(binaryPath, args);

if (existsEnvironmentContext) {
pc = createProcessContext(binaryPath, args);
install(pc, true);
} else {
install(true);
pc = createProcessContext(binaryPath, args);
}

pc.run(processMode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ public ProcessContext withEnvVar(String key, String value) {
return this;
}

@Override
public ProcessContext withPathEntry(Path path) {

return this;
}

@Override
public ProcessResult run(ProcessMode processMode) {

Expand Down
Loading