mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-11 05:28:35 +00:00
Changes to build system to accomodate docking
This commit is contained in:
parent
86fa5913cd
commit
08b778524d
@ -25,3 +25,4 @@ pkg-config = { version="0.3", optional=true }
|
||||
default = []
|
||||
wasm = []
|
||||
freetype = ["pkg-config"]
|
||||
docking = []
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
16
imgui-sys/include_imgui_docking.cpp
Normal file
16
imgui-sys/include_imgui_docking.cpp
Normal file
@ -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
|
||||
|
||||
|
||||
16
imgui-sys/include_imgui_master.cpp
Normal file
16
imgui-sys/include_imgui_master.cpp
Normal file
@ -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
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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<String>,
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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<Vec<PathBuf>> {
|
||||
// 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(())
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user