Lilac-breated Roller, by David Clode
inline-c

Have you ever written a C API for your Rust program? I'm sure you did, otherwise what are you waiting for? It's fun! Have you ever dreamed of running your C API directly in your Rust implementation, for example to unit test it? Nah? Because I did. Bah, I'm probably not the only one. Right? Please tell me I'm not.

The inline-c crate allows you to write C code within Rust directly, to compile it and to run some assertions. Note that C and Rust are fully sandboxed, values cannot be exchanged between the two. The purpose of inline-c is to ease the testing of a C API, that is for example automatically generated with [cbindgen].

Install

Add the following lines to your Cargo.toml file:

toml [dev-dependencies] inline-c = "0.1"

Example

The assert_c! and assert_cxx! macros

Basic usage of the assert_c! (or assert_cxx!) macro. In the following example a simple Hello, World! C program is compiled and executed. It is then asserted than the exit code and the outputs are correct. The next example asserts than the C program correctly returns an error.

```rust

[test]

fn testsuccessful() { (assertc! { #include

    int main() {
        printf("Hello, World!\n");

        return 0;
    }
})
.success()
.stdout("Hello, World!\n");

}

[test]

fn testbadly() { (assertc! { int main() { int x = 1; int y = 2;

        return x + y;
    }
})
.failure()
.code(3);

} ```

Define environment variables

Now, let's enter the real reason to live of this project. Let's way we want a C program to link against a specific shared library.

Note: The CFLAGS, CXXFLAGS, CPPFLAGS and LDFLAGS are supported environment variables.

Great! We may want to define a value for the CFLAGS and the LDFLAGS environment variables. First way is to use the #inline_c_rs C directive with the following syntax:

```

inlinecrs : ""

```

Please note the double quotes around the variable value.

Let's see a concrete example. We declare 3 environment variables, resp. FOO, CFLAGS and LDFLAGS. The C program prints their corresponding values, and exit accordingly.

```rust

[test]

fn testcmacrowithenvvarsinlined() { (assertc! { #inlinecrs FOO: "bar baz qux" #inlinecrs CFLAGs: "-Ixyz/include -Lzyx/lib" #inlinec_rs LDFLAGS: "-lfoo"

    #include <stdio.h>
    #include <stdlib.h>

    int main() {
        const char* foo = getenv("FOO");

        if (NULL == foo) {
            return 1;
        }

        printf("FOO is set to `%s`\n", foo);

        return 0;
    }
})
.success()
.stdout("FOO is set to `bar baz qux`\n");

} ```

This is cool isn't it? But it can be repetitive. What if we can define environment variables globally, for all the C program written in assert_c! or assert_cxx!?

It is possible with meta environment variables, with the following syntax:

INLINE_C_RS_<variable_name>=<variable_value>

Let's see it in action. We set 2 environments variables, resp. INLINE_C_RS_FOO, INLINE_C_RS_CFLAGS and INLINE_C_RS_LDFLAGS, that will create FOO, CFLAGS and LDFLAGS for this C program specifically:

```rust

[test]

fn testcmacrowithenvvarsfromenvvars() { setvar("INLINECRSFOO", "bar baz qux"); setvar("INLINECRSCFLAGS", "-Ixyz/include -Lxyz/lib"); setvar("INLINECRSLDFLAGS", "-lfoo");

(assert_c! {
    #include <stdio.h>
    #include <stdlib.h>

    int main() {
        const char* foo = getenv("FOO");

        if (NULL == foo) {
            return 1;
        }

        printf("FOO is set to `%s`\n", foo);

        return 0;
    }
})
.success()
.stdout("FOO is set to `bar baz qux`\n");

remove_var("INLINE_C_RS_FOO");
remove_var("INLINE_C_RS_CFLAGS");
remove_var("INLINE_C_RS_LDFLAGS");

} ```

Note that we have use set_var and remove_var to set or remove the environment variables. That's for the sake of simplicity: It is possible to set those variables before running your tests or anything.

License

BSD-3-Clause, see LICENSE.md.