On Mon, Jun 10, 2024 at 09:22:36PM +0300, Manos Pitsidianakis wrote:
> Add options for Rust in meson_options.txt, meson.build, configure to
> prepare for adding Rust code in the followup commits.
> 
> `rust` is a reserved meson name, so we have to use an alternative.
> `with_rust` was chosen.
> 
> Signed-off-by: Manos Pitsidianakis <manos.pitsidiana...@linaro.org>
> ---
> The cargo wrapper script hardcodes some rust target triples. This is 
> just temporary.
> ---
>  .gitignore               |   2 +
>  configure                |  12 +++
>  meson.build              |  11 ++
>  meson_options.txt        |   4 +
>  scripts/cargo_wrapper.py | 211 +++++++++++++++++++++++++++++++++++++++
>  5 files changed, 240 insertions(+)
>  create mode 100644 scripts/cargo_wrapper.py
> 
> diff --git a/.gitignore b/.gitignore
> index 61fa39967b..f42b0d937e 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -2,6 +2,8 @@
>  /build/
>  /.cache/
>  /.vscode/
> +/target/
> +rust/**/target

Are these necessary since the cargo build command-line below uses
--target-dir <meson-build-dir>?

Adding new build output directories outside build/ makes it harder to
clean up the source tree and ensure no state from previous builds
remains.

>  *.pyc
>  .sdk
>  .stgit-*
> diff --git a/configure b/configure
> index 38ee257701..c195630771 100755
> --- a/configure
> +++ b/configure
> @@ -302,6 +302,9 @@ else
>    objcc="${objcc-${cross_prefix}clang}"
>  fi
>  
> +with_rust="auto"
> +with_rust_target_triple=""
> +
>  ar="${AR-${cross_prefix}ar}"
>  as="${AS-${cross_prefix}as}"
>  ccas="${CCAS-$cc}"
> @@ -760,6 +763,12 @@ for opt do
>    ;;
>    --gdb=*) gdb_bin="$optarg"
>    ;;
> +  --enable-rust) with_rust=enabled
> +  ;;
> +  --disable-rust) with_rust=disabled
> +  ;;
> +  --rust-target-triple=*) with_rust_target_triple="$optarg"
> +  ;;
>    # everything else has the same name in configure and meson
>    --*) meson_option_parse "$opt" "$optarg"
>    ;;
> @@ -1796,6 +1805,9 @@ if test "$skip_meson" = no; then
>    test -n "${LIB_FUZZING_ENGINE+xxx}" && meson_option_add 
> "-Dfuzzing_engine=$LIB_FUZZING_ENGINE"
>    test "$plugins" = yes && meson_option_add "-Dplugins=true"
>    test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg"
> +  test "$with_rust" != enabled && meson_option_add "-Dwith_rust=$with_rust"
> +  test "$with_rust" != enabled && meson_option_add "-Dwith_rust=$with_rust"

Duplicate line.

> +  test "$with_rust_target_triple" != "" && meson_option_add 
> "-Dwith_rust_target_triple=$with_rust_target_triple"
>    run_meson() {
>      NINJA=$ninja $meson setup "$@" "$PWD" "$source_path"
>    }
> diff --git a/meson.build b/meson.build
> index a9de71d450..3533889852 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -290,6 +290,12 @@ foreach lang : all_languages
>    endif
>  endforeach
>  
> +cargo = not_found
> +if get_option('with_rust').allowed()
> +  cargo = find_program('cargo', required: get_option('with_rust'))
> +endif
> +with_rust = cargo.found()
> +
>  # default flags for all hosts
>  # We use -fwrapv to tell the compiler that we require a C dialect where
>  # left shift of signed integers is well defined and has the expected
> @@ -2066,6 +2072,7 @@ endif
>  
>  config_host_data = configuration_data()
>  
> +config_host_data.set('CONFIG_WITH_RUST', with_rust)
>  audio_drivers_selected = []
>  if have_system
>    audio_drivers_available = {
> @@ -4190,6 +4197,10 @@ if 'objc' in all_languages
>  else
>    summary_info += {'Objective-C compiler': false}
>  endif
> +summary_info += {'Rust support':      with_rust}
> +if with_rust and get_option('with_rust_target_triple') != ''
> +  summary_info += {'Rust target':     get_option('with_rust_target_triple')}
> +endif
>  option_cflags = (get_option('debug') ? ['-g'] : [])
>  if get_option('optimization') != 'plain'
>    option_cflags += ['-O' + get_option('optimization')]
> diff --git a/meson_options.txt b/meson_options.txt
> index 4c1583eb40..223491b731 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -366,3 +366,7 @@ option('qemu_ga_version', type: 'string', value: '',
>  
>  option('hexagon_idef_parser', type : 'boolean', value : true,
>         description: 'use idef-parser to automatically generate TCG code for 
> the Hexagon frontend')
> +option('with_rust', type: 'feature', value: 'auto',
> +       description: 'Enable Rust support')
> +option('with_rust_target_triple', type : 'string', value: '',
> +       description: 'Rust target triple')
> diff --git a/scripts/cargo_wrapper.py b/scripts/cargo_wrapper.py
> new file mode 100644
> index 0000000000..d338effdaa
> --- /dev/null
> +++ b/scripts/cargo_wrapper.py
> @@ -0,0 +1,211 @@
> +#!/usr/bin/env python3
> +# Copyright (c) 2020 Red Hat, Inc.
> +# Copyright (c) 2023 Linaro Ltd.
> +#
> +# Authors:
> +#  Manos Pitsidianakis <manos.pitsidiana...@linaro.org>
> +#  Marc-André Lureau <marcandre.lur...@redhat.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 json
> +import logging
> +import os
> +import os.path
> +import re
> +import subprocess
> +import sys
> +import pathlib
> +import shutil
> +import tomllib
> +
> +from pathlib import Path
> +from typing import Any, Dict, List, Tuple
> +
> +RUST_TARGET_TRIPLES = (
> +    "aarch64-unknown-linux-gnu",
> +    "x86_64-unknown-linux-gnu",
> +    "x86_64-apple-darwin",
> +    "aarch64-apple-darwin",
> +)

Is this hardcoded to avoid calling `rustc --print target-list`?

Or is this the support matrix? In that case it would be interesting to
figure out the target triples for all host OSes and CPUs that QEMU is
supported on.

> +
> +
> +def cfg_name(name: str) -> str:
> +    if (
> +        name.startswith("CONFIG_")
> +        or name.startswith("TARGET_")
> +        or name.startswith("HAVE_")
> +    ):
> +        return name
> +    return ""
> +
> +
> +def generate_cfg_flags(header: str) -> List[str]:
> +    with open(header, encoding="utf-8") as cfg:
> +        config = [l.split()[1:] for l in cfg if l.startswith("#define")]
> +
> +    cfg_list = []
> +    for cfg in config:
> +        name = cfg_name(cfg[0])
> +        if not name:
> +            continue
> +        if len(cfg) >= 2 and cfg[1] != "1":
> +            continue
> +        cfg_list.append("--cfg")
> +        cfg_list.append(name)
> +    return cfg_list
> +
> +
> +def cargo_target_dir(args: argparse.Namespace) -> pathlib.Path:
> +    return args.meson_build_dir
> +
> +
> +def manifest_path(args: argparse.Namespace) -> pathlib.Path:
> +    return args.crate_dir / "Cargo.toml"
> +
> +
> +def get_cargo_rustc(args: argparse.Namespace) -> tuple[Dict[str, Any], 
> List[str]]:
> +    # See 
> https://doc.rust-lang.org/cargo/reference/environment-variables.html
> +    # Item `CARGO_ENCODED_RUSTFLAGS — A list of custom flags separated by
> +    # 0x1f (ASCII Unit Separator) to pass to all compiler invocations that 
> Cargo
> +    # performs`
> +    cfg = chr(0x1F).join(
> +        [c for h in args.config_headers for c in generate_cfg_flags(h)]
> +    )
> +    target_dir = cargo_target_dir(args)
> +    cargo_path = manifest_path(args)
> +
> +    cargo_cmd = [
> +        "cargo",
> +        "build",
> +        "--target-dir",
> +        str(target_dir),
> +        "--manifest-path",
> +        str(cargo_path),
> +    ]
> +    if args.target_triple:
> +        cargo_cmd += ["--target", args.target_triple]
> +    if args.profile == "release":
> +        cargo_cmd += ["--release"]
> +
> +    env = os.environ
> +    env["CARGO_ENCODED_RUSTFLAGS"] = cfg
> +
> +    return (env, cargo_cmd)
> +
> +
> +def run_cargo(env: Dict[str, Any], cargo_cmd: List[str]) -> str:
> +    envlog = " ".join(["{}={}".format(k, v) for k, v in env.items()])
> +    cmdlog = " ".join(cargo_cmd)
> +    logging.debug("Running %s %s", envlog, cmdlog)
> +    try:
> +        out = subprocess.check_output(
> +            cargo_cmd,
> +            env=dict(os.environ, **env),
> +            stderr=subprocess.STDOUT,
> +            universal_newlines=True,
> +        )
> +    except subprocess.CalledProcessError as err:
> +        print("Environment: " + envlog)
> +        print("Command: " + cmdlog)
> +        print(err.output)
> +        sys.exit(1)
> +
> +    return out
> +
> +
> +def build_lib(args: argparse.Namespace) -> None:
> +    logging.debug("build-lib")
> +    target_dir = cargo_target_dir(args)
> +    cargo_toml_path = manifest_path(args)
> +
> +    with open(cargo_toml_path, "rb") as f:
> +        config = tomllib.load(f)
> +
> +    package_name = config["package"]["name"].strip('"').replace("-", "_")
> +
> +    liba_filename = "lib" + package_name + ".a"
> +    liba = target_dir / args.target_triple / args.profile / liba_filename
> +
> +    env, cargo_cmd = get_cargo_rustc(args)
> +    out = run_cargo(env, cargo_cmd)
> +    logging.debug("cp %s %s", liba, args.outdir)
> +    shutil.copy2(liba, args.outdir)
> +
> +
> +def main() -> None:
> +    parser = argparse.ArgumentParser()
> +    parser.add_argument("-v", "--verbose", action="store_true")
> +    parser.add_argument(
> +        "--color",
> +        metavar="WHEN",
> +        choices=["auto", "always", "never"],
> +        default="auto",
> +        help="Coloring: auto, always, never",
> +    )
> +    parser.add_argument(
> +        "--config-headers",
> +        metavar="CONFIG_HEADER",
> +        action="append",
> +        dest="config_headers",
> +        required=False,
> +        default=[],
> +    )
> +    parser.add_argument(
> +        "--meson-build-dir",
> +        metavar="BUILD_DIR",
> +        help="meson.current_build_dir()",
> +        type=pathlib.Path,
> +        dest="meson_build_dir",
> +        required=True,
> +    )
> +    parser.add_argument(
> +        "--meson-source-dir",
> +        metavar="SOURCE_DIR",
> +        help="meson.current_source_dir()",
> +        type=pathlib.Path,
> +        dest="meson_source_dir",
> +        required=True,
> +    )
> +    parser.add_argument(
> +        "--crate-dir",
> +        metavar="CRATE_DIR",
> +        type=pathlib.Path,
> +        dest="crate_dir",
> +        help="Absolute path that contains the manifest file of the crate to 
> compile",
> +        required=True,
> +    )
> +    parser.add_argument(
> +        "--outdir",
> +        metavar="OUTDIR",
> +        type=pathlib.Path,
> +        dest="outdir",
> +        help="Path to copy compiled artifacts to for Meson to use.",
> +        required=True,
> +    )
> +    parser.add_argument(
> +        "--profile", type=str, choices=["release", "debug"], required=True
> +    )
> +    parser.add_argument(
> +        "--target-triple", type=str, choices=RUST_TARGET_TRIPLES, 
> required=True
> +    )
> +
> +    subparsers = parser.add_subparsers()
> +
> +    buildlib = subparsers.add_parser("build-lib")
> +    buildlib.set_defaults(func=build_lib)
> +
> +    args = parser.parse_args()
> +    if args.verbose:
> +        logging.basicConfig(level=logging.DEBUG)
> +    logging.debug("args: %s", args)
> +
> +    args.func(args)
> +
> +
> +if __name__ == "__main__":
> +    main()
> -- 
> γαῖα πυρί μιχθήτω
> 

Attachment: signature.asc
Description: PGP signature

Reply via email to