diff --git a/ctest/src/generator.rs b/ctest/src/generator.rs index a09f80fbc30d7..58383aa2d4352 100644 --- a/ctest/src/generator.rs +++ b/ctest/src/generator.rs @@ -13,8 +13,8 @@ use crate::ffi_items::FfiItems; use crate::template::{CTestTemplate, RustTestTemplate}; use crate::translator::translate_primitive_type; use crate::{ - Const, Field, MapInput, Parameter, Result, Static, Struct, TranslationError, Type, Union, - VolatileItemKind, expand, get_build_target, + BoxStr, Const, Field, Language, MapInput, Parameter, Result, Static, Struct, TranslationError, + Type, Union, VolatileItemKind, expand, get_build_target, }; /// A function that takes a mappable input and returns its mapping as `Some`, otherwise @@ -35,14 +35,16 @@ type CEnum = Box bool>; #[derive(Default)] #[expect(missing_debug_implementations)] pub struct TestGenerator { - pub(crate) headers: Vec, + pub(crate) headers: Vec<(BoxStr, Vec)>, pub(crate) target: Option, pub(crate) includes: Vec, out_dir: Option, pub(crate) flags: Vec, - pub(crate) defines: Vec<(String, Option)>, + pub(crate) global_defines: Vec<(String, Option)>, cfg: Vec<(String, Option)>, mapped_names: Vec, + /// The programming language to generate tests in. + pub(crate) language: Language, pub(crate) skips: Vec, pub(crate) verbose_skip: bool, pub(crate) volatile_items: Vec, @@ -100,7 +102,53 @@ impl TestGenerator { /// .header("bar.h"); /// ``` pub fn header(&mut self, header: &str) -> &mut Self { - self.headers.push(header.to_string()); + self.headers.push((header.into(), vec![])); + self + } + + /// Add a header to be included as part of the generated C file, as well as defines for it. + /// + /// The generated C test will be compiled by a C compiler, and this can be + /// used to ensure that all the necessary header files are included to test + /// all FFI definitions. The defines are only set for the inclusion of that header file, and are + /// undefined immediately after. + /// + /// # Examples + /// + /// ```no_run + /// use ctest::TestGenerator; + /// + /// let mut cfg = TestGenerator::new(); + /// cfg.header_with_defines("foo.h", Vec::::new()) + /// .header_with_defines("bar.h", vec!["DEBUG", "DEPRECATED"]); + /// ``` + pub fn header_with_defines( + &mut self, + header: &str, + defines: impl IntoIterator>, + ) -> &mut Self { + self.headers.push(( + header.into(), + defines.into_iter().map(|d| d.as_ref().into()).collect(), + )); + self + } + + /// Sets the programming language, by default it is C. + /// + /// This determines what compiler is chosen to compile the C/C++ tests, as well as adding + /// external linkage to the tests if set to C++, so that they can be used in Rust. + /// + /// # Examples + /// + /// ```no_run + /// use ctest::{TestGenerator, Language}; + /// + /// let mut cfg = TestGenerator::new(); + /// cfg.language(Language::CXX); + /// ``` + pub fn language(&mut self, language: Language) -> &mut Self { + self.language = language; self } @@ -585,7 +633,7 @@ impl TestGenerator { /// Set a `-D` flag for the C compiler being called. /// - /// This can be used to define various variables to configure how header + /// This can be used to define various global variables to configure how header /// files are included or what APIs are exposed from header files. /// /// # Examples @@ -598,7 +646,7 @@ impl TestGenerator { /// .define("_WIN32_WINNT", Some("0x8000")); /// ``` pub fn define(&mut self, k: &str, v: Option<&str>) -> &mut Self { - self.defines + self.global_defines .push((k.to_string(), v.map(std::string::ToString::to_string))); self } @@ -999,7 +1047,7 @@ impl TestGenerator { ensure_trailing_newline(&mut c_file); // Generate the C/Cxx side of the tests. - let c_output_path = output_file_path.with_extension("c"); + let c_output_path = output_file_path.with_extension(self.language.extension()); File::create(&c_output_path) .map_err(GenerationError::OsError)? .write_all(c_file.as_bytes()) diff --git a/ctest/src/lib.rs b/ctest/src/lib.rs index 133b31e7fbf8d..45dc41088d5d5 100644 --- a/ctest/src/lib.rs +++ b/ctest/src/lib.rs @@ -78,6 +78,27 @@ pub(crate) enum MapInput<'a> { UnionFieldType(&'a Union, &'a Field), } +/// The language used to generate the tests. +#[derive(Debug, Default, Clone)] +#[non_exhaustive] +pub enum Language { + /// The C Programming Language. + #[default] + C, + /// The C++ Programming Language. + CXX, +} + +impl Language { + /// Return the file extension of the programming language. + pub(crate) fn extension(&self) -> &str { + match self { + Self::C => "c", + Self::CXX => "cpp", + } + } +} + /// Search for the target to build for, specified manually or through an environment variable. /// /// This function will check the following places for the target name: diff --git a/ctest/src/runner.rs b/ctest/src/runner.rs index 9d2430345d17a..161db54c78e81 100644 --- a/ctest/src/runner.rs +++ b/ctest/src/runner.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use crate::generator::GenerationError; -use crate::{EDITION, Result, TestGenerator, get_build_target}; +use crate::{EDITION, Language, Result, TestGenerator, get_build_target}; /// Generate all tests for the given crate and output the Rust side to a file. #[doc(hidden)] @@ -24,7 +24,7 @@ pub fn generate_test( .map_err(|_| GenerationError::EnvVarNotFound("HOST, HOST_PLATFORM".to_string()))?; let mut cfg = cc::Build::new(); - cfg.file(output_file_path.with_extension("c")); + cfg.file(output_file_path.with_extension(generator.language.extension())); cfg.host(&host); if target.contains("msvc") { @@ -64,10 +64,12 @@ pub fn generate_test( cfg.flag(flag); } - for (k, v) in &generator.defines { + for (k, v) in &generator.global_defines { cfg.define(k, v.as_ref().map(|s| &s[..])); } + cfg.cpp(matches!(generator.language, Language::CXX)); + let stem: &str = output_file_path.file_stem().unwrap().to_str().unwrap(); cfg.target(&target) .out_dir(output_file_path.parent().unwrap()) diff --git a/ctest/src/template.rs b/ctest/src/template.rs index 2b0191ffe02a0..71ffb21ac4fc8 100644 --- a/ctest/src/template.rs +++ b/ctest/src/template.rs @@ -35,7 +35,7 @@ impl RustTestTemplate { #[template(path = "test.c")] pub(crate) struct CTestTemplate { pub template: TestTemplate, - pub headers: Vec, + pub headers: Vec<(BoxStr, Vec)>, } impl CTestTemplate { diff --git a/ctest/templates/test.c b/ctest/templates/test.c index ede1f37b67130..fe9d80ef68f8b 100644 --- a/ctest/templates/test.c +++ b/ctest/templates/test.c @@ -8,11 +8,25 @@ #include #include -{%- for header in self.headers +%} +{%- for (header, defines) in self.headers +%} +{%- for define in defines +%} +#define {{ define }} +{%- endfor +%} #include <{{ header }}> +{%- for define in defines +%} +#undef {{ define }} +{%- endfor +%} {%- endfor +%} +#if defined(__cplusplus) + #define CTEST_ALIGNOF(T) alignof(T) + #define CTEST_EXTERN extern "C" +#else + #define CTEST_ALIGNOF(T) _Alignof(T) + #define CTEST_EXTERN +#endif + typedef void (*ctest_void_func)(void); {%- for const_cstr in ctx.const_cstr_tests +%} @@ -21,7 +35,7 @@ static char *ctest_const_{{ const_cstr.id }}_val_static = {{ const_cstr.c_val }} // Define a function that returns a pointer to the value of the constant to test. // This will later be called on the Rust side via FFI. -char *ctest_const_cstr__{{ const_cstr.id }}(void) { +CTEST_EXTERN char *ctest_const_cstr__{{ const_cstr.id }}(void) { return ctest_const_{{ const_cstr.id }}_val_static; } {%- endfor +%} @@ -32,7 +46,7 @@ static {{ constant.c_ty }} ctest_const_{{ constant.id }}_val_static = {{ constan // Define a function that returns a pointer to the value of the constant to test. // This will later be called on the Rust side via FFI. -{{ constant.c_ty }} *ctest_const__{{ constant.id }}(void) { +CTEST_EXTERN {{ constant.c_ty }} *ctest_const__{{ constant.id }}(void) { return &ctest_const_{{ constant.id }}_val_static; } {%- endfor +%} @@ -40,17 +54,17 @@ static {{ constant.c_ty }} ctest_const_{{ constant.id }}_val_static = {{ constan {%- for item in ctx.size_align_tests +%} // Return the size of a type. -uint64_t ctest_size_of__{{ item.id }}(void) { return sizeof({{ item.c_ty }}); } +CTEST_EXTERN uint64_t ctest_size_of__{{ item.id }}(void) { return sizeof({{ item.c_ty }}); } // Return the alignment of a type. -uint64_t ctest_align_of__{{ item.id }}(void) { return _Alignof({{ item.c_ty }}); } +CTEST_EXTERN uint64_t ctest_align_of__{{ item.id }}(void) { return CTEST_ALIGNOF({{ item.c_ty }}); } {%- endfor +%} {%- for alias in ctx.signededness_tests +%} // Return `1` if the type is signed, otherwise return `0`. // Casting -1 to the aliased type if signed evaluates to `-1 < 0`, if unsigned to `MAX_VALUE < 0` -uint32_t ctest_signededness_of__{{ alias.id }}(void) { +CTEST_EXTERN uint32_t ctest_signededness_of__{{ alias.id }}(void) { {{ alias.c_ty }} all_ones = ({{ alias.c_ty }}) -1; return all_ones < 0; } @@ -59,12 +73,12 @@ uint32_t ctest_signededness_of__{{ alias.id }}(void) { {%- for item in ctx.field_size_offset_tests +%} // Return the offset of a struct/union field. -uint64_t ctest_offset_of__{{ item.id }}__{{ item.field.ident() }}(void) { +CTEST_EXTERN uint64_t ctest_offset_of__{{ item.id }}__{{ item.field.ident() }}(void) { return offsetof({{ item.c_ty }}, {{ item.c_field }}); } // Return the size of a struct/union field. -uint64_t ctest_size_of__{{ item.id }}__{{ item.field.ident() }}(void) { +CTEST_EXTERN uint64_t ctest_size_of__{{ item.id }}__{{ item.field.ident() }}(void) { return sizeof((({{ item.c_ty }}){}).{{ item.c_field }}); } {%- endfor +%} @@ -75,7 +89,7 @@ uint64_t ctest_size_of__{{ item.id }}__{{ item.field.ident() }}(void) { // This field can have a normal data type, or it could be a function pointer or an array, which // have different syntax. A typedef is used for convenience, but the syntax must be precomputed. typedef {{ item.volatile_keyword }}{{ item.field_return_type }}; -ctest_field_ty__{{ item.id }}__{{ item.field.ident() }} +CTEST_EXTERN ctest_field_ty__{{ item.id }}__{{ item.field.ident() }} ctest_field_ptr__{{ item.id }}__{{ item.field.ident() }}({{ item.c_ty }} *b) { return &b->{{ item.c_field }}; } @@ -92,7 +106,7 @@ ctest_field_ptr__{{ item.id }}__{{ item.field.ident() }}({{ item.c_ty }} *b) { // Tests whether the struct/union/alias `x` when passed by value to C and back to Rust // remains unchanged. // It checks if the size is the same as well as if the padding bytes are all in the correct place. -{{ item.c_ty }} ctest_roundtrip__{{ item.id }}( +CTEST_EXTERN {{ item.c_ty }} ctest_roundtrip__{{ item.id }}( {{ item.c_ty }} value, const uint8_t is_padding_byte[sizeof({{ item.c_ty }})], uint8_t value_bytes[sizeof({{ item.c_ty }})] @@ -130,7 +144,8 @@ ctest_field_ptr__{{ item.id }}__{{ item.field.ident() }}({{ item.c_ty }} *b) { {%- for item in ctx.foreign_fn_tests +%} -ctest_void_func ctest_foreign_fn__{{ item.id }}(void) { +// Return a function pointer. +CTEST_EXTERN ctest_void_func ctest_foreign_fn__{{ item.id }}(void) { return (ctest_void_func){{ item.c_val }}; } {%- endfor +%} @@ -142,7 +157,7 @@ ctest_void_func ctest_foreign_fn__{{ item.id }}(void) { {%- for static_ in ctx.foreign_static_tests +%} // Return a pointer to the static variable content. -void *ctest_static__{{ static_.id }}(void) { +CTEST_EXTERN void *ctest_static__{{ static_.id }}(void) { // FIXME(ctest): Not correct due to casting the function to a data pointer. return (void *)&{{ static_.c_val }}; } diff --git a/ctest/tests/basic.rs b/ctest/tests/basic.rs index faddc579bf386..960be68223123 100644 --- a/ctest/tests/basic.rs +++ b/ctest/tests/basic.rs @@ -11,15 +11,18 @@ use pretty_assertions::assert_eq; /// /// The files will be generated in a unique temporary directory that gets /// deleted when it goes out of scope. -fn default_generator(opt_level: u8, header: &str) -> Result<(TestGenerator, tempfile::TempDir)> { +fn default_generator( + opt_level: u8, + header: Option<&str>, +) -> Result<(TestGenerator, tempfile::TempDir)> { // FIXME(mbyx): Remove this in favor of not-unsafe alternatives. unsafe { env::set_var("OPT_LEVEL", opt_level.to_string()) }; let temp_dir = tempfile::tempdir()?; let mut generator = TestGenerator::new(); - generator - .out_dir(&temp_dir) - .include("tests/input") - .header(header); + generator.out_dir(&temp_dir).include("tests/input"); + if let Some(header) = header { + generator.header(header); + } Ok((generator, temp_dir)) } @@ -84,7 +87,7 @@ fn test_entrypoint_hierarchy() { let crate_path = include_path.join("hierarchy/lib.rs"); let library_path = "hierarchy.out.a"; - let (mut gen_, out_dir) = default_generator(1, "hierarchy.h").unwrap(); + let (mut gen_, out_dir) = default_generator(1, Some("hierarchy.h")).unwrap(); check_entrypoint(&mut gen_, out_dir, crate_path, library_path, include_path); } @@ -95,7 +98,7 @@ fn test_skip_simple() { let crate_path = include_path.join("simple.rs"); let library_path = "simple.out.with-skips.a"; - let (mut gen_, out_dir) = default_generator(1, "simple.h").unwrap(); + let (mut gen_, out_dir) = default_generator(1, Some("simple.h")).unwrap(); gen_.skip_const(|c| c.ident() == "B" || c.ident() == "A") .skip_c_enum(|e| e == "Color") .skip_alias(|a| a.ident() == "Byte") @@ -114,7 +117,7 @@ fn test_map_simple() { let crate_path = include_path.join("simple.rs"); let library_path = "simple.out.with-renames.a"; - let (mut gen_, out_dir) = default_generator(1, "simple.h").unwrap(); + let (mut gen_, out_dir) = default_generator(1, Some("simple.h")).unwrap(); gen_.rename_constant(|c| (c.ident() == "B").then(|| "C_B".to_string())) .alias_is_c_enum(|e| e == "Color") .skip_signededness(|ty| ty == "Color"); @@ -129,7 +132,9 @@ fn test_entrypoint_macro() { let crate_path = include_path.join("macro.rs"); let library_path = "macro.out.a"; - let (mut gen_, out_dir) = default_generator(1, "macro.h").unwrap(); + let (mut gen_, out_dir) = default_generator(1, None).unwrap(); + gen_.header_with_defines("macro.h", vec!["SUPPRESS_ERROR"]); + check_entrypoint(&mut gen_, out_dir, crate_path, library_path, include_path); } diff --git a/ctest/tests/input/hierarchy.h b/ctest/tests/input/hierarchy.h index 051136be9ab9b..b3ac0bfd9e1b9 100644 --- a/ctest/tests/input/hierarchy.h +++ b/ctest/tests/input/hierarchy.h @@ -1,6 +1,10 @@ #include #include +#ifdef SUPPRESS_ERROR +#error Expected SUPPRESS_ERROR to not be defined (testing per-file defines) +#endif + typedef unsigned int in6_addr; #define ON true diff --git a/ctest/tests/input/hierarchy.out.c b/ctest/tests/input/hierarchy.out.c index 47dbd18d45084..fc3dfcd62dbe1 100644 --- a/ctest/tests/input/hierarchy.out.c +++ b/ctest/tests/input/hierarchy.out.c @@ -4,28 +4,35 @@ #include #include #include - #include +#if defined(__cplusplus) + #define CTEST_ALIGNOF(T) alignof(T) + #define CTEST_EXTERN extern "C" +#else + #define CTEST_ALIGNOF(T) _Alignof(T) + #define CTEST_EXTERN +#endif + typedef void (*ctest_void_func)(void); static bool ctest_const_ON_val_static = ON; // Define a function that returns a pointer to the value of the constant to test. // This will later be called on the Rust side via FFI. -bool *ctest_const__ON(void) { +CTEST_EXTERN bool *ctest_const__ON(void) { return &ctest_const_ON_val_static; } // Return the size of a type. -uint64_t ctest_size_of__in6_addr(void) { return sizeof(in6_addr); } +CTEST_EXTERN uint64_t ctest_size_of__in6_addr(void) { return sizeof(in6_addr); } // Return the alignment of a type. -uint64_t ctest_align_of__in6_addr(void) { return _Alignof(in6_addr); } +CTEST_EXTERN uint64_t ctest_align_of__in6_addr(void) { return CTEST_ALIGNOF(in6_addr); } // Return `1` if the type is signed, otherwise return `0`. // Casting -1 to the aliased type if signed evaluates to `-1 < 0`, if unsigned to `MAX_VALUE < 0` -uint32_t ctest_signededness_of__in6_addr(void) { +CTEST_EXTERN uint32_t ctest_signededness_of__in6_addr(void) { in6_addr all_ones = (in6_addr) -1; return all_ones < 0; } @@ -39,7 +46,7 @@ uint32_t ctest_signededness_of__in6_addr(void) { // Tests whether the struct/union/alias `x` when passed by value to C and back to Rust // remains unchanged. // It checks if the size is the same as well as if the padding bytes are all in the correct place. -in6_addr ctest_roundtrip__in6_addr( +CTEST_EXTERN in6_addr ctest_roundtrip__in6_addr( in6_addr value, const uint8_t is_padding_byte[sizeof(in6_addr)], uint8_t value_bytes[sizeof(in6_addr)] @@ -73,7 +80,8 @@ in6_addr ctest_roundtrip__in6_addr( # pragma warning(disable:4191) #endif -ctest_void_func ctest_foreign_fn__malloc(void) { +// Return a function pointer. +CTEST_EXTERN ctest_void_func ctest_foreign_fn__malloc(void) { return (ctest_void_func)malloc; } @@ -82,7 +90,7 @@ ctest_void_func ctest_foreign_fn__malloc(void) { #endif // Return a pointer to the static variable content. -void *ctest_static__in6addr_any(void) { +CTEST_EXTERN void *ctest_static__in6addr_any(void) { // FIXME(ctest): Not correct due to casting the function to a data pointer. return (void *)&in6addr_any; } diff --git a/ctest/tests/input/macro.h b/ctest/tests/input/macro.h index 2b0ef6b80e351..c1fdc1e902f14 100644 --- a/ctest/tests/input/macro.h +++ b/ctest/tests/input/macro.h @@ -1,5 +1,9 @@ #include +#ifndef SUPPRESS_ERROR +#error Expected SUPPRESS_ERROR to be defined (testing per-file defines) +#endif + struct VecU8 { uint8_t x; diff --git a/ctest/tests/input/macro.out.c b/ctest/tests/input/macro.out.c index 2c035a8aeeae4..cb91c70ec76e3 100644 --- a/ctest/tests/input/macro.out.c +++ b/ctest/tests/input/macro.out.c @@ -5,59 +5,69 @@ #include #include +#define SUPPRESS_ERROR #include +#undef SUPPRESS_ERROR + +#if defined(__cplusplus) + #define CTEST_ALIGNOF(T) alignof(T) + #define CTEST_EXTERN extern "C" +#else + #define CTEST_ALIGNOF(T) _Alignof(T) + #define CTEST_EXTERN +#endif typedef void (*ctest_void_func)(void); // Return the size of a type. -uint64_t ctest_size_of__VecU8(void) { return sizeof(struct VecU8); } +CTEST_EXTERN uint64_t ctest_size_of__VecU8(void) { return sizeof(struct VecU8); } // Return the alignment of a type. -uint64_t ctest_align_of__VecU8(void) { return _Alignof(struct VecU8); } +CTEST_EXTERN uint64_t ctest_align_of__VecU8(void) { return CTEST_ALIGNOF(struct VecU8); } // Return the size of a type. -uint64_t ctest_size_of__VecU16(void) { return sizeof(struct VecU16); } +CTEST_EXTERN uint64_t ctest_size_of__VecU16(void) { return sizeof(struct VecU16); } // Return the alignment of a type. -uint64_t ctest_align_of__VecU16(void) { return _Alignof(struct VecU16); } +CTEST_EXTERN uint64_t ctest_align_of__VecU16(void) { return CTEST_ALIGNOF(struct VecU16); } // Return the offset of a struct/union field. -uint64_t ctest_offset_of__VecU8__x(void) { +CTEST_EXTERN uint64_t ctest_offset_of__VecU8__x(void) { return offsetof(struct VecU8, x); } // Return the size of a struct/union field. -uint64_t ctest_size_of__VecU8__x(void) { +CTEST_EXTERN uint64_t ctest_size_of__VecU8__x(void) { return sizeof(((struct VecU8){}).x); } // Return the offset of a struct/union field. -uint64_t ctest_offset_of__VecU8__y(void) { +CTEST_EXTERN uint64_t ctest_offset_of__VecU8__y(void) { return offsetof(struct VecU8, y); } // Return the size of a struct/union field. -uint64_t ctest_size_of__VecU8__y(void) { +CTEST_EXTERN uint64_t ctest_size_of__VecU8__y(void) { return sizeof(((struct VecU8){}).y); } // Return the offset of a struct/union field. -uint64_t ctest_offset_of__VecU16__x(void) { +CTEST_EXTERN uint64_t ctest_offset_of__VecU16__x(void) { return offsetof(struct VecU16, x); } // Return the size of a struct/union field. -uint64_t ctest_size_of__VecU16__x(void) { +CTEST_EXTERN uint64_t ctest_size_of__VecU16__x(void) { return sizeof(((struct VecU16){}).x); } // Return the offset of a struct/union field. -uint64_t ctest_offset_of__VecU16__y(void) { +CTEST_EXTERN uint64_t ctest_offset_of__VecU16__y(void) { return offsetof(struct VecU16, y); } // Return the size of a struct/union field. -uint64_t ctest_size_of__VecU16__y(void) { +CTEST_EXTERN uint64_t ctest_size_of__VecU16__y(void) { return sizeof(((struct VecU16){}).y); } @@ -65,7 +75,7 @@ uint64_t ctest_size_of__VecU16__y(void) { // This field can have a normal data type, or it could be a function pointer or an array, which // have different syntax. A typedef is used for convenience, but the syntax must be precomputed. typedef uint8_t *ctest_field_ty__VecU8__x; -ctest_field_ty__VecU8__x +CTEST_EXTERN ctest_field_ty__VecU8__x ctest_field_ptr__VecU8__x(struct VecU8 *b) { return &b->x; } @@ -74,7 +84,7 @@ ctest_field_ptr__VecU8__x(struct VecU8 *b) { // This field can have a normal data type, or it could be a function pointer or an array, which // have different syntax. A typedef is used for convenience, but the syntax must be precomputed. typedef uint8_t *ctest_field_ty__VecU8__y; -ctest_field_ty__VecU8__y +CTEST_EXTERN ctest_field_ty__VecU8__y ctest_field_ptr__VecU8__y(struct VecU8 *b) { return &b->y; } @@ -83,7 +93,7 @@ ctest_field_ptr__VecU8__y(struct VecU8 *b) { // This field can have a normal data type, or it could be a function pointer or an array, which // have different syntax. A typedef is used for convenience, but the syntax must be precomputed. typedef uint16_t *ctest_field_ty__VecU16__x; -ctest_field_ty__VecU16__x +CTEST_EXTERN ctest_field_ty__VecU16__x ctest_field_ptr__VecU16__x(struct VecU16 *b) { return &b->x; } @@ -92,7 +102,7 @@ ctest_field_ptr__VecU16__x(struct VecU16 *b) { // This field can have a normal data type, or it could be a function pointer or an array, which // have different syntax. A typedef is used for convenience, but the syntax must be precomputed. typedef uint16_t *ctest_field_ty__VecU16__y; -ctest_field_ty__VecU16__y +CTEST_EXTERN ctest_field_ty__VecU16__y ctest_field_ptr__VecU16__y(struct VecU16 *b) { return &b->y; } @@ -106,7 +116,7 @@ ctest_field_ptr__VecU16__y(struct VecU16 *b) { // Tests whether the struct/union/alias `x` when passed by value to C and back to Rust // remains unchanged. // It checks if the size is the same as well as if the padding bytes are all in the correct place. -struct VecU8 ctest_roundtrip__VecU8( +CTEST_EXTERN struct VecU8 ctest_roundtrip__VecU8( struct VecU8 value, const uint8_t is_padding_byte[sizeof(struct VecU8)], uint8_t value_bytes[sizeof(struct VecU8)] @@ -133,7 +143,7 @@ struct VecU8 ctest_roundtrip__VecU8( // Tests whether the struct/union/alias `x` when passed by value to C and back to Rust // remains unchanged. // It checks if the size is the same as well as if the padding bytes are all in the correct place. -struct VecU16 ctest_roundtrip__VecU16( +CTEST_EXTERN struct VecU16 ctest_roundtrip__VecU16( struct VecU16 value, const uint8_t is_padding_byte[sizeof(struct VecU16)], uint8_t value_bytes[sizeof(struct VecU16)] diff --git a/ctest/tests/input/simple.out.with-renames.c b/ctest/tests/input/simple.out.with-renames.c index 7427c752cb78f..cdb4c348e7a4f 100644 --- a/ctest/tests/input/simple.out.with-renames.c +++ b/ctest/tests/input/simple.out.with-renames.c @@ -4,16 +4,23 @@ #include #include #include - #include +#if defined(__cplusplus) + #define CTEST_ALIGNOF(T) alignof(T) + #define CTEST_EXTERN extern "C" +#else + #define CTEST_ALIGNOF(T) _Alignof(T) + #define CTEST_EXTERN +#endif + typedef void (*ctest_void_func)(void); static char *ctest_const_A_val_static = A; // Define a function that returns a pointer to the value of the constant to test. // This will later be called on the Rust side via FFI. -char *ctest_const_cstr__A(void) { +CTEST_EXTERN char *ctest_const_cstr__A(void) { return ctest_const_A_val_static; } @@ -21,7 +28,7 @@ static char *ctest_const_B_val_static = C_B; // Define a function that returns a pointer to the value of the constant to test. // This will later be called on the Rust side via FFI. -char *ctest_const_cstr__B(void) { +CTEST_EXTERN char *ctest_const_cstr__B(void) { return ctest_const_B_val_static; } @@ -29,7 +36,7 @@ static enum Color ctest_const_RED_val_static = RED; // Define a function that returns a pointer to the value of the constant to test. // This will later be called on the Rust side via FFI. -enum Color *ctest_const__RED(void) { +CTEST_EXTERN enum Color *ctest_const__RED(void) { return &ctest_const_RED_val_static; } @@ -37,7 +44,7 @@ static enum Color ctest_const_BLUE_val_static = BLUE; // Define a function that returns a pointer to the value of the constant to test. // This will later be called on the Rust side via FFI. -enum Color *ctest_const__BLUE(void) { +CTEST_EXTERN enum Color *ctest_const__BLUE(void) { return &ctest_const_BLUE_val_static; } @@ -45,88 +52,88 @@ static enum Color ctest_const_GREEN_val_static = GREEN; // Define a function that returns a pointer to the value of the constant to test. // This will later be called on the Rust side via FFI. -enum Color *ctest_const__GREEN(void) { +CTEST_EXTERN enum Color *ctest_const__GREEN(void) { return &ctest_const_GREEN_val_static; } // Return the size of a type. -uint64_t ctest_size_of__Byte(void) { return sizeof(Byte); } +CTEST_EXTERN uint64_t ctest_size_of__Byte(void) { return sizeof(Byte); } // Return the alignment of a type. -uint64_t ctest_align_of__Byte(void) { return _Alignof(Byte); } +CTEST_EXTERN uint64_t ctest_align_of__Byte(void) { return CTEST_ALIGNOF(Byte); } // Return the size of a type. -uint64_t ctest_size_of__Color(void) { return sizeof(enum Color); } +CTEST_EXTERN uint64_t ctest_size_of__Color(void) { return sizeof(enum Color); } // Return the alignment of a type. -uint64_t ctest_align_of__Color(void) { return _Alignof(enum Color); } +CTEST_EXTERN uint64_t ctest_align_of__Color(void) { return CTEST_ALIGNOF(enum Color); } // Return the size of a type. -uint64_t ctest_size_of__Person(void) { return sizeof(struct Person); } +CTEST_EXTERN uint64_t ctest_size_of__Person(void) { return sizeof(struct Person); } // Return the alignment of a type. -uint64_t ctest_align_of__Person(void) { return _Alignof(struct Person); } +CTEST_EXTERN uint64_t ctest_align_of__Person(void) { return CTEST_ALIGNOF(struct Person); } // Return the size of a type. -uint64_t ctest_size_of__Word(void) { return sizeof(union Word); } +CTEST_EXTERN uint64_t ctest_size_of__Word(void) { return sizeof(union Word); } // Return the alignment of a type. -uint64_t ctest_align_of__Word(void) { return _Alignof(union Word); } +CTEST_EXTERN uint64_t ctest_align_of__Word(void) { return CTEST_ALIGNOF(union Word); } // Return `1` if the type is signed, otherwise return `0`. // Casting -1 to the aliased type if signed evaluates to `-1 < 0`, if unsigned to `MAX_VALUE < 0` -uint32_t ctest_signededness_of__Byte(void) { +CTEST_EXTERN uint32_t ctest_signededness_of__Byte(void) { Byte all_ones = (Byte) -1; return all_ones < 0; } // Return the offset of a struct/union field. -uint64_t ctest_offset_of__Person__name(void) { +CTEST_EXTERN uint64_t ctest_offset_of__Person__name(void) { return offsetof(struct Person, name); } // Return the size of a struct/union field. -uint64_t ctest_size_of__Person__name(void) { +CTEST_EXTERN uint64_t ctest_size_of__Person__name(void) { return sizeof(((struct Person){}).name); } // Return the offset of a struct/union field. -uint64_t ctest_offset_of__Person__age(void) { +CTEST_EXTERN uint64_t ctest_offset_of__Person__age(void) { return offsetof(struct Person, age); } // Return the size of a struct/union field. -uint64_t ctest_size_of__Person__age(void) { +CTEST_EXTERN uint64_t ctest_size_of__Person__age(void) { return sizeof(((struct Person){}).age); } // Return the offset of a struct/union field. -uint64_t ctest_offset_of__Person__job(void) { +CTEST_EXTERN uint64_t ctest_offset_of__Person__job(void) { return offsetof(struct Person, job); } // Return the size of a struct/union field. -uint64_t ctest_size_of__Person__job(void) { +CTEST_EXTERN uint64_t ctest_size_of__Person__job(void) { return sizeof(((struct Person){}).job); } // Return the offset of a struct/union field. -uint64_t ctest_offset_of__Word__word(void) { +CTEST_EXTERN uint64_t ctest_offset_of__Word__word(void) { return offsetof(union Word, word); } // Return the size of a struct/union field. -uint64_t ctest_size_of__Word__word(void) { +CTEST_EXTERN uint64_t ctest_size_of__Word__word(void) { return sizeof(((union Word){}).word); } // Return the offset of a struct/union field. -uint64_t ctest_offset_of__Word__byte(void) { +CTEST_EXTERN uint64_t ctest_offset_of__Word__byte(void) { return offsetof(union Word, byte); } // Return the size of a struct/union field. -uint64_t ctest_size_of__Word__byte(void) { +CTEST_EXTERN uint64_t ctest_size_of__Word__byte(void) { return sizeof(((union Word){}).byte); } @@ -134,7 +141,7 @@ uint64_t ctest_size_of__Word__byte(void) { // This field can have a normal data type, or it could be a function pointer or an array, which // have different syntax. A typedef is used for convenience, but the syntax must be precomputed. typedef const char **ctest_field_ty__Person__name; -ctest_field_ty__Person__name +CTEST_EXTERN ctest_field_ty__Person__name ctest_field_ptr__Person__name(struct Person *b) { return &b->name; } @@ -143,7 +150,7 @@ ctest_field_ptr__Person__name(struct Person *b) { // This field can have a normal data type, or it could be a function pointer or an array, which // have different syntax. A typedef is used for convenience, but the syntax must be precomputed. typedef uint8_t *ctest_field_ty__Person__age; -ctest_field_ty__Person__age +CTEST_EXTERN ctest_field_ty__Person__age ctest_field_ptr__Person__age(struct Person *b) { return &b->age; } @@ -152,7 +159,7 @@ ctest_field_ptr__Person__age(struct Person *b) { // This field can have a normal data type, or it could be a function pointer or an array, which // have different syntax. A typedef is used for convenience, but the syntax must be precomputed. typedef void (**ctest_field_ty__Person__job)(uint8_t, const char *); -ctest_field_ty__Person__job +CTEST_EXTERN ctest_field_ty__Person__job ctest_field_ptr__Person__job(struct Person *b) { return &b->job; } @@ -161,7 +168,7 @@ ctest_field_ptr__Person__job(struct Person *b) { // This field can have a normal data type, or it could be a function pointer or an array, which // have different syntax. A typedef is used for convenience, but the syntax must be precomputed. typedef uint16_t *ctest_field_ty__Word__word; -ctest_field_ty__Word__word +CTEST_EXTERN ctest_field_ty__Word__word ctest_field_ptr__Word__word(union Word *b) { return &b->word; } @@ -170,7 +177,7 @@ ctest_field_ptr__Word__word(union Word *b) { // This field can have a normal data type, or it could be a function pointer or an array, which // have different syntax. A typedef is used for convenience, but the syntax must be precomputed. typedef Byte (*ctest_field_ty__Word__byte)[2]; -ctest_field_ty__Word__byte +CTEST_EXTERN ctest_field_ty__Word__byte ctest_field_ptr__Word__byte(union Word *b) { return &b->byte; } @@ -184,7 +191,7 @@ ctest_field_ptr__Word__byte(union Word *b) { // Tests whether the struct/union/alias `x` when passed by value to C and back to Rust // remains unchanged. // It checks if the size is the same as well as if the padding bytes are all in the correct place. -Byte ctest_roundtrip__Byte( +CTEST_EXTERN Byte ctest_roundtrip__Byte( Byte value, const uint8_t is_padding_byte[sizeof(Byte)], uint8_t value_bytes[sizeof(Byte)] @@ -211,7 +218,7 @@ Byte ctest_roundtrip__Byte( // Tests whether the struct/union/alias `x` when passed by value to C and back to Rust // remains unchanged. // It checks if the size is the same as well as if the padding bytes are all in the correct place. -enum Color ctest_roundtrip__Color( +CTEST_EXTERN enum Color ctest_roundtrip__Color( enum Color value, const uint8_t is_padding_byte[sizeof(enum Color)], uint8_t value_bytes[sizeof(enum Color)] @@ -238,7 +245,7 @@ enum Color ctest_roundtrip__Color( // Tests whether the struct/union/alias `x` when passed by value to C and back to Rust // remains unchanged. // It checks if the size is the same as well as if the padding bytes are all in the correct place. -struct Person ctest_roundtrip__Person( +CTEST_EXTERN struct Person ctest_roundtrip__Person( struct Person value, const uint8_t is_padding_byte[sizeof(struct Person)], uint8_t value_bytes[sizeof(struct Person)] @@ -265,7 +272,7 @@ struct Person ctest_roundtrip__Person( // Tests whether the struct/union/alias `x` when passed by value to C and back to Rust // remains unchanged. // It checks if the size is the same as well as if the padding bytes are all in the correct place. -union Word ctest_roundtrip__Word( +CTEST_EXTERN union Word ctest_roundtrip__Word( union Word value, const uint8_t is_padding_byte[sizeof(union Word)], uint8_t value_bytes[sizeof(union Word)] @@ -299,7 +306,8 @@ union Word ctest_roundtrip__Word( # pragma warning(disable:4191) #endif -ctest_void_func ctest_foreign_fn__calloc(void) { +// Return a function pointer. +CTEST_EXTERN ctest_void_func ctest_foreign_fn__calloc(void) { return (ctest_void_func)calloc; } @@ -308,7 +316,7 @@ ctest_void_func ctest_foreign_fn__calloc(void) { #endif // Return a pointer to the static variable content. -void *ctest_static__byte(void) { +CTEST_EXTERN void *ctest_static__byte(void) { // FIXME(ctest): Not correct due to casting the function to a data pointer. return (void *)&byte; } diff --git a/ctest/tests/input/simple.out.with-skips.c b/ctest/tests/input/simple.out.with-skips.c index e080e64cde6bc..47960c986934a 100644 --- a/ctest/tests/input/simple.out.with-skips.c +++ b/ctest/tests/input/simple.out.with-skips.c @@ -4,9 +4,16 @@ #include #include #include - #include +#if defined(__cplusplus) + #define CTEST_ALIGNOF(T) alignof(T) + #define CTEST_EXTERN extern "C" +#else + #define CTEST_ALIGNOF(T) _Alignof(T) + #define CTEST_EXTERN +#endif + typedef void (*ctest_void_func)(void); #ifdef _MSC_VER