Skip to content

zeta1999/inline-c-rs

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lilac-breated Roller, by David Clode
inline-c

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

The inline-c crate is a really immature project that allows you to write C code within Rust directly, to compile it and to run some assertions.

Please, do not use this crate for evil purposes. Stay reasonable. The main idea is to test a C API, for instance of a Rust program that is automatically generated with cbindgen.

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.

#[test]
fn test_successful() {
    (assert_c! {
        #include <stdio.h>

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

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

#[test]
fn test_badly() {
    (assert_c! {
        int main() {
            int x = 1;
            int y = 2;

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

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:

#include_c_rs <variable_name>: "<variable_value>"

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.

#[test]
fn test_c_macro_with_env_vars_inlined() {
    (assert_c! {
        #inline_c_rs FOO: "bar baz qux"
        #inline_c_rs CFLAGs: "-Ixyz/include -Lzyx/lib"
        #inline_c_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")
    .no_stderr();
}

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:

#[test]
fn test_c_macro_with_env_vars_from_env_vars() {
    set_var("INLINE_C_RS_FOO", "bar baz qux");
    set_var("INLINE_C_RS_CFLAGS", "-Ixyz/include -Lxyz/lib");
    set_var("INLINE_C_RS_LDFLAGS", "-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")
    .no_stderr();

    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.

About

Write and execute C code inside Rust.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Rust 100.0%