1 use crate::error::{Error, Result};
2 use crate::gen::fs;
3 use crate::paths;
4 use std::io;
5 use std::path::Path;
6
write(path: impl AsRef<Path>, content: &[u8]) -> Result<()>7 pub(crate) fn write(path: impl AsRef<Path>, content: &[u8]) -> Result<()> {
8 let path = path.as_ref();
9
10 let mut create_dir_error = None;
11 if fs::exists(path) {
12 if let Ok(existing) = fs::read(path) {
13 if existing == content {
14 // Avoid bumping modified time with unchanged contents.
15 return Ok(());
16 }
17 }
18 best_effort_remove(path);
19 } else {
20 let parent = path.parent().unwrap();
21 create_dir_error = fs::create_dir_all(parent).err();
22 }
23
24 match fs::write(path, content) {
25 // As long as write succeeded, ignore any create_dir_all error.
26 Ok(()) => Ok(()),
27 // If create_dir_all and write both failed, prefer the first error.
28 Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
29 }
30 }
31
symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()>32 pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
33 let original = original.as_ref();
34 let link = link.as_ref();
35
36 let mut create_dir_error = None;
37 if fs::exists(link) {
38 best_effort_remove(link);
39 } else {
40 let parent = link.parent().unwrap();
41 create_dir_error = fs::create_dir_all(parent).err();
42 }
43
44 match paths::symlink_or_copy(original, link) {
45 // As long as symlink_or_copy succeeded, ignore any create_dir_all error.
46 Ok(()) => Ok(()),
47 Err(err) => {
48 if err.kind() == io::ErrorKind::AlreadyExists {
49 // This is fine, a different simultaneous build script already
50 // created the same link or copy. The cxx_build target directory
51 // is laid out such that the same path never refers to two
52 // different targets during the same multi-crate build, so if
53 // some other build script already created the same path then we
54 // know it refers to the identical target that the current build
55 // script was trying to create.
56 Ok(())
57 } else {
58 // If create_dir_all and symlink_or_copy both failed, prefer the
59 // first error.
60 Err(Error::Fs(create_dir_error.unwrap_or(err)))
61 }
62 }
63 }
64 }
65
symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()>66 pub(crate) fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
67 let original = original.as_ref();
68 let link = link.as_ref();
69
70 let mut create_dir_error = None;
71 if fs::exists(link) {
72 best_effort_remove(link);
73 } else {
74 let parent = link.parent().unwrap();
75 create_dir_error = fs::create_dir_all(parent).err();
76 }
77
78 match fs::symlink_dir(original, link) {
79 // As long as symlink_dir succeeded, ignore any create_dir_all error.
80 Ok(()) => Ok(()),
81 // If create_dir_all and symlink_dir both failed, prefer the first error.
82 Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
83 }
84 }
85
best_effort_remove(path: &Path)86 fn best_effort_remove(path: &Path) {
87 use std::fs;
88
89 if cfg!(windows) {
90 // On Windows, the correct choice of remove_file vs remove_dir needs to
91 // be used according to what the symlink *points to*. Trying to use
92 // remove_file to remove a symlink which points to a directory fails
93 // with "Access is denied".
94 if let Ok(metadata) = fs::metadata(path) {
95 if metadata.is_dir() {
96 let _ = fs::remove_dir_all(path);
97 } else {
98 let _ = fs::remove_file(path);
99 }
100 } else if fs::symlink_metadata(path).is_ok() {
101 // The symlink might exist but be dangling, in which case there is
102 // no standard way to determine what "kind" of symlink it is. Try
103 // deleting both ways.
104 if fs::remove_dir_all(path).is_err() {
105 let _ = fs::remove_file(path);
106 }
107 }
108 } else {
109 // On non-Windows, we check metadata not following symlinks. All
110 // symlinks are removed using remove_file.
111 if let Ok(metadata) = fs::symlink_metadata(path) {
112 if metadata.is_dir() {
113 let _ = fs::remove_dir_all(path);
114 } else {
115 let _ = fs::remove_file(path);
116 }
117 }
118 }
119 }
120