/
WindowsEnvVarUpdater.scala
123 lines (99 loc) · 4.33 KB
/
WindowsEnvVarUpdater.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package coursier.env
import dataclass.data
@data class WindowsEnvVarUpdater(
powershellRunner: PowershellRunner = PowershellRunner(),
target: String = "User",
useJni: Option[Boolean] = None
) extends EnvVarUpdater {
private lazy val useJni0 = useJni.getOrElse {
// FIXME Should be coursier.paths.Util.useJni(), but it's not available from here.
!System.getProperty("coursier.jni", "").equalsIgnoreCase("false")
}
// https://stackoverflow.com/questions/9546324/adding-directory-to-path-environment-variable-in-windows/29109007#29109007
// https://docs.microsoft.com/fr-fr/dotnet/api/system.environment.getenvironmentvariable?view=netframework-4.8#System_Environment_GetEnvironmentVariable_System_String_System_EnvironmentVariableTarget_
// https://docs.microsoft.com/fr-fr/dotnet/api/system.environment.setenvironmentvariable?view=netframework-4.8#System_Environment_SetEnvironmentVariable_System_String_System_String_System_EnvironmentVariableTarget_
private def getEnvironmentVariable(name: String): Option[String] =
if (useJni0)
Option(coursier.jniutils.WindowsEnvironmentVariables.get(name))
else {
val output = powershellRunner.runScript(WindowsEnvVarUpdater.getEnvVarScript(name)).stripSuffix(System.lineSeparator())
if (output == "null") // if ever the actual value is "null", we'll miss it
None
else
Some(output)
}
private def setEnvironmentVariable(name: String, value: String): Unit =
if (useJni0)
coursier.jniutils.WindowsEnvironmentVariables.set(name, value)
else
powershellRunner.runScript(WindowsEnvVarUpdater.setEnvVarScript(name, value))
private def clearEnvironmentVariable(name: String): Unit =
if (useJni0)
coursier.jniutils.WindowsEnvironmentVariables.delete(name)
else
powershellRunner.runScript(WindowsEnvVarUpdater.clearEnvVarScript(name))
def applyUpdate(update: EnvironmentUpdate): Boolean = {
// Beware, these are not an atomic operation overall
// (we might discard values added by others between our get and our set)
var setSomething = false
for ((k, v) <- update.set) {
val formerValueOpt = getEnvironmentVariable(k)
val needsUpdate = formerValueOpt.forall(_ != v)
if (needsUpdate) {
setEnvironmentVariable(k, v)
setSomething = true
}
}
for ((k, v) <- update.pathLikeAppends) {
val formerValueOpt = getEnvironmentVariable(k)
val alreadyInList = formerValueOpt.exists(_.split(WindowsEnvVarUpdater.windowsPathSeparator).contains(v))
if (!alreadyInList) {
val newValue = formerValueOpt.fold(v)(_ + WindowsEnvVarUpdater.windowsPathSeparator + v)
setEnvironmentVariable(k, newValue)
setSomething = true
}
}
setSomething
}
def tryRevertUpdate(update: EnvironmentUpdate): Boolean = {
// Beware, these are not an atomic operation overall
// (we might discard values added by others between our get and our set)
var setSomething = false
for ((k, v) <- update.set) {
val formerValueOpt = getEnvironmentVariable(k)
val wasUpdated = formerValueOpt.exists(_ == v)
if (wasUpdated) {
clearEnvironmentVariable(k)
setSomething = true
}
}
for ((k, v) <- update.pathLikeAppends; formerValue <- getEnvironmentVariable(k)) {
val parts = formerValue.split(WindowsEnvVarUpdater.windowsPathSeparator)
val isInList = parts.contains(v)
if (isInList) {
val newValue = parts.filter(_ != v)
if (newValue.isEmpty)
clearEnvironmentVariable(k)
else
setEnvironmentVariable(k, newValue.mkString(WindowsEnvVarUpdater.windowsPathSeparator))
setSomething = true
}
}
setSomething
}
}
object WindowsEnvVarUpdater {
private def getEnvVarScript(name: String): String =
s"""[Environment]::GetEnvironmentVariable("$name", "User")
|""".stripMargin
private def setEnvVarScript(name: String, value: String): String =
// FIXME value might need some escaping here
s"""[Environment]::SetEnvironmentVariable("$name", "$value", "User")
|""".stripMargin
private def clearEnvVarScript(name: String): String =
// FIXME value might need some escaping here
s"""[Environment]::SetEnvironmentVariable("$name", $$null, "User")
|""".stripMargin
private def windowsPathSeparator: String =
";"
}