• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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