summaryrefslogtreecommitdiff
path: root/rust/binaryninjacore-sys/build.rs
blob: 38a656e62eb9d2c1b4fea585e4c36ed9773ba6b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
extern crate bindgen;

use std::env;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::path::PathBuf;

#[cfg(target_os = "macos")]
static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun");

#[cfg(target_os = "linux")]
static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun");

#[cfg(windows)]
static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun");

// Check last run location for path to BinaryNinja; Otherwise check the default install locations
fn link_path() -> PathBuf {
    use std::io::prelude::*;

    let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap());
    let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1);

    File::open(lastrun)
        .and_then(|f| {
            let mut binja_path = String::new();
            let mut reader = BufReader::new(f);

            reader.read_line(&mut binja_path)?;
            Ok(PathBuf::from(binja_path.trim()))
        })
        .unwrap_or_else(|_| {
            #[cfg(target_os = "macos")]
            return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS");

            #[cfg(target_os = "linux")]
            return home.join("binaryninja");

            #[cfg(windows)]
            return PathBuf::from(env::var("PROGRAMFILES").unwrap())
                .join("Vector35\\BinaryNinja\\");
        })
}

fn main() {
    println!("cargo:rerun-if-changed=../../binaryninjacore.h");

    //Cargo's output directory
    let out_dir = env::var("OUT_DIR").unwrap();

    // Detect for custom Clang or LLVM installations (BN devs/build server)
    let llvm_dir = env::var("LIBCLANG_PATH");
    let llvm_version = env::var("LLVM_VERSION");
    let llvm_install_dir = env::var("LLVM_INSTALL_DIR");

    // Use BINARYNINJADIR first for custom BN builds/configurations (BN devs/build server), fallback on defaults
    let install_path = env::var("BINARYNINJADIR")
        .map(PathBuf::from)
        .unwrap_or_else(|_| link_path());

    #[cfg(target_os = "linux")]
    println!(
        "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-l:libbinaryninjacore.so.1",
        install_path.to_str().unwrap(),
        install_path.to_str().unwrap(),
    );

    #[cfg(not(target_os = "linux"))]
    println!(
        "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-lbinaryninjacore",
        install_path.to_str().unwrap(),
        install_path.to_str().unwrap(),
    );

    // TODO : Clean this up even more
    #[warn(unused_assignments)]
    let is_mac = {
        #[cfg(target_os = "macos")]
        {
            true
        }
        #[cfg(not(target_os = "macos"))]
        {
            false
        }
    };

    let current_line = "#define BN_CURRENT_UI_ABI_VERSION ";
    let minimum_line = "#define BN_MINIMUM_UI_ABI_VERSION ";
    let mut current_version = "0".to_string();
    let mut minimum_version = "0".to_string();
    let file = File::open("../../ui/uitypes.h").expect("Couldn't open uitypes.h");
    for line in BufReader::new(file).lines() {
        let line = line.unwrap();
        if line.starts_with(current_line) {
            current_version = (&line[current_line.len()..]).to_owned();
        } else if line.starts_with(minimum_line) {
            minimum_version = (&line[minimum_line.len()..]).to_owned();
        }
    }

    // Difference between global LLVM/Clang install and custom LLVM/Clang install...
    //  First option is for the build server, second option is being nice to our dev who have `LLVM_INSTALL_DIR` set, third is for people with "normal" setups (and Macs)
    let mut bindings = bindgen::builder()
        .header("../../binaryninjacore.h")
        .clang_arg("-std=c++17")
        .clang_arg("-x")
        .clang_arg("c++")
        .size_t_is_usize(true)
        .generate_comments(false)
        .allowlist_function("BN.*")
        .allowlist_var("BN_CURRENT_CORE_ABI_VERSION")
        .allowlist_var("BN_MINIMUM_CORE_ABI_VERSION")
        .raw_line(format!(
            "pub const BN_CURRENT_UI_ABI_VERSION: u32 = {};",
            current_version
        ))
        .raw_line(format!(
            "pub const BN_MINIMUM_UI_ABI_VERSION: u32 = {};",
            minimum_version
        ))
        .rustified_enum("BN.*");
    if let (false, Ok(llvm_dir), Ok(llvm_version)) = (is_mac, llvm_dir, llvm_version) {
        let llvm_include_path = format!("-I{}/clang/{}/include", llvm_dir, llvm_version);
        bindings = bindings.clang_arg(llvm_include_path);
    } else if let (false, Ok(llvm_install_dir)) = (is_mac, llvm_install_dir) {
        let llvm_include_path = format!("-I{}/12.0.0/lib/clang/12.0.0/include", llvm_install_dir);
        env::set_var("LIBCLANG_PATH", format!("{}/12.0.0/lib", llvm_install_dir));
        bindings = bindings.clang_arg(llvm_include_path);
    }

    bindings
        .generate()
        .expect("Unable to generate bindings")
        .write_to_file(PathBuf::from(out_dir).join("bindings.rs"))
        .expect("Couldn't write bindings!");
}