From 08b778524de37e775798fe7ccd57127767c222e7 Mon Sep 17 00:00:00 2001 From: dbr Date: Wed, 27 Oct 2021 19:10:37 +1100 Subject: [PATCH] Changes to build system to accomodate docking --- imgui-sys/Cargo.toml | 1 + imgui-sys/build.rs | 34 ++--- imgui-sys/include_all_imgui.cpp | 16 --- imgui-sys/include_imgui_docking.cpp | 16 +++ imgui-sys/include_imgui_master.cpp | 16 +++ xtask/src/bindgen.rs | 38 +++--- xtask/src/flags.rs | 10 +- xtask/src/main.rs | 4 - xtask/src/submodules.rs | 201 ---------------------------- 9 files changed, 76 insertions(+), 260 deletions(-) delete mode 100644 imgui-sys/include_all_imgui.cpp create mode 100644 imgui-sys/include_imgui_docking.cpp create mode 100644 imgui-sys/include_imgui_master.cpp delete mode 100644 xtask/src/submodules.rs diff --git a/imgui-sys/Cargo.toml b/imgui-sys/Cargo.toml index 2515d41..7e4abcc 100644 --- a/imgui-sys/Cargo.toml +++ b/imgui-sys/Cargo.toml @@ -25,3 +25,4 @@ pkg-config = { version="0.3", optional=true } default = [] wasm = [] freetype = ["pkg-config"] +docking = [] diff --git a/imgui-sys/build.rs b/imgui-sys/build.rs index 3dd6f79..30e3934 100644 --- a/imgui-sys/build.rs +++ b/imgui-sys/build.rs @@ -2,7 +2,6 @@ use std::fs; use std::io; -use std::path::Path; const DEFINES: &[(&str, Option<&str>)] = &[ // Rust `char` is a unicode scalar value, e.g. 32 bits. @@ -27,23 +26,18 @@ fn assert_file_exists(path: &str) -> io::Result<()> { } fn main() -> io::Result<()> { - let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); - println!( - "cargo:THIRD_PARTY={}", - manifest_dir.join("third-party").display() - ); + // Output define args for compiler for (key, value) in DEFINES.iter() { println!("cargo:DEFINE_{}={}", key, value.unwrap_or("")); } + + // If we aren't building WASM output, bunch of extra stuff to do if std::env::var_os("CARGO_FEATURE_WASM").is_none() { - // Check submodule status. (Anything else should be a compile error in - // the C code). - assert_file_exists("third-party/cimgui.cpp")?; - assert_file_exists("third-party/imgui/imgui.cpp")?; - + // C++ compiler let mut build = cc::Build::new(); - build.cpp(true); + + // Set defines for compiler for (key, value) in DEFINES.iter() { build.define(key, *value); } @@ -58,11 +52,21 @@ fn main() -> io::Result<()> { build.define("IMGUI_ENABLE_FREETYPE", None); println!("cargo:DEFINE_IMGUI_ENABLE_FREETYPE="); - // imgui_freetype.cpp needs access to imgui.h - build.include(manifest_dir.join("third-party/imgui/")); + // imgui_freetype.cpp needs access to `#include "imgui.h"` + let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); + #[cfg(feature = "docking")] + build.include(manifest_dir.join("third-party/imgui-docking/imgui")); + #[cfg(not(feature = "docking"))] + build.include(manifest_dir.join("third-party/imgui-master/imgui")); } + #[cfg(feature = "docking")] + let imgui_cpp = "include_imgui_docking.cpp"; + #[cfg(not(feature = "docking"))] + let imgui_cpp = "include_imgui_master.cpp"; + let compiler = build.get_compiler(); + // Avoid the if-supported flag functions for easy cases, as they're // kinda costly. if compiler.is_like_gnu() || compiler.is_like_clang() { @@ -71,7 +75,7 @@ fn main() -> io::Result<()> { // TODO: disable linking C++ stdlib? Not sure if it's allowed. build .warnings(false) - .file("include_all_imgui.cpp") + .file(imgui_cpp) .compile("libcimgui.a"); } Ok(()) diff --git a/imgui-sys/include_all_imgui.cpp b/imgui-sys/include_all_imgui.cpp deleted file mode 100644 index fbda88d..0000000 --- a/imgui-sys/include_all_imgui.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// This improves build speed by only compiling a single file, and performance by -// allowing the optimizer to inline across separate object files (note that even -// when rust is built with LTO, unless the steps are taken to allow cross-lang -// LTO (tricky), the C/C++ code won't be LTOed). -#include "./third-party/imgui/imgui.cpp" -#include "./third-party/imgui/imgui_demo.cpp" -#include "./third-party/imgui/imgui_draw.cpp" -#include "./third-party/imgui/imgui_widgets.cpp" -#include "./third-party/imgui/imgui_tables.cpp" -#include "./third-party/cimgui.cpp" - -#ifdef IMGUI_ENABLE_FREETYPE -#include "./third-party/imgui/misc/freetype/imgui_freetype.cpp" -#endif - - diff --git a/imgui-sys/include_imgui_docking.cpp b/imgui-sys/include_imgui_docking.cpp new file mode 100644 index 0000000..81d66a6 --- /dev/null +++ b/imgui-sys/include_imgui_docking.cpp @@ -0,0 +1,16 @@ +// This improves build speed by only compiling a single file, and performance by +// allowing the optimizer to inline across separate object files (note that even +// when rust is built with LTO, unless the steps are taken to allow cross-lang +// LTO (tricky), the C/C++ code won't be LTOed). +#include "./third-party/imgui-docking/imgui/imgui.cpp" +#include "./third-party/imgui-docking/imgui/imgui_demo.cpp" +#include "./third-party/imgui-docking/imgui/imgui_draw.cpp" +#include "./third-party/imgui-docking/imgui/imgui_widgets.cpp" +#include "./third-party/imgui-docking/imgui/imgui_tables.cpp" +#include "./third-party/imgui-docking/cimgui.cpp" + +#ifdef IMGUI_ENABLE_FREETYPE +#include "./third-party/imgui-docking/imgui/misc/freetype/imgui_freetype.cpp" +#endif + + diff --git a/imgui-sys/include_imgui_master.cpp b/imgui-sys/include_imgui_master.cpp new file mode 100644 index 0000000..aa32652 --- /dev/null +++ b/imgui-sys/include_imgui_master.cpp @@ -0,0 +1,16 @@ +// This improves build speed by only compiling a single file, and performance by +// allowing the optimizer to inline across separate object files (note that even +// when rust is built with LTO, unless the steps are taken to allow cross-lang +// LTO (tricky), the C/C++ code won't be LTOed). +#include "./third-party/imgui-master/imgui/imgui.cpp" +#include "./third-party/imgui-master/imgui/imgui_demo.cpp" +#include "./third-party/imgui-master/imgui/imgui_draw.cpp" +#include "./third-party/imgui-master/imgui/imgui_widgets.cpp" +#include "./third-party/imgui-master/imgui/imgui_tables.cpp" +#include "./third-party/imgui-master/cimgui.cpp" + +#ifdef IMGUI_ENABLE_FREETYPE +#include "./third-party/imgui-master/imgui/misc/freetype/imgui_freetype.cpp" +#endif + + diff --git a/xtask/src/bindgen.rs b/xtask/src/bindgen.rs index ececdde..1beb900 100644 --- a/xtask/src/bindgen.rs +++ b/xtask/src/bindgen.rs @@ -5,10 +5,6 @@ use std::path::{Path, PathBuf}; impl Bindgen { pub fn run(self) -> Result<()> { let root = crate::project_root(); - let bindings = self - .cimgui_path - .map(PathBuf::from) - .unwrap_or_else(|| root.join("imgui-sys/third-party")); let output = self .output_path @@ -20,18 +16,28 @@ impl Bindgen { .or_else(|| std::env::var("IMGUI_RS_WASM_IMPORT_NAME").ok()) .unwrap_or_else(|| "imgui-sys-v0".to_string()); - let types = get_types(&bindings.join("structs_and_enums.json"))?; - let funcs = get_definitions(&bindings.join("definitions.json"))?; - let header = bindings.join("cimgui.h"); + for variant in ["master", "docking"] { + let cimgui_output = root.join(&format!("imgui-sys/third-party/imgui-{}", variant)); - generate_binding_file(&header, &output.join("bindings.rs"), &types, &funcs, None)?; - generate_binding_file( - &header, - &output.join("wasm_bindings.rs"), - &types, - &funcs, - Some(&wasm_name), - )?; + let types = get_types(&cimgui_output.join("structs_and_enums.json"))?; + let funcs = get_definitions(&cimgui_output.join("definitions.json"))?; + let header = cimgui_output.join("cimgui.h"); + + let output_name = if variant != "master" { + format!("{}_bindings.rs", variant) + } else { + "bindings.rs".into() + }; + + generate_binding_file(&header, &output.join(&output_name), &types, &funcs, None)?; + generate_binding_file( + &header, + &output.join(&format!("wasm_{}", &output_name)), + &types, + &funcs, + Some(&wasm_name), + )?; + } Ok(()) } @@ -104,7 +110,7 @@ fn generate_binding_file( "--no-prepend-enum-name", "--no-doc-comments", // Layout tests aren't portable (they hardcode type sizes), and for - // our case they just serve to sanity check rustc's implementation of + // our case they just serve to sanity check rustc's implementation of // `#[repr(C)]`. If we bind directly to C++ ever, we should reconsider this. "--no-layout-tests", "--with-derive-default", diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs index fde689f..f0643af 100644 --- a/xtask/src/flags.rs +++ b/xtask/src/flags.rs @@ -23,12 +23,10 @@ xflags::args_parser! { /// Print help information. optional -h, --help } - /// Run lints the way we'd run it in CI + /// Run lints the way we run it in CI cmd lint {} - /// Run tests the way we'd run them in CI + /// Run tests the way we run them in CI cmd test {} - /// magically wrangle the submodules if needed - cmd modfix {} /// produce bindings using installed `bindgen`. cmd bindgen { /// folder containing cimgui output (default: imgui-sys/third-party) @@ -55,7 +53,6 @@ pub enum XtaskCmd { Help(Help), Lint(Lint), Test(Test), - Modfix(Modfix), Bindgen(Bindgen), } @@ -70,9 +67,6 @@ pub struct Lint {} #[derive(Debug)] pub struct Test {} -#[derive(Debug)] -pub struct Modfix {} - #[derive(Debug)] pub struct Bindgen { pub cimgui_path: Option, diff --git a/xtask/src/main.rs b/xtask/src/main.rs index dd10cce..f6ceb37 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,13 +1,10 @@ mod bindgen; mod flags; -mod submodules; use anyhow::Result; use flags::XtaskCmd; use std::path::{Path, PathBuf}; -pub use submodules::autofix_submodules; - fn main() { if let Err(e) = try_main() { eprintln!("{}", e); @@ -29,7 +26,6 @@ fn try_main() -> Result<()> { XtaskCmd::Help(_) => eprintln!("{}", flags::Xtask::HELP), XtaskCmd::Lint(_) => lint_all()?, XtaskCmd::Test(_) => test_all()?, - XtaskCmd::Modfix(_) => submodules::fixup_submodules()?, XtaskCmd::Bindgen(cmd) => cmd.run()?, } Ok(()) diff --git a/xtask/src/submodules.rs b/xtask/src/submodules.rs deleted file mode 100644 index 7d9b864..0000000 --- a/xtask/src/submodules.rs +++ /dev/null @@ -1,201 +0,0 @@ -use anyhow::Result; -use std::{ - path::{Path, PathBuf}, - sync::atomic::{AtomicBool, Ordering::Relaxed}, -}; - -/// "Automagic" submodule wrangling (sync, clean, fix, etc) -/// -/// Ported from rustc's `x.py` code, which runs something like this -/// automatically (even for `./x.py --help`) -/// -/// (We only have one submodule, so the handling of multiple submodules is -/// pointless for us for now. However, I snarfed this from the xtask in my game -/// engine, which has several submodules, and we might have more later). -/// -/// In theory this can loose local changes made within the submodule. This is -/// unlikely (I asked if this had caused issues within rustc and nobody who -/// replied had experienced it), but should be avoidable if you -/// -/// 1. don't modify `.gitignore`d files in submodules unless those files don't -/// need to survive updates. -/// -/// 2. don't add modifications to submodules when they're unsynchronized (e.g. -/// doing a `git pull` that needs to update the submodule, and making local -/// modifications to it before syncing it) I don't see a reason for we'd do -/// the first, and the 2nd seems particularly unlikely -/// -/// The first won't be an issue for us, and shouldn't be an issue for other -/// projects unless they're doing fairly weird stuff. The second is hard to -/// imagine for us, since most updates to dear imgui are backwards incompatible, -/// so you'll probably notice you need to update them. -pub fn fixup_submodules() -> Result<()> { - // execute from workspace root - let _guard = xshell::pushd(&crate::project_root())?; - let mods = all_submodules()?; - for path in mods { - fix_submodule(path.as_ref())?; - } - Ok(()) -} - -/// Same as `fixup_submodules` unless it's explicitly disabled by -/// setting `IMGUI_XTASK_NO_SUBMODULE_FIXUP` in the environment. -/// -/// I don't know why you'd need this, but it seems safer to have than not. That -/// said, rustc (where we took our logic from) is way bigger, has a lot more dev -/// going on in submodules, and has no ability to disable the behavior. -pub fn autofix_submodules() { - if option_env!("IMGUI_XTASK_NO_SUBMODULE_FIXUP").is_none() { - if let Err(e) = fixup_submodules() { - eprintln!("Warning: failed to sync submodules: {:?}", e); - } - } -} - -fn fix_submodule(rel_path: &Path) -> Result<()> { - let checked_out_hash = { - // would like to use `cmd` but need - // https://github.com/matklad/xshell/pull/19 - let out = std::process::Command::new("git") - .args(&["rev-parse", "HEAD"]) - .current_dir(rel_path) - .output()?; - if !out.status.success() { - anyhow::bail!( - "`git rev-parse HEAD` (from {}) failed with {:?}", - rel_path.display(), - out.status - ); - } - String::from_utf8_lossy(&out.stdout).trim().to_string() - }; - - let recorded_hash = { - let out = std::process::Command::new("git") - .args(&["ls-tree", "HEAD"]) - .arg(rel_path) - .output()?; - if !out.status.success() { - anyhow::bail!( - "`git ls-tree HEAD {}` failed with {:?}", - rel_path.display(), - out.status, - ); - } - let stdout = String::from_utf8_lossy(&out.stdout); - if stdout.trim().lines().count() != 1 { - anyhow::bail!("Weird output from git ls-tree: {:?}", stdout) - } - // stdout is in the format `mode kind hash\tfilename` and we want `hash` - stdout.trim().split_whitespace().nth(2).unwrap().to_owned() - }; - - // if the hashes are the same they're the same, we're good - if checked_out_hash == recorded_hash { - if crate::verbose() { - eprintln!( - "Nothing to be done for {} ({:?} == {:?})", - rel_path.display(), - checked_out_hash, - recorded_hash - ); - } - return Ok(()); - } - - // otherwise, update the submodule - eprintln!("Updating submodule {}", rel_path.display()); - // force it to sync - xshell::cmd!("git submodule -q sync {rel_path}") - .echo_cmd(crate::verbose()) - .run()?; - - // NB: rustc supports older version of `git`, and so retries - // without the --progress arg if running with it fails. - git_submodule_update_init_recursive()?; - { - // enter the submodule and update, reset, and clean to - // force it to be fully in-sync. If you have unsaved changes, - // this will lose them (but they can be recovered from the reflog) - let _d = xshell::pushd(rel_path)?; - git_submodule_update_init_recursive()?; - - xshell::cmd!("git reset -q --hard") - .echo_cmd(crate::verbose()) - .run()?; - xshell::cmd!("git clean -qdfx") - .echo_cmd(crate::verbose()) - .run()?; - } - Ok(()) -} - -fn all_submodules() -> Result> { - // use `--null` to get \0-separated output, which lets us handle weird shit - // like paths with `\n` in them. - let mut out = xshell::cmd!("git config --file .gitmodules --path --null --get-regexp path") - .echo_cmd(false) - .output()?; - // trim the end so that split works. - while out.stdout.ends_with(b"\0") { - out.stdout.pop(); - } - let mut pb = vec![]; - for kv in out.stdout.split(|n| *n == 0).filter(|v| !v.is_empty()) { - if let Ok(v) = std::str::from_utf8(kv) { - // `v` is formatted like "{name}\n{path}", so discard everything up - // to (and including) the newline. - if let Some(path) = v.find('\n').map(|p| &v[(p + 1)..]) { - pb.push(path.into()); - } else { - eprintln!( - "warning: invalid format for gitmodule entry: {:?}", - String::from_utf8_lossy(kv), - ); - } - } else { - eprintln!( - "warning: ignoring invalid utf8 in: {:?}", - String::from_utf8_lossy(kv), - ); - } - } - Ok(pb) -} - -/// Note: This is to support older versions of git that don't have `--progress`. -/// I have no idea how old they are, or i'd make a decision on whether to support -/// it. Basically, we run `git submodule update --init --recursive --progress`, -/// and if it fails we omit the `--progress` flag from then on, and immediately -/// retry. -/// -/// If we don't care about those git versions, this could just be -/// `xshell::cmd!("git submodule update --init --recursive --progress")` -fn git_submodule_update_init_recursive() -> Result<()> { - return if NO_PROGRESS_FLAG.load(Relaxed) { - do_git_smu(false) - } else if do_git_smu(true).is_err() { - NO_PROGRESS_FLAG.store(true, Relaxed); - if crate::verbose() { - eprintln!(" retrying without `--progress` flag (old git?)"); - } - do_git_smu(false) - } else { - Ok(()) - }; - - static NO_PROGRESS_FLAG: AtomicBool = AtomicBool::new(false); - - fn do_git_smu(with_progress_flag: bool) -> Result<()> { - let flag = if with_progress_flag { - Some("--progress") - } else { - None - }; - xshell::cmd!("git submodule update --init --recursive {flag...}") - .echo_cmd(crate::verbose()) - .run()?; - Ok(()) - } -}