aboutsummaryrefslogtreecommitdiff
path: root/build.rs
blob: f22e64fb8197a823159469dc583a3b781a55f01e (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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
use std::env;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

use anyhow::{anyhow, ensure, Result};
use url::Url;

// there's probably a crate for this somewhere
fn os_concat(a: impl AsRef<OsStr>, b: impl AsRef<OsStr>) -> OsString {
    let mut result = OsString::from(&a);
    result.push(b);
    result
}

fn out_dir() -> PathBuf {
    PathBuf::from(env::var_os("OUT_DIR").expect("no OUT_DIR found"))
}

fn download(url: &str) -> Result<PathBuf> {
    let url = Url::parse(url)?;
    let file_name = url
        .path_segments()
        .and_then(|x| x.last())
        .ok_or_else(|| anyhow!("Weird URL downloaded"))?;
    let out_path = out_dir().join(file_name);
    if !out_path.exists() {
        let mut body_reader = ureq::request_url("GET", &url).call()?.into_reader();
        let mut out_file = fs::File::create(&out_path)?;
        io::copy(&mut body_reader, &mut out_file)?;
    }
    Ok(out_path)
}

fn un7z(archive: PathBuf) -> Result<()> {
    // WARNING: This code is horrifying and also bad.
    // It should never be used by anyone.
    // TODO port the 7zip LZMA SDK to Rust
    use std::process::{Command, Stdio};
    let my_7z_exe = Path::new(r"C:\Program Files\7-Zip\7z.exe");
    ensure!(my_7z_exe.exists());
    let output_folder = archive
        .parent()
        .ok_or_else(|| anyhow!("archive with no parent"))?;
    let output_folder_arg = os_concat("-o", &output_folder);
    let extract_result = Command::new(my_7z_exe)
        .args(["x", "-aos"])
        .arg(output_folder_arg)
        .arg(&archive)
        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .status()?;
    ensure!(extract_result.success());
    Ok(())
}

fn main() -> Result<()> {
    println!("cargo:rerun-if-changed=build.rs");
    let target = env::var("TARGET").expect("no TARGET found");

    match target.as_str() {
        "x86_64-pc-windows-msvc" => build_x64_windows_msvc(),
        _ => panic!("unsupported target: {}", target),
    }
}

/*
 <Choose>
   <When Condition="Exists('lib\vc14x_x64_dll\wxbase31$(wxSuffix).lib')">
     <PropertyGroup Label="UserMacros">
       <wxUsingVersionABICompat>1</wxUsingVersionABICompat>
       <wxUsingDll>1</wxUsingDll>
       <wxLibOrDllDir>lib\vc14x_x64_dll</wxLibOrDllDir>
     </PropertyGroup>
   </When>

   <When Condition="Exists('lib\vc14x_x64_lib\wxbase31$(wxSuffix).lib')">
     <PropertyGroup Label="UserMacros">
       <wxUsingVersionABICompat>1</wxUsingVersionABICompat>
       <wxUsingLib>1</wxUsingLib>
       <wxLibOrDllDir>lib\vc14x_x64_lib</wxLibOrDllDir>
     </PropertyGroup>
   </When>
 </Choose>
*/

fn build_x64_windows_msvc() -> Result<()> {
    let headers = download("https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.5/wxWidgets-3.1.5-headers.7z")?;
    un7z(headers)?;

    // TODO make sure VS2015+ is actually the right thing
    let libs = download("https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.5/wxMSW-3.1.5_vc14x_x64_Dev.7z")?;
    un7z(libs)?;

    let out_dir = out_dir();

    // TODO consider actually parsing wxwidgets.props at build time instead of doing it manually
    // TODO bindgen ahead of time
    // TODO can we really get away with this to do static linking
    let wx_using_lib = true;
    if wx_using_lib {
        let dll_folder = out_dir.join("lib/vc14x_x64_dll");
        let lib_folder = out_dir.join("lib/vc14x_x64_lib");
        if dll_folder.exists() && !lib_folder.exists() {
            fs::rename(dll_folder, lib_folder)?;
        }
    }
    let libs_folder = out_dir.join(if wx_using_lib {
        "lib/vc14x_x64_lib"
    } else {
        "lib/vc14x_x64_dll"
    });
    let wx_using_dll = !wx_using_lib;
    let debug = env::var("PROFILE")? == "release";
    let wx_suffix = if debug { "u" } else { "ud" };
    let mut defines = vec![
        "__WXMSW__",
        "wxMSVC_VERSION_AUTO",
        "wxMSVC_VERSION_ABI_COMPAT",
        "_UNICODE",
    ];
    if debug {
        defines.push("_DEBUG");
    }
    if wx_using_dll {
        defines.push("WXUSINGDLL");
    }

    let mut include_dirs = vec![out_dir.join("include/msvc"), out_dir.join("include")];
    // TODO this is only in ResourceCompile in wxwidgets.props, is that bad
    include_dirs.push(libs_folder.join(format!("msw{}", wx_suffix)));
    let bindings = bindgen::Builder::default()
        .header("wrapper.hpp")
        // pain
        .clang_arg("-Wno-invalid-token-paste")
        .clang_args(defines.iter().map(|x| format!("-D{}", x)))
        // TODO don't .display()
        .clang_args(include_dirs.iter().map(|x| format!("-I{}", x.display())))
        .opaque_type("std::.*")
        // pain 2: the sequel to pain
        .blocklist_type("CharType")
        .blocklist_type("value_type")
        .blocklist_type("const_reference")
        // pain 3: the shriequel to pain
        .blocklist_type("wxBaseArray_CMPFUNC")
        .blocklist_type("wxBaseObjectArray")
        .blocklist_type("wxBaseObjectArray_base")
        .blocklist_type("pointer")
        .blocklist_type("const_pointer")
        .blocklist_type("wxBaseObjectArrayForwxDateTimeArray")
        .blocklist_type("wxBaseObjectArrayForwxIconArray")
        .blocklist_type("wxBaseObjectArrayForwxStatusBarPaneArray")
        .opaque_type("_IMAGE_TLS_DIRECTORY64")
        // that's enough pain? maybe?
        .generate()
        .map_err(|_| anyhow!("failed to generate bindings"))?;
    let out_path = out_dir.join("bindings.rs");
    bindings.write_to_file(&out_path)?;

    let win32_libs = [
        "kernel32", "user32", "gdi32", "comdlg32", "winspool", "shell32", "shlwapi", "ole32",
        "oleaut32", "uuid", "advapi32", "version", "comctl32", "rpcrt4", "wsock32", "wininet",
        "winmm",
    ];
    for lib in win32_libs {
        // TODO dylib or static
        println!("cargo:rustc-link-lib={}", lib);
    }
    // TODO is "native=" the right thing there (or display() for that matter)
    println!("cargo:rustc-link-search=native={}", libs_folder.display());

    // TODO which libs do we actually want to link
    println!("cargo:rustc-link-lib=static=wxbase31{}", wx_suffix);
    println!("cargo:rustc-link-lib=static=wxmsw31{}_core", wx_suffix);
    Ok(())
}