| # Copyright 2017 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Recipe for building QEMU.""" |
| |
| import shlex |
| |
| from PB.go.chromium.org.luci.common.proto.srcman.manifest import Manifest |
| from PB.recipes.fuchsia.qemu import InputProperties |
| |
| DEPS = [ |
| "fuchsia/cas_util", |
| "fuchsia/cipd_util", |
| "fuchsia/cmake", |
| "fuchsia/git_checkout", |
| "fuchsia/goma", |
| "fuchsia/linux_sdk", |
| "fuchsia/macos_sdk", |
| "fuchsia/ninja", |
| "fuchsia/platform_util", |
| "fuchsia/toolchain", |
| "fuchsia/zlib", |
| "recipe_engine/cipd", |
| "recipe_engine/context", |
| "recipe_engine/file", |
| "recipe_engine/path", |
| "recipe_engine/platform", |
| "recipe_engine/properties", |
| "recipe_engine/step", |
| ] |
| |
| PROPERTIES = InputProperties |
| |
| |
| def RunSteps(api, props): |
| manifest = Manifest() |
| |
| checkout_dir, revision = api.git_checkout( |
| props.remote, |
| fallback_ref=props.ref or "refs/heads/master", |
| submodules=True, |
| config={ |
| "url.https://qemu.googlesource.com/.insteadOf": "https://gitlab.com/qemu-project/" |
| }, |
| ) |
| qemu_version = api.file.read_text( |
| "read qemu version", checkout_dir / "VERSION" |
| ).strip() |
| |
| git_checkout = manifest.directories[str(checkout_dir)].git_checkout |
| git_checkout.repo_url = props.remote |
| git_checkout.revision = revision |
| |
| host_platform = api.platform_util.host |
| target_platform = api.platform_util.platform( |
| props.platform or host_platform.platform |
| ) |
| |
| with api.step.nest("ensure_packages"), api.context(infra_steps=True): |
| cipd_dir = api.path.start_dir / "cipd" |
| pkgs = api.cipd.EnsureFile() |
| pkgs.add_package("fuchsia/third_party/clang/${platform}", "integration") |
| pkgs.add_package("fuchsia/third_party/autoconf/${platform}", "version:2.69") |
| pkgs.add_package("fuchsia/third_party/automake/${platform}", "version:1.16.2") |
| pkgs.add_package("fuchsia/third_party/libtool/${platform}", "version:2.4.6") |
| pkgs.add_package("fuchsia/third_party/m4/${platform}", "version:1.4.18") |
| pkgs.add_package("fuchsia/third_party/make/${platform}", "version:4.3") |
| pkgs.add_package("fuchsia/third_party/pkg-config/${platform}", "version:0.29.2") |
| pkgs.add_package( |
| "fuchsia/third_party/source/gettext", |
| "version:0.19.8.1", |
| "source/gettext", |
| ) |
| api.cipd.ensure(cipd_dir, pkgs) |
| |
| pkg_dir = api.path.start_dir / "pkgconfig" |
| api.file.ensure_directory("create pkg dir", pkg_dir) |
| |
| variables = { |
| "PKG_CONFIG_PATH": "", |
| # TODO: We should be using the following but that breaks the SDL dependency. |
| # "PKG_CONFIG_SYSROOT_DIR": platform_sysroot(api, target_platform), |
| "PKG_CONFIG_ALLOW_SYSTEM_CFLAGS": 0, |
| "PKG_CONFIG_ALLOW_SYSTEM_LIBS": 0, |
| "PKG_CONFIG_LIBDIR": ":".join( |
| [ |
| str(pkg_dir.joinpath("share", "pkgconfig")), |
| str(pkg_dir.joinpath("lib", "pkgconfig")), |
| str(pkg_dir.joinpath("lib64", "pkgconfig")), |
| ] |
| ), |
| } |
| |
| with api.linux_sdk(), api.macos_sdk(), api.context( |
| env=variables, |
| env_prefixes={"PATH": [cipd_dir.joinpath("bin"), pkg_dir.joinpath("bin")]}, |
| ): |
| with api.step.nest("meson"): |
| build_meson(api, pkg_dir) |
| |
| if target_platform.is_linux: |
| with api.step.nest("sdl"): |
| build_sdl( |
| api, |
| cipd_dir, |
| pkg_dir, |
| target_platform, |
| host_platform, |
| props.prod, |
| manifest, |
| ) |
| |
| with api.step.nest("zlib"): |
| api.zlib.build( |
| cmake_extra_args=cmake_args(api, cipd_dir, target_platform) |
| + [ |
| "-DCMAKE_INSTALL_PREFIX=%s" % pkg_dir, |
| ], |
| platform=target_platform, |
| ) |
| |
| with api.step.nest("bzip2"): |
| build_bzip2( |
| api, |
| cipd_dir, |
| pkg_dir, |
| target_platform, |
| host_platform, |
| props.prod, |
| manifest, |
| ) |
| |
| with api.step.nest("pixman"): |
| build_pixman( |
| api, |
| cipd_dir, |
| pkg_dir, |
| target_platform, |
| host_platform, |
| props.prod, |
| manifest, |
| ) |
| |
| with api.step.nest("libffi"): |
| build_libffi( |
| api, |
| cipd_dir, |
| pkg_dir, |
| target_platform, |
| host_platform, |
| props.prod, |
| manifest, |
| ) |
| |
| if target_platform.is_mac: |
| with api.step.nest("gettext"): |
| build_gettext( |
| api, |
| cipd_dir, |
| pkg_dir, |
| target_platform, |
| host_platform, |
| props.prod, |
| manifest, |
| ) |
| |
| with api.step.nest("pcre2"): |
| build_pcre2( |
| api, |
| cipd_dir, |
| pkg_dir, |
| target_platform, |
| host_platform, |
| props.prod, |
| manifest, |
| ) |
| |
| with api.step.nest("glib"): |
| build_glib( |
| api, |
| cipd_dir, |
| pkg_dir, |
| target_platform, |
| host_platform, |
| props.prod, |
| manifest, |
| ) |
| |
| with api.step.nest("libslirp"): |
| build_libslirp( |
| api, |
| cipd_dir, |
| pkg_dir, |
| target_platform, |
| host_platform, |
| props.prod, |
| manifest, |
| ) |
| |
| with api.step.nest("qemu"): |
| install_dir = build_qemu( |
| api, |
| checkout_dir, |
| pkg_dir, |
| cipd_dir, |
| target_platform, |
| host_platform, |
| props.prod, |
| ) |
| |
| api.file.write_proto( |
| "source manifest", install_dir / "source_manifest.json", manifest, "JSONPB" |
| ) |
| |
| cas_digest = api.cas_util.upload(install_dir, output_property="isolated") |
| if props.prod: |
| api.cipd_util.upload_package( |
| f"fuchsia/third_party/qemu/{target_platform.platform}", |
| install_dir, |
| search_tag={"git_revision": revision}, |
| repository=props.remote, |
| metadata=[("version", qemu_version)], |
| ) |
| |
| if props.builders and target_platform.platform in props.builders: |
| # Do a full integration build. This will use the just-built QEMU to |
| # test against a representative set of Fuchsia builders to smoke test |
| # for any regressions. |
| api.toolchain.trigger_build( |
| "qemu", |
| props.remote, |
| revision, |
| cas_digest, |
| builders=props.builders[target_platform.platform], |
| ) |
| |
| |
| def cmake_args(api, cipd_dir, platform): |
| # TODO(b/270963251): Enable optimizations for cmake. |
| args = [ |
| f"-DCMAKE_MAKE_PROGRAM={api.ninja.path}", |
| f"-DCMAKE_C_COMPILER={cipd_dir.joinpath('bin', 'clang')}", |
| f"-DCMAKE_C_COMPILER_TARGET={platform.triple}", |
| f"-DCMAKE_CXX_COMPILER={cipd_dir.joinpath('bin', 'clang++')}", |
| f"-DCMAKE_CXX_COMPILER_TARGET={platform.triple}", |
| f"-DCMAKE_SYSROOT={platform_sysroot(api, platform)}", |
| "-DCMAKE_INSTALL_PREFIX=", |
| f"-DCMAKE_LINKER={'/usr/bin/ld' if platform.is_mac else cipd_dir.joinpath('bin', 'ld.lld')}", |
| f"-DCMAKE_NM={cipd_dir.joinpath('bin', 'llvm-nm')}", |
| f"-DCMAKE_OBJCOPY={cipd_dir.joinpath('bin', 'llvm-objcopy')}", |
| f"-DCMAKE_OBJDUMP={cipd_dir.joinpath('bin', 'llvm-objdump')}", |
| f"-DCMAKE_RANLIB={cipd_dir.joinpath('bin', 'llvm-ranlib')}", |
| f"-DCMAKE_STRIP={cipd_dir.joinpath('bin', 'llvm-strip')}", |
| ] |
| return args |
| |
| |
| def platform_sysroot(api, platform): |
| if platform.is_linux: |
| return api.linux_sdk.sysroot |
| if platform.is_mac: |
| return api.macos_sdk.sysroot |
| raise api.step.StepFailure("supported platform") # pragma: no cover |
| |
| |
| def environment( |
| api, cipd_dir, platform, host, optimize, cflags=(), cxxflags=(), ldflags=() |
| ): |
| sysroot = [f"--sysroot={str(platform_sysroot(api, platform)).replace('%', '%%')}"] |
| target = ( |
| [f"--target={platform.triple}"] if platform.platform != host.platform else [] |
| ) |
| opt = [] |
| if optimize: |
| opt = ["-O3"] |
| # TODO: Enable LTO for Mac targets. |
| # |
| # On Mac this currently fails basic Make compile checks |
| # configure: error: in `/opt/s/w/ir/x/w/make': |
| # configure: error: C compiler cannot create executables |
| if platform.is_linux: |
| opt.extend(["-flto", "-fwhole-program-vtables"]) |
| variables = { |
| "CC": cipd_dir.joinpath("bin", "clang"), |
| "CXX": cipd_dir.joinpath("bin", "clang++"), |
| "LD": cipd_dir.joinpath("bin", "ld.lld"), |
| "CFLAGS": " ".join(sysroot + target + opt + list(cflags)), |
| "CPPFLAGS": " ".join(sysroot + target + opt + list(cflags)), |
| "CXXFLAGS": " ".join(sysroot + target + opt + list(cxxflags)), |
| "LDFLAGS": " ".join(sysroot + target + opt + list(ldflags)), |
| "AR": cipd_dir.joinpath("bin", "llvm-ar"), |
| "RANLIB": cipd_dir.joinpath("bin", "llvm-ranlib"), |
| "NM": cipd_dir.joinpath("bin", "llvm-nm"), |
| "STRIP": cipd_dir.joinpath("bin", "llvm-strip"), |
| "OBJCOPY": cipd_dir.joinpath("bin", "llvm-objcopy"), |
| # The context API interprets bare percent signs as formatting |
| # directives so they must be escaped. |
| "NINJA": str(api.ninja.path).replace("%", "%%"), |
| } |
| |
| return variables |
| |
| |
| def configure( |
| api, |
| cipd_dir, |
| src_dir, |
| platform, |
| host, |
| optimize, |
| flags=(), |
| cflags=(), |
| cxxflags=(), |
| ldflags=(), |
| step_name="configure", |
| ): |
| variables = environment( |
| api, cipd_dir, platform, host, optimize, cflags, cxxflags, ldflags |
| ) |
| if platform.platform != host.platform: |
| flags.extend( |
| [ |
| # config.sub used by QEMU doesn't yet recognize arm64-apple-darwin which |
| # is used by Clang, so replace it with aarch64-apple-darwin to make the |
| # script not fail. |
| f"--build={host.triple.replace('arm64-apple-darwin', 'aarch64-apple-darwin')}", |
| f"--host={platform.triple.replace('arm64-apple-darwin', 'aarch64-apple-darwin')}", |
| ] |
| ) |
| return api.step( |
| step_name, |
| [src_dir / "configure"] |
| + flags |
| + sorted(f"{k}={v}" for k, v in variables.items()), |
| ) |
| |
| |
| def meson( |
| api, cipd_dir, platform, _host, _optimize, cflags=(), cxxflags=(), ldflags=() |
| ): |
| cross_file = api.path.start_dir.joinpath("%s.txt" % platform.triple) |
| api.file.write_text( |
| "write cross-file", |
| cross_file, |
| f""" |
| [constants] |
| sysroot = '--sysroot=' + '{str(platform_sysroot(api, platform)).replace("%", "%%")}' |
| target = '--target={platform.triple}' |
| |
| [built-in options] |
| buildtype = 'release' |
| c_args = [sysroot, target] + [{", ".join("'%s'" % f for f in cflags)}] |
| cpp_args = [sysroot, target] + [{", ".join("'%s'" % f for f in cxxflags)}] |
| c_link_args = c_args + [{", ".join("'%s'" % f for f in ldflags)}] |
| cpp_link_args = cpp_args + [{", ".join("'%s'" % f for f in ldflags)}] |
| default_library = 'static' |
| includedir = 'include' |
| libdir = 'lib' |
| # sys_root = /some/path |
| # pkg_config_libdir = /some/path |
| |
| [binaries] |
| c = '{str(cipd_dir.joinpath("bin", "clang"))}' |
| c_ld = 'lld' |
| objc = '{str(cipd_dir.joinpath("bin", "clang"))}' |
| objc_ld = 'lld' |
| cpp = '{str(cipd_dir.joinpath("bin", "clang++"))}' |
| cpp_ld = 'lld' |
| ar = '{str(cipd_dir.joinpath("bin", "llvm-ar"))}' |
| strip = '{str(cipd_dir.joinpath("bin", "llvm-strip"))}' |
| pkgconfig = '{str(cipd_dir.joinpath("bin", "pkg-config"))}' |
| # exe_wrapper = 'qemu-aarch64' |
| |
| [host_machine] |
| system = '{platform.os.replace('mac', 'darwin')}' |
| cpu_family = '{platform.arch.replace('amd64', 'x86_64').replace('arm64', 'aarch64')}' |
| cpu = cpu_family |
| endian = 'little' |
| """, |
| ) |
| # Pass as `--native-file` when not cross-compiling |
| return cross_file |
| |
| |
| def build_meson(api, pkg_dir): |
| MESON_GIT = "https://qemu.googlesource.com/meson" |
| src_dir, _ = api.git_checkout(MESON_GIT, revision="refs/tags/1.2.1") |
| |
| bin_dir = pkg_dir / "bin" |
| api.file.ensure_directory("bin", bin_dir) |
| outfile = bin_dir / "meson.pyz" |
| |
| api.step( |
| "create zipapp", |
| [ |
| "vpython3", |
| src_dir.joinpath("packaging", "create_zipapp.py"), |
| "--outfile", |
| outfile, |
| "--interpreter", |
| "/usr/bin/env vpython3", |
| src_dir, |
| ], |
| ) |
| |
| return outfile |
| |
| |
| def build_sdl(api, cipd_dir, pkg_dir, platform, _host, _optimize, manifest): |
| SDL_GIT = "https://fuchsia.googlesource.com/third_party/sdl" |
| src_dir, revision = api.git_checkout(SDL_GIT, revision="refs/tags/release-2.26.5") |
| git_checkout = manifest.directories[str(src_dir)].git_checkout |
| git_checkout.repo_url = SDL_GIT |
| git_checkout.revision = revision |
| |
| api.cmake.build_with_ninja( |
| src_dir=src_dir, |
| extra_args=cmake_args(api, cipd_dir, platform) |
| + [ |
| "-DBUILD_SHARED_LIBS=OFF", |
| "-DCMAKE_INSTALL_PREFIX=%s" % pkg_dir, |
| "-DSDL_GCC_ATOMICS=ON", |
| "-DSDL_STATIC_PIC=ON", |
| "-DSDL_TEST=OFF", |
| "-DSDL_WAYLAND=OFF", |
| ], |
| ) |
| |
| |
| def build_bzip2(api, cipd_dir, pkg_dir, platform, host, optimize, manifest): |
| BZIP2_GIT = "https://fuchsia.googlesource.com/third_party/bzip2" |
| src_dir, revision = api.git_checkout(BZIP2_GIT, revision="refs/heads/main") |
| git_checkout = manifest.directories[str(src_dir)].git_checkout |
| git_checkout.repo_url = BZIP2_GIT |
| git_checkout.revision = revision |
| |
| build_dir = api.path.start_dir / "bzip2" |
| api.file.ensure_directory("bzip2", build_dir) |
| |
| with api.context(cwd=src_dir): |
| # First build for host. |
| env = environment(api, cipd_dir, host, host, optimize) |
| var = ["%s=%s" % (k, v) for k, v in env.items()] |
| api.step( |
| "build (host)", |
| ["make", f"-j{int(api.goma.jobs)}", "install", f"PREFIX={pkg_dir}", *var], |
| ) |
| api.step("clean", ["make", "clean"]) |
| # Second build for target. |
| env = environment(api, cipd_dir, platform, host, optimize) |
| var = ["%s=%s" % (k, v) for k, v in env.items()] |
| api.step( |
| "build (target)", |
| ["make", f"-j{int(api.goma.jobs)}", "install", f"PREFIX={build_dir}", *var], |
| ) |
| api.file.copy( |
| "copy libbz2.a", |
| build_dir.joinpath("lib", "libbz2.a"), |
| pkg_dir.joinpath("lib"), |
| ) |
| |
| |
| def build_pixman(api, cipd_dir, pkg_dir, platform, host, optimize, manifest): |
| PIXMAN_GIT = "https://fuchsia.googlesource.com/third_party/pixman" |
| src_dir, revision = api.git_checkout( |
| PIXMAN_GIT, revision="593a970266042d702c1577fdec8c33449f60b628" |
| ) |
| git_checkout = manifest.directories[str(src_dir)].git_checkout |
| git_checkout.repo_url = PIXMAN_GIT |
| git_checkout.revision = revision |
| |
| build_dir = api.path.start_dir / "pixman" |
| api.file.ensure_directory("pixman", build_dir) |
| |
| with api.context( |
| cwd=src_dir, env=environment(api, cipd_dir, platform, host, False) |
| ): |
| try: |
| cross_file = meson(api, cipd_dir, platform, host, optimize) |
| api.step( |
| "setup", |
| [ |
| pkg_dir.joinpath("bin", "meson.pyz"), |
| "setup", |
| "--default-library=static", |
| "--prefix=%s" % pkg_dir, |
| "--includedir=include", |
| "--libdir=lib", |
| "--buildtype=release", |
| "--wrap-mode=nofallback", |
| "--cross-file=%s" % cross_file, |
| ] |
| + (["-Db_lto=true"] if optimize else []) |
| + [ |
| build_dir, |
| ], |
| ) |
| finally: |
| try: |
| api.file.read_text( |
| "meson-log.txt", |
| build_dir.joinpath("meson-logs", "meson-log.txt"), |
| test_data="The Meson build system\nVersion: 1.0.0", |
| ) |
| except api.step.StepFailure: # pragma: no cover |
| pass |
| api.ninja("install", ["install", "-v"], build_dir=build_dir) |
| |
| |
| def build_libffi(api, cipd_dir, pkg_dir, platform, host, optimize, manifest): |
| # TODO(phosek): Newer versions require autoconf >=2.71. |
| LIBFFI_GIT = "https://fuchsia.googlesource.com/third_party/libffi" |
| src_dir, revision = api.git_checkout(LIBFFI_GIT, revision="refs/tags/v3.4.2") |
| git_checkout = manifest.directories[str(src_dir)].git_checkout |
| git_checkout.repo_url = LIBFFI_GIT |
| git_checkout.revision = revision |
| |
| build_dir = api.path.start_dir / "libffi" |
| api.file.ensure_directory("libffi", build_dir) |
| |
| with api.context(cwd=src_dir, env={"M4": cipd_dir.joinpath("bin", "m4")}): |
| api.step("autogen", [src_dir / "autogen.sh"]) |
| with api.context(cwd=build_dir): |
| try: |
| configure( |
| api, |
| cipd_dir, |
| src_dir, |
| platform, |
| host, |
| optimize, |
| [ |
| "--disable-debug", |
| "--disable-dependency-tracking", |
| "--disable-docs", |
| "--disable-shared", |
| "--enable-static", |
| "--prefix=%s" % pkg_dir, |
| "--target=%s" |
| # config.sub used by libffi doesn't yet recognize arm64-apple-darwin which |
| # is used by Clang, so replace it with aarch64-apple-darwin to make the |
| # script not fail. |
| % platform.triple.replace( |
| "arm64-apple-darwin", "aarch64-apple-darwin" |
| ), |
| "--with-pic", |
| ], |
| ) |
| finally: |
| try: |
| api.file.read_text( |
| "config.log", |
| build_dir / "config.log", |
| test_data="# libffi configure log", |
| ) |
| except api.step.StepFailure: # pragma: no cover |
| pass |
| |
| api.step("build", ["make", f"-j{int(api.goma.jobs)}"]) |
| api.step("install", ["make", "install"]) # , f"DESTDIR={pkg_dir}"]) |
| |
| |
| def build_gettext(api, cipd_dir, pkg_dir, platform, host, optimize, _manifest): |
| # Build gettext-{runtime,tools} separately, when cross-compiling we need to |
| # always build `tools` for the host and the `runtime` for the target. This |
| # is how the PACKAGING file recommends we split them. |
| # |
| # The order of tools, runtime is important here; the tools build will |
| # overwrite the runtimes installation. |
| for package in ["gettext-tools", "gettext-runtime"]: |
| with api.step.nest(package): |
| src_dir = cipd_dir.joinpath("source", "gettext", package) |
| build_dir = api.path.start_dir.joinpath("gettext", package) |
| api.file.ensure_directory("gettext", build_dir) |
| |
| with api.context(cwd=build_dir): |
| configure( |
| api, |
| cipd_dir, |
| src_dir, |
| platform if package == "gettext-runtime" else host, |
| host, |
| optimize, |
| [ |
| "--disable-curses", |
| "--disable-dependency-tracking", |
| "--disable-silent-rules", |
| "--disable-debug", |
| "--disable-shared", |
| "--disable-java", |
| "--disable-csharp", |
| "--disable-c++", |
| "--disable-openmp", |
| "--enable-static", |
| "--prefix=%s" % pkg_dir, |
| "--with-pic", |
| "--with-included-gettext", |
| "--with-included-glib", |
| "--with-included-libcroco", |
| "--with-included-libunistring", |
| "--with-included-libxml", |
| "--without-git", |
| "--without-cvs", |
| "--without-xz", |
| ], |
| ldflags=["-lm"], |
| ) |
| api.step("build", ["make", f"-j{int(api.goma.jobs)}"]) |
| api.step("install", ["make", "install"]) |
| |
| |
| def build_pcre2(api, cipd_dir, pkg_dir, platform, _host, _optimize, manifest): |
| # TODO(phosek): We should be using our mirror. |
| PCRE2_GIT = "https://github.com/PCRE2Project/pcre2" |
| src_dir, revision = api.git_checkout(PCRE2_GIT, revision="refs/tags/pcre2-10.42") |
| git_checkout = manifest.directories[str(src_dir)].git_checkout |
| git_checkout.repo_url = PCRE2_GIT |
| git_checkout.revision = revision |
| |
| api.cmake.build_with_ninja( |
| src_dir=src_dir, |
| extra_args=cmake_args(api, cipd_dir, platform) |
| + [ |
| "-DBUILD_SHARED_LIBS=OFF", |
| "-DCMAKE_INSTALL_PREFIX=%s" % pkg_dir, |
| ], |
| ) |
| |
| |
| def build_glib(api, cipd_dir, pkg_dir, platform, host, optimize, manifest): |
| GLIB_GIT = "https://fuchsia.googlesource.com/third_party/glib" |
| src_dir, revision = api.git_checkout( |
| GLIB_GIT, |
| revision="refs/tags/2.77.0", |
| submodules=True, |
| config={ |
| # TODO(phosek): We should be using our mirror. |
| "url.https://gitlab.gnome.org/GNOME/gvdb.insteadOf": "https://fuchsia.googlesource.com/GNOME/gvdb" |
| }, |
| ) |
| git_checkout = manifest.directories[str(src_dir)].git_checkout |
| git_checkout.repo_url = GLIB_GIT |
| git_checkout.revision = revision |
| |
| build_dir = api.path.start_dir / "glib" |
| api.file.ensure_directory("glib", build_dir) |
| |
| with api.context( |
| cwd=src_dir, |
| env=environment( |
| api, |
| cipd_dir, |
| platform, |
| host, |
| optimize, |
| cflags=["-I%s" % pkg_dir.joinpath("include")], |
| ldflags=["-L%s" % pkg_dir.joinpath("lib")], |
| ), |
| ): |
| try: |
| cross_file = meson(api, cipd_dir, platform, host, optimize) |
| api.step( |
| "setup", |
| [ |
| pkg_dir.joinpath("bin", "meson.pyz"), |
| "setup", |
| "--default-library=static", |
| "--prefix=%s" % pkg_dir, |
| "--includedir=include", |
| "--libdir=lib", |
| "--buildtype=release", |
| # "--wrap-mode=nofallback", |
| "--cross-file=%s" % cross_file, |
| build_dir, |
| ], |
| ) |
| finally: |
| try: |
| api.file.read_text( |
| "meson-log.txt", |
| build_dir.joinpath("meson-logs", "meson-log.txt"), |
| test_data="The Meson build system\nVersion: 1.0.0", |
| ) |
| except api.step.StepFailure: # pragma: no cover |
| pass |
| api.ninja("install", ["install"], build_dir=build_dir) |
| |
| |
| def build_libslirp(api, cipd_dir, pkg_dir, platform, host, optimize, manifest): |
| LIBSLIRP_GIT = "https://qemu.googlesource.com/libslirp" |
| src_dir, revision = api.git_checkout(LIBSLIRP_GIT, revision="refs/tags/v4.7.0") |
| git_checkout = manifest.directories[str(src_dir)].git_checkout |
| git_checkout.repo_url = LIBSLIRP_GIT |
| git_checkout.revision = revision |
| |
| build_dir = api.path.start_dir / "libslirp" |
| api.file.ensure_directory("libslirp", build_dir) |
| |
| with api.context( |
| cwd=src_dir, env=environment(api, cipd_dir, platform, host, optimize) |
| ): |
| try: |
| cross_file = meson(api, cipd_dir, platform, host, optimize) |
| api.step( |
| "setup", |
| [ |
| pkg_dir.joinpath("bin", "meson.pyz"), |
| "setup", |
| "--default-library=static", |
| "--prefix=%s" % pkg_dir, |
| "--includedir=include", |
| "--libdir=lib", |
| "--buildtype=release", |
| "--wrap-mode=nofallback", |
| "--cross-file=%s" % cross_file, |
| build_dir, |
| ], |
| ) |
| finally: |
| try: |
| api.file.read_text( |
| "meson-log.txt", |
| build_dir.joinpath("meson-logs", "meson-log.txt"), |
| test_data="The Meson build system\nVersion: 1.0.0", |
| ) |
| except api.step.StepFailure: # pragma: no cover |
| pass |
| api.ninja("install", ["install"], build_dir=build_dir) |
| |
| |
| def build_qemu( |
| api, |
| checkout_dir, |
| pkg_dir, |
| cipd_dir, |
| target_platform, |
| host_platform, |
| optimize, |
| ): |
| """ |
| Build QEMU. |
| |
| Args: |
| checkout_dir (Path): Path to QEMU Git repository. |
| cipd_dir (Path): Path to prepopulated CIPD package dependencies. |
| target_platform (Platform): Target platform. |
| host_platform (Platform): Host platform. |
| |
| Returns: |
| Path: Path to install directory. |
| """ |
| build_dir = api.path.start_dir.joinpath("qemu", "build") |
| api.file.ensure_directory("qemu/build", build_dir) |
| install_dir = api.path.start_dir.joinpath("qemu", "install") |
| api.file.ensure_directory("qemu/install", install_dir) |
| |
| sysroot = [ |
| f"--sysroot={str(platform_sysroot(api, target_platform)).replace('%', '%%')}" |
| ] |
| target = ( |
| [f"--target={target_platform.triple}"] |
| if target_platform.platform != host_platform.platform |
| else [] |
| ) |
| # TODO: QEMU builds fails to find zlib headers without an explicit -I which |
| # is a bug. |
| includes = ["-I%s" % pkg_dir.joinpath("include")] |
| opt = [ |
| # TODO: Remove these once we upgrade the host Linux sysroot. |
| "-DHWCAP_ATOMICS=256", |
| "-DHWCAP_USCAT=33554432", |
| ] |
| if target_platform.arch == "amd64": |
| opt.append("-mcx16") |
| variables = { |
| "AR": cipd_dir.joinpath("bin", "llvm-ar"), |
| "RANLIB": cipd_dir.joinpath("bin", "llvm-ranlib"), |
| "NM": cipd_dir.joinpath("bin", "llvm-nm"), |
| "STRIP": cipd_dir.joinpath("bin", "llvm-strip"), |
| "OBJCOPY": cipd_dir.joinpath("bin", "llvm-objcopy"), |
| "QEMU_PKG_CONFIG_FLAGS": "--static", |
| "CFLAGS": " ".join(sysroot + opt + target + includes), |
| "CXXFLAGS": " ".join(sysroot + opt + target + includes), |
| "OBJCFLAGS": " ".join(sysroot + opt + target + includes), |
| "NINJA": str(api.ninja.path).replace("%", "%%"), |
| } |
| if target_platform.is_linux: |
| variables["LDFLAGS"] = " ".join( |
| sysroot + target + opt + ["-ldl -static-libstdc++ -Qunused-arguments"] |
| ) |
| if target_platform.is_mac: |
| variables["LDFLAGS"] = " ".join( |
| sysroot + target + ["-nostdlib++ %s" % cipd_dir.joinpath("lib", "libc++.a")] |
| ) |
| |
| # NOTE: We don't want to pass --static to configure since we still link libc |
| # as a shared library, but we want everything else to use static linking. |
| configure_options = [] |
| meson_options = [] |
| if target_platform.is_linux: |
| configure_options.extend( |
| [ |
| f"--build={host_platform.triple}", |
| f"--host={target_platform.triple}", |
| f"--extra-cflags={' '.join(sysroot + opt + target + includes)}", |
| f"--extra-cxxflags={' '.join(sysroot + opt + target + includes)}", |
| # Suppress warning about the unused arguments because QEMU ignores |
| # --disable-werror at configure time which triggers an error because |
| # -static-libstdc++ is unused when linking C code. |
| f"--extra-ldflags={' '.join(sysroot + target + opt + ['-ldl -static-libstdc++ -Qunused-arguments'])}", |
| ] |
| ) |
| meson_options.extend( |
| [ |
| "-Dkvm=enabled", |
| "-Dgtk=disabled", |
| "-Dsdl=enabled", |
| ] |
| ) |
| if target_platform.is_mac: |
| configure_options.extend( |
| [ |
| f"--objcc={cipd_dir.joinpath('bin', 'clang')}", |
| "--enable-cocoa", |
| f"--extra-cflags={' '.join(sysroot + opt + target + includes)}", |
| f"--extra-cxxflags={' '.join(sysroot + opt + target + includes)}", |
| f"--extra-objcflags={' '.join(sysroot + opt + target + includes)}", |
| "--extra-ldflags=%s" |
| % " ".join( |
| sysroot |
| + target |
| + ["-nostdlib++ %s" % cipd_dir.joinpath("lib", "libc++.a")] |
| ), |
| ] |
| ) |
| meson_options.extend( |
| [ |
| "-Dcocoa=enabled", |
| "-Dhvf=enabled", |
| ] |
| ) |
| |
| # TODO: Use `meson subprojects` to download all subprojects and override their URL |
| # to point to https://qemu.googlesource.com/. |
| |
| with api.context(cwd=build_dir, env=variables): |
| try: |
| api.step( |
| "configure qemu", |
| [ |
| checkout_dir / "configure", |
| f"--cc={cipd_dir.joinpath('bin', 'clang')}", |
| # See function docstring for justification and associated bug. |
| f"--cxx={wrap_clang(api, cipd_dir.joinpath('bin', 'clang++'))}", |
| f"--ninja={api.ninja.path}", |
| f"--prefix={install_dir}", |
| "--target-list=aarch64-softmmu,arm-softmmu,riscv64-softmmu,x86_64-softmmu", |
| "--skip-meson", |
| ] |
| + configure_options, |
| ) |
| finally: |
| try: |
| api.file.read_text( |
| "config.log", |
| build_dir / "config.log", |
| test_data="# QEMU configure log", |
| ) |
| except api.step.StepFailure: # pragma: no cover |
| pass |
| |
| with api.context(cwd=checkout_dir, env=variables): |
| try: |
| cross_file = meson( |
| api, |
| cipd_dir, |
| target_platform, |
| host_platform, |
| optimize, |
| cflags=opt, |
| cxxflags=opt, |
| ) |
| api.step( |
| "setup", |
| [ |
| pkg_dir.joinpath("bin", "meson.pyz"), |
| "setup", |
| "--default-library=static", |
| "--prefix=%s" % install_dir, |
| "--includedir=include", |
| "--libdir=lib", |
| "--buildtype=release", |
| "--wrap-mode=nofallback", |
| "--cross-file=%s" % cross_file, |
| "-Dattr=disabled", |
| "-Dauth_pam=disabled", |
| "-Dbochs=disabled", |
| "-Dbrlapi=disabled", |
| "-Dcap_ng=disabled", |
| "-Dcapstone=disabled", |
| "-Dcloop=disabled", |
| "-Dcrypto_afalg=disabled", |
| "-Dcurl=disabled", |
| "-Dcurses=disabled", |
| "-Ddbus_display=disabled", |
| "-Ddebug_graph_lock=false", |
| "-Ddebug_mutex=false", |
| "-Ddebug_stack_usage=false", |
| "-Ddmg=disabled", |
| "-Ddocs=disabled", |
| "-Dfdt=internal", |
| "-Dgcrypt=disabled", |
| "-Dglusterfs=disabled", |
| "-Dgnutls=disabled", |
| "-Dguest_agent=disabled", |
| "-Diconv=disabled", |
| "-Dlibdaxctl=disabled", |
| "-Dlibiscsi=disabled", |
| "-Dlibnfs=disabled", |
| "-Dlibpmem=disabled", |
| "-Dlibssh=disabled", |
| "-Dlibudev=disabled", |
| "-Dlibusb=disabled", |
| "-Dlinux_aio=disabled", |
| "-Dlinux_io_uring=disabled", |
| "-Dlzfse=disabled", |
| "-Dlzo=disabled", |
| "-Dmpath=disabled", |
| "-Dmultiprocess=disabled", |
| "-Dnetmap=disabled", |
| "-Dnettle=disabled", |
| "-Dnuma=disabled", |
| "-Dnvmm=disabled", |
| "-Dopengl=disabled", |
| "-Dparallels=disabled", |
| "-Dqcow1=disabled", |
| "-Dqed=disabled", |
| "-Dqga_vss=disabled", |
| "-Dqom_cast_debug=false", |
| "-Drbd=disabled", |
| "-Drdma=disabled", |
| "-Dreplication=disabled", |
| "-Dsdl_image=disabled", |
| "-Dseccomp=disabled", |
| "-Dsmartcard=disabled", |
| "-Dsnappy=disabled", |
| "-Dsparse=disabled", |
| "-Dspice=disabled", |
| "-Dtcg=enabled", |
| "-Dtcg_interpreter=false", |
| "-Dtpm=disabled", |
| "-Dusb_redir=disabled", |
| "-Dvde=disabled", |
| "-Dvdi=disabled", |
| "-Dvirglrenderer=disabled", |
| "-Dvirtfs=disabled", |
| "-Dvnc=disabled", |
| "-Dvte=disabled", |
| "-Dvvfat=enabled", |
| "-Dwhpx=disabled", |
| "-Dxen=disabled", |
| "-Dxkbcommon=disabled", |
| ] |
| + meson_options |
| + (["-Db_lto=true"] if optimize else []) |
| + [ |
| build_dir, |
| ], |
| ) |
| finally: |
| try: |
| api.file.read_text( |
| "meson-log.txt", |
| build_dir.joinpath("meson-logs", "meson-log.txt"), |
| test_data="The Meson build system\nVersion: 1.0.0", |
| ) |
| except api.step.StepFailure: # pragma: no cover |
| pass |
| |
| # config-host.h is a generated header consisting of #-defines detailing |
| # configuration settings, including whether certain features are |
| # enabled. It serves as an important diagnostic. |
| config_host = build_dir / "config-host.h" |
| config = api.file.read_text( |
| "config-host.h", |
| config_host, |
| test_data="#pragma once\n#define CONFIG_FOO\n#undef CONFIG_BAR", |
| ) |
| if target_platform.is_linux: |
| # <sys/auxv.h> in glibc 2.27 doesn't define HWCAP_USCAT which breaks the |
| # build, disable getauxval to force the use of <asm/hwcap.h>. |
| api.file.write_text( |
| "update config-host.h", |
| config_host, |
| config.replace("#define CONFIG_GETAUXVAL", "#undef CONFIG_GETAUXVAL"), |
| ) |
| |
| api.step( |
| "compile", |
| [ |
| pkg_dir.joinpath("bin", "meson.pyz"), |
| "compile", |
| "-C", |
| build_dir, |
| ], |
| ) |
| api.step( |
| "install", |
| [ |
| pkg_dir.joinpath("bin", "meson.pyz"), |
| "install", |
| "-C", |
| build_dir, |
| "--skip-subprojects", |
| ], |
| ) |
| |
| # TODO(fxbug.dev/106763): Re-enable tests when passing again. |
| |
| return install_dir |
| |
| |
| # TODO(https://github.com/mesonbuild/meson/issues/8592): Meson links against |
| # libstdc++ by default when using clang. We use libc++ and so need to filter |
| # that out. |
| def wrap_clang(api, clang): |
| contents = ( |
| """#!/bin/sh |
| args=("$@") |
| args=("${args[@]/-lstdc++}") |
| exec %s "${args[@]}" |
| """ |
| % clang |
| ) |
| wrapper = api.path.tmp_base_dir / "clang-wrapper.sh" |
| api.file.write_text(f"write {api.path.basename(wrapper)}", wrapper, contents) |
| api.step( |
| f"make {api.path.basename(wrapper)} executable", |
| ["chmod", "+x", wrapper], |
| ) |
| return wrapper |
| |
| |
| def GenTests(api): |
| version = "8.0.50" |
| |
| def properties(platform, prod=True, builders=None): |
| return api.properties( |
| remote="https://fuchsia.googlesource.com/third_party/qemu", |
| platform=platform, |
| prod=prod, |
| builders=builders, |
| ) |
| |
| yield ( |
| api.test("linux-amd64") |
| + api.platform.name("linux") |
| + properties( |
| "linux-amd64", |
| builders={ |
| "linux-amd64": ["fuchsia/linux-x64-builder"], |
| "mac-amd64": ["fuchsia/mac-x64-builder"], |
| }, |
| ) |
| + api.step_data("read qemu version", api.file.read_text(version)) |
| ) |
| |
| yield ( |
| api.test("mac-amd64") |
| + api.platform.name("mac") |
| + properties("mac-amd64") |
| + api.step_data("read qemu version", api.file.read_text(version)) |
| ) |
| |
| yield ( |
| api.test("linux-arm64") |
| + api.platform.name("linux") |
| + properties("linux-arm64", prod=False) |
| + api.step_data("read qemu version", api.file.read_text(version)) |
| ) |