Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cli): keep default cc flags in build #3188

Merged
merged 3 commits into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
- { platform: windows-x64 , target: x86_64-pc-windows-msvc , os: windows-latest , cli_features: wasm }
- { platform: windows-x86 , target: i686-pc-windows-msvc , os: windows-latest }
- { platform: macos-arm64 , target: aarch64-apple-darwin , os: macos-14 , cli_features: wasm }
- { platform: macos-x64 , target: x86_64-apple-darwin , os: macos-latest , cli_features: wasm }
- { platform: macos-x64 , target: x86_64-apple-darwin , os: macos-12 , cli_features: wasm }

# Cross compilers for C library
- { platform: linux-arm64 , cc: aarch64-linux-gnu-gcc , ar: aarch64-linux-gnu-ar }
Expand Down
12 changes: 4 additions & 8 deletions .github/workflows/sanitize.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Sanitize

env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-D warnings"
RUSTFLAGS: -D warnings

on:
workflow_call:
Expand Down Expand Up @@ -34,17 +34,13 @@ jobs:

- name: Run main tests with undefined behaviour sanitizer (UBSAN)
env:
UBSAN_OPTIONS: halt_on_error=1
CFLAGS: -fsanitize=undefined
RUSTFLAGS: ${{ env.RUSTFLAGS }} -lubsan
run: cargo test -- --test-threads 1

- name: Run main tests with address sanitizer (ASAN)
env:
ASAN_OPTIONS: halt_on_error=1
ASAN_OPTIONS: verify_asan_link_order=0
CFLAGS: -fsanitize=address
RUSTFLAGS: ${{ env.RUSTFLAGS }} -Zsanitizer=address --cfg=sanitizing
run: |
rustup install nightly
rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
cargo +nightly test -Z build-std --target x86_64-unknown-linux-gnu -- --test-threads 1
RUSTFLAGS: ${{ env.RUSTFLAGS }} -lasan --cfg sanitizing
run: cargo test -- --test-threads 1
4 changes: 4 additions & 0 deletions cli/loader/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ fn main() {
"cargo:rustc-env=BUILD_TARGET={}",
std::env::var("TARGET").unwrap()
);
println!(
"cargo:rustc-env=BUILD_HOST={}",
std::env::var("HOST").unwrap()
);

let emscripten_version = std::fs::read_to_string("emscripten-version").unwrap();
println!("cargo:rustc-env=EMSCRIPTEN_VERSION={emscripten_version}");
Expand Down
127 changes: 53 additions & 74 deletions cli/loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ impl Config {
}

const BUILD_TARGET: &str = env!("BUILD_TARGET");
const BUILD_HOST: &str = env!("BUILD_HOST");

pub struct LanguageConfiguration<'a> {
pub scope: Option<String>,
Expand Down Expand Up @@ -143,7 +144,7 @@ impl<'a> CompileConfig<'a> {
scanner_path: None,
external_files: externals,
output_path,
flags: &["TREE_SITTER_REUSE_ALLOCATOR", "TREE_SITTER_INTERNAL_BUILD"],
flags: &[],
name: String::new(),
}
}
Expand Down Expand Up @@ -561,110 +562,88 @@ impl Loader {
lock_file: &fs::File,
lock_path: &Path,
) -> Result<(), Error> {
let mut cc = cc::Build::new();
cc.cpp(true)
.opt_level(2)
let mut cc_config = cc::Build::new();
cc_config
.cargo_metadata(false)
.cargo_warnings(false)
.target(BUILD_TARGET)
.host(BUILD_TARGET)
.flag_if_supported("-Werror=implicit-function-declaration");
let compiler = cc.get_compiler();
.host(BUILD_HOST)
.file(&config.parser_path)
.includes(&config.header_paths);

if let Some(scanner_path) = config.scanner_path.as_ref() {
if scanner_path.extension() != Some("c".as_ref()) {
cc_config.cpp(true);
eprintln!("Warning: Using a C++ scanner is now deprecated. Please migrate your scanner code to C, as C++ support will be removed in the near future.");
amaanq marked this conversation as resolved.
Show resolved Hide resolved
} else {
cc_config.std("c11");
}
cc_config.file(scanner_path);
}

if self.debug_build {
cc_config.opt_level(0).extra_warnings(true);
} else {
cc_config.opt_level(2).extra_warnings(false);
}

for flag in config.flags {
cc_config.define(flag, None);
}

let compiler = cc_config.get_compiler();
let mut command = Command::new(compiler.path());
command.args(compiler.args());
for (key, value) in compiler.env() {
command.env(key, value);
}

let output_path = config.output_path.as_ref().unwrap();

if compiler.is_like_msvc() {
command.args(["/nologo", "/LD"]);

for path in &config.header_paths {
command.arg(format!("/I{}", path.to_string_lossy()));
}

if self.debug_build {
command.arg("/Od");
} else {
command.arg("/O2");
}
command.arg(&config.parser_path);

if let Some(scanner_path) = config.scanner_path.as_ref() {
if scanner_path.extension() != Some("c".as_ref()) {
eprintln!("Warning: Using a C++ scanner is now deprecated. Please migrate your scanner code to C, as C++ support will be removed in the near future.");
}

command.arg(scanner_path);
}
command
.arg("/utf-8")
.arg("/link")
.arg(format!("/out:{}", output_path.to_str().unwrap()));
let out = format!("-out:{}", output_path.to_str().unwrap());
command.arg(if self.debug_build { "-LDd" } else { "-LD" });
command.arg("-utf-8");
command.args(cc_config.get_files());
command.arg("-link").arg(out);
} else {
command
.arg("-shared")
.arg("-fno-exceptions")
.arg("-g")
.arg("-o")
.arg(output_path);

for path in &config.header_paths {
command.arg(format!("-I{}", path.to_string_lossy()));
}

if !cfg!(windows) {
command.arg("-fPIC");
}

if self.debug_build {
command.arg("-O0");
command.args(["-Werror=implicit-function-declaration", "-g"]);
if cfg!(any(target_os = "macos", target_os = "ios")) {
command.arg("-dynamiclib");
// TODO: remove when supported
command.arg("-UTREE_SITTER_REUSE_ALLOCATOR");
} else {
command.arg("-O2");
}

if let Some(scanner_path) = config.scanner_path.as_ref() {
if scanner_path.extension() == Some("c".as_ref()) {
command.arg("-xc").arg("-std=c11").arg(scanner_path);
} else {
eprintln!("Warning: Using a C++ scanner is now deprecated. Please migrate your scanner code to C, as C++ support will be removed in the near future.");
command.arg(scanner_path);
}
command.arg("-shared");
}
command.arg("-xc").arg(&config.parser_path);
command.args(cc_config.get_files());
command.arg("-o").arg(output_path);
}

// For conditional compilation of external scanner code when
// used internally by `tree-sitter parse` and other sub commands.
command.arg("-DTREE_SITTER_INTERNAL_BUILD");

// Always use the same allocator in the CLI as any scanner, useful for debugging and
// tracking memory leaks in tests.
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
command.arg("-DTREE_SITTER_REUSE_ALLOCATOR");

let output = command.output().with_context(|| {
format!("Failed to execute the C compiler with the following command:\n{command:?}")
})?;

lock_file.unlock()?;
fs::remove_file(lock_path)?;

if !output.status.success() {
return Err(anyhow!(
if output.status.success() {
Ok(())
} else {
Err(anyhow!(
"Parser compilation failed.\nStdout: {}\nStderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
));
))
}

Ok(())
}

#[cfg(unix)]
fn check_external_scanner(&self, name: &str, library_path: &Path) -> Result<()> {
let prefix = if cfg!(target_os = "macos") { "_" } else { "" };
let prefix = if cfg!(any(target_os = "macos", target_os = "ios")) {
"_"
} else {
""
};
let mut must_have = vec![
format!("{prefix}tree_sitter_{name}_external_scanner_create"),
format!("{prefix}tree_sitter_{name}_external_scanner_destroy"),
Expand Down
2 changes: 1 addition & 1 deletion cli/src/generate/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ impl Generator {
.count()
+ 1;
let constant_name = if let Some(symbol) = symbol {
format!("{}_character_set_{}", self.symbol_ids[&symbol], count)
format!("{}_character_set_{}", self.symbol_ids[symbol], count)
} else {
format!("extras_character_set_{}", count)
};
Expand Down
2 changes: 1 addition & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ fn run() -> Result<()> {
}
(true, false) => &["TREE_SITTER_REUSE_ALLOCATOR"],
(false, true) => &["TREE_SITTER_INTERNAL_BUILD"],
(false, false) => &[""],
(false, false) => &[],
};

let config = Config::load(None)?;
Expand Down
9 changes: 5 additions & 4 deletions cli/src/tests/corpus_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,11 @@ fn test_corpus_for_json(seed: usize) {
test_language_corpus("json", seed, None, None);
}

// #[test_with_seed(retry=10, seed=*START_SEED, seed_fn=new_seed)]
// fn test_corpus_for_php(seed: usize) {
// test_language_corpus("php", seed, None, Some("php"));
// }
#[ignore]
#[test_with_seed(retry=10, seed=*START_SEED, seed_fn=new_seed)]
fn test_corpus_for_php(seed: usize) {
test_language_corpus("php", seed, None, Some("php"));
}

#[test_with_seed(retry=10, seed=*START_SEED, seed_fn=new_seed)]
fn test_corpus_for_python(seed: usize) {
Expand Down
4 changes: 3 additions & 1 deletion docs/section-3-creating-parsers.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ If there is an ambiguity or *local ambiguity* in your grammar, Tree-sitter will

The `build` command compiles your parser into a dynamically-loadable library, either as a shared object (`.so`, `.dylib`, or `.dll`) or as a WASM module.

You can specify whether to compile it as a wasm module with the `--wasm`/`-w` flag, and you can opt in to use docker or podman to supply emscripten with the `--docker`/`-d` flag. This removes the need to install emscripten on your machine locally.
You can change the compiler executable via the `CC` environment variable and add extra flags via `CFLAGS`. For macOS or iOS, you can set `MACOSX_DEPLOYMENT_TARGET` or `IPHONEOS_DEPLOYMENT_TARGET` respectively to define the minimum supported version.

You can specify whether to compile it as a wasm module with the `--wasm`/`-w` flag, and you can opt to use docker or podman to supply emscripten with the `--docker`/`-d` flag. This removes the need to install emscripten on your machine locally.

You can specify where to output the shared object file (native or WASM) with the `--output`/`-o` flag, which accepts either an absolute path or relative path. Note that if you don't supply this flag, the CLI will attempt to figure out what the language name is based on the parent directory (so building in `tree-sitter-javascript` will resolve to `javascript`) to use for the output file. If it can't figure it out, it will default to `parser`, thus generating `parser.so` or `parser.wasm` in the current working directory.

Expand Down