5256e3e79f
* fix(misc): fix clippy warnings * fix(misc): fix clippy warnings * fix(misc): fix clippy warnings * fix(misc): fix clippy warnings * fix(misc): fix clippy warnings
503 lines
15 KiB
Rust
503 lines
15 KiB
Rust
/*
|
|
* This file is part of espanso.
|
|
*
|
|
* Copyright (C) 2021 Federico Terzi
|
|
*
|
|
* espanso is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* espanso is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
use std::path::PathBuf;
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
use std::path::Path;
|
|
|
|
#[cfg(not(target_os = "linux"))]
|
|
const WX_WIDGETS_ARCHIVE_NAME: &str = "wxWidgets-3.1.5.zip";
|
|
|
|
#[cfg(not(target_os = "linux"))]
|
|
const WX_WIDGETS_BUILD_OUT_DIR_ENV_NAME: &str = "WX_WIDGETS_BUILD_OUT_DIR";
|
|
|
|
#[cfg(target_os = "windows")]
|
|
fn build_native() {
|
|
use std::process::Command;
|
|
|
|
let project_dir =
|
|
PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("missing CARGO_MANIFEST_DIR"));
|
|
let wx_archive = project_dir.join("vendor").join(WX_WIDGETS_ARCHIVE_NAME);
|
|
if !wx_archive.is_file() {
|
|
panic!("could not find wxWidgets archive!");
|
|
}
|
|
|
|
let out_dir = if let Ok(out_path) = std::env::var(WX_WIDGETS_BUILD_OUT_DIR_ENV_NAME) {
|
|
println!(
|
|
"detected wxWidgets build output directory override: {}",
|
|
out_path
|
|
);
|
|
let path = PathBuf::from(out_path);
|
|
std::fs::create_dir_all(&path).expect("unable to create wxWidgets out dir");
|
|
path
|
|
} else {
|
|
PathBuf::from(std::env::var("OUT_DIR").expect("missing OUT_DIR"))
|
|
};
|
|
let out_wx_dir = out_dir.join("wx");
|
|
|
|
if !out_wx_dir.is_dir() {
|
|
// Extract the wxWidgets archive
|
|
let wx_archive =
|
|
std::fs::File::open(&wx_archive).expect("unable to open wxWidgets source archive");
|
|
let mut archive = zip::ZipArchive::new(wx_archive).expect("unable to read wxWidgets archive");
|
|
archive
|
|
.extract(&out_wx_dir)
|
|
.expect("unable to extract wxWidgets source dir");
|
|
|
|
// Compile wxWidgets
|
|
let tool = cc::windows_registry::find_tool("msvc", "devenv")
|
|
.expect("unable to locate MSVC compiler, did you install Visual Studio?");
|
|
let mut vcvars_path = None;
|
|
let mut current_root = tool.path();
|
|
while let Some(parent) = current_root.parent() {
|
|
let target = parent
|
|
.join("VC")
|
|
.join("Auxiliary")
|
|
.join("Build")
|
|
.join("vcvars64.bat");
|
|
if target.exists() {
|
|
vcvars_path = Some(target);
|
|
break;
|
|
}
|
|
current_root = parent;
|
|
}
|
|
|
|
let vcvars_path = vcvars_path.expect("unable to find vcvars64.bat file");
|
|
let mut handle = Command::new("cmd")
|
|
.current_dir(
|
|
out_wx_dir
|
|
.join("build")
|
|
.join("msw")
|
|
.to_string_lossy()
|
|
.to_string(),
|
|
)
|
|
.args(&[
|
|
"/k",
|
|
&vcvars_path.to_string_lossy(),
|
|
"&",
|
|
"nmake",
|
|
"/f",
|
|
"makefile.vc",
|
|
"BUILD=release",
|
|
"TARGET_CPU=X64",
|
|
"&",
|
|
"exit",
|
|
])
|
|
.spawn()
|
|
.expect("failed to execute nmake");
|
|
if !handle
|
|
.wait()
|
|
.expect("unable to wait for nmake command")
|
|
.success()
|
|
{
|
|
panic!("nmake returned non-zero exit code!");
|
|
}
|
|
}
|
|
|
|
// Make sure wxWidgets is compiled
|
|
if !out_wx_dir
|
|
.join("build")
|
|
.join("msw")
|
|
.join("vc_mswu_x64")
|
|
.is_dir()
|
|
{
|
|
panic!("wxWidgets is not compiled correctly, missing 'build/msw/vc_mswu_x64' directory")
|
|
}
|
|
|
|
let wx_include_dir = out_wx_dir.join("include");
|
|
let wx_include_msvc_dir = wx_include_dir.join("msvc");
|
|
let wx_lib_dir = out_wx_dir.join("lib").join("vc_x64_lib");
|
|
|
|
cc::Build::new()
|
|
.cpp(true)
|
|
.file("src/sys/form/form.cpp")
|
|
.file("src/sys/search/search.cpp")
|
|
.file("src/sys/common/common.cpp")
|
|
.file("src/sys/wizard/wizard.cpp")
|
|
.file("src/sys/wizard/wizard_gui.cpp")
|
|
.file("src/sys/welcome/welcome.cpp")
|
|
.file("src/sys/welcome/welcome_gui.cpp")
|
|
.file("src/sys/textview/textview.cpp")
|
|
.file("src/sys/textview/textview_gui.cpp")
|
|
.file("src/sys/troubleshooting/troubleshooting.cpp")
|
|
.file("src/sys/troubleshooting/troubleshooting_gui.cpp")
|
|
.flag("/EHsc")
|
|
.include(wx_include_dir)
|
|
.include(wx_include_msvc_dir)
|
|
.compile("espansomodulosys");
|
|
|
|
// Add resources (manifest)
|
|
let mut resources = winres::WindowsResource::new();
|
|
resources.set_manifest(include_str!("res/win.manifest"));
|
|
resources
|
|
.compile()
|
|
.expect("unable to compile resource file");
|
|
|
|
println!(
|
|
"cargo:rustc-link-search=native={}",
|
|
wx_lib_dir.to_string_lossy()
|
|
);
|
|
}
|
|
|
|
#[cfg(target_os = "macos")]
|
|
fn build_native() {
|
|
use std::process::Command;
|
|
|
|
let project_dir =
|
|
PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("missing CARGO_MANIFEST_DIR"));
|
|
let wx_archive = project_dir.join("vendor").join(WX_WIDGETS_ARCHIVE_NAME);
|
|
if !wx_archive.is_file() {
|
|
panic!("could not find wxWidgets archive!");
|
|
}
|
|
|
|
let out_dir = if let Ok(out_path) = std::env::var(WX_WIDGETS_BUILD_OUT_DIR_ENV_NAME) {
|
|
println!(
|
|
"detected wxWidgets build output directory override: {}",
|
|
out_path
|
|
);
|
|
let path = PathBuf::from(out_path);
|
|
std::fs::create_dir_all(&path).expect("unable to create wxWidgets out dir");
|
|
path
|
|
} else {
|
|
PathBuf::from(std::env::var("OUT_DIR").expect("missing OUT_DIR"))
|
|
};
|
|
let out_wx_dir = out_dir.join("wx");
|
|
println!("wxWidgets will be compiled into: {}", out_wx_dir.display());
|
|
|
|
let target_arch = match std::env::var("CARGO_CFG_TARGET_ARCH")
|
|
.expect("unable to read target arch")
|
|
.as_str()
|
|
{
|
|
"x86_64" => "x86_64",
|
|
"aarch64" => "arm64",
|
|
arch => panic!("unsupported arch {}", arch),
|
|
};
|
|
|
|
let should_use_ci_m1_workaround =
|
|
std::env::var("CI").unwrap_or_default() == "true" && target_arch == "arm64";
|
|
|
|
if !out_wx_dir.is_dir() {
|
|
// Extract the wxWidgets archive
|
|
let wx_archive =
|
|
std::fs::File::open(&wx_archive).expect("unable to open wxWidgets source archive");
|
|
let mut archive = zip::ZipArchive::new(wx_archive).expect("unable to read wxWidgets archive");
|
|
archive
|
|
.extract(&out_wx_dir)
|
|
.expect("unable to extract wxWidgets source dir");
|
|
|
|
// Compile wxWidgets
|
|
let build_dir = out_wx_dir.join("build-cocoa");
|
|
std::fs::create_dir_all(&build_dir).expect("unable to create build-cocoa directory");
|
|
|
|
let mut handle = if should_use_ci_m1_workaround {
|
|
// Because of a configuration problem on the GitHub CI pipeline,
|
|
// we need to use a series of workarounds to build for M1 machines.
|
|
// See: https://github.com/actions/virtual-environments/issues/3288#issuecomment-830207746
|
|
Command::new(out_wx_dir.join("configure"))
|
|
.current_dir(build_dir.to_string_lossy().to_string())
|
|
.args(&[
|
|
"--disable-shared",
|
|
"--without-libtiff",
|
|
"--without-liblzma",
|
|
"--with-libjpeg=builtin",
|
|
"--with-libpng=builtin",
|
|
"--enable-universal-binary=arm64,x86_64",
|
|
])
|
|
.spawn()
|
|
.expect("failed to execute configure")
|
|
} else {
|
|
Command::new(out_wx_dir.join("configure"))
|
|
.current_dir(build_dir.to_string_lossy().to_string())
|
|
.args(&[
|
|
"--disable-shared",
|
|
"--without-libtiff",
|
|
"--without-liblzma",
|
|
"--with-libjpeg=builtin",
|
|
"--with-libpng=builtin",
|
|
&format!("--enable-macosx_arch={}", target_arch),
|
|
])
|
|
.spawn()
|
|
.expect("failed to execute configure")
|
|
};
|
|
|
|
if !handle
|
|
.wait()
|
|
.expect("unable to wait for configure command")
|
|
.success()
|
|
{
|
|
panic!("configure returned non-zero exit code!");
|
|
}
|
|
|
|
let mut handle = Command::new("make")
|
|
.current_dir(build_dir.to_string_lossy().to_string())
|
|
.args(&["-j8"])
|
|
.spawn()
|
|
.expect("failed to execute make");
|
|
if !handle
|
|
.wait()
|
|
.expect("unable to wait for make command")
|
|
.success()
|
|
{
|
|
panic!("make returned non-zero exit code!");
|
|
}
|
|
}
|
|
|
|
// Make sure wxWidgets is compiled
|
|
if !out_wx_dir.join("build-cocoa").is_dir() {
|
|
panic!("wxWidgets is not compiled correctly, missing 'build-cocoa/' directory")
|
|
}
|
|
|
|
// If using the M1 CI workaround, convert all the universal libraries to arm64 ones
|
|
// This is needed until https://github.com/rust-lang/rust/issues/55235 is fixed
|
|
if should_use_ci_m1_workaround {
|
|
convert_fat_libraries_to_arm(&out_wx_dir.join("build-cocoa").join("lib"));
|
|
convert_fat_libraries_to_arm(&out_wx_dir.join("build-cocoa"));
|
|
}
|
|
|
|
let config_path = out_wx_dir.join("build-cocoa").join("wx-config");
|
|
let cpp_flags = get_cpp_flags(&config_path);
|
|
|
|
let mut build = cc::Build::new();
|
|
build
|
|
.cpp(true)
|
|
.file("src/sys/form/form.cpp")
|
|
.file("src/sys/common/common.cpp")
|
|
.file("src/sys/search/search.cpp")
|
|
.file("src/sys/wizard/wizard.cpp")
|
|
.file("src/sys/wizard/wizard_gui.cpp")
|
|
.file("src/sys/welcome/welcome.cpp")
|
|
.file("src/sys/welcome/welcome_gui.cpp")
|
|
.file("src/sys/textview/textview.cpp")
|
|
.file("src/sys/textview/textview_gui.cpp")
|
|
.file("src/sys/troubleshooting/troubleshooting.cpp")
|
|
.file("src/sys/troubleshooting/troubleshooting_gui.cpp")
|
|
.file("src/sys/common/mac.mm");
|
|
build.flag("-std=c++17");
|
|
|
|
for flag in cpp_flags {
|
|
build.flag(&flag);
|
|
}
|
|
|
|
build.compile("espansomodulosys");
|
|
|
|
// Render linker flags
|
|
|
|
generate_linker_flags(&config_path);
|
|
|
|
// On (older) OSX we need to link against the clang runtime,
|
|
// which is hidden in some non-default path.
|
|
//
|
|
// More details at https://github.com/alexcrichton/curl-rust/issues/279.
|
|
if let Some(path) = macos_link_search_path() {
|
|
println!("cargo:rustc-link-lib=clang_rt.osx");
|
|
println!("cargo:rustc-link-search={}", path);
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "macos")]
|
|
fn convert_fat_libraries_to_arm(lib_dir: &Path) {
|
|
for entry in
|
|
glob::glob(&format!("{}/*", lib_dir.to_string_lossy())).expect("failed to glob directory")
|
|
{
|
|
let path = entry.expect("unable to unwrap glob entry");
|
|
|
|
// Make sure it's a fat library
|
|
let lipo_output = std::process::Command::new("lipo")
|
|
.args(&["-detailed_info", &path.to_string_lossy()])
|
|
.output()
|
|
.expect("unable to check if library is fat");
|
|
let lipo_output = String::from_utf8_lossy(&lipo_output.stdout);
|
|
let lipo_output = lipo_output.trim();
|
|
if !lipo_output.contains("Fat header") {
|
|
println!(
|
|
"skipping {} as it's not a fat library",
|
|
path.to_string_lossy()
|
|
);
|
|
continue;
|
|
}
|
|
|
|
println!("converting {} to arm", path.to_string_lossy(),);
|
|
|
|
let result = std::process::Command::new("lipo")
|
|
.args(&[
|
|
"-thin",
|
|
"arm64",
|
|
&path.to_string_lossy(),
|
|
"-output",
|
|
&path.to_string_lossy(),
|
|
])
|
|
.output()
|
|
.expect("unable to extract arm64 slice from library");
|
|
|
|
if !result.status.success() {
|
|
panic!("unable to convert fat library to arm64 version");
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
fn get_cpp_flags(wx_config_path: &Path) -> Vec<String> {
|
|
let config_output = std::process::Command::new(&wx_config_path)
|
|
.arg("--cxxflags")
|
|
.output()
|
|
.expect("unable to execute wx-config");
|
|
let config_libs =
|
|
String::from_utf8(config_output.stdout).expect("unable to parse wx-config output");
|
|
let cpp_flags: Vec<String> = config_libs
|
|
.split(' ')
|
|
.filter_map(|s| {
|
|
if !s.trim().is_empty() {
|
|
Some(s.trim().to_owned())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect();
|
|
cpp_flags
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
fn generate_linker_flags(wx_config_path: &Path) {
|
|
use regex::Regex;
|
|
let config_output = std::process::Command::new(&wx_config_path)
|
|
.arg("--libs")
|
|
.output()
|
|
.expect("unable to execute wx-config libs");
|
|
let config_libs =
|
|
String::from_utf8(config_output.stdout).expect("unable to parse wx-config libs output");
|
|
let linker_flags: Vec<String> = config_libs
|
|
.split(' ')
|
|
.filter_map(|s| {
|
|
if !s.trim().is_empty() {
|
|
Some(s.trim().to_owned())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
let static_lib_extract = Regex::new(r"lib/lib(.*)\.a").unwrap();
|
|
|
|
// Translate the flags generated by `wx-config` to commands
|
|
// that cargo can understand.
|
|
for (i, flag) in linker_flags.iter().enumerate() {
|
|
if flag.starts_with("-L") {
|
|
let path = flag.trim_start_matches("-L");
|
|
println!("cargo:rustc-link-search=native={}", path);
|
|
} else if flag.starts_with("-framework") {
|
|
println!("cargo:rustc-link-lib=framework={}", linker_flags[i + 1]);
|
|
} else if flag.starts_with('/') {
|
|
let captures = static_lib_extract
|
|
.captures(flag)
|
|
.expect("unable to capture flag regex");
|
|
let libname = captures.get(1).expect("unable to find static libname");
|
|
println!("cargo:rustc-link-lib=static={}", libname.as_str());
|
|
} else if flag.starts_with("-l") {
|
|
let libname = flag.trim_start_matches("-l");
|
|
println!("cargo:rustc-link-lib=dylib={}", libname);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Taken from curl-rust: https://github.com/alexcrichton/curl-rust/pull/283/files
|
|
#[cfg(target_os = "macos")]
|
|
fn macos_link_search_path() -> Option<String> {
|
|
let output = std::process::Command::new("clang")
|
|
.arg("--print-search-dirs")
|
|
.output()
|
|
.ok()?;
|
|
if !output.status.success() {
|
|
println!("failed to run 'clang --print-search-dirs', continuing without a link search path");
|
|
return None;
|
|
}
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
for line in stdout.lines() {
|
|
if line.contains("libraries: =") {
|
|
let path = line.split('=').nth(1)?;
|
|
return Some(format!("{}/lib/darwin", path));
|
|
}
|
|
}
|
|
|
|
println!("failed to determine link search path, continuing without it");
|
|
None
|
|
}
|
|
|
|
// TODO: add documentation for linux
|
|
// Install wxWidgets:
|
|
// sudo apt install libwxgtk3.0-0v5 libwxgtk3.0-dev
|
|
//
|
|
// cargo run
|
|
#[cfg(target_os = "linux")]
|
|
fn build_native() {
|
|
// Make sure wxWidgets is installed
|
|
// Depending on the installation package, the 'wx-config' command might also be available as 'wx-config-gtk3',
|
|
// so we need to check for both.
|
|
// See also: https://github.com/federico-terzi/espanso/issues/840
|
|
let wx_config_command = if std::process::Command::new("wx-config")
|
|
.arg("--version")
|
|
.output()
|
|
.is_ok()
|
|
{
|
|
"wx-config"
|
|
} else if std::process::Command::new("wx-config-gtk3")
|
|
.arg("--version")
|
|
.output()
|
|
.is_ok()
|
|
{
|
|
"wx-config-gtk3"
|
|
} else {
|
|
panic!("wxWidgets is not installed, as `wx-config` cannot be executed")
|
|
};
|
|
|
|
let config_path = PathBuf::from(wx_config_command);
|
|
let cpp_flags = get_cpp_flags(&config_path);
|
|
|
|
let mut build = cc::Build::new();
|
|
build
|
|
.cpp(true)
|
|
.file("src/sys/form/form.cpp")
|
|
.file("src/sys/search/search.cpp")
|
|
.file("src/sys/common/common.cpp")
|
|
.file("src/sys/wizard/wizard.cpp")
|
|
.file("src/sys/wizard/wizard_gui.cpp")
|
|
.file("src/sys/welcome/welcome.cpp")
|
|
.file("src/sys/welcome/welcome_gui.cpp")
|
|
.file("src/sys/textview/textview.cpp")
|
|
.file("src/sys/textview/textview_gui.cpp")
|
|
.file("src/sys/troubleshooting/troubleshooting.cpp")
|
|
.file("src/sys/troubleshooting/troubleshooting_gui.cpp");
|
|
build.flag("-std=c++17");
|
|
|
|
for flag in cpp_flags {
|
|
build.flag(&flag);
|
|
}
|
|
|
|
build.compile("espansomodulosys");
|
|
|
|
// Render linker flags
|
|
|
|
generate_linker_flags(&config_path);
|
|
}
|
|
|
|
fn main() {
|
|
build_native();
|
|
}
|