@@ -36,9 +36,10 @@ pub(crate) fn relative_symlink_file(
36
36
let original = original. as_ref ( ) ;
37
37
let link = link. as_ref ( ) ;
38
38
39
+ let parent_directory_error = prepare_parent_directory_for_symlink ( link) . err ( ) ;
39
40
let relativized = best_effort_relativize_symlink ( original, link) ;
40
41
41
- symlink_file ( & relativized, original, link)
42
+ symlink_file ( & relativized, original, link, parent_directory_error )
42
43
}
43
44
44
45
pub ( crate ) fn absolute_symlink_file (
@@ -48,7 +49,9 @@ pub(crate) fn absolute_symlink_file(
48
49
let original = original. as_ref ( ) ;
49
50
let link = link. as_ref ( ) ;
50
51
51
- symlink_file ( original, original, link)
52
+ let parent_directory_error = prepare_parent_directory_for_symlink ( link) . err ( ) ;
53
+
54
+ symlink_file ( original, original, link, parent_directory_error)
52
55
}
53
56
54
57
pub ( crate ) fn relative_symlink_dir (
@@ -58,20 +61,28 @@ pub(crate) fn relative_symlink_dir(
58
61
let original = original. as_ref ( ) ;
59
62
let link = link. as_ref ( ) ;
60
63
64
+ let parent_directory_error = prepare_parent_directory_for_symlink ( link) . err ( ) ;
61
65
let relativized = best_effort_relativize_symlink ( original, link) ;
62
66
63
- symlink_dir ( & relativized, link)
67
+ symlink_dir ( & relativized, link, parent_directory_error )
64
68
}
65
69
66
- fn symlink_file ( path_for_symlink : & Path , path_for_copy : & Path , link : & Path ) -> Result < ( ) > {
67
- let mut create_dir_error = None ;
70
+ fn prepare_parent_directory_for_symlink ( link : & Path ) -> fs:: Result < ( ) > {
68
71
if fs:: exists ( link) {
69
72
best_effort_remove ( link) ;
73
+ Ok ( ( ) )
70
74
} else {
71
75
let parent = link. parent ( ) . unwrap ( ) ;
72
- create_dir_error = fs:: create_dir_all ( parent) . err ( ) ;
76
+ fs:: create_dir_all ( parent)
73
77
}
78
+ }
74
79
80
+ fn symlink_file (
81
+ path_for_symlink : & Path ,
82
+ path_for_copy : & Path ,
83
+ link : & Path ,
84
+ parent_directory_error : Option < fs:: Error > ,
85
+ ) -> Result < ( ) > {
75
86
match paths:: symlink_or_copy ( path_for_symlink, path_for_copy, link) {
76
87
// As long as symlink_or_copy succeeded, ignore any create_dir_all error.
77
88
Ok ( ( ) ) => Ok ( ( ) ) ,
@@ -88,26 +99,22 @@ fn symlink_file(path_for_symlink: &Path, path_for_copy: &Path, link: &Path) -> R
88
99
} else {
89
100
// If create_dir_all and symlink_or_copy both failed, prefer the
90
101
// first error.
91
- Err ( Error :: Fs ( create_dir_error . unwrap_or ( err) ) )
102
+ Err ( Error :: Fs ( parent_directory_error . unwrap_or ( err) ) )
92
103
}
93
104
}
94
105
}
95
106
}
96
107
97
- fn symlink_dir ( path_for_symlink : & Path , link : & Path ) -> Result < ( ) > {
98
- let mut create_dir_error = None ;
99
- if fs:: exists ( link) {
100
- best_effort_remove ( link) ;
101
- } else {
102
- let parent = link. parent ( ) . unwrap ( ) ;
103
- create_dir_error = fs:: create_dir_all ( parent) . err ( ) ;
104
- }
105
-
108
+ fn symlink_dir (
109
+ path_for_symlink : & Path ,
110
+ link : & Path ,
111
+ parent_directory_error : Option < fs:: Error > ,
112
+ ) -> Result < ( ) > {
106
113
match fs:: symlink_dir ( path_for_symlink, link) {
107
114
// As long as symlink_dir succeeded, ignore any create_dir_all error.
108
115
Ok ( ( ) ) => Ok ( ( ) ) ,
109
116
// If create_dir_all and symlink_dir both failed, prefer the first error.
110
- Err ( err) => Err ( Error :: Fs ( create_dir_error . unwrap_or ( err) ) ) ,
117
+ Err ( err) => Err ( Error :: Fs ( parent_directory_error . unwrap_or ( err) ) ) ,
111
118
}
112
119
}
113
120
@@ -150,6 +157,34 @@ fn best_effort_relativize_symlink(original: impl AsRef<Path>, link: impl AsRef<P
150
157
let original = original. as_ref ( ) ;
151
158
let link = link. as_ref ( ) ;
152
159
160
+ let relative_path = match abstractly_relativize_symlink ( original, link) {
161
+ Some ( relative_path) => relative_path,
162
+ None => return original. to_path_buf ( ) ,
163
+ } ;
164
+
165
+ // Sometimes "a/b/../c" refers to a different canonical location than "a/c".
166
+ // This can happen if 'b' is a symlink. The '..' canonicalizes to the parent
167
+ // directory of the symlink's target, not back to 'a'. In cxx-build's case
168
+ // someone could be using `--target-dir` with a location containing such
169
+ // symlinks.
170
+ if let Ok ( original_canonical) = original. canonicalize ( ) {
171
+ if let Ok ( relative_canonical) = link. parent ( ) . unwrap ( ) . join ( & relative_path) . canonicalize ( ) {
172
+ if original_canonical == relative_canonical {
173
+ return relative_path;
174
+ }
175
+ }
176
+ }
177
+
178
+ original. to_path_buf ( )
179
+ }
180
+
181
+ fn abstractly_relativize_symlink (
182
+ original : impl AsRef < Path > ,
183
+ link : impl AsRef < Path > ,
184
+ ) -> Option < PathBuf > {
185
+ let original = original. as_ref ( ) ;
186
+ let link = link. as_ref ( ) ;
187
+
153
188
// Relativization only makes sense if there is a semantically meaningful
154
189
// base directory shared between the two paths.
155
190
//
@@ -171,13 +206,13 @@ fn best_effort_relativize_symlink(original: impl AsRef<Path>, link: impl AsRef<P
171
206
|| path_contains_intermediate_components ( original)
172
207
|| path_contains_intermediate_components ( link)
173
208
{
174
- return original . to_path_buf ( ) ;
209
+ return None ;
175
210
}
176
211
177
212
let ( common_prefix, rest_of_original, rest_of_link) = split_after_common_prefix ( original, link) ;
178
213
179
214
if common_prefix == Path :: new ( "" ) {
180
- return original . to_path_buf ( ) ;
215
+ return None ;
181
216
}
182
217
183
218
let mut rest_of_link = rest_of_link. components ( ) ;
@@ -190,7 +225,7 @@ fn best_effort_relativize_symlink(original: impl AsRef<Path>, link: impl AsRef<P
190
225
path_to_common_prefix. push ( Component :: ParentDir ) ;
191
226
}
192
227
193
- path_to_common_prefix. join ( rest_of_original)
228
+ Some ( path_to_common_prefix. join ( rest_of_original) )
194
229
}
195
230
196
231
fn path_contains_intermediate_components ( path : impl AsRef < Path > ) -> bool {
@@ -230,23 +265,23 @@ fn split_after_common_prefix<'first, 'second>(
230
265
231
266
#[ cfg( test) ]
232
267
mod tests {
233
- use crate :: out:: best_effort_relativize_symlink ;
268
+ use crate :: out:: abstractly_relativize_symlink ;
234
269
use std:: path:: Path ;
235
270
236
271
#[ cfg( not( windows) ) ]
237
272
#[ test]
238
273
fn test_relativize_symlink_unix ( ) {
239
274
assert_eq ! (
240
- best_effort_relativize_symlink ( "/foo/bar/baz" , "/foo/spam/eggs" ) ,
241
- Path :: new( "../bar/baz" ) ,
275
+ abstractly_relativize_symlink ( "/foo/bar/baz" , "/foo/spam/eggs" ) . as_deref ( ) ,
276
+ Some ( Path :: new( "../bar/baz" ) ) ,
242
277
) ;
243
278
assert_eq ! (
244
- best_effort_relativize_symlink ( "/foo/bar/../baz" , "/foo/spam/eggs" ) ,
245
- Path :: new ( "/foo/bar/../baz" ) ,
279
+ abstractly_relativize_symlink ( "/foo/bar/../baz" , "/foo/spam/eggs" ) ,
280
+ None ,
246
281
) ;
247
282
assert_eq ! (
248
- best_effort_relativize_symlink ( "/foo/bar/baz" , "/foo/spam/./eggs" ) ,
249
- Path :: new( "../bar/baz" ) ,
283
+ abstractly_relativize_symlink ( "/foo/bar/baz" , "/foo/spam/./eggs" ) . as_deref ( ) ,
284
+ Some ( Path :: new( "../bar/baz" ) ) ,
250
285
) ;
251
286
}
252
287
@@ -260,12 +295,12 @@ mod tests {
260
295
let windows_different_volume_link = PathBuf :: from_iter ( [ "d:\\ " , "users" , "link" ] ) ;
261
296
262
297
assert_eq ! (
263
- best_effort_relativize_symlink ( & windows_target, windows_link) ,
264
- Path :: new( "..\\ windows\\ foo" ) ,
298
+ abstractly_relativize_symlink ( & windows_target, windows_link) . as_deref ( ) ,
299
+ Some ( Path :: new( "..\\ windows\\ foo" ) ) ,
265
300
) ;
266
301
assert_eq ! (
267
- best_effort_relativize_symlink ( & windows_target, windows_different_volume_link) ,
268
- windows_target ,
302
+ abstractly_relativize_symlink ( & windows_target, windows_different_volume_link) ,
303
+ None ,
269
304
) ;
270
305
}
271
306
}
0 commit comments