@@ -15,82 +15,32 @@ pub(super) static ALTERNATIVE_LOCATIONS: Lazy<Vec<PathBuf>> = Lazy::new(|| {
15
15
#[ cfg( not( windows) ) ]
16
16
pub ( super ) static ALTERNATIVE_LOCATIONS : Lazy < Vec < PathBuf > > = Lazy :: new ( || vec ! [ ] ) ;
17
17
18
- #[ cfg( any( windows, test) ) ] // So this can always be tested.
19
- fn alternative_locations_from_env < F > ( var_os_func : F ) -> Vec < PathBuf >
18
+ #[ cfg( any( windows, test) ) ]
19
+ fn alternative_windows_locations_from_environment < F > ( var_os_func : F ) -> Vec < PathBuf >
20
20
where
21
21
F : Fn ( & str ) -> Option < std:: ffi:: OsString > ,
22
22
{
23
- // I am not really sure what this should do to handle the ProgramFiles environment variable and
24
- // figure out if it is 64-bit or 32-bit -- which we need in order to know whether to append
25
- // `Git/mingw64/bin` or `Git/mingw32/bin`. We do need to look at the ProgramFiles environment
26
- // variable in addition to the other two preferable architecture-specific ones (for which the
27
- // mingw64 vs. mingw32 decision is easy), at least some of the time, for two reasons.
28
- //
29
- // First, we may be on a 32-bit system. Then we do not have either of the other two variables.
30
- //
31
- // Second, the others may also absent on a 64-bit system, if a parent process whittles down the
32
- // variables to pass to this child process, out of an erroneous belief about which ones are
33
- // needed to provide access to the program files directory.
34
- //
35
- // On a 64-bit system, the way a child process inherits its ProgramFiles variable to be for its
36
- // own architecture -- since after all its parent's architecture could be different -- is:
37
- //
38
- // - The value of a 64-bit child's ProgramFiles variable comes from whatever the parent passed
39
- // down as ProgramW6432, if that variable was passed down.
40
- //
41
- // - The value of a 32-bit child's ProgramFiles variable comes from whatever the parent passed
42
- // down as ProgramFiles(x86), if that variable was passed down.
43
- //
44
- // - If the variable corresponding to the child's architecture was not passed down by the
45
- // parent, but ProgramFiles was passed down, then the child receives that as the value of
46
- // its ProgramFiles variable. Only in this case does ProgramFiles come from ProgramFiles.
47
- //
48
- // The problem -- besides that those rules are not well known, and parent processes that seek
49
- // to pass down minimal environments often do not take heed of them, such that a child process
50
- // will get the wrong architecture's value for its ProgramFiles environment variable -- is that
51
- // the point of this function is to make use of environment variables in order to avoid:
52
- //
53
- // - Calling Windows API functions explicitly, even via the higher level `windows` crate.
54
- // - Accessing the Windows registry, even through the very widely used `winreg` crate.
55
- // - Adding any significant new production dependencies, such as the `known-folders` crate.
56
- //
57
- // Possible solutions:
58
- //
59
- // 1. Abandon at least one of those goals.
60
- // 2. Check both `mingw*` subdirectories regardless of which program files Git is in.
61
- // 3. Inspect the names for substrings like ` (x86)` in an expected position.
62
- // 4. Assume the value of `ProgramFiles` is correct, i.e., is for the process's architecture.
63
- //
64
- // I think (4) is the way to go, at least until (1) is assessed. With (2), we would be checking
65
- // paths that there is no specific reason to think have *working* Git executables...though this
66
- // does have the advantage that its logic would be the same as would be needed in the local
67
- // program files directory (usually `%LocalAppData$/Programs`) if we ever add that. With (3),
68
- // the risk of getting it wrong is low, but the logic is more complex, and we lose the
69
- // simplicity of getting the paths from outside rather than applying assumptions about them.
70
- //
71
- // With (4), we take the ProgramFiles environment variable at its word. This is good not just
72
- // for abstract correctness, but also if the parent modifies these variables intentionally on a
73
- // 64-bit system. A parent process can't reasonably expect this to be followed, because a child
74
- // process may use another mechanism such as known folders. However, following it, when we are
75
- // using environment variables already, satisfies a weaker expectation that the environment
76
- // value *or* actual value (obtainable via known folders or the registry), rather than some
77
- // third value, is used. (4) is also a simple way to do the right thing on a 32-bit system.
78
-
79
23
// Should give a 64-bit program files path from a 32-bit or 64-bit process on a 64-bit system.
80
24
let varname_64bit = "ProgramW6432" ;
81
25
82
26
// Should give a 32-bit program files path from a 32-bit or 64-bit process on a 64-bit system.
83
27
// This variable is x86-specific, but neither Git nor Rust target 32-bit ARM on Windows.
84
28
let varname_x86 = "ProgramFiles(x86)" ;
85
29
86
- // Should give a 32-bit program files path on a 32-bit system. We also need to check it on a
87
- // 64-bit system. [FIXME: Somehow briefly explain why we still need to do that.]
30
+ // Should give a 32-bit program files path on a 32-bit system. We also check this on a 64-bit
31
+ // system, even though it *should* equal the process architecture specific variable, so that we
32
+ // cover the case of a parent process that passes down an overly sanitized environment that
33
+ // lacks the architecture-specific variable. On a 64-bit system, because parent and child
34
+ // processes' architectures can be different, Windows sets the child's ProgramFiles variable
35
+ // from the ProgramW6432 or ProgramFiles(x86) variable applicable to the child's architecture.
36
+ // Only if the parent does not pass that down is the passed-down ProgramFiles variable even
37
+ // used. But this behavior is not well known, so that situation does sometimes happen.
88
38
let varname_current = "ProgramFiles" ;
89
39
90
40
// 64-bit relative bin dir. So far, this is always mingw64, not ucrt64, clang64, or clangarm64.
91
41
let suffix_64 = Path :: new ( "bin/mingw64" ) ;
92
42
93
- // 32-bit relative bin dir. This is always mingw32, not clang32.
43
+ // 32-bit relative bin dir. So far, this is always mingw32, not clang32.
94
44
let suffix_32 = Path :: new ( "bin/mingw32" ) ;
95
45
96
46
// Whichever of the 64-bit or 32-bit relative bin better matches this process's architecture.
0 commit comments