2023-05-17 02:02:22 +08:00
|
|
|
use rsconf::{LinkType, Target};
|
2023-05-25 23:19:07 +08:00
|
|
|
use std::error::Error;
|
|
|
|
|
|
|
|
fn main() {
|
2023-03-26 23:23:05 +08:00
|
|
|
cc::Build::new().file("src/compat.c").compile("libcompat.a");
|
|
|
|
|
2023-01-15 06:56:24 +08:00
|
|
|
let rust_dir = std::env::var("CARGO_MANIFEST_DIR").expect("Env var CARGO_MANIFEST_DIR missing");
|
|
|
|
let target_dir =
|
|
|
|
std::env::var("FISH_RUST_TARGET_DIR").unwrap_or(format!("{}/{}", rust_dir, "target/"));
|
|
|
|
let fish_src_dir = format!("{}/{}", rust_dir, "../src/");
|
|
|
|
|
|
|
|
// Where cxx emits its header.
|
|
|
|
let cxx_include_dir = format!("{}/{}", target_dir, "cxxbridge/rust/");
|
|
|
|
|
|
|
|
// If FISH_BUILD_DIR is given by CMake, then use it; otherwise assume it's at ../build.
|
|
|
|
let fish_build_dir =
|
|
|
|
std::env::var("FISH_BUILD_DIR").unwrap_or(format!("{}/{}", rust_dir, "../build/"));
|
|
|
|
|
|
|
|
// Where autocxx should put its stuff.
|
|
|
|
let autocxx_gen_dir = std::env::var("FISH_AUTOCXX_GEN_DIR")
|
|
|
|
.unwrap_or(format!("{}/{}", fish_build_dir, "fish-autocxx-gen/"));
|
|
|
|
|
2023-05-17 02:02:22 +08:00
|
|
|
let mut build = cc::Build::new();
|
|
|
|
// Add to the default library search path
|
|
|
|
build.flag_if_supported("-L/usr/local/lib/");
|
|
|
|
rsconf::add_library_search_path("/usr/local/lib");
|
|
|
|
let mut detector = Target::new_from(build).unwrap();
|
|
|
|
// Keep verbose mode on until we've ironed out rust build script stuff
|
|
|
|
// Note that if autocxx fails to compile any rust code, you'll see the full and unredacted
|
|
|
|
// stdout/stderr output, which will include things that LOOK LIKE compilation errors as rsconf
|
|
|
|
// tries to build various test files to try and figure out which libraries and symbols are
|
|
|
|
// available. IGNORE THESE and scroll to the very bottom of the build script output, past all
|
|
|
|
// these errors, to see the actual issue.
|
|
|
|
detector.set_verbose(true);
|
|
|
|
detect_features(detector);
|
2023-03-20 06:50:32 +08:00
|
|
|
|
2023-01-15 06:56:24 +08:00
|
|
|
// Emit cxx junk.
|
|
|
|
// This allows "Rust to be used from C++"
|
|
|
|
// This must come before autocxx so that cxx can emit its cxx.h header.
|
|
|
|
let source_files = vec![
|
2023-02-25 00:00:05 +08:00
|
|
|
"src/abbrs.rs",
|
Port AST to Rust
The translation is fairly direct though it adds some duplication, for example
there are multiple "match" statements that mimic function overloading.
Rust has no overloading, and we cannot have generic methods in the Node trait
(due to a Rust limitation, the error is like "cannot be made into an object")
so we include the type name in method names.
Give clients like "indent_visitor_t" a Rust companion ("IndentVisitor")
that takes care of the AST traversal while the AST consumption remains
in C++ for now. In future, "IndentVisitor" should absorb the entirety of
"indent_visitor_t". This pattern requires that "fish_indent" be exposed
includable header to the CXX bridge.
Alternatively, we could define FFI wrappers for recursive AST traversal.
Rust requires we separate the AST visitors for "mut" and "const"
scenarios. Take this opportunity to concretize both visitors:
The only client that requires mutable access is the populator. To match the
structure of the C++ populator which makes heavy use of function overloading,
we need to add a bunch of functions to the trait. Since there is no other
mutable visit, this seems acceptable.
The "const" visitors never use "will_visit_fields_of()" or
"did_visit_fields_of()", so remove them (though this is debatable).
Like in the C++ implementation, the AST nodes themselves are largely defined
via macros. Union fields like "Statement" and "ArgumentOrRedirection"
do currently not use macros but may in future.
This commit also introduces a precedent for a type that is defined in one
CXX bridge and used in another one - "ParseErrorList". To make this work
we need to manually define "ExternType".
There is one annoyance with CXX: functions that take explicit lifetime
parameters require to be marked as unsafe. This makes little sense
because functions that return `&Foo` with implicit lifetime can be
misused the same way on the C++ side.
One notable change is that we cannot directly port "find_block_open_keyword()"
(which is used to compute an error) because it relies on the stack of visited
nodes. We cannot modify a stack of node references while we do the "mut"
walk. Happily, an idiomatic solution is easy: we can tell the AST visitor
to backtrack to the parent node and create the error there.
Since "node_t::accept_base" is no longer a template we don't need the
"node_visitation_t" trampoline anymore.
The added copying at the FFI boundary makes things slower (memcpy dominates
the profile) but it's not unusable, which is good news:
$ hyperfine ./fish.{old,new}" -c 'source ../share/completions/git.fish'"
Benchmark 1: ./fish.old -c 'source ../share/completions/git.fish'
Time (mean ± σ): 195.5 ms ± 2.9 ms [User: 190.1 ms, System: 4.4 ms]
Range (min … max): 193.2 ms … 205.1 ms 15 runs
Benchmark 2: ./fish.new -c 'source ../share/completions/git.fish'
Time (mean ± σ): 677.5 ms ± 62.0 ms [User: 665.4 ms, System: 10.0 ms]
Range (min … max): 611.7 ms … 805.5 ms 10 runs
Summary
'./fish.old -c 'source ../share/completions/git.fish'' ran
3.47 ± 0.32 times faster than './fish.new -c 'source ../share/completions/git.fish''
Leftovers:
- Enum variants are still snakecase; I didn't get around to changing this yet.
- "ast_type_to_string()" still returns a snakecase name. This could be
changed since it's not user visible.
2023-04-02 22:42:59 +08:00
|
|
|
"src/ast.rs",
|
2023-05-17 09:51:34 +08:00
|
|
|
"src/builtins/shared.rs",
|
|
|
|
"src/common.rs",
|
2023-04-30 10:58:45 +08:00
|
|
|
"src/env/env_ffi.rs",
|
2023-05-17 09:51:34 +08:00
|
|
|
"src/env_dispatch.rs",
|
2023-02-12 04:31:08 +08:00
|
|
|
"src/event.rs",
|
2023-02-18 09:21:44 +08:00
|
|
|
"src/fd_monitor.rs",
|
2023-01-15 06:56:24 +08:00
|
|
|
"src/fd_readable_set.rs",
|
2023-02-18 09:21:44 +08:00
|
|
|
"src/fds.rs",
|
2023-01-15 06:56:24 +08:00
|
|
|
"src/ffi_init.rs",
|
2023-01-16 06:56:04 +08:00
|
|
|
"src/ffi_tests.rs",
|
Port AST to Rust
The translation is fairly direct though it adds some duplication, for example
there are multiple "match" statements that mimic function overloading.
Rust has no overloading, and we cannot have generic methods in the Node trait
(due to a Rust limitation, the error is like "cannot be made into an object")
so we include the type name in method names.
Give clients like "indent_visitor_t" a Rust companion ("IndentVisitor")
that takes care of the AST traversal while the AST consumption remains
in C++ for now. In future, "IndentVisitor" should absorb the entirety of
"indent_visitor_t". This pattern requires that "fish_indent" be exposed
includable header to the CXX bridge.
Alternatively, we could define FFI wrappers for recursive AST traversal.
Rust requires we separate the AST visitors for "mut" and "const"
scenarios. Take this opportunity to concretize both visitors:
The only client that requires mutable access is the populator. To match the
structure of the C++ populator which makes heavy use of function overloading,
we need to add a bunch of functions to the trait. Since there is no other
mutable visit, this seems acceptable.
The "const" visitors never use "will_visit_fields_of()" or
"did_visit_fields_of()", so remove them (though this is debatable).
Like in the C++ implementation, the AST nodes themselves are largely defined
via macros. Union fields like "Statement" and "ArgumentOrRedirection"
do currently not use macros but may in future.
This commit also introduces a precedent for a type that is defined in one
CXX bridge and used in another one - "ParseErrorList". To make this work
we need to manually define "ExternType".
There is one annoyance with CXX: functions that take explicit lifetime
parameters require to be marked as unsafe. This makes little sense
because functions that return `&Foo` with implicit lifetime can be
misused the same way on the C++ side.
One notable change is that we cannot directly port "find_block_open_keyword()"
(which is used to compute an error) because it relies on the stack of visited
nodes. We cannot modify a stack of node references while we do the "mut"
walk. Happily, an idiomatic solution is easy: we can tell the AST visitor
to backtrack to the parent node and create the error there.
Since "node_t::accept_base" is no longer a template we don't need the
"node_visitation_t" trampoline anymore.
The added copying at the FFI boundary makes things slower (memcpy dominates
the profile) but it's not unusable, which is good news:
$ hyperfine ./fish.{old,new}" -c 'source ../share/completions/git.fish'"
Benchmark 1: ./fish.old -c 'source ../share/completions/git.fish'
Time (mean ± σ): 195.5 ms ± 2.9 ms [User: 190.1 ms, System: 4.4 ms]
Range (min … max): 193.2 ms … 205.1 ms 15 runs
Benchmark 2: ./fish.new -c 'source ../share/completions/git.fish'
Time (mean ± σ): 677.5 ms ± 62.0 ms [User: 665.4 ms, System: 10.0 ms]
Range (min … max): 611.7 ms … 805.5 ms 10 runs
Summary
'./fish.old -c 'source ../share/completions/git.fish'' ran
3.47 ± 0.32 times faster than './fish.new -c 'source ../share/completions/git.fish''
Leftovers:
- Enum variants are still snakecase; I didn't get around to changing this yet.
- "ast_type_to_string()" still returns a snakecase name. This could be
changed since it's not user visible.
2023-04-02 22:42:59 +08:00
|
|
|
"src/fish_indent.rs",
|
2023-02-03 23:34:29 +08:00
|
|
|
"src/future_feature_flags.rs",
|
Port AST to Rust
The translation is fairly direct though it adds some duplication, for example
there are multiple "match" statements that mimic function overloading.
Rust has no overloading, and we cannot have generic methods in the Node trait
(due to a Rust limitation, the error is like "cannot be made into an object")
so we include the type name in method names.
Give clients like "indent_visitor_t" a Rust companion ("IndentVisitor")
that takes care of the AST traversal while the AST consumption remains
in C++ for now. In future, "IndentVisitor" should absorb the entirety of
"indent_visitor_t". This pattern requires that "fish_indent" be exposed
includable header to the CXX bridge.
Alternatively, we could define FFI wrappers for recursive AST traversal.
Rust requires we separate the AST visitors for "mut" and "const"
scenarios. Take this opportunity to concretize both visitors:
The only client that requires mutable access is the populator. To match the
structure of the C++ populator which makes heavy use of function overloading,
we need to add a bunch of functions to the trait. Since there is no other
mutable visit, this seems acceptable.
The "const" visitors never use "will_visit_fields_of()" or
"did_visit_fields_of()", so remove them (though this is debatable).
Like in the C++ implementation, the AST nodes themselves are largely defined
via macros. Union fields like "Statement" and "ArgumentOrRedirection"
do currently not use macros but may in future.
This commit also introduces a precedent for a type that is defined in one
CXX bridge and used in another one - "ParseErrorList". To make this work
we need to manually define "ExternType".
There is one annoyance with CXX: functions that take explicit lifetime
parameters require to be marked as unsafe. This makes little sense
because functions that return `&Foo` with implicit lifetime can be
misused the same way on the C++ side.
One notable change is that we cannot directly port "find_block_open_keyword()"
(which is used to compute an error) because it relies on the stack of visited
nodes. We cannot modify a stack of node references while we do the "mut"
walk. Happily, an idiomatic solution is easy: we can tell the AST visitor
to backtrack to the parent node and create the error there.
Since "node_t::accept_base" is no longer a template we don't need the
"node_visitation_t" trampoline anymore.
The added copying at the FFI boundary makes things slower (memcpy dominates
the profile) but it's not unusable, which is good news:
$ hyperfine ./fish.{old,new}" -c 'source ../share/completions/git.fish'"
Benchmark 1: ./fish.old -c 'source ../share/completions/git.fish'
Time (mean ± σ): 195.5 ms ± 2.9 ms [User: 190.1 ms, System: 4.4 ms]
Range (min … max): 193.2 ms … 205.1 ms 15 runs
Benchmark 2: ./fish.new -c 'source ../share/completions/git.fish'
Time (mean ± σ): 677.5 ms ± 62.0 ms [User: 665.4 ms, System: 10.0 ms]
Range (min … max): 611.7 ms … 805.5 ms 10 runs
Summary
'./fish.old -c 'source ../share/completions/git.fish'' ran
3.47 ± 0.32 times faster than './fish.new -c 'source ../share/completions/git.fish''
Leftovers:
- Enum variants are still snakecase; I didn't get around to changing this yet.
- "ast_type_to_string()" still returns a snakecase name. This could be
changed since it's not user visible.
2023-04-02 22:42:59 +08:00
|
|
|
"src/highlight.rs",
|
2023-02-26 06:42:45 +08:00
|
|
|
"src/job_group.rs",
|
2023-05-31 05:22:09 +08:00
|
|
|
"src/kill.rs",
|
2023-04-24 03:39:34 +08:00
|
|
|
"src/null_terminated_array.rs",
|
2023-05-29 07:13:54 +08:00
|
|
|
"src/output.rs",
|
2023-02-05 16:35:06 +08:00
|
|
|
"src/parse_constants.rs",
|
Port AST to Rust
The translation is fairly direct though it adds some duplication, for example
there are multiple "match" statements that mimic function overloading.
Rust has no overloading, and we cannot have generic methods in the Node trait
(due to a Rust limitation, the error is like "cannot be made into an object")
so we include the type name in method names.
Give clients like "indent_visitor_t" a Rust companion ("IndentVisitor")
that takes care of the AST traversal while the AST consumption remains
in C++ for now. In future, "IndentVisitor" should absorb the entirety of
"indent_visitor_t". This pattern requires that "fish_indent" be exposed
includable header to the CXX bridge.
Alternatively, we could define FFI wrappers for recursive AST traversal.
Rust requires we separate the AST visitors for "mut" and "const"
scenarios. Take this opportunity to concretize both visitors:
The only client that requires mutable access is the populator. To match the
structure of the C++ populator which makes heavy use of function overloading,
we need to add a bunch of functions to the trait. Since there is no other
mutable visit, this seems acceptable.
The "const" visitors never use "will_visit_fields_of()" or
"did_visit_fields_of()", so remove them (though this is debatable).
Like in the C++ implementation, the AST nodes themselves are largely defined
via macros. Union fields like "Statement" and "ArgumentOrRedirection"
do currently not use macros but may in future.
This commit also introduces a precedent for a type that is defined in one
CXX bridge and used in another one - "ParseErrorList". To make this work
we need to manually define "ExternType".
There is one annoyance with CXX: functions that take explicit lifetime
parameters require to be marked as unsafe. This makes little sense
because functions that return `&Foo` with implicit lifetime can be
misused the same way on the C++ side.
One notable change is that we cannot directly port "find_block_open_keyword()"
(which is used to compute an error) because it relies on the stack of visited
nodes. We cannot modify a stack of node references while we do the "mut"
walk. Happily, an idiomatic solution is easy: we can tell the AST visitor
to backtrack to the parent node and create the error there.
Since "node_t::accept_base" is no longer a template we don't need the
"node_visitation_t" trampoline anymore.
The added copying at the FFI boundary makes things slower (memcpy dominates
the profile) but it's not unusable, which is good news:
$ hyperfine ./fish.{old,new}" -c 'source ../share/completions/git.fish'"
Benchmark 1: ./fish.old -c 'source ../share/completions/git.fish'
Time (mean ± σ): 195.5 ms ± 2.9 ms [User: 190.1 ms, System: 4.4 ms]
Range (min … max): 193.2 ms … 205.1 ms 15 runs
Benchmark 2: ./fish.new -c 'source ../share/completions/git.fish'
Time (mean ± σ): 677.5 ms ± 62.0 ms [User: 665.4 ms, System: 10.0 ms]
Range (min … max): 611.7 ms … 805.5 ms 10 runs
Summary
'./fish.old -c 'source ../share/completions/git.fish'' ran
3.47 ± 0.32 times faster than './fish.new -c 'source ../share/completions/git.fish''
Leftovers:
- Enum variants are still snakecase; I didn't get around to changing this yet.
- "ast_type_to_string()" still returns a snakecase name. This could be
changed since it's not user visible.
2023-04-02 22:42:59 +08:00
|
|
|
"src/parse_tree.rs",
|
|
|
|
"src/parse_util.rs",
|
2023-05-29 09:05:10 +08:00
|
|
|
"src/print_help.rs",
|
2023-02-04 18:21:42 +08:00
|
|
|
"src/redirection.rs",
|
2023-05-01 06:40:06 +08:00
|
|
|
"src/signal.rs",
|
2023-01-15 06:56:24 +08:00
|
|
|
"src/smoke.rs",
|
2023-03-20 06:50:33 +08:00
|
|
|
"src/termsize.rs",
|
2023-05-17 09:51:34 +08:00
|
|
|
"src/threads.rs",
|
2023-02-15 05:54:18 +08:00
|
|
|
"src/timer.rs",
|
2023-02-05 16:35:06 +08:00
|
|
|
"src/tokenizer.rs",
|
2023-01-15 06:56:24 +08:00
|
|
|
"src/topic_monitor.rs",
|
2023-03-28 23:59:51 +08:00
|
|
|
"src/trace.rs",
|
2023-01-31 04:23:01 +08:00
|
|
|
"src/util.rs",
|
2023-03-14 10:23:31 +08:00
|
|
|
"src/wait_handle.rs",
|
2023-01-15 06:56:24 +08:00
|
|
|
];
|
2023-02-03 14:56:32 +08:00
|
|
|
cxx_build::bridges(&source_files)
|
2023-01-15 06:56:24 +08:00
|
|
|
.flag_if_supported("-std=c++11")
|
|
|
|
.include(&fish_src_dir)
|
|
|
|
.include(&fish_build_dir) // For config.h
|
|
|
|
.include(&cxx_include_dir) // For cxx.h
|
2023-03-04 03:43:59 +08:00
|
|
|
.flag("-Wno-comment")
|
2023-01-15 06:56:24 +08:00
|
|
|
.compile("fish-rust");
|
|
|
|
|
|
|
|
// Emit autocxx junk.
|
|
|
|
// This allows "C++ to be used from Rust."
|
|
|
|
let include_paths = [&fish_src_dir, &fish_build_dir, &cxx_include_dir];
|
2023-03-13 06:38:39 +08:00
|
|
|
let mut builder = autocxx_build::Builder::new("src/ffi.rs", include_paths);
|
|
|
|
// Use autocxx's custom output directory unless we're being called by `rust-analyzer` and co.,
|
|
|
|
// in which case stick to the default target directory so code intelligence continues to work.
|
|
|
|
if std::env::var("RUSTC_WRAPPER").map_or(true, |wrapper| {
|
|
|
|
!(wrapper.contains("rust-analyzer") || wrapper.contains("intellij-rust-native-helper"))
|
|
|
|
}) {
|
|
|
|
// We need this reassignment because of how the builder pattern works
|
|
|
|
builder = builder.custom_gendir(autocxx_gen_dir.into());
|
|
|
|
}
|
2023-05-25 23:19:07 +08:00
|
|
|
let mut b = builder.build().unwrap();
|
2023-01-15 06:56:24 +08:00
|
|
|
b.flag_if_supported("-std=c++11")
|
2023-03-04 03:43:59 +08:00
|
|
|
.flag("-Wno-comment")
|
2023-01-15 06:56:24 +08:00
|
|
|
.compile("fish-rust-autocxx");
|
2023-05-17 02:02:22 +08:00
|
|
|
rsconf::rebuild_if_paths_changed(&source_files);
|
2023-01-15 06:56:24 +08:00
|
|
|
}
|
2023-03-20 06:50:32 +08:00
|
|
|
|
|
|
|
/// Dynamically enables certain features at build-time, without their having to be explicitly
|
|
|
|
/// enabled in the `cargo build --features xxx` invocation.
|
|
|
|
///
|
|
|
|
/// This can be used to enable features that we check for and conditionally compile according to in
|
|
|
|
/// our own codebase, but [can't be used to pull in dependencies](0) even if they're gated (in
|
|
|
|
/// `Cargo.toml`) behind a feature we just enabled.
|
|
|
|
///
|
|
|
|
/// [0]: https://github.com/rust-lang/cargo/issues/5499
|
2023-05-17 02:02:22 +08:00
|
|
|
fn detect_features(target: Target) {
|
|
|
|
for (feature, handler) in [
|
|
|
|
// Ignore the first entry, it just sets up the type inference. Model new entries after the
|
2023-03-20 07:11:09 +08:00
|
|
|
// second line.
|
2023-05-25 23:19:07 +08:00
|
|
|
(
|
|
|
|
"",
|
2023-05-17 02:02:22 +08:00
|
|
|
&(|_: &Target| Ok(false)) as &dyn Fn(&Target) -> Result<bool, Box<dyn Error>>,
|
2023-05-25 23:19:07 +08:00
|
|
|
),
|
2023-03-20 07:11:09 +08:00
|
|
|
("bsd", &detect_bsd),
|
2023-05-17 02:02:22 +08:00
|
|
|
("gettext", &have_gettext),
|
2023-03-20 07:11:09 +08:00
|
|
|
] {
|
2023-05-17 02:02:22 +08:00
|
|
|
match handler(&target) {
|
|
|
|
Err(e) => rsconf::warn!("{}: {}", feature, e),
|
|
|
|
Ok(true) => rsconf::enable_feature(feature),
|
2023-03-20 06:50:32 +08:00
|
|
|
Ok(false) => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-06 07:38:52 +08:00
|
|
|
/// Detect if we're being compiled for a BSD-derived OS, allowing targeting code conditionally with
|
|
|
|
/// `#[cfg(feature = "bsd")]`.
|
2023-03-20 06:50:32 +08:00
|
|
|
///
|
|
|
|
/// Rust offers fine-grained conditional compilation per-os for the popular operating systems, but
|
|
|
|
/// doesn't necessarily include less-popular forks nor does it group them into families more
|
|
|
|
/// specific than "windows" vs "unix" so we can conditionally compile code for BSD systems.
|
2023-05-17 02:02:22 +08:00
|
|
|
fn detect_bsd(_: &Target) -> Result<bool, Box<dyn Error>> {
|
2023-05-06 07:38:52 +08:00
|
|
|
// Instead of using `uname`, we can inspect the TARGET env variable set by Cargo. This lets us
|
|
|
|
// support cross-compilation scenarios.
|
|
|
|
let mut target = std::env::var("TARGET").unwrap();
|
|
|
|
if !target.chars().all(|c| c.is_ascii_lowercase()) {
|
|
|
|
target = target.to_ascii_lowercase();
|
|
|
|
}
|
|
|
|
let result = target.ends_with("bsd") || target.ends_with("dragonfly");
|
|
|
|
#[cfg(any(
|
|
|
|
target_os = "dragonfly",
|
|
|
|
target_os = "freebsd",
|
|
|
|
target_os = "netbsd",
|
|
|
|
target_os = "openbsd",
|
|
|
|
))]
|
|
|
|
assert!(result, "Target incorrectly detected as not BSD!");
|
|
|
|
Ok(result)
|
2023-03-20 06:50:32 +08:00
|
|
|
}
|
2023-05-17 02:02:22 +08:00
|
|
|
|
|
|
|
/// Detect libintl/gettext and its needed symbols to enable internationalization/localization
|
|
|
|
/// support.
|
|
|
|
fn have_gettext(target: &Target) -> Result<bool, Box<dyn Error>> {
|
|
|
|
// The following script correctly detects and links against gettext, but so long as we are using
|
|
|
|
// C++ and generate a static library linked into the C++ binary via CMake, we need to account
|
|
|
|
// for the CMake option WITH_GETTEXT being explicitly disabled.
|
|
|
|
rsconf::rebuild_if_env_changed("CMAKE_WITH_GETTEXT");
|
|
|
|
if let Some(with_gettext) = std::env::var_os("CMAKE_WITH_GETTEXT") {
|
|
|
|
if with_gettext.eq_ignore_ascii_case("0") {
|
|
|
|
return Ok(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// In order for fish to correctly operate, we need some way of notifying libintl to invalidate
|
|
|
|
// its localizations when the locale environment variables are modified. Without the libintl
|
|
|
|
// symbol _nl_msg_cat_cntr, we cannot use gettext even if we find it.
|
|
|
|
let mut libraries = Vec::new();
|
|
|
|
let mut found = 0;
|
|
|
|
let symbols = ["gettext", "_nl_msg_cat_cntr"];
|
|
|
|
for symbol in &symbols {
|
|
|
|
// Historically, libintl was required in order to use gettext() and co, but that
|
|
|
|
// functionality was subsumed by some versions of libc.
|
|
|
|
if target.has_symbol_in::<&str>(symbol, &[]) {
|
|
|
|
// No need to link anything special for this symbol
|
|
|
|
found += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for library in ["intl", "gettextlib"] {
|
|
|
|
if target.has_symbol(symbol, library) {
|
|
|
|
libraries.push(library);
|
|
|
|
found += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
match found {
|
|
|
|
0 => Ok(false),
|
|
|
|
1 => Err(format!("gettext found but cannot be used without {}", symbols[1]).into()),
|
|
|
|
_ => {
|
|
|
|
rsconf::link_libraries(&libraries, LinkType::Default);
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|