{"id":16872250,"url":"https://github.com/hywan/inline-c-rs","last_synced_at":"2025-04-07T16:13:11.127Z","repository":{"id":51296023,"uuid":"301422806","full_name":"Hywan/inline-c-rs","owner":"Hywan","description":"Write and execute C code inside Rust.","archived":false,"fork":false,"pushed_at":"2024-10-08T07:33:50.000Z","size":182,"stargazers_count":150,"open_issues_count":3,"forks_count":19,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-31T15:08:58.534Z","etag":null,"topics":["c","rust","rust-library"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Hywan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-10-05T13:44:51.000Z","updated_at":"2024-12-31T04:44:52.000Z","dependencies_parsed_at":"2024-06-19T17:36:52.164Z","dependency_job_id":"e5530c18-bafe-489f-91f1-61e5f21b064d","html_url":"https://github.com/Hywan/inline-c-rs","commit_stats":{"total_commits":88,"total_committers":8,"mean_commits":11.0,"dds":0.09090909090909094,"last_synced_commit":"e24acb7036bbe7254a0151fa1cdf1a86ace16bab"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hywan%2Finline-c-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hywan%2Finline-c-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hywan%2Finline-c-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hywan%2Finline-c-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hywan","download_url":"https://codeload.github.com/Hywan/inline-c-rs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247685627,"owners_count":20979085,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","rust","rust-library"],"created_at":"2024-10-13T15:14:04.102Z","updated_at":"2025-04-07T16:13:11.108Z","avatar_url":"https://github.com/Hywan.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  \u003cimg src=\"./doc/lilac.jpg\" alt=\"Lilac-breated Roller, by David Clode\" width=\"300px\" /\u003e\u003cbr /\u003e\n  inline-c\n\u003c/h1\u003e\n\n[![crates.io](https://img.shields.io/crates/v/inline-c)](https://crates.io/crates/inline-c)\n[![documentation](https://img.shields.io/badge/doc-inline--c-green)](https://docs.rs/inline-c)\n\n`inline-c` is a small crate that allows a user to write C (including\nC++) code inside Rust. Both environments are strictly sandboxed: it is\nnon-obvious for a value to cross the boundary. The C code is\ntransformed into a string which is written in a temporary file. This\nfile is then compiled into an object file, that is finally\nexecuted. It is possible to run assertions about the execution of the\nC program.\n\nThe primary goal of `inline-c` is to ease the testing of a C API of a\nRust program (generated with\n[`cbindgen`](https://github.com/eqrion/cbindgen/) for example). Note\nthat it's not tied to a Rust program exclusively, it's just its\ninitial reason to live.\n\n## Install\n\nAdd the following lines to your `Cargo.toml` file:\n\n```toml\n[dev-dependencies]\ninline-c = \"0.1\"\n```\n\n## Documentation\n\nThe `assert_c` and `assert_cxx` macros live in the `inline-c-macro`\ncrate, but are re-exported in this crate for the sake of simplicity.\n\nBeing able to write C code directly in Rust offers nice opportunities,\nlike having C examples inside the Rust documentation that are\nexecutable and thus tested (with `cargo test --doc`). Let's dig into\nsome examples.\n\n### Basic usage\n\nThe following example is super basic: C prints `Hello, World!` on the\nstandard output, and Rust asserts that.\n\n```rust\nuse inline_c::assert_c;\n\nfn test_stdout() {\n    (assert_c! {\n        #include \u003cstdio.h\u003e\n\n        int main() {\n            printf(\"Hello, World!\");\n\n            return 0;\n        }\n    })\n    .success()\n    .stdout(\"Hello, World!\");\n}\n```\n\nOr with a C++ program:\n\n```rust\nuse inline_c::assert_cxx;\n\nfn test_cxx() {\n    (assert_cxx! {\n        #include \u003ciostream\u003e\n\n        using namespace std;\n\n        int main() {\n            cout \u003c\u003c \"Hello, World!\";\n\n            return 0;\n        }\n    })\n    .success()\n    .stdout(\"Hello, World!\");\n}\n```\n\nThe `assert_c` and `assert_cxx` macros return a `Result\u003cAssert,\nBox\u003cdyn Error\u003e\u003e`. See `Assert` to learn more about the possible\nassertions.\n\nThe following example tests the returned value:\n\n```rust\nuse inline_c::assert_c;\n\nfn test_result() {\n    (assert_c! {\n        int main() {\n            int x = 1;\n            int y = 2;\n\n            return x + y;\n        }\n    })\n    .failure()\n    .code(3);\n}\n```\n\n### Environment variables\n\nIt is possible to define environment variables for the execution of\nthe given C program. The syntax is using the special `#inline_c_rs` C\ndirective with the following syntax:\n\n```c\n#inline_c_rs \u003cvariable_name\u003e: \"\u003cvariable_value\u003e\"\n```\n\nPlease note the double quotes around the variable value.\n\n```rust\nuse inline_c::assert_c;\n\nfn test_environment_variable() {\n    (assert_c! {\n        #inline_c_rs FOO: \"bar baz qux\"\n\n        #include \u003cstdio.h\u003e\n        #include \u003cstdlib.h\u003e\n\n        int main() {\n            const char* foo = getenv(\"FOO\");\n\n            if (NULL == foo) {\n                return 1;\n            }\n\n            printf(\"FOO is set to `%s`\", foo);\n\n            return 0;\n        }\n    })\n    .success()\n    .stdout(\"FOO is set to `bar baz qux`\");\n}\n```\n\n#### Meta environment variables\n\nUsing the `#inline_c_rs` C directive can be repetitive if one needs to\ndefine the same environment variable again and again. That's why meta\nenvironment variables exist. They have the following syntax:\n\n```sh\nINLINE_C_RS_\u003cvariable_name\u003e=\u003cvariable_value\u003e\n```\n\nIt is usually best to define them in [a `build.rs`\nscript](https://doc.rust-lang.org/cargo/reference/build-scripts.html)\nfor example. Let's see it in action with a tiny example:\n\n```rust\nuse inline_c::assert_c;\nuse std::env::{set_var, remove_var};\n\nfn test_meta_environment_variable() {\n    set_var(\"INLINE_C_RS_FOO\", \"bar baz qux\");\n\n    (assert_c! {\n        #include \u003cstdio.h\u003e\n        #include \u003cstdlib.h\u003e\n\n        int main() {\n            const char* foo = getenv(\"FOO\");\n\n            if (NULL == foo) {\n                return 1;\n            }\n\n            printf(\"FOO is set to `%s`\", foo);\n\n            return 0;\n        }\n    })\n    .success()\n    .stdout(\"FOO is set to `bar baz qux`\");\n\n    remove_var(\"INLINE_C_RS_FOO\");\n}\n```\nNote: If you have multiple inline C tests that use the same environment \nvariables, you may see flakiness in test runs because by default `cargo test` \nruns tests parallely. This problem can occur even if your tests are removing \nthe environment variables during teardown. To fix this, you can do either \nof the following:\n\n- Use unique environment variable names for each test\n- (Not recommended for Production) Run the tests serially instead of parallely. \nYou can use the following command: `cargo test -- --test-threads=1` \n\n\n#### `CFLAGS`, `CPPFLAGS`, `CXXFLAGS` and `LDFLAGS`\n\nSome classical `Makefile` variables like `CFLAGS`, `CPPFLAGS`,\n`CXXFLAGS` and `LDFLAGS` are understood by `inline-c` and consequently\nhave a special treatment. Their values are added to the appropriate\ncompilers when the C code is compiled and linked into an object file.\n\nPro tip: Let's say we have a Rust crate named `foo`, and it exports a\nC API. It is possible to define `CFLAGS` and `LDFLAGS` as follow to\ncorrectly compile and link all the C codes to the Rust `libfoo` shared\nobject by writing this in a `build.rs` script (it is assumed that\n`libfoo` lands in the `target/\u003cprofile\u003e/` directory, and that `foo.h`\nlands in the root directory):\n\n```rust\nuse std::{env, path::PathBuf};\n\nfn main() {\n    let include_dir = env::var(\"CARGO_MANIFEST_DIR\").unwrap();\n\n    let mut shared_object_dir = PathBuf::from(env::var(\"CARGO_MANIFEST_DIR\").unwrap());\n    shared_object_dir.push(\"target\");\n    shared_object_dir.push(env::var(\"PROFILE\").unwrap());\n    let shared_object_dir = shared_object_dir.as_path().to_string_lossy();\n\n    // The following options mean:\n    //\n    // * `-I`, add `include_dir` to include search path,\n    // * `-L`, add `shared_object_dir` to library search path,\n    // * `-D_DEBUG`, enable debug mode to enable `assert.h`.\n    println!(\n        \"cargo:rustc-env=INLINE_C_RS_CFLAGS=-I{I} -L{L} -D_DEBUG\",\n        I = include_dir,\n        L = shared_object_dir.clone(),\n    );\n\n    // Here, we pass the fullpath to the shared object with\n    // `LDFLAGS`.\n    println!(\n        \"cargo:rustc-env=INLINE_C_RS_LDFLAGS={shared_object_dir}/{lib}\",\n        shared_object_dir = shared_object_dir,\n        lib = if cfg!(target_os = \"windows\") {\n            \"foo.dll\".to_string()\n        } else if cfg!(target_os = \"macos\") {\n            \"libfoo.dylib\".to_string()\n        } else {\n            \"libfoo.so\".to_string()\n        }\n    );\n}\n```\n\n_Et voilà !_ Now run `cargo build --release` (to generate the\nshared objects) and then `cargo test --release` to see it in\naction.\n\n### Using `inline-c` inside Rust documentation\n\nSince it is now possible to write C code inside Rust, it is\nconsequently possible to write C examples, that are:\n\n1. Part of the Rust documentation with `cargo doc`, and\n2. Tested with all the other Rust examples with `cargo test --doc`.\n\nYes. Testing C code with `cargo test --doc`. How _fun_ is that? No\ntrick needed. One can write:\n\n```rust\n/// Blah blah blah.\n///\n/// # Example\n///\n/// ```rust\n/// # use inline_c::assert_c;\n/// #\n/// # fn main() {\n/// #     (assert_c! {\n/// #include \u003cstdio.h\u003e\n///\n/// int main() {\n///     printf(\"Hello, World!\");\n///\n///     return 0;\n/// }\n/// #    })\n/// #    .success()\n/// #    .stdout(\"Hello, World!\");\n/// # }\n/// ```\npub extern \"C\" fn some_function() {}\n```\n\nwhich will compile down into something like this:\n\n```c\nint main() {\n    printf(\"Hello, World!\");\n\n    return 0;\n}\n```\n\nNotice that this example above is actually Rust code, with C code\ninside. Only the C code is printed, due to the `#` hack of `rustdoc`,\nbut this example is a valid Rust example, and is fully tested!\n\nThere is one minor caveat though: the highlighting. The Rust set of\nrules are applied, rather than the C ruleset. [See this issue on\n`rustdoc` to follow the\nfix](https://github.com/rust-lang/rust/issues/78917).\n\n### C macros\n\nC macros with the `#define` directive is supported only with Rust\nnightly. One can write:\n\n```rust,ignore\nuse inline_c::assert_c;\n\nfn test_c_macro() {\n    (assert_c! {\n        #define sum(a, b) ((a) + (b))\n\n        int main() {\n            return !(sum(1, 2) == 3);\n        }\n    })\n    .success();\n}\n```\n\nNote that multi-lines macros don't work! That's because the `\\` symbol\nis consumed by the Rust lexer. The best workaround is to define the\nmacro in another `.h` file, and to include it with the `#include`\ndirective.\n\n## License\n\n`BSD-3-Clause`, see `LICENSE.md`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhywan%2Finline-c-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhywan%2Finline-c-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhywan%2Finline-c-rs/lists"}