1
1
// Copyright (c) Microsoft Corporation.
2
2
// Licensed under the MIT License.
3
3
4
- use std:: { fs , path :: PathBuf } ;
4
+ use std:: fs ;
5
5
6
- use log:: error ;
6
+ use log:: warn ;
7
7
use pet_core:: {
8
8
python_environment:: { PythonEnvironment , PythonEnvironmentBuilder , PythonEnvironmentCategory } ,
9
9
reporter:: Reporter ,
@@ -44,34 +44,35 @@ impl Locator for LinuxGlobalPython {
44
44
return None ;
45
45
}
46
46
47
- if let ( Some ( prefix) , Some ( _) ) = ( env. prefix . clone ( ) , env. version . clone ( ) ) {
48
- let executable = env. executable . clone ( ) ;
49
-
50
- // If prefix or version is not available, then we cannot use this method.
51
- // 1. For files in /bin or /usr/bin, the prefix is always /usr
52
- // 2. For files in /usr/local/bin, the prefix is always /usr/local
53
- if !executable. starts_with ( "/bin" )
54
- && !executable. starts_with ( "/usr/bin" )
55
- && !executable. starts_with ( "/usr/local/bin" )
56
- && !prefix. starts_with ( "/usr" )
57
- && !prefix. starts_with ( "/usr/local" )
58
- {
59
- return None ;
60
- }
47
+ // If we do not have a version, then we cannot use this method.
48
+ // Without version means we have not spawned the Python exe, thus do not have the real info.
49
+ env. version . clone ( ) ?;
50
+ let prefix = env. prefix . clone ( ) ?;
51
+ let executable = env. executable . clone ( ) ;
52
+
53
+ // If prefix or version is not available, then we cannot use this method.
54
+ // 1. For files in /bin or /usr/bin, the prefix is always /usr
55
+ // 2. For files in /usr/local/bin, the prefix is always /usr/local
56
+ if !executable. starts_with ( "/bin" )
57
+ && !executable. starts_with ( "/usr/bin" )
58
+ && !executable. starts_with ( "/usr/local/bin" )
59
+ && !prefix. starts_with ( "/usr" )
60
+ && !prefix. starts_with ( "/usr/local" )
61
+ {
62
+ return None ;
63
+ }
61
64
62
- // All known global linux are always installed in `/bin` or `/usr/bin`
63
- if executable. starts_with ( "/bin" ) || executable. starts_with ( "/usr/bin" ) {
64
- get_python_in_usr_bin ( env)
65
- } else if executable. starts_with ( "/usr/local/bin" ) {
66
- get_python_in_usr_local_bin ( env)
67
- } else {
68
- error ! (
69
- "Invalid state, ex ({:?}) is not in any of /bin, /usr/bin, /usr/local/bin" ,
65
+ // All known global linux are always installed in `/bin` or `/usr/bin` or `/usr/local/bin`
66
+ if executable. starts_with ( "/bin" )
67
+ || executable. starts_with ( "/usr/bin" )
68
+ || executable. starts_with ( "/usr/local/bin" )
69
+ {
70
+ get_python_in_bin ( env)
71
+ } else {
72
+ warn ! (
73
+ "Unknown Python exe ({:?}), not in any of the known locations /bin, /usr/bin, /usr/local/bin" ,
70
74
executable
71
75
) ;
72
- None
73
- }
74
- } else {
75
76
None
76
77
}
77
78
}
@@ -84,7 +85,7 @@ impl Locator for LinuxGlobalPython {
84
85
}
85
86
}
86
87
87
- fn get_python_in_usr_bin ( env : & PythonEnv ) -> Option < PythonEnvironment > {
88
+ fn get_python_in_bin ( env : & PythonEnv ) -> Option < PythonEnvironment > {
88
89
// If we do not have the prefix, then do not try
89
90
// This method will be called with resolved Python where prefix & version is available.
90
91
if env. version . clone ( ) . is_none ( ) || env. prefix . clone ( ) . is_none ( ) {
@@ -94,86 +95,54 @@ fn get_python_in_usr_bin(env: &PythonEnv) -> Option<PythonEnvironment> {
94
95
let mut symlinks = env. symlinks . clone ( ) . unwrap_or_default ( ) ;
95
96
symlinks. push ( executable. clone ( ) ) ;
96
97
97
- let bin = PathBuf :: from ( "/bin" ) ;
98
- let usr_bin = PathBuf :: from ( "/usr/bin" ) ;
98
+ let bin = executable. parent ( ) ?;
99
+
100
+ // Keep track of what the exe resolves to.
101
+ // Will have a value only if the exe is in another dir
102
+ // E.g. /bin/python3 might be a symlink to /usr/bin/python3.12
103
+ // However due to legacy reasons we'll be treating these two as separate exes.
104
+ // Hence they will be separate Python environments.
105
+ let mut resolved_exe_is_from_another_dir = None ;
99
106
100
107
// Possible this exe is a symlink to another file in the same directory.
101
- // E.g. /usr/bin/python3 is a symlink to /usr/bin/python3.12
108
+ // E.g. Generally /usr/bin/python3 is a symlink to /usr/bin/python3.12
109
+ // E.g. Generally /usr/local/bin/python3 is a symlink to /usr/local/bin/python3.12
110
+ // E.g. Generally /bin/python3 is a symlink to /bin/python3.12
102
111
// let bin = executable.parent()?;
103
112
// We use canonicalize to get the real path of the symlink.
104
113
// Only used in this case, see notes for resolve_symlink.
105
114
if let Some ( symlink) = resolve_symlink ( & executable) . or ( fs:: canonicalize ( & executable) . ok ( ) ) {
106
115
// Ensure this is a symlink in the bin or usr/bin directory.
107
- if symlink. starts_with ( & bin) || symlink . starts_with ( & usr_bin ) {
116
+ if symlink. starts_with ( bin) {
108
117
symlinks. push ( symlink) ;
118
+ } else {
119
+ resolved_exe_is_from_another_dir = Some ( symlink) ;
109
120
}
110
121
}
111
122
112
- // Look for other symlinks in /usr/bin and /bin folder
113
- // https://stackoverflow.com/questions/68728225/what-is-the-difference-between-usr-bin-python3-and-bin-python3
114
- // We know that on linux there are symlinks in both places.
123
+ // Look for other symlinks in the same folder
124
+ // We know that on linux there are sym links in the same folder as the exe.
115
125
// & they all point to one exe and have the same version and same prefix.
116
- for possible_symlink in [ find_executables ( & bin) , find_executables ( & usr_bin ) ] . concat ( ) {
117
- if let Some ( symlink) =
118
- resolve_symlink ( & possible_symlink) . or ( fs:: canonicalize ( & possible_symlink) . ok ( ) )
126
+ for possible_symlink in find_executables ( bin) . iter ( ) {
127
+ if let Some ( ref symlink) =
128
+ resolve_symlink ( & possible_symlink) . or ( fs:: canonicalize ( possible_symlink) . ok ( ) )
119
129
{
120
- // the file /bin/python3 is a symlink to /usr/bin/python3.12
121
- // the file /bin/python3.12 is a symlink to /usr/bin/python3.12
122
- // the file /usr/bin/python3 is a symlink to /usr/bin/python3.12
123
- // Thus we have 3 symlinks pointing to the same exe /usr/bin/python3.12
124
- if symlinks. contains ( & symlink) {
125
- symlinks. push ( possible_symlink) ;
130
+ // Generally the file /bin/python3 is a symlink to /usr/bin/python3.12
131
+ // Generally the file /bin/python3.12 is a symlink to /usr/bin/python3.12
132
+ // Generally the file /usr/bin/python3 is a symlink to /usr/bin/python3.12
133
+ // HOWEVER, we will be treating the files in /bin and /usr/bin as different.
134
+ // Hence check whether the resolve symlink is in the same directory.
135
+ if symlink. starts_with ( bin) & symlinks. contains ( symlink) {
136
+ symlinks. push ( possible_symlink. to_owned ( ) ) ;
126
137
}
127
- }
128
- }
129
- symlinks. sort ( ) ;
130
- symlinks. dedup ( ) ;
131
-
132
- Some (
133
- PythonEnvironmentBuilder :: new ( PythonEnvironmentCategory :: LinuxGlobal )
134
- . executable ( Some ( executable) )
135
- . version ( env. version . clone ( ) )
136
- . prefix ( env. prefix . clone ( ) )
137
- . symlinks ( Some ( symlinks) )
138
- . build ( ) ,
139
- )
140
- }
141
-
142
- fn get_python_in_usr_local_bin ( env : & PythonEnv ) -> Option < PythonEnvironment > {
143
- // If we do not have the prefix, then do not try
144
- // This method will be called with resolved Python where prefix & version is available.
145
- if env. version . clone ( ) . is_none ( ) || env. prefix . clone ( ) . is_none ( ) {
146
- return None ;
147
- }
148
- let executable = env. executable . clone ( ) ;
149
- let mut symlinks = env. symlinks . clone ( ) . unwrap_or_default ( ) ;
150
- symlinks. push ( executable. clone ( ) ) ;
151
138
152
- let usr_local_bin = PathBuf :: from ( "/usr/local/bin" ) ;
153
-
154
- // Possible this exe is a symlink to another file in the same directory.
155
- // E.g. /usr/local/bin/python3 could be a symlink to /usr/local/bin/python3.12
156
- // let bin = executable.parent()?;
157
- // We use canonicalize to get the real path of the symlink.
158
- // Only used in this case, see notes for resolve_symlink.
159
- if let Some ( symlink) = resolve_symlink ( & executable) . or ( fs:: canonicalize ( & executable) . ok ( ) ) {
160
- // Ensure this is a symlink in the bin or usr/local/bin directory.
161
- if symlink. starts_with ( & usr_local_bin) {
162
- symlinks. push ( symlink) ;
163
- }
164
- }
165
-
166
- // Look for other symlinks in this same folder
167
- for possible_symlink in find_executables ( & usr_local_bin) {
168
- if let Some ( symlink) =
169
- resolve_symlink ( & possible_symlink) . or ( fs:: canonicalize ( & possible_symlink) . ok ( ) )
170
- {
171
- // the file /bin/python3 is a symlink to /usr/bin/python3.12
172
- // the file /bin/python3.12 is a symlink to /usr/bin/python3.12
173
- // the file /usr/bin/python3 is a symlink to /usr/bin/python3.12
174
- // Thus we have 3 symlinks pointing to the same exe /usr/bin/python3.12
175
- if symlinks. contains ( & symlink) {
176
- symlinks. push ( possible_symlink) ;
139
+ // Possible the env.executable = /bin/python3
140
+ // And the possible_symlink = /bin/python3.12
141
+ // & possible that both of the above are symlinks and point to /usr/bin/python3.12
142
+ // In this case /bin/python3 === /bin/python.3.12
143
+ // However as mentioned earlier we will not be treating these the same as /usr/bin/python3.12
144
+ if resolved_exe_is_from_another_dir == Some ( symlink. to_owned ( ) ) {
145
+ symlinks. push ( possible_symlink. to_owned ( ) ) ;
177
146
}
178
147
}
179
148
}
0 commit comments