How to elegantly #include "gtk-layer-shell.h" in Rust

Viewed 153

I am trying to have some C++ code in my Rust code and I have the following include statement:

#include "gtk-layer-shell.h"

I specified the location of the files in the build.rs but because of all the dependencies its a bit messy. My build.rs looks like this:

extern crate cpp_build;

fn main() {
    let include_layer_shell = "/usr/include/gtk-layer-shell";
    let include_gtk = "/usr/include/gtk-3.0";
    let include_glib = "/usr/include/glib-2.0";
    let include_glib_config = "/usr/lib/glib-2.0/include";
    let include_pango = "/usr/include/pango-1.0";
    let include_harfbuzz = "/usr/include/harfbuzz";
    let include_cairo = "/usr/include/cairo";
    let include_gdk_pixbuf = "/usr/include/gdk-pixbuf-2.0";
    let include_atk = "/usr/include/atk-1.0";
    cpp_build::Config::new()
        .include(include_layer_shell)
        .include(include_gtk)
        .include(include_glib)
        .include(include_glib_config)
        .include(include_pango)
        .include(include_harfbuzz)
        .include(include_cairo)
        .include(include_gdk_pixbuf)
        .include(include_atk)
        .build("src/main.rs");
}

Is there a way I can more elegantly include it and only specify the location for gtk-layer-shell.h and have all the other ones automatically found? I also want to be able to cross-compile the application for aarch64. The way I have it set up right now I guess I would have to change the paths according to the arch which also seems a bit messy.

Thank you for your help! :)

PS: The reason why I need to have C++ code is basically only because I don't know how I would write gtk_layer_init_for_window (gtk_window); in Rust, so if anybody knows how to completely avoid the c++ code, that would be even better!

1 Answers

Thanks for the suggestions, I now used gir to generate both the bindings and a safe Rust wrapper. It is a LOT easier but only works on GTK related things and a .gir file is necessary. A tutorial can be found here: https://github.com/gtk-rs/gir

Before that I used the bindgen and pkg_config crates and changed my build.rs to the following:

extern crate bindgen;
extern crate pkg_config;

use std::env;
use std::path::PathBuf;

fn main() {
    let library = pkg_config::Config::new()
        .probe("gtk-layer-shell-0")
        .expect("No gtk-layer-shell found on your system");
    let include_paths = library
        .include_paths
        .into_iter()
        .map(|p| p.into_os_string().into_string().unwrap())
        .collect::<Vec<_>>();

    for path in library.link_paths {
        println!("cargo:rustc-link-path={}", path.to_str().unwrap());
    }
    for lib in library.libs {
        println!("cargo:rustc-link-lib={}", lib);
    }

    // Tell cargo to invalidate the built crate whenever the wrapper changes
    println!("cargo:rerun-if-changed=wrapper.h");

    // The bindgen::Builder is the main entry point
    // to bindgen, and lets you build up options for
    // the resulting bindings.
    let mut bindings = bindgen::Builder::default()
        // The input header we would like to generate
        // bindings for.
        .header("wrapper.h")
        // Tell cargo to invalidate the built crate whenever any of the
        // included header files changed.
        .parse_callbacks(Box::new(bindgen::CargoCallbacks));

    for path in include_paths {
        bindings = bindings.clang_args(&["-F", &path]);
    }

    let bindings = bindings
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}
Related