Skip to content
Snippets Groups Projects
Select Git revision
  • a05ff5d30ebe47cbaea9acbe0ca440fbef596713
  • 2.11.5-picodata default protected
  • ipotemin/trigger-binding
  • picodata-25.4
  • astrochuk/fiber-stack-addr
  • mlaletin/ci-tarantool-module protected
  • astrochuk/remove-rows
  • artshmelev/try-remove-condition-replicaset_connect_quorum
  • kusancho/fiber_stack_report
  • e.dmitriev/osx-arm-build-fix
  • nnk/warning-limit-and-iproto-executor
  • kdy/ci_build_with_tag protected
  • nnk/iproto-emergency-executor
  • nnk/add-vdbe-soft-limit
  • origin/picodata-25.3
  • artshmelev/debug-tls
  • v.klimenko/box_index_iterator_with_offset
  • picodata-25.3
  • funbringer/2.11.5-fix-select-param-having
  • max/new-cache2
  • nnk/expose-actual-vdbe-opcode-count
  • 2.11.5.283 protected
  • 2.11.5.266 protected
  • 2.11.5.265 protected
  • 2.11.5.254 protected
  • 2.11.5.253 protected
  • 2.11.5.233 protected
  • 2.11.5.230 protected
  • 2.11.5.228 protected
  • 2.11.5.226 protected
  • 2.11.5.225 protected
  • 2.11.5.223 protected
  • 2.11.5.221 protected
  • 2.11.5.220 protected
  • 2.11.5.219 protected
  • 2.11.5.218 protected
  • 2.11.5.217 protected
  • 2.11.5 protected
  • 2.11.2.159 protected
  • 2.11.4 protected
  • 2.11.2.155 protected
41 results

LICENSE

Blame
  • This project is licensed under the BSD 2-Clause "Simplified" License. Learn more
    build.rs 13.25 KiB
    use std::collections::HashSet;
    use std::io::Write;
    use std::os::unix::io::{AsRawFd, FromRawFd};
    use std::panic::Location;
    use std::path::Path;
    use std::process::Command;
    use std::process::Stdio;
    
    // See also: https://doc.rust-lang.org/cargo/reference/build-scripts.html
    fn main() {
        // The file structure roughly looks as follows:
        // .
        // ├── build.rs                         // you are here
        // ├── src/
        // ├── tarantool-sys
        // │   └── static-build
        // │       └── CMakeLists.txt
        // └── <target-dir>/<build-type>/build  // <- build_root
        //     ├── picodata-<smth>/out          // <- out_dir
        //     ├── tarantool-http
        //     └── tarantool-sys
        //         ├── ncurses-prefix
        //         ├── openssl-prefix
        //         ├── readline-prefix
        //         └── tarantool-prefix
        //
        let out_dir = std::env::var("OUT_DIR").unwrap();
        dbg!(&out_dir); // "<target-dir>/<build-type>/build/picodata-<smth>/out"
    
        // Running `cargo build` and `cargo clippy` produces 2 different
        // `out_dir` paths. This is stupid, we're not going to use them.
        let build_root = Path::new(&out_dir).parent().unwrap().parent().unwrap();
        dbg!(&build_root); // "<target-dir>/<build-type>/build"
    
        // See also:
        // https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
        if let Some(ref makeflags) = std::env::var_os("CARGO_MAKEFLAGS") {
            std::env::set_var("MAKEFLAGS", makeflags);
        }
    
        #[allow(clippy::print_literal)]
        for (var, value) in std::env::vars() {
            println!("[{}:{}] {var}={value}", file!(), line!());
        }
    
        generate_export_stubs(&out_dir);
        build_tarantool(build_root);
        build_http(build_root);
        #[cfg(feature = "webui")]
        build_webui(build_root);
    
        println!("cargo:rerun-if-changed=tarantool-sys");
        println!("cargo:rerun-if-changed=http/http");
        #[cfg(feature = "webui")]
        rerun_if_webui_changed();
    }
    
    fn generate_export_stubs(out_dir: &str) {
        let mut symbols = HashSet::with_capacity(1024);
        let exports = std::fs::read_to_string("tarantool-sys/extra/exports").unwrap();
        read_symbols_into(&exports, &mut symbols);
    
        let exports = std::fs::read_to_string("tarantool-sys/extra/exports_libcurl").unwrap();
        read_symbols_into(&exports, &mut symbols);
    
        let exports = std::fs::read_to_string("src/sql/exports").unwrap();
        read_symbols_into(&exports, &mut symbols);
    
        let mut code = Vec::with_capacity(2048);
        writeln!(code, "pub fn export_symbols() {{").unwrap();
        writeln!(code, "    extern \"C\" {{").unwrap();
        for symbol in &symbols {
            writeln!(code, "        static {symbol}: *const ();").unwrap();
        }
        writeln!(code, "    }}").unwrap();
        // TODO: use std::hint::black_box, when we move to rust 1.66
        writeln!(code, "    fn black_box(_: *const ()) {{}}").unwrap();
        writeln!(code, "    unsafe {{").unwrap();
        for symbol in &symbols {
            writeln!(code, "        black_box({symbol});").unwrap();
        }
        writeln!(code, "    }}").unwrap();
        writeln!(code, "}}").unwrap();
    
        let gen_path = std::path::Path::new(out_dir).join("export_symbols.rs");
        std::fs::write(gen_path, code).unwrap();
    
        fn read_symbols_into<'a>(file_contents: &'a str, symbols: &mut HashSet<&'a str>) {
            for line in file_contents.lines() {
                let line = line.trim();
                if line.starts_with('#') {
                    continue;
                }
                if line.is_empty() {
                    continue;
                }
                symbols.insert(line);
            }
        }
    }
    
    #[cfg(feature = "webui")]
    fn rerun_if_webui_changed() {
        use std::fs;
    
        let source_dir = std::env::current_dir().unwrap().join("picodata-webui");
        // Do not rerun for generated files changes
        let ignored_files = ["node_modules", ".husky"];
        for entry in fs::read_dir(&source_dir)
            .expect("failed to scan picodata-webui dir")
            .flatten()
        {
            if !ignored_files.contains(&entry.file_name().to_str().unwrap()) {
                println!(
                    "cargo:rerun-if-changed=picodata-webui/{}",
                    entry.file_name().to_string_lossy()
                );
            }
        }
    }
    
    #[cfg(feature = "webui")]
    fn build_webui(build_root: &Path) {
        let source_dir = std::env::current_dir().unwrap().join("picodata-webui");
        let build_dir = build_root.join("picodata-webui");
        let build_dir_str = build_dir.display().to_string();
    
        Command::new("yarn")
            .arg("install")
            .arg("--prefer-offline")
            .arg("--frozen-lockfile")
            .arg("--no-progress")
            .arg("--non-interactive")
            .current_dir(&source_dir)
            .run();
        Command::new("yarn")
            .arg("vite")
            .arg("build")
            .args(["--outDir", &build_dir_str])
            .arg("--emptyOutDir")
            .current_dir(&source_dir)
            .run();
    }
    
    fn build_http(build_root: &Path) {
        let build_dir = build_root.join("tarantool-http");
        let build_dir_str = build_dir.display().to_string();
    
        let tarantool_dir = build_root.join("tarantool-sys/tarantool-prefix");
        let tarantool_dir_str = tarantool_dir.display().to_string();
    
        Command::new("cmake")
            .args(["-S", "http"])
            .args(["-B", &build_dir_str])
            .arg(format!("-DTARANTOOL_DIR={tarantool_dir_str}"))
            .run();
    
        Command::new("cmake")
            .args(["--build", &build_dir_str])
            .arg("-j")
            .run();
    
        Command::new("ar")
            .arg("-rcs")
            .arg(build_dir.join("libhttpd.a"))
            .arg(build_dir.join("http/CMakeFiles/httpd.dir/lib.c.o"))
            .run();
    
        rustc::link_search(build_dir_str);
    }
    
    fn build_tarantool(build_root: &Path) {
        let tarantool_sys = build_root.join("tarantool-sys");
        let tarantool_build = tarantool_sys.join("tarantool-prefix/src/tarantool-build");
    
        if !tarantool_build.exists() {
            // Build from scratch
            Command::new("cmake")
                .args(["-S", "tarantool-sys/static-build"])
                .arg("-B")
                .arg(&tarantool_sys)
                .arg(concat!(
                    "-DCMAKE_TARANTOOL_ARGS=",
                    "-DCMAKE_BUILD_TYPE=RelWithDebInfo;",
                    "-DBUILD_TESTING=FALSE;",
                    "-DBUILD_DOC=FALSE",
                ))
                .run();
            Command::new("cmake")
                .arg("--build")
                .arg(&tarantool_sys)
                .arg("-j")
                .run();
        } else {
            // static-build/CMakeFiles.txt builds tarantool via the ExternalProject
            // module, which doesn't rebuild subprojects if their contents changed,
            // therefore we dive into `tarantool-prefix/src/tarantool-build`
            // directly and try to rebuild it individually.
            Command::new("cmake")
                .arg("--build")
                .arg(&tarantool_build)
                .arg("-j")
                .run();
        }
    
        let tarantool_sys = tarantool_sys.display();
        let tarantool_build = tarantool_build.display();
    
        // Don't build a shared object in case it's the default for the compiler
        rustc::link_arg("-no-pie");
    
        for l in [
            "core",
            "small",
            "msgpuck",
            "vclock",
            "bit",
            "swim",
            "uri",
            "json",
            "http_parser",
            "mpstream",
            "raft",
            "csv",
            "bitset",
            "coll",
            "fakesys",
            "salad",
            "tzcode",
        ] {
            rustc::link_search(format!("{tarantool_build}/src/lib/{l}"));
            rustc::link_lib_static(l);
        }
    
        rustc::link_search(format!("{tarantool_build}/src/lib/crypto"));
        rustc::link_lib_static("tcrypto");
    
        rustc::link_search(format!("{tarantool_build}"));
        rustc::link_search(format!("{tarantool_build}/src"));
        rustc::link_search(format!("{tarantool_build}/src/box"));
        rustc::link_search(format!("{tarantool_build}/third_party/luajit/src"));
        rustc::link_search(format!("{tarantool_build}/third_party/libyaml"));
        rustc::link_search(format!("{tarantool_build}/third_party/c-dt/build"));
        rustc::link_search(format!("{tarantool_build}/build/nghttp2/dest/lib"));
    
        rustc::link_lib_static("tarantool");
        rustc::link_lib_static("ev");
        rustc::link_lib_static("coro");
        rustc::link_lib_static("cdt");
        rustc::link_lib_static("server");
        rustc::link_lib_static("misc");
        rustc::link_lib_static("nghttp2");
        rustc::link_lib_static("zstd");
        rustc::link_lib_static("decNumber");
        rustc::link_lib_static("eio");
        rustc::link_lib_static("box");
        rustc::link_lib_static("tuple");
        rustc::link_lib_static("xrow");
        rustc::link_lib_static("box_error");
        rustc::link_lib_static("xlog");
        rustc::link_lib_static("crc32");
        rustc::link_lib_static("stat");
        rustc::link_lib_static("shutdown");
        rustc::link_lib_static("swim_udp");
        rustc::link_lib_static("swim_ev");
        rustc::link_lib_static("symbols");
        rustc::link_lib_static("cpu_feature");
        rustc::link_lib_static("luajit");
        rustc::link_lib_static("yaml_static");
        rustc::link_lib_static("xxhash");
    
        if cfg!(target_os = "macos") {
            // Currently we link against 2 versions of `decNumber` library: one
            // comes with tarantool and ther other comes from the `dec` cargo crate.
            // On macos this seems to confuse the linker, which just chooses one of
            // the libraries and complains that it can't find symbols from the other.
            // So we add the second library explicitly via full path to the file.
            rustc::link_arg(format!("{tarantool_build}/libdecNumber.a"));
    
            // OpenMP and Libunwind are builtin to the compiler on macos
        } else {
            rustc::link_lib_static("gomp");
    
            // These two must be linked as positional arguments, because they define
            // duplicate symbols which is not allowed (by default) when linking with
            // via -l... option
            let lib_dir = format!("{tarantool_build}/third_party/libunwind/src/.libs");
            if cfg!(target_arch = "x86_64") {
                rustc::link_arg(format!("{lib_dir}/libunwind-x86_64.a"));
            } else if cfg!(target_arch = "aarch64") {
                rustc::link_arg(format!("{lib_dir}/libunwind-aarch64.a"));
            }
            rustc::link_arg(format!("{lib_dir}/libunwind.a"));
        }
    
        rustc::link_arg("-lc");
    
        rustc::link_search(format!("{tarantool_sys}/readline-prefix/lib"));
        rustc::link_lib_static("readline");
    
        rustc::link_search(format!("{tarantool_sys}/icu-prefix/lib"));
        rustc::link_lib_static("icudata");
        rustc::link_lib_static("icui18n");
        rustc::link_lib_static("icuio");
        rustc::link_lib_static("icutu");
        rustc::link_lib_static("icuuc");
    
        rustc::link_search(format!("{tarantool_sys}/zlib-prefix/lib"));
        rustc::link_lib_static("z");
    
        rustc::link_search(format!("{tarantool_build}/build/curl/dest/lib"));
        rustc::link_lib_static("curl");
    
        rustc::link_search(format!("{tarantool_build}/build/ares/dest/lib"));
        rustc::link_lib_static("cares");
    
        rustc::link_search(format!("{tarantool_sys}/openssl-prefix/lib"));
        rustc::link_lib_static("ssl");
        rustc::link_lib_static("crypto");
    
        rustc::link_search(format!("{tarantool_sys}/ncurses-prefix/lib"));
        rustc::link_lib_static("tinfo");
    
        rustc::link_search(format!("{tarantool_sys}/iconv-prefix/lib"));
        if cfg!(target_os = "macos") {
            // -lc++ instead of -lstdc++ on macos
            rustc::link_lib_dynamic("c++");
    
            // -lresolv on macos
            rustc::link_lib_dynamic("resolv");
        } else {
            // not supported on macos
            rustc::link_arg("-export-dynamic");
            rustc::link_lib_dynamic("stdc++");
        }
    }
    
    trait CommandExt {
        fn run(&mut self);
    }
    
    impl CommandExt for Command {
        #[track_caller]
        fn run(&mut self) {
            let loc = Location::caller();
            println!("[{}:{}] running [{:?}]", loc.file(), loc.line(), self);
    
            // Redirect stderr to stdout. This is needed to preserve the order of
            // error messages in the output, because otherwise cargo separates the
            // streams into 2 chunks destroying any hope of understanding when the
            // errors happened.
            let stdout_fd = std::io::stdout().as_raw_fd();
            let stdout_dup_fd = nix::unistd::dup(stdout_fd).expect("what could go wrong?");
            let stdout = unsafe { Stdio::from_raw_fd(stdout_dup_fd) };
            self.stderr(stdout);
    
            let prog = self.get_program().to_owned().into_string().unwrap();
    
            match self.status() {
                Ok(status) if status.success() => (),
                Ok(status) => panic!("{} failed: {}", prog, status),
                Err(e) => panic!("failed running `{}`: {}", prog, e),
            }
        }
    }
    
    mod rustc {
        pub fn link_search(path: impl AsRef<str>) {
            println!("cargo:rustc-link-search=native={}", path.as_ref());
        }
    
        pub fn link_lib_static(lib: impl AsRef<str>) {
            // See also:
            // https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-liblib
            // https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-link-lib
            println!(
                "cargo:rustc-link-lib=static:+whole-archive,-bundle={}",
                lib.as_ref()
            );
        }
    
        pub fn link_lib_dynamic(lib: impl AsRef<str>) {
            println!("cargo:rustc-link-lib=dylib={}", lib.as_ref());
        }
    
        pub fn link_arg(arg: impl AsRef<str>) {
            println!("cargo:rustc-link-arg={}", arg.as_ref());
        }
    }