diff --git a/.github/workflows/cbindgen.yml b/.github/workflows/cbindgen.yml index 5c13353b4..f0208e993 100644 --- a/.github/workflows/cbindgen.yml +++ b/.github/workflows/cbindgen.yml @@ -23,15 +23,15 @@ jobs: - name: Run rustfmt run: | - cargo fmt --check + cargo +stable fmt --check - name: Run clippy run: | - cargo clippy --workspace -- -D warnings + cargo +stable clippy --workspace -- -D warnings - name: Install minimum supported Rust version id: msrv - uses: dtolnay/rust-toolchain@1.57 + uses: dtolnay/rust-toolchain@1.64 - name: Build with minimum supported Rust version run: | @@ -64,18 +64,18 @@ jobs: - name: Build run: | - cargo build --verbose + cargo +stable build --verbose - name: Build no-default-features run: | - cargo build --verbose --no-default-features + cargo +stable build --verbose --no-default-features - name: Test package env: CBINDGEN_TEST_VERIFY: 1 run: | - cargo package --verbose - (cd target/package/cbindgen-$(cargo run -- --version | cut -d ' ' -f 2) && cargo test --verbose) + cargo +stable package --verbose + (cd target/package/cbindgen-$(cargo +stable run -- --version | cut -d ' ' -f 2) && cargo +stable test --verbose) - name: Install nightly Rust uses: dtolnay/rust-toolchain@nightly diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 379e476d4..6aa6168da 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,8 +8,13 @@ on: jobs: linux-binaries: + permissions: + # Grant the GITHUB_TOKEN additional permissions necessary for creating a release. + # We only run this action for tags, so any code has already been reviewed by + # someone with permissions to create a tag. + contents: write - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 @@ -17,9 +22,14 @@ jobs: - name: Install stable uses: dtolnay/rust-toolchain@stable + - name: semver + run: | + cargo +stable install cargo-semver-checks --locked + cargo semver-checks check-release + - name: Build cbindgen run: | - cargo build --release + cargo +stable build --release - name: Strip cbindgen run: | @@ -39,11 +49,8 @@ jobs: sed '$ d' | awk '{$1=$1};1' > CHANGES.txt - name: Create a release - uses: softprops/action-gh-release@v1 - with: - name: v${{ steps.tagName.outputs.version }} - body_path: CHANGES.txt - files: | - target/release/cbindgen + run: | + TAG=${{ steps.tagName.outputs.version }} + gh release create ${TAG} --title "${TAG}" --notes-file "CHANGES.txt" --draft 'target/release/cbindgen#cbindgen-ubuntu20.04' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGES b/CHANGES index c19d62a78..2cbf62984 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,15 @@ +# Unreleased + + * Fix swapping of `>>=` and `<<=` in constants. + +## 0.25.0 + + * Re-release of yanked 0.24.6 as a major release + * Update MSRV to 1.57 + * Support variadic arguments (`...`) (#805) + * Add --depfile option (#820) + * Breaking changes: The `Config` struct now has a private member. + ## 0.24.6 (YANKED: depfile option was breaking, see #841) * Update MSRV to 1.57 diff --git a/Cargo.lock b/Cargo.lock index 8f71f5e2d..499d92a09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,9 +192,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] diff --git a/Cargo.toml b/Cargo.toml index 7b44bc399..e654e58b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["bindings", "ffi", "code-generation"] categories = ["external-ffi-bindings", "development-tools::ffi"] repository = "https://github.com/eqrion/cbindgen" edition = "2018" -rust-version = "1.57" +rust-version = "1.64" exclude = [ "tests/profile.rs", # Test relies in a sub-crate, see https://github.com/rust-lang/cargo/issues/9017 ] @@ -26,7 +26,7 @@ serde = { version = "1.0.103", default-features = false, features = ["derive"] } serde_json = "1.0" tempfile = "3" toml = "0.5" -proc-macro2 = "1" +proc-macro2 = "1.0.60" quote = "1" heck = "0.4" diff --git a/contributing.md b/contributing.md index 5625d50d0..85822639f 100644 --- a/contributing.md +++ b/contributing.md @@ -16,6 +16,7 @@ There is continuous integration setup for `cbindgen` using [GitHub Actions](http In addition to a C/C++ compiler `cargo test` requires Python and Cython (`python -m pip install Cython`) for checking Cython bindings generated from tests (`.pyx` files). +Note that the tests will be failed with Cython 3.x or later. Please run `cargo test` before filing a pull request to be sure that all tests pass. This will also update the test expectations. diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..5d56faf9a --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs index a266e595d..1227e140c 100644 --- a/src/bindgen/bindings.rs +++ b/src/bindgen/bindings.rs @@ -141,7 +141,7 @@ impl Bindings { let mut canon_source_files: Vec<_> = self .source_files .iter() - .chain(self.config.config_path.as_ref().into_iter()) + .chain(self.config.config_path.as_ref()) .map(|p| p.canonicalize().unwrap()) .collect(); // Sorting makes testing easier by ensuring the output is ordered. diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index 4022a09e2..dfbfa6cdb 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -321,8 +321,8 @@ impl Literal { syn::BinOp::BitXorEq(..) => "^=", syn::BinOp::BitAndEq(..) => "&=", syn::BinOp::BitOrEq(..) => "|=", - syn::BinOp::ShlEq(..) => ">>=", - syn::BinOp::ShrEq(..) => "<<=", + syn::BinOp::ShlEq(..) => "<<=", + syn::BinOp::ShrEq(..) => ">>=", }; Ok(Literal::BinOp { left: Box::new(l), diff --git a/src/bindgen/ir/global.rs b/src/bindgen/ir/global.rs index 47c996055..82a1756e1 100644 --- a/src/bindgen/ir/global.rs +++ b/src/bindgen/ir/global.rs @@ -4,8 +4,6 @@ use std::io::Write; -use syn::ext::IdentExt; - use crate::bindgen::cdecl; use crate::bindgen::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; @@ -26,7 +24,11 @@ pub struct Static { } impl Static { - pub fn load(item: &syn::ItemStatic, mod_cfg: Option<&Cfg>) -> Result { + pub fn load( + path: Path, + item: &syn::ItemStatic, + mod_cfg: Option<&Cfg>, + ) -> Result { let ty = Type::load(&item.ty)?; if ty.is_none() { @@ -34,7 +36,7 @@ impl Static { } Ok(Static::new( - Path::new(item.ident.unraw().to_string()), + path, ty.unwrap(), item.mutability.is_some(), Cfg::append(mod_cfg, Cfg::load(&item.attrs)), diff --git a/src/bindgen/parser.rs b/src/bindgen/parser.rs index e8add192a..9cd6a54a4 100644 --- a/src/bindgen/parser.rs +++ b/src/bindgen/parser.rs @@ -18,7 +18,7 @@ use crate::bindgen::ir::{ AnnotationSet, Cfg, Constant, Documentation, Enum, Function, GenericParam, GenericParams, ItemMap, OpaqueItem, Path, Static, Struct, Type, Typedef, Union, }; -use crate::bindgen::utilities::{SynAbiHelpers, SynAttributeHelpers, SynItemFnHelpers}; +use crate::bindgen::utilities::{SynAbiHelpers, SynAttributeHelpers, SynItemHelpers}; const STD_CRATES: &[&str] = &[ "std", @@ -643,7 +643,6 @@ impl Parse { item, Some(self_type), &item.sig, - &item.vis, &item.attrs, ) } @@ -665,7 +664,6 @@ impl Parse { item, None, &item.sig, - &item.vis, &item.attrs, ); } @@ -677,10 +675,9 @@ impl Parse { binding_crate_name: &str, crate_name: &str, mod_cfg: Option<&Cfg>, - named_symbol: &dyn SynItemFnHelpers, + named_symbol: &dyn SynItemHelpers, self_type: Option<&Path>, sig: &syn::Signature, - vis: &syn::Visibility, attrs: &[syn::Attribute], ) { if !config @@ -707,53 +704,29 @@ impl Parse { let is_extern_c = sig.abi.is_omitted() || sig.abi.is_c(); let exported_name = named_symbol.exported_name(); - if let syn::Visibility::Public(_) = vis { - match (is_extern_c, exported_name) { - (true, Some(exported_name)) => { - let path = Path::new(exported_name); - match Function::load(path, self_type, sig, false, attrs, mod_cfg) { - Ok(func) => { - info!("Take {}.", loggable_item_name()); - self.functions.push(func); - } - Err(msg) => { - error!("Cannot use fn {} ({}).", loggable_item_name(), msg); - } + match (is_extern_c, exported_name) { + (true, Some(exported_name)) => { + let path = Path::new(exported_name); + match Function::load(path, self_type, sig, false, attrs, mod_cfg) { + Ok(func) => { + info!("Take {}.", loggable_item_name()); + self.functions.push(func); + } + Err(msg) => { + error!("Cannot use fn {} ({}).", loggable_item_name(), msg); } } - (true, None) => { - warn!( - "Skipping {} - (not `no_mangle`, and has no `export_name` attribute)", - loggable_item_name() - ); - } - (false, Some(_exported_name)) => { - warn!("Skipping {} - (not `extern \"C\"`", loggable_item_name()); - } - (false, None) => {} } - } else { - match (is_extern_c, exported_name) { - (true, Some(..)) => { - warn!( - "Skipping {} - (not `pub` but is `extern \"C\"` and `no_mangle`)", - loggable_item_name() - ); - } - (true, None) => { - warn!( - "Skipping {} - (not `pub` but is `extern \"C\"`)", - loggable_item_name() - ); - } - (false, Some(..)) => { - warn!( - "Skipping {} - (not `pub` but is `no_mangle`)", - loggable_item_name() - ); - } - (false, None) => {} + (true, None) => { + warn!( + "Skipping {} - (not `no_mangle`, and has no `export_name` attribute)", + loggable_item_name() + ); } + (false, Some(_exported_name)) => { + warn!("Skipping {} - (not `extern \"C\"`", loggable_item_name()); + } + (false, None) => {} } } @@ -892,27 +865,18 @@ impl Parse { return; } - if let syn::Visibility::Public(_) = item.vis { - if item.is_no_mangle() { - match Static::load(item, mod_cfg) { - Ok(constant) => { - info!("Take {}::{}.", crate_name, &item.ident); - - self.globals.try_insert(constant); - } - Err(msg) => { - warn!("Skip {}::{} - ({})", crate_name, &item.ident, msg); - } + if let Some(exported_name) = item.exported_name() { + let path = Path::new(exported_name); + match Static::load(path, item, mod_cfg) { + Ok(constant) => { + info!("Take {}::{}.", crate_name, &item.ident); + self.globals.try_insert(constant); + } + Err(msg) => { + warn!("Skip {}::{} - ({})", crate_name, &item.ident, msg); } } - } - - // TODO - if let syn::Visibility::Public(_) = item.vis { } else { - warn!("Skip {}::{} - (not `pub`).", crate_name, &item.ident); - } - if !item.is_no_mangle() { warn!("Skip {}::{} - (not `no_mangle`).", crate_name, &item.ident); } } diff --git a/src/bindgen/utilities.rs b/src/bindgen/utilities.rs index ae3ee6032..bea1d99a4 100644 --- a/src/bindgen/utilities.rs +++ b/src/bindgen/utilities.rs @@ -30,11 +30,11 @@ where } } -pub trait SynItemFnHelpers: SynAttributeHelpers { +pub trait SynItemHelpers: SynAttributeHelpers { fn exported_name(&self) -> Option; } -impl SynItemFnHelpers for syn::ItemFn { +impl SynItemHelpers for syn::ItemFn { fn exported_name(&self) -> Option { self.attrs .attr_name_value_lookup("export_name") @@ -48,7 +48,7 @@ impl SynItemFnHelpers for syn::ItemFn { } } -impl SynItemFnHelpers for syn::ImplItemMethod { +impl SynItemHelpers for syn::ImplItemMethod { fn exported_name(&self) -> Option { self.attrs .attr_name_value_lookup("export_name") @@ -62,6 +62,20 @@ impl SynItemFnHelpers for syn::ImplItemMethod { } } +impl SynItemHelpers for syn::ItemStatic { + fn exported_name(&self) -> Option { + self.attrs + .attr_name_value_lookup("export_name") + .or_else(|| { + if self.is_no_mangle() { + Some(self.ident.unraw().to_string()) + } else { + None + } + }) + } +} + /// Returns whether this attribute causes us to skip at item. This basically /// checks for `#[cfg(test)]`, `#[test]`, `/// cbindgen::ignore` and /// variations thereof. diff --git a/src/logging.rs b/src/logging.rs index 3b698c8ee..0b49ce2d8 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -14,7 +14,7 @@ pub struct ErrorLogger; impl TraceLogger { pub fn init() -> Result<(), SetLoggerError> { - log::set_logger(&InfoLogger)?; + log::set_logger(&TraceLogger)?; log::set_max_level(LevelFilter::Trace); Ok(()) } @@ -37,7 +37,7 @@ impl log::Log for TraceLogger { impl WarnLogger { pub fn init() -> Result<(), SetLoggerError> { - log::set_logger(&InfoLogger)?; + log::set_logger(&WarnLogger)?; log::set_max_level(LevelFilter::Warn); Ok(()) } @@ -60,7 +60,7 @@ impl log::Log for WarnLogger { impl ErrorLogger { pub fn init() -> Result<(), SetLoggerError> { - log::set_logger(&InfoLogger)?; + log::set_logger(&ErrorLogger)?; log::set_max_level(LevelFilter::Error); Ok(()) } diff --git a/tests/expectations/non_pub_extern.c b/tests/expectations/non_pub_extern.c new file mode 100644 index 000000000..779000d65 --- /dev/null +++ b/tests/expectations/non_pub_extern.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +extern const uint32_t FIRST; + +extern const uint32_t RENAMED; + +void first(void); + +void renamed(void); diff --git a/tests/expectations/non_pub_extern.compat.c b/tests/expectations/non_pub_extern.compat.c new file mode 100644 index 000000000..549345195 --- /dev/null +++ b/tests/expectations/non_pub_extern.compat.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +extern const uint32_t FIRST; + +extern const uint32_t RENAMED; + +void first(void); + +void renamed(void); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/non_pub_extern.cpp b/tests/expectations/non_pub_extern.cpp new file mode 100644 index 000000000..c931fbc39 --- /dev/null +++ b/tests/expectations/non_pub_extern.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include +#include + +extern "C" { + +extern const uint32_t FIRST; + +extern const uint32_t RENAMED; + +void first(); + +void renamed(); + +} // extern "C" diff --git a/tests/expectations/non_pub_extern.pyx b/tests/expectations/non_pub_extern.pyx new file mode 100644 index 000000000..112841ffd --- /dev/null +++ b/tests/expectations/non_pub_extern.pyx @@ -0,0 +1,15 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + extern const uint32_t FIRST; + + extern const uint32_t RENAMED; + + void first(); + + void renamed(); diff --git a/tests/rust/non_pub_extern.rs b/tests/rust/non_pub_extern.rs new file mode 100644 index 000000000..4174324d7 --- /dev/null +++ b/tests/rust/non_pub_extern.rs @@ -0,0 +1,13 @@ +#[no_mangle] +static FIRST: u32 = 10; + +#[export_name = "RENAMED"] +static SECOND: u32 = 42; + +#[no_mangle] +extern "C" fn first() +{ } + +#[export_name = "renamed"] +extern fn second() +{ }