Bindgen default search path / system's include path

Hello,

I can't find any information on bindgen's default search path for headers.

If I only pass a header's name without specifying a search path, where will bindgen search for by default ?

Thank you very much in advance for any help.

bindgen uses clang-sys to detect the default search path. note, this will not work if you only have libclang (not clang the binary) installed, but typically, libclang and clang are installed together.

and I believe clang_sys::support::Clang::find() is what does the actual work. basically, it runs the compiler with the -v option on empty input, then parses the printed messages. you can read the code for details.

Thank you for your quick reply.

I'm not sure to understand.

You share me the find method, which, if I understand well, seems to indicate the path of the clang binary and the detect_include_paths which sets whether to detect include paths using clang_sys.

I'm sorry but I'm not really sure to understand the correlation between the two methods ?

I suppose that:

detect_include_paths(true)

is more what I'm searching for ?

What happens if it's set to false ? Which path is used ?

Thank you very much in advance for any help

sorry for the confusion.

detect_include_paths(bool) is the method you call to configure bindgen, and when it is set to true, bindgen will call Clang::find(path, args) to detect the system default search paths.

for regular users of bindgen, yes, you set this to true and it just works.

in fact, this option is true by default, which means bindgen should use whatever is the default for the C compiler on your host system unless you explicitly disable it.

if you set it to false, then there's no *default" search path. if your code uses headers from the system, you need to set the correct compiler flags yourself, for example, builder.clang_arg("-I/usr/include").

libclang is a compiler toolkit library. it implements the mechanisms required by a C compiler, such as lexing, preprocessing, ast parsing, semantic analysis, etc, but it deliberately does NOT hardcode any policies for a specific system, such as the default libraries, header search paths, things like that. these policies are owned by the tool/frontend, as a client of libclang, which is bindgen in this case.

what's the use cases for such configuration?

a) you have some source code that is completely self-contained, it does not have external dependencies, not even the system headers, you only use the quoted form of #include "path/to/header.h"

b) all your angle bracket includes use hardcoded absolute path, e.g. #include </user/include/stdio.h>

c) you need to use a toolchain or sdk that is isolated from the host system, such as cross compiling for a different operating system, so you need to detect the include paths in your build script, and set the correct CFLAGS accordingly.

Thank you for your quick and great reply.

I'm trying to use bindgen with wayland-client.h. The header is in /usr/include.

$ clang -E -x c++ - -v
clang version 22.1.8
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-pc-linux-gnu/16
Found candidate GCC installation: /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16
Selected GCC installation: /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Selected multilib: .;@m64
 (in-process)
 "/usr/bin/clang-22" -cc1 -triple x86_64-pc-linux-gnu -E -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name - -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/home/coredump/Dev/slbar -v -fcoverage-compilation-dir=/home/coredump/Dev/slbar -resource-dir /usr/lib/clang/22 -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16/x86_64-pc-linux-gnu -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16/../../../../x86_64-pc-linux-gnu/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -ferror-limit 19 -fmessage-length=194 -stack-protector 2 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -fcolor-diagnostics -faddrsig -fdwarf2-cfi-asm -o - -x c++ -
clang -cc1 version 22.1.8 based upon LLVM 22.1.8 default target x86_64-pc-linux-gnu
ignoring nonexistent directory "/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16/../../../../x86_64-pc-linux-gnu/include"
ignoring nonexistent directory "/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16
 /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16/x86_64-pc-linux-gnu
 /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16/backward
 /usr/lib/clang/22/include
 /usr/local/include
 /usr/include
End of search list

In my build.rs I wrote:

let mut bindgens = bindgen::Builder::default().detect_include_paths(true);
bindgens.header("wayland-client.h") // shouldn't it also search in /usr/include ?

But at the compilation I get:

thread 'main' (44066) panicked at backend/wayland-sys/build.rs:38:50:
called `Result::unwrap()` on an `Err` value: NotExist("wayland-client.h")
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Do I need to call find ? If yes, how do I use it ?

Thank you very much in advance for any help

that's incorrect assumption.

the search path specifies where to look for a file when the compiler (or rather, libclang) sees an #include <header.h> preprocessor directive.

whe you call bindgen::Builder::header(), you give it a file path, it's just a regular file path, which can be absolute or relative, just like how you call File::open().

if you want the compiler to search a header in the system path, it needs to be an #include preprocessing directive.

you can do it two ways:

  • create a separate file, for example, wrapper.h.

    // wrapper.h
    #include <wayland-client.h>
    // and potentailly other ffi definitions
    //...
    
    // build.rs
    fn main() {
        bindgen::Builder::default()
            .detect_include_paths(true) //<-- this is redundent, it is `true` by default
            .header("wrapper.h")
            .generate().
            .unwrap();
    }
    
  • pass C source code as string to bindgen:

    // build.rs
    fn main() {
        bindgen::Builder::default()
            .header_contents(
                "wrapper.h",  //<-- file name is for diagnostic messages, can be arbitrary
                "#include <wayland-client.h>" //<-- the content to be parsed as C source code
            )
            .generate()
            .unwrap();
    }
    

Thank you for your great answer.

It answers my initial questions.