autocorrect-rb 2.5.6.pre1 → 2.5.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,319 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
-
3
- extern crate glob;
4
-
5
- use std::cell::RefCell;
6
- use std::collections::HashMap;
7
- use std::env;
8
- use std::path::{Path, PathBuf};
9
- use std::process::Command;
10
-
11
- use glob::{MatchOptions, Pattern};
12
-
13
- //================================================
14
- // Commands
15
- //================================================
16
-
17
- thread_local! {
18
- /// The errors encountered by the build script while executing commands.
19
- static COMMAND_ERRORS: RefCell<HashMap<String, Vec<String>>> = RefCell::default();
20
- }
21
-
22
- /// Adds an error encountered by the build script while executing a command.
23
- fn add_command_error(name: &str, path: &str, arguments: &[&str], message: String) {
24
- COMMAND_ERRORS.with(|e| {
25
- e.borrow_mut()
26
- .entry(name.into())
27
- .or_insert_with(Vec::new)
28
- .push(format!(
29
- "couldn't execute `{} {}` (path={}) ({})",
30
- name,
31
- arguments.join(" "),
32
- path,
33
- message,
34
- ))
35
- });
36
- }
37
-
38
- /// A struct that prints the errors encountered by the build script while
39
- /// executing commands when dropped (unless explictly discarded).
40
- ///
41
- /// This is handy because we only want to print these errors when the build
42
- /// script fails to link to an instance of `libclang`. For example, if
43
- /// `llvm-config` couldn't be executed but an instance of `libclang` was found
44
- /// anyway we don't want to pollute the build output with irrelevant errors.
45
- #[derive(Default)]
46
- pub struct CommandErrorPrinter {
47
- discard: bool,
48
- }
49
-
50
- impl CommandErrorPrinter {
51
- pub fn discard(mut self) {
52
- self.discard = true;
53
- }
54
- }
55
-
56
- impl Drop for CommandErrorPrinter {
57
- fn drop(&mut self) {
58
- if self.discard {
59
- return;
60
- }
61
-
62
- let errors = COMMAND_ERRORS.with(|e| e.borrow().clone());
63
-
64
- if let Some(errors) = errors.get("llvm-config") {
65
- println!(
66
- "cargo:warning=could not execute `llvm-config` one or more \
67
- times, if the LLVM_CONFIG_PATH environment variable is set to \
68
- a full path to valid `llvm-config` executable it will be used \
69
- to try to find an instance of `libclang` on your system: {}",
70
- errors
71
- .iter()
72
- .map(|e| format!("\"{}\"", e))
73
- .collect::<Vec<_>>()
74
- .join("\n "),
75
- )
76
- }
77
-
78
- if let Some(errors) = errors.get("xcode-select") {
79
- println!(
80
- "cargo:warning=could not execute `xcode-select` one or more \
81
- times, if a valid instance of this executable is on your PATH \
82
- it will be used to try to find an instance of `libclang` on \
83
- your system: {}",
84
- errors
85
- .iter()
86
- .map(|e| format!("\"{}\"", e))
87
- .collect::<Vec<_>>()
88
- .join("\n "),
89
- )
90
- }
91
- }
92
- }
93
-
94
- /// Executes a command and returns the `stdout` output if the command was
95
- /// successfully executed (errors are added to `COMMAND_ERRORS`).
96
- fn run_command(name: &str, path: &str, arguments: &[&str]) -> Option<String> {
97
- let output = match Command::new(path).args(arguments).output() {
98
- Ok(output) => output,
99
- Err(error) => {
100
- let message = format!("error: {}", error);
101
- add_command_error(name, path, arguments, message);
102
- return None;
103
- }
104
- };
105
-
106
- if output.status.success() {
107
- Some(String::from_utf8_lossy(&output.stdout).into_owned())
108
- } else {
109
- let message = format!("exit code: {}", output.status);
110
- add_command_error(name, path, arguments, message);
111
- None
112
- }
113
- }
114
-
115
- /// Executes the `llvm-config` command and returns the `stdout` output if the
116
- /// command was successfully executed (errors are added to `COMMAND_ERRORS`).
117
- pub fn run_llvm_config(arguments: &[&str]) -> Option<String> {
118
- let path = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into());
119
- run_command("llvm-config", &path, arguments)
120
- }
121
-
122
- /// Executes the `xcode-select` command and returns the `stdout` output if the
123
- /// command was successfully executed (errors are added to `COMMAND_ERRORS`).
124
- pub fn run_xcode_select(arguments: &[&str]) -> Option<String> {
125
- run_command("xcode-select", "xcode-select", arguments)
126
- }
127
-
128
- //================================================
129
- // Search Directories
130
- //================================================
131
-
132
- /// `libclang` directory patterns for Haiku.
133
- const DIRECTORIES_HAIKU: &[&str] = &[
134
- "/boot/system/lib",
135
- "/boot/system/develop/lib",
136
- "/boot/system/non-packaged/lib",
137
- "/boot/system/non-packaged/develop/lib",
138
- "/boot/home/config/non-packaged/lib",
139
- "/boot/home/config/non-packaged/develop/lib",
140
- ];
141
-
142
- /// `libclang` directory patterns for Linux (and FreeBSD).
143
- const DIRECTORIES_LINUX: &[&str] = &[
144
- "/usr/lib*",
145
- "/usr/lib*/*",
146
- "/usr/lib*/*/*",
147
- "/usr/local/lib*",
148
- "/usr/local/lib*/*",
149
- "/usr/local/lib*/*/*",
150
- "/usr/local/llvm*/lib*",
151
- ];
152
-
153
- /// `libclang` directory patterns for macOS.
154
- const DIRECTORIES_MACOS: &[&str] = &[
155
- "/usr/local/opt/llvm*/lib",
156
- "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
157
- "/Library/Developer/CommandLineTools/usr/lib",
158
- "/usr/local/opt/llvm*/lib/llvm*/lib",
159
- ];
160
-
161
- /// `libclang` directory patterns for Windows.
162
- const DIRECTORIES_WINDOWS: &[&str] = &[
163
- "C:\\LLVM\\lib",
164
- "C:\\Program Files*\\LLVM\\lib",
165
- "C:\\MSYS*\\MinGW*\\lib",
166
- // LLVM + Clang can be installed as a component of Visual Studio.
167
- // https://github.com/KyleMayes/clang-sys/issues/121
168
- "C:\\Program Files*\\Microsoft Visual Studio\\*\\BuildTools\\VC\\Tools\\Llvm\\**\\bin",
169
- // LLVM + Clang can be installed using Scoop (https://scoop.sh).
170
- // Other Windows package managers install LLVM + Clang to previously listed
171
- // system-wide directories.
172
- "C:\\Users\\*\\scoop\\apps\\llvm\\current\\bin",
173
- ];
174
-
175
- /// `libclang` directory patterns for illumos
176
- const DIRECTORIES_ILLUMOS: &[&str] = &[
177
- "/opt/ooce/clang-*/lib",
178
- "/opt/ooce/llvm-*/lib",
179
- ];
180
-
181
- //================================================
182
- // Searching
183
- //================================================
184
-
185
- /// Finds the files in a directory that match one or more filename glob patterns
186
- /// and returns the paths to and filenames of those files.
187
- fn search_directory(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
188
- // Escape the specified directory in case it contains characters that have
189
- // special meaning in glob patterns (e.g., `[` or `]`).
190
- let directory = Pattern::escape(directory.to_str().unwrap());
191
- let directory = Path::new(&directory);
192
-
193
- // Join the escaped directory to the filename glob patterns to obtain
194
- // complete glob patterns for the files being searched for.
195
- let paths = filenames
196
- .iter()
197
- .map(|f| directory.join(f).to_str().unwrap().to_owned());
198
-
199
- // Prevent wildcards from matching path separators to ensure that the search
200
- // is limited to the specified directory.
201
- let mut options = MatchOptions::new();
202
- options.require_literal_separator = true;
203
-
204
- paths
205
- .map(|p| glob::glob_with(&p, options))
206
- .filter_map(Result::ok)
207
- .flatten()
208
- .filter_map(|p| {
209
- let path = p.ok()?;
210
- let filename = path.file_name()?.to_str().unwrap();
211
-
212
- // The `libclang_shared` library has been renamed to `libclang-cpp`
213
- // in Clang 10. This can cause instances of this library (e.g.,
214
- // `libclang-cpp.so.10`) to be matched by patterns looking for
215
- // instances of `libclang`.
216
- if filename.contains("-cpp.") {
217
- return None;
218
- }
219
-
220
- Some((directory.to_owned(), filename.into()))
221
- })
222
- .collect::<Vec<_>>()
223
- }
224
-
225
- /// Finds the files in a directory (and any relevant sibling directories) that
226
- /// match one or more filename glob patterns and returns the paths to and
227
- /// filenames of those files.
228
- fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
229
- let mut results = search_directory(directory, filenames);
230
-
231
- // On Windows, `libclang.dll` is usually found in the LLVM `bin` directory
232
- // while `libclang.lib` is usually found in the LLVM `lib` directory. To
233
- // keep things consistent with other platforms, only LLVM `lib` directories
234
- // are included in the backup search directory globs so we need to search
235
- // the LLVM `bin` directory here.
236
- if cfg!(target_os = "windows") && directory.ends_with("lib") {
237
- let sibling = directory.parent().unwrap().join("bin");
238
- results.extend(search_directory(&sibling, filenames).into_iter());
239
- }
240
-
241
- results
242
- }
243
-
244
- /// Finds the `libclang` static or dynamic libraries matching one or more
245
- /// filename glob patterns and returns the paths to and filenames of those files.
246
- pub fn search_libclang_directories(filenames: &[String], variable: &str) -> Vec<(PathBuf, String)> {
247
- // Search only the path indicated by the relevant environment variable
248
- // (e.g., `LIBCLANG_PATH`) if it is set.
249
- if let Ok(path) = env::var(variable).map(|d| Path::new(&d).to_path_buf()) {
250
- // Check if the path is a matching file.
251
- if let Some(parent) = path.parent() {
252
- let filename = path.file_name().unwrap().to_str().unwrap();
253
- let libraries = search_directories(parent, filenames);
254
- if libraries.iter().any(|(_, f)| f == filename) {
255
- return vec![(parent.into(), filename.into())];
256
- }
257
- }
258
-
259
- // Check if the path is directory containing a matching file.
260
- return search_directories(&path, filenames);
261
- }
262
-
263
- let mut found = vec![];
264
-
265
- // Search the `bin` and `lib` directories in the directory returned by
266
- // `llvm-config --prefix`.
267
- if let Some(output) = run_llvm_config(&["--prefix"]) {
268
- let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
269
- found.extend(search_directories(&directory.join("bin"), filenames));
270
- found.extend(search_directories(&directory.join("lib"), filenames));
271
- found.extend(search_directories(&directory.join("lib64"), filenames));
272
- }
273
-
274
- // Search the toolchain directory in the directory returned by
275
- // `xcode-select --print-path`.
276
- if cfg!(target_os = "macos") {
277
- if let Some(output) = run_xcode_select(&["--print-path"]) {
278
- let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
279
- let directory = directory.join("Toolchains/XcodeDefault.xctoolchain/usr/lib");
280
- found.extend(search_directories(&directory, filenames));
281
- }
282
- }
283
-
284
- // Search the directories in the `LD_LIBRARY_PATH` environment variable.
285
- if let Ok(path) = env::var("LD_LIBRARY_PATH") {
286
- for directory in env::split_paths(&path) {
287
- found.extend(search_directories(&directory, filenames));
288
- }
289
- }
290
-
291
- // Determine the `libclang` directory patterns.
292
- let directories = if cfg!(target_os = "haiku") {
293
- DIRECTORIES_HAIKU
294
- } else if cfg!(any(target_os = "linux", target_os = "freebsd")) {
295
- DIRECTORIES_LINUX
296
- } else if cfg!(target_os = "macos") {
297
- DIRECTORIES_MACOS
298
- } else if cfg!(target_os = "windows") {
299
- DIRECTORIES_WINDOWS
300
- } else if cfg!(target_os = "illumos") {
301
- DIRECTORIES_ILLUMOS
302
- } else {
303
- &[]
304
- };
305
-
306
- // Search the directories provided by the `libclang` directory patterns.
307
- let mut options = MatchOptions::new();
308
- options.case_sensitive = false;
309
- options.require_literal_separator = true;
310
- for directory in directories.iter().rev() {
311
- if let Ok(directories) = glob::glob_with(directory, options) {
312
- for directory in directories.filter_map(Result::ok).filter(|p| p.is_dir()) {
313
- found.extend(search_directories(&directory, filenames));
314
- }
315
- }
316
- }
317
-
318
- found
319
- }
@@ -1,262 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
-
3
- use std::env;
4
- use std::fs::File;
5
- use std::io::{self, Error, ErrorKind, Read, Seek, SeekFrom};
6
- use std::path::{Path, PathBuf};
7
-
8
- use super::common;
9
-
10
- //================================================
11
- // Validation
12
- //================================================
13
-
14
- /// Extracts the ELF class from the ELF header in a shared library.
15
- fn parse_elf_header(path: &Path) -> io::Result<u8> {
16
- let mut file = File::open(path)?;
17
- let mut buffer = [0; 5];
18
- file.read_exact(&mut buffer)?;
19
- if buffer[..4] == [127, 69, 76, 70] {
20
- Ok(buffer[4])
21
- } else {
22
- Err(Error::new(ErrorKind::InvalidData, "invalid ELF header"))
23
- }
24
- }
25
-
26
- /// Extracts the magic number from the PE header in a shared library.
27
- fn parse_pe_header(path: &Path) -> io::Result<u16> {
28
- let mut file = File::open(path)?;
29
-
30
- // Extract the header offset.
31
- let mut buffer = [0; 4];
32
- let start = SeekFrom::Start(0x3C);
33
- file.seek(start)?;
34
- file.read_exact(&mut buffer)?;
35
- let offset = i32::from_le_bytes(buffer);
36
-
37
- // Check the validity of the header.
38
- file.seek(SeekFrom::Start(offset as u64))?;
39
- file.read_exact(&mut buffer)?;
40
- if buffer != [80, 69, 0, 0] {
41
- return Err(Error::new(ErrorKind::InvalidData, "invalid PE header"));
42
- }
43
-
44
- // Extract the magic number.
45
- let mut buffer = [0; 2];
46
- file.seek(SeekFrom::Current(20))?;
47
- file.read_exact(&mut buffer)?;
48
- Ok(u16::from_le_bytes(buffer))
49
- }
50
-
51
- /// Checks that a `libclang` shared library matches the target platform.
52
- fn validate_library(path: &Path) -> Result<(), String> {
53
- if cfg!(any(target_os = "linux", target_os = "freebsd")) {
54
- let class = parse_elf_header(path).map_err(|e| e.to_string())?;
55
-
56
- if cfg!(target_pointer_width = "32") && class != 1 {
57
- return Err("invalid ELF class (64-bit)".into());
58
- }
59
-
60
- if cfg!(target_pointer_width = "64") && class != 2 {
61
- return Err("invalid ELF class (32-bit)".into());
62
- }
63
-
64
- Ok(())
65
- } else if cfg!(target_os = "windows") {
66
- let magic = parse_pe_header(path).map_err(|e| e.to_string())?;
67
-
68
- if cfg!(target_pointer_width = "32") && magic != 267 {
69
- return Err("invalid DLL (64-bit)".into());
70
- }
71
-
72
- if cfg!(target_pointer_width = "64") && magic != 523 {
73
- return Err("invalid DLL (32-bit)".into());
74
- }
75
-
76
- Ok(())
77
- } else {
78
- Ok(())
79
- }
80
- }
81
-
82
- //================================================
83
- // Searching
84
- //================================================
85
-
86
- /// Extracts the version components in a `libclang` shared library filename.
87
- fn parse_version(filename: &str) -> Vec<u32> {
88
- let version = if let Some(version) = filename.strip_prefix("libclang.so.") {
89
- version
90
- } else if filename.starts_with("libclang-") {
91
- &filename[9..filename.len() - 3]
92
- } else {
93
- return vec![];
94
- };
95
-
96
- version.split('.').map(|s| s.parse().unwrap_or(0)).collect()
97
- }
98
-
99
- /// Finds `libclang` shared libraries and returns the paths to, filenames of,
100
- /// and versions of those shared libraries.
101
- fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Vec<u32>)>, String> {
102
- let mut files = vec![format!(
103
- "{}clang{}",
104
- env::consts::DLL_PREFIX,
105
- env::consts::DLL_SUFFIX
106
- )];
107
-
108
- if cfg!(target_os = "linux") {
109
- // Some Linux distributions don't create a `libclang.so` symlink, so we
110
- // need to look for versioned files (e.g., `libclang-3.9.so`).
111
- files.push("libclang-*.so".into());
112
-
113
- // Some Linux distributions don't create a `libclang.so` symlink and
114
- // don't have versioned files as described above, so we need to look for
115
- // suffix versioned files (e.g., `libclang.so.1`). However, `ld` cannot
116
- // link to these files, so this will only be included when linking at
117
- // runtime.
118
- if runtime {
119
- files.push("libclang.so.*".into());
120
- files.push("libclang-*.so.*".into());
121
- }
122
- }
123
-
124
- if cfg!(any(
125
- target_os = "freebsd",
126
- target_os = "haiku",
127
- target_os = "netbsd",
128
- target_os = "openbsd",
129
- )) {
130
- // Some BSD distributions don't create a `libclang.so` symlink either,
131
- // but use a different naming scheme for versioned files (e.g.,
132
- // `libclang.so.7.0`).
133
- files.push("libclang.so.*".into());
134
- }
135
-
136
- if cfg!(target_os = "windows") {
137
- // The official LLVM build uses `libclang.dll` on Windows instead of
138
- // `clang.dll`. However, unofficial builds such as MinGW use `clang.dll`.
139
- files.push("libclang.dll".into());
140
- }
141
-
142
- // Find and validate `libclang` shared libraries and collect the versions.
143
- let mut valid = vec![];
144
- let mut invalid = vec![];
145
- for (directory, filename) in common::search_libclang_directories(&files, "LIBCLANG_PATH") {
146
- let path = directory.join(&filename);
147
- match validate_library(&path) {
148
- Ok(()) => {
149
- let version = parse_version(&filename);
150
- valid.push((directory, filename, version))
151
- }
152
- Err(message) => invalid.push(format!("({}: {})", path.display(), message)),
153
- }
154
- }
155
-
156
- if !valid.is_empty() {
157
- return Ok(valid);
158
- }
159
-
160
- let message = format!(
161
- "couldn't find any valid shared libraries matching: [{}], set the \
162
- `LIBCLANG_PATH` environment variable to a path where one of these files \
163
- can be found (invalid: [{}])",
164
- files
165
- .iter()
166
- .map(|f| format!("'{}'", f))
167
- .collect::<Vec<_>>()
168
- .join(", "),
169
- invalid.join(", "),
170
- );
171
-
172
- Err(message)
173
- }
174
-
175
- /// Finds the "best" `libclang` shared library and returns the directory and
176
- /// filename of that library.
177
- pub fn find(runtime: bool) -> Result<(PathBuf, String), String> {
178
- search_libclang_directories(runtime)?
179
- .iter()
180
- // We want to find the `libclang` shared library with the highest
181
- // version number, hence `max_by_key` below.
182
- //
183
- // However, in the case where there are multiple such `libclang` shared
184
- // libraries, we want to use the order in which they appeared in the
185
- // list returned by `search_libclang_directories` as a tiebreaker since
186
- // that function returns `libclang` shared libraries in descending order
187
- // of preference by how they were found.
188
- //
189
- // `max_by_key`, perhaps surprisingly, returns the *last* item with the
190
- // maximum key rather than the first which results in the opposite of
191
- // the tiebreaking behavior we want. This is easily fixed by reversing
192
- // the list first.
193
- .rev()
194
- .max_by_key(|f| &f.2)
195
- .cloned()
196
- .map(|(path, filename, _)| (path, filename))
197
- .ok_or_else(|| "unreachable".into())
198
- }
199
-
200
- //================================================
201
- // Linking
202
- //================================================
203
-
204
- /// Finds and links to a `libclang` shared library.
205
- #[cfg(not(feature = "runtime"))]
206
- pub fn link() {
207
- let cep = common::CommandErrorPrinter::default();
208
-
209
- use std::fs;
210
-
211
- let (directory, filename) = find(false).unwrap();
212
- println!("cargo:rustc-link-search={}", directory.display());
213
-
214
- if cfg!(all(target_os = "windows", target_env = "msvc")) {
215
- // Find the `libclang` stub static library required for the MSVC
216
- // toolchain.
217
- let lib = if !directory.ends_with("bin") {
218
- directory
219
- } else {
220
- directory.parent().unwrap().join("lib")
221
- };
222
-
223
- if lib.join("libclang.lib").exists() {
224
- println!("cargo:rustc-link-search={}", lib.display());
225
- } else if lib.join("libclang.dll.a").exists() {
226
- // MSYS and MinGW use `libclang.dll.a` instead of `libclang.lib`.
227
- // It is linkable with the MSVC linker, but Rust doesn't recognize
228
- // the `.a` suffix, so we need to copy it with a different name.
229
- //
230
- // FIXME: Maybe we can just hardlink or symlink it?
231
- let out = env::var("OUT_DIR").unwrap();
232
- fs::copy(
233
- lib.join("libclang.dll.a"),
234
- Path::new(&out).join("libclang.lib"),
235
- )
236
- .unwrap();
237
- println!("cargo:rustc-link-search=native={}", out);
238
- } else {
239
- panic!(
240
- "using '{}', so 'libclang.lib' or 'libclang.dll.a' must be \
241
- available in {}",
242
- filename,
243
- lib.display(),
244
- );
245
- }
246
-
247
- println!("cargo:rustc-link-lib=dylib=libclang");
248
- } else {
249
- let name = filename.trim_start_matches("lib");
250
-
251
- // Strip extensions and trailing version numbers (e.g., the `.so.7.0` in
252
- // `libclang.so.7.0`).
253
- let name = match name.find(".dylib").or_else(|| name.find(".so")) {
254
- Some(index) => &name[0..index],
255
- None => name,
256
- };
257
-
258
- println!("cargo:rustc-link-lib=dylib={}", name);
259
- }
260
-
261
- cep.discard();
262
- }