diff mbox series

[PoCv2,04/15] build-sys: add a cargo-wrapper script

Message ID 20201011203513.1621355-5-marcandre.lureau@redhat.com
State New
Headers show
Series Rust binding for QAPI (qemu-ga only, for now) | expand

Commit Message

Marc-André Lureau Oct. 11, 2020, 8:35 p.m. UTC
From: Marc-André Lureau <marcandre.lureau@redhat.com>

Introduce a script to help calling cargo from Rust.

Cargo is the most convenient way to build Rust code, with various
crates, and has many features that meson lacks in general for Rust.
Trying to convert projects to meson automatically is an option I
considered (see for ex https://github.com/badboy/bygge for ninja
conversion), but the complexity of the task and the need of build.rs
mechanism in various crates makes this endeavour out of scope at this
point.

This script will help to invoke cargo in different ways. For now, it
supports building static libraries. It sets up a common environment, run
the compiler and grep its output for the linker flags. Finally it copies
the built library to the expected meson output directory, and create a
file with the linker arguments.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 meson.build              |   1 +
 scripts/cargo_wrapper.py | 101 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 102 insertions(+)
 create mode 100644 scripts/cargo_wrapper.py
diff mbox series

Patch

diff --git a/meson.build b/meson.build
index d8526dc999..c30bb290c5 100644
--- a/meson.build
+++ b/meson.build
@@ -75,6 +75,7 @@  endif
 
 with_rust = 'CONFIG_WITH_RUST' in config_host
 cargo = find_program('cargo', required: with_rust)
+cargo_wrapper = find_program('scripts/cargo_wrapper.py')
 
 if with_rust
   rs_target_triple = config_host['CONFIG_WITH_RUST_TARGET']
diff --git a/scripts/cargo_wrapper.py b/scripts/cargo_wrapper.py
new file mode 100644
index 0000000000..164fad5123
--- /dev/null
+++ b/scripts/cargo_wrapper.py
@@ -0,0 +1,101 @@ 
+#!/usr/bin/env python3
+# Copyright (c) 2020 Red Hat, Inc.
+#
+# Author:
+#  Marc-André Lureau <marcandre.lureau@gmail.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later.  See the COPYING file in the top-level directory.
+
+import argparse
+import configparser
+import distutils.file_util
+import glob
+import os
+import os.path
+import re
+import subprocess
+import sys
+from typing import List
+
+
+def get_cargo_target_dir(args: argparse.Namespace) -> str:
+    # avoid conflict with qemu "target" directory
+    return os.path.join(args.build_dir, "rs-target")
+
+
+def get_manifest_path(args: argparse.Namespace) -> str:
+    return os.path.join(args.src_dir, "Cargo.toml")
+
+
+def build_lib(args: argparse.Namespace) -> None:
+    target_dir = get_cargo_target_dir(args)
+    manifest_path = get_manifest_path(args)
+    # let's pretend it's an INI file to avoid extra dependency
+    config = configparser.ConfigParser()
+    config.read(manifest_path)
+    package_name = config["package"]["name"].strip('"')
+    liba = os.path.join(
+        target_dir, args.target_triple, args.build_type, "lib" + package_name + ".a"
+    )
+    libargs = os.path.join(args.build_dir, "lib" + package_name + ".args")
+
+    env = {}
+    env["MESON_CURRENT_BUILD_DIR"] = args.build_dir
+    env["MESON_BUILD_ROOT"] = args.build_root
+    env["WINAPI_NO_BUNDLED_LIBRARIES"] = "1"
+    cargo_cmd = [
+        "cargo",
+        "rustc",
+        "--target-dir",
+        target_dir,
+        "--manifest-path",
+        manifest_path,
+    ]
+    if args.target_triple:
+        cargo_cmd += ["--target", args.target_triple]
+    if args.build_type == "release":
+        cargo_cmd += ["--release"]
+    cargo_cmd += ["--", "--print", "native-static-libs"]
+    cargo_cmd += args.EXTRA
+    try:
+        out = subprocess.check_output(
+            cargo_cmd,
+            env=dict(os.environ, **env),
+            stderr=subprocess.STDOUT,
+            universal_newlines=True,
+        )
+        native_static_libs = re.search(r"native-static-libs:(.*)", out)
+        link_args = native_static_libs.group(1)
+        with open(libargs, "w") as f:
+            print(link_args, file=f)
+
+        distutils.file_util.copy_file(liba, args.build_dir, update=True)
+    except subprocess.CalledProcessError as e:
+        print(
+            "Environment: " + " ".join(["{}={}".format(k, v) for k, v in env.items()])
+        )
+        print("Command: " + " ".join(cargo_cmd))
+        print(e.output)
+        sys.exit(1)
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser()
+    parser.add_argument("command")
+    parser.add_argument("build_dir")
+    parser.add_argument("src_dir")
+    parser.add_argument("build_root")
+    parser.add_argument("build_type")
+    parser.add_argument("target_triple")
+    parser.add_argument("EXTRA", nargs="*")
+    args = parser.parse_args()
+
+    if args.command == "build-lib":
+        build_lib(args)
+    else:
+        raise argparse.ArgumentTypeError("Unknown command: %s" % args.command)
+
+
+if __name__ == "__main__":
+    main()