wxbuild_rs/
lib.rs

1use cc::Build;
2use std::env;
3use std::ffi::OsStr;
4use std::fs;
5use std::path::{Path, PathBuf};
6use std::process::Command;
7
8fn is_modified(static_lib_path: &PathBuf, folder: &str) -> std::io::Result<bool> {
9    let lib_meta = fs::metadata(static_lib_path)?;
10    let lib_age = lib_meta.modified()?;
11    if let Ok(lib_age) = lib_age.elapsed() {
12        let dir_iter = fs::read_dir(folder)?;
13        for entry in dir_iter {
14            if let Ok(entry) = entry {
15                if let Ok(md) = entry.metadata() {
16                    if let Ok(file_age) = md.modified() {
17                        if let Ok(file_age) = file_age.elapsed() {
18                            if file_age < lib_age {
19                                return Ok(true);
20                            }
21                        }
22                    }
23                }
24            }
25        }
26    } else {
27        return Ok(true);
28    }
29    Ok(false)
30}
31
32/// Build all cpp files in specified folder and correctly link with wxWidgets.
33///
34/// If add_start is true:
35///
36/// - It will create a cpp file containing wxIMPLEMENT_APP_NO_MAIN(appname)
37/// - It will create wxffi.rs file you should include with: include!(concat!(env!("OUT_DIR"), "/wxffi.rs"))
38/// - wxffi.rs will contain function start() that will run your wx gui. This function will not return while GUI is active.
39/// - appname.h (all lowercase) must exist and have appname class declared.
40pub fn build(folder: &str, add_start: bool, appname: &str) -> std::io::Result<()> {
41    let target = env::var("TARGET").unwrap();
42    let out_dir_s = std::env::var("OUT_DIR").unwrap();
43    let out_dir = Path::new(&out_dir_s);
44    let static_lib_path = out_dir.join("libwxrs.a");
45    let wxcfg = env::var("WX_CONFIG").unwrap_or("wx-config".to_owned());
46    let wxdir = env::var("WX_DIR").unwrap_or("".to_owned());
47
48    if is_modified(&static_lib_path, folder).unwrap_or(true) {
49        let mut cc = Build::new();
50        for entry in fs::read_dir(folder)? {
51            let entry = entry?;
52            let path = entry.path();
53            let extension = path.extension().and_then(OsStr::to_str).unwrap_or("");
54            if extension == "cpp" {
55                cc.file(&path);
56            }
57        }
58        if let Ok(cxx) = Command::new(wxcfg.as_str()).args(&["--cxxflags"]).output() {
59            let cxx = std::str::from_utf8(cxx.stdout.as_ref()).unwrap();
60            for word in cxx.split_whitespace() {
61                cc.flag(word);
62            }
63        } else if wxdir.len() > 0 && target.contains("msvc") {
64            cc.define("__WXMSW__", "1");
65            cc.define("_UNICODE", "1");
66            cc.include(Path::new(&wxdir).join("include"));
67            cc.include(Path::new(&wxdir).join("include").join("msvc"));
68        // cc.include(Path::new(&wxdir).join("lib").join("vc_x64_lib").join("mswu"));
69        } else {
70            panic!("No WX_CONFIG or WX_DIR set");
71        }
72        cc.cpp(true);
73        if target.contains("darwin") {
74            cc.flag("-mmacosx-version-min=10.12");
75            cc.flag("-std=c++11");
76        } else if target.contains("msvc") {
77            cc.flag("/EHsc");
78        }
79        if add_start {
80            cc.include(folder);
81            let start = out_dir.join("start.cpp");
82            let mut file = fs::File::create(start.clone()).unwrap();
83            use std::io::Write;
84            let cpp = format!("#include <wx/wx.h>\n
85                #include \"{}.h\"
86                wxIMPLEMENT_APP_NO_MAIN({});
87                extern \"C\" {{ void wx_start() {{ char **argv = nullptr; int argc = 0; wxEntry(argc, argv); }} }}", appname.to_ascii_lowercase(), appname);
88            file.write(cpp.as_bytes()).unwrap();
89            cc.file(start);
90
91            let mut file = fs::File::create(&out_dir.join("wxffi.rs")).unwrap();
92            file.write(
93                br#"
94                pub fn start() {
95                    unsafe {
96                        wx_start();
97                    }
98                }
99                extern "C" {
100                    fn wx_start();
101                }
102            "#,
103            )
104            .unwrap();
105            // #[link_name = "\u{1}_wx_start"]
106        }
107
108        cc.extra_warnings(false);
109        cc.compile("libwxrs.a");
110    }
111
112    println!("cargo:rustc-link-search=native={}", out_dir_s);
113    println!("cargo:rustc-link-lib=wxrs");
114
115    if wxdir.len() > 0 && target.contains("msvc") {
116        println!("cargo:rustc-link-search=native={}\\lib\\vc_x64_lib", wxdir);
117        // println!(
118        //     "cargo:rustc-link-lib=static={}",
119        //     part.trim_start_matches("-l")
120        // );
121        let dir_iter = fs::read_dir(Path::new(&wxdir).join("lib").join("vc_x64_lib"))?;
122        for entry in dir_iter {
123            if let Ok(entry) = entry {
124                let path = entry.path();
125                let extension = path.extension().and_then(OsStr::to_str).unwrap_or("");
126                let file_stem = path.file_stem().and_then(OsStr::to_str).unwrap_or("");
127                if extension == "lib" {
128                    // if cfg!(debug_assertions) {
129                    //     if file_stem.starts_with("wxbase31ud") {
130                    //         println!("cargo:rustc-link-lib=static={}", file_stem);
131                    //     } else if file_stem.starts_with("wxbase31u") {
132                    //         continue;
133                    //     } else if file_stem.ends_with("d") {
134                    //         println!("cargo:rustc-link-lib=static={}", file_stem);
135                    //     }
136                    // } else {
137
138                    if file_stem.starts_with("wxmsw31ud") {
139                        continue;
140                    } else if file_stem.starts_with("wxbase31ud") {
141                        continue;
142                    } else if file_stem.starts_with("wxmsw31u") {
143                        println!("cargo:rustc-link-lib=static={}", file_stem);
144                    } else if file_stem.starts_with("wxbase31u") {
145                        println!("cargo:rustc-link-lib=static={}", file_stem);
146                    } else if !file_stem.ends_with("d") {
147                        println!("cargo:rustc-link-lib=static={}", file_stem);
148                    }
149                    // }
150                }
151            }
152        }
153        return Ok(());
154    }
155
156    let libs = Command::new(wxcfg.as_str())
157        .args(&["--libs"])
158        .output()
159        .expect("failed to execute wx-config");
160    let libs = std::str::from_utf8(libs.stdout.as_ref()).unwrap();
161    let mut framework: bool = false;
162    for part in libs.split_whitespace() {
163        if part.starts_with("-L") {
164            println!(
165                "cargo:rustc-link-search=native={}",
166                part.trim_start_matches("-L")
167            );
168        } else if part.starts_with("-l") {
169            let static_pth = format!("/usr/local/lib/lib{}.a", part.trim_start_matches("-l"));
170            if fs::metadata(static_pth).is_ok() {
171                println!("cargo:rustc-link-search=native=/usr/local/lib/");
172                println!(
173                    "cargo:rustc-link-lib=static={}",
174                    part.trim_start_matches("-l")
175                );
176            } else {
177                println!("cargo:rustc-link-lib={}", part.trim_start_matches("-l"));
178            }
179        } else if part == "-framework" {
180            framework = true;
181        } else {
182            if framework {
183                println!("cargo:rustc-link-lib=framework={}", part);
184                framework = false;
185            } else if part.ends_with(".a") {
186                let path = PathBuf::from(part.to_string());
187                println!(
188                    "cargo:rustc-link-search=native={}",
189                    path.parent().unwrap().to_str().unwrap()
190                );
191                println!(
192                    "cargo:rustc-link-lib={}",
193                    path.file_stem()
194                        .unwrap()
195                        .to_str()
196                        .unwrap()
197                        .trim_start_matches("lib")
198                );
199            }
200        }
201    }
202    println!("cargo:rustc-link-lib=c++");
203
204    Ok(())
205}