Skip to content

Commit

Permalink
Create self-contained cxx_zig_toolchain (#22)
Browse files Browse the repository at this point in the history
Summary:
Closes facebook/buck2#20

Adds `prelude//toolchains/cxx/zig` with a C/C++ toolchain based on `zig cc`.
The doc-string of the added module contains a description and examples
configurations.

Also adds an example to the repository that builds a C++ library and a binary
depending on that library using this toolchain.

Note, for that example to work in the open source buck2 tree the following additional patch needs to be applied:
```diff
 diff --git a/.buckconfig b/.buckconfig
deleted file mode 100644
index c32af92..0000000
 --- a/.buckconfig
+++ /dev/null
@@ -1,19 +0,0 @@
-[repositories]
-root = .
-prelude = prelude
-ovr_config = prelude
-shim = shim
-toolchains = shim
-fbcode = shim
-fbcode_macros = shim
-fbsource = shim
-buck = shim
-
-[buildfile]
-name = TARGETS
-
-[build]
-execution_platforms = ovr_config//platforms:default
-
-[parser]
-target_platform_detector_spec = target://...->ovr_config//platforms:default
 diff --git a/prelude/cxx/tools/TARGETS.v2 b/prelude/cxx/tools/TARGETS.v2
index 2030d2f..5db1689 100644
 --- a/prelude/cxx/tools/TARGETS.v2
+++ b/prelude/cxx/tools/TARGETS.v2
@@ -1,4 +1,3 @@
-load("fbcode_macros//build_defs/lib:python_common.bzl", "get_ldflags", "get_strip_mode")
 load(":defs.bzl", "cxx_hacks", "omnibus_environment")

 prelude = native
@@ -538,7 +537,7 @@ omnibus_environment(
     # We override the binary-level ldflags with library level ldfalgs for
     # shared roots. We include 2 important things: stripping binaries, and not
     # discarding GPU code.
-    shared_root_ld_flags = get_ldflags("", "", "omnibus_root", get_strip_mode("", ""), "") + [
+    shared_root_ld_flags = [
         "-Wl,--no-discard-section=.nv_fatbin",
         "-Wl,--no-discard-section=.nvFatBinSegment",
         # Reorder nv_fatbin after the bss section to avoid overflow
```

X-link: facebook/buck2#22

Reviewed By: arlyon

Differential Revision: D40179209

Pulled By: arlyon

fbshipit-source-id: d334f2ad4563281f78ba3f479b8defd09616cabf
  • Loading branch information
aherrmann authored and facebook-github-bot committed Oct 10, 2022
1 parent 620e3ba commit 2b12ccb
Show file tree
Hide file tree
Showing 2 changed files with 1,015 additions and 0 deletions.
383 changes: 383 additions & 0 deletions toolchains/cxx/zig/defs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,383 @@
"""Self-contained C/C++ toolchain based on zig cc.
Most C/C++ compiler toolchains will depend on a system wide installation of
libc and other standard libraries. This means that the build outputs may vary
from system to system and that advanced use-cases, like cross-compilation,
require installation of special system packages.
The zig cc compiler is based on clang and comes bundled with the standard
library sources and supports on-the-fly cross-compilation, making it easier to
define a reproducible build setup and cross-compilation use-cases.
Further details on zig cc are available [here][zig-cc-annoncement]. Note, at
the time of writing this is still experimental. If this is a problem for your
use-case then you may wish to rely on a system toolchain or define your own.
The toolchain is not fully hermetic as it still relies on system tools like nm.
[zig-cc-annoncement]: https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html
## Examples
To automatically fetch a distribution suitable for the host-platform configure
the toolchain like so:
`toolchains//BUILD`
```bzl
load("@prelude//toolchains/cxx:zig.bzl", "download_zig_distribution", "cxx_zig_toolchain")
download_zig_distribution(
name = "zig",
version = "0.9.1",
)
cxx_zig_toolchain(
name = "cxx",
distribution = ":zig",
visibility = ["PUBLIC"],
)
```
To define toolchains for multiple platforms and configure cross-compilation you
can configure the toolchain like so:
```bzl
load("@prelude//toolchains/cxx:zig.bzl", "download_zig_distribution", "cxx_zig_toolchain")
download_zig_distribution(
name = "zig-x86_64-linux",
version = "0.9.1",
arch = "x86_64",
os = "linux",
)
download_zig_distribution(
name = "zig-x86_64-macos",
version = "0.9.1",
arch = "x86_64",
os = "macos",
)
download_zig_distribution(
name = "zig-x86_64-windows",
version = "0.9.1",
arch = "x86_64",
os = "windows",
)
alias(
name = "zig",
actual = select({
"prelude//os:linux": ":zig-x86_64-linux",
"prelude//os:macos": ":zig-x86_64-macos",
"prelude//os:windows": ":zig-x86_64-windows",
}),
)
cxx_zig_toolchain(
name = "cxx",
distribution = ":zig",
target = select({
"prelude//os:linux": "x86_64-linux-gnu",
"prelude//os:macos": "x86_64-macos-gnu",
"prelude//os:windows": "x86_64-windows-gnu",
}),
visibility = ["PUBLIC"],
)
```
"""

load(
"@prelude//cxx:cxx_toolchain_types.bzl",
"BinaryUtilitiesInfo",
"CCompilerInfo",
"CxxCompilerInfo",
"LinkerInfo",
"StripFlagsInfo",
"cxx_toolchain_infos",
)
load(
"@prelude//cxx:headers.bzl",
"HeaderMode",
)
load(
"@prelude//linking:link_info.bzl",
"LinkStyle",
)
load(
":releases.bzl",
"releases",
)

DEFAULT_MAKE_COMP_DB = "prelude//cxx/tools:make_comp_db"

ZigReleaseInfo = provider(fields = [
"version",
"url",
"sha256",
])

def _get_zig_release(
version: "string",
platform: "string") -> ZigReleaseInfo.type:
if not version in releases:
fail("Unknown zig release version '{}'. Available versions: {}".format(
version,
", ".join(releases.keys()),
))
zig_version = releases[version]
if not platform in zig_version:
fail("Unsupported platfrom '{}'. Supported platforms: {}".format(
platform,
", ".join(zig_version.keys()),
))
zig_platform = zig_version[platform]
return ZigReleaseInfo(
version = zig_version.get("version", version),
url = zig_platform["tarball"],
sha256 = zig_platform["shasum"],
)

ZigDistributionInfo = provider(fields = [
"version",
"arch",
"os",
])

def _zig_distribution_impl(ctx: "context") -> ["provider"]:
dst = ctx.actions.declare_output("zig")
path_tpl = "{}/" + ctx.attrs.prefix + "/zig" + ctx.attrs.suffix
src = cmd_args(ctx.attrs.dist[DefaultInfo].default_outputs[0], format = path_tpl)
ctx.actions.run(["ln", "-srf", src, dst.as_output()], category = "cp_compiler")

compiler = cmd_args([dst])
compiler.hidden(ctx.attrs.dist[DefaultInfo].default_outputs)

return [
ctx.attrs.dist[DefaultInfo],
RunInfo(args = compiler),
ZigDistributionInfo(
version = ctx.attrs.version,
arch = ctx.attrs.arch,
os = ctx.attrs.os,
),
]

zig_distribution = rule(
impl = _zig_distribution_impl,
attrs = {
"arch": attrs.string(),
"dist": attrs.dep(providers = [DefaultInfo]),
"os": attrs.string(),
"prefix": attrs.string(),
"suffix": attrs.string(default = ""),
"version": attrs.string(),
},
)

def _http_archive_impl(ctx: "context") -> ["provider"]:
url = ctx.attrs.urls[0]
if url.endswith(".tar.xz"):
type = "tar.xz"
flags = ["tar", "xJf"]
elif url.endswith(".zip"):
flags = ["unzip"]
type = "zip"
else:
fail("Unknown archive type in URL '{}'".format(url))

# Download archive.
archive = ctx.actions.declare_output("archive." + type)
ctx.actions.download_file(archive.as_output(), url, sha256 = ctx.attrs.sha256, is_deferrable = True)

# Unpack archive to output directory.
output = ctx.actions.declare_output(ctx.label.name)
script, _ = ctx.actions.write(
"unpack.sh",
[
cmd_args(output, format = "mkdir -p {}"),
cmd_args(output, format = "cd {}"),
cmd_args(flags, archive, delimiter = " ").relative_to(output),
],
is_executable = True,
allow_args = True,
)
ctx.actions.run(cmd_args(["/bin/sh", script])
.hidden([archive, output.as_output()]), category = "http_archive")

return [DefaultInfo(default_outputs = [output])]

# TODO Switch to http_archive once that supports zip download.
# See https://github.com/facebookincubator/buck2/issues/21
_http_archive = rule(
impl = _http_archive_impl,
attrs = {
"sha256": attrs.string(default = ""),
"urls": attrs.list(attrs.string(), default = []),
},
)

def _host_arch() -> "string":
arch = host_info().arch
if arch.is_x86_64:
return "x86_64"
elif host_info().arch.is_aarch64:
return "aarch64"
elif host_info().arch.is_arm:
return "armv7a"
elif host_info().arch.is_i386:
return "i386"
elif host_info().arch.is_i386:
return "i386"
else:
fail("Unsupported host architecture.")

def _host_os() -> "string":
os = host_info().os
if os.is_freebsd:
return "freebsd"
elif os.is_linux:
return "linux"
elif os.is_macos:
return "macos"
elif os.is_windows:
return "windows"
else:
fail("Unsupported host os.")

def download_zig_distribution(
name: "string",
version: "string",
arch: [None, "string"] = None,
os: [None, "string"] = None):
if arch == None:
arch = _host_arch()
if os == None:
os = _host_os()
archive_name = name + "-archive"
release = _get_zig_release(version, "{}-{}".format(arch, os))
_http_archive(
name = archive_name,
urls = [release.url],
sha256 = release.sha256,
)
zig_distribution(
name = name,
dist = ":" + archive_name,
prefix = "zig-{}-{}-{}/".format(os, arch, release.version),
suffix = ".exe" if os == "windows" else "",
version = release.version,
arch = arch,
os = os,
)

def _get_linker_type(os: "string") -> "string":
if os == "linux":
return "gnu"
elif os == "macos" or os == "freebsd":
return "darwin"
elif os == "windows":
return "windows"
else:
fail("Cannot determine linker type: Unknown OS '{}'".format(os))

def _cxx_zig_toolchain_impl(ctx: "context") -> ["provider"]:
dist = ctx.attrs.distribution[ZigDistributionInfo]
zig = ctx.attrs.distribution[RunInfo]
target = ["-target", ctx.attrs.target] if ctx.attrs.target else []
return [ctx.attrs.distribution[DefaultInfo]] + cxx_toolchain_infos(
platform_name = dist.arch,
c_compiler_info = CCompilerInfo(
compiler = RunInfo(args = cmd_args([zig, "cc"])),
compiler_type = "clang",
compiler_flags = cmd_args(target + ctx.attrs.c_compiler_flags),
#preprocessor = None,
#preprocessor_type = None,
preprocessor_flags = cmd_args(ctx.attrs.c_preprocessor_flags),
#dep_files_processor = None,
),
cxx_compiler_info = CxxCompilerInfo(
compiler = RunInfo(args = cmd_args([zig, "c++"])),
compiler_type = "clang",
compiler_flags = cmd_args(target + ctx.attrs.cxx_compiler_flags),
#preprocessor = None,
#preprocessor_type = None,
preprocessor_flags = cmd_args(ctx.attrs.cxx_preprocessor_flags),
#dep_files_processor = None,
),
linker_info = LinkerInfo(
archiver = RunInfo(args = cmd_args([zig, "ar"])),
archiver_supports_argfiles = True,
#archive_contents = None,
archive_objects_locally = False,
link_binaries_locally = False,
link_libraries_locally = False,
link_style = LinkStyle(ctx.attrs.link_style),
link_weight = 1,
#link_ordering = None,
linker = RunInfo(args = cmd_args([zig, "c++"])),
linker_flags = cmd_args(target + ctx.attrs.linker_flags),
#lto_mode = None, # TODO support LTO
#mk_shlib_intf = None, # not needed if shlib_interfaces = "disabled"
shlib_interfaces = "disabled",
shared_dep_runtime_ld_flags = ctx.attrs.shared_dep_runtime_ld_flags,
static_dep_runtime_ld_flags = ctx.attrs.static_dep_runtime_ld_flags,
static_pic_dep_runtime_ld_flags = ctx.attrs.static_pic_dep_runtime_ld_flags,
#requires_archives = None,
#requires_objects = None,
#supports_distributed_thinlto = None,
independent_shlib_interface_linker_flags = ctx.attrs.shared_library_interface_flags,
type = _get_linker_type(dist.os),
use_archiver_flags = True,
),
binary_utilities_info = BinaryUtilitiesInfo(
bolt_msdk = None,
dwp = None,
nm = RunInfo(args = ["nm"]), # not included in the zig distribution.
objcopy = RunInfo(args = ["objcopy"]), # not included in the zig distribution.
ranlib = RunInfo(args = cmd_args([zig, "ranlib"])),
strip = RunInfo(args = ["strip"]), # not included in the zig distribution.
),
header_mode = HeaderMode("symlink_tree_only"), # header map modes require mk_hmap
#headers_as_raw_headers_mode = None,
#conflicting_header_basename_allowlist = [],
#asm_compiler_info = None,
#as_compiler_info = None,
#hip_compiler_info = None,
#cuda_compiler_info = None,
mk_comp_db = ctx.attrs.make_comp_db,
#mk_hmap = None,
#use_distributed_thinlto = False,
#use_dep_files = False, # requires dep_files_processor
strip_flags_info = StripFlagsInfo(
strip_debug_flags = ctx.attrs.strip_debug_flags,
strip_non_global_flags = ctx.attrs.strip_non_global_flags,
strip_all_flags = ctx.attrs.strip_all_flags,
),
#dist_lto_tools_info: [DistLtoToolsInfo.type, None] = None,
#split_debug_mode = SplitDebugMode("none"),
#bolt_enabled = False,
)

cxx_zig_toolchain = rule(
impl = _cxx_zig_toolchain_impl,
attrs = {
"c_compiler_flags": attrs.list(attrs.arg(), default = []),
"c_preprocessor_flags": attrs.list(attrs.arg(), default = []),
"cxx_compiler_flags": attrs.list(attrs.arg(), default = []),
"cxx_preprocessor_flags": attrs.list(attrs.arg(), default = []),
"distribution": attrs.exec_dep(providers = [RunInfo, ZigDistributionInfo]),
"link_style": attrs.enum(LinkStyle.values(), default = "static"),
"linker_flags": attrs.list(attrs.arg(), default = []),
"make_comp_db": attrs.dep(providers = [RunInfo], default = DEFAULT_MAKE_COMP_DB),
"shared_dep_runtime_ld_flags": attrs.list(attrs.arg(), default = []),
"shared_library_interface_flags": attrs.list(attrs.string(), default = []),
"static_dep_runtime_ld_flags": attrs.list(attrs.arg(), default = []),
"static_pic_dep_runtime_ld_flags": attrs.list(attrs.arg(), default = []),
"strip_all_flags": attrs.option(attrs.list(attrs.arg()), default = None),
"strip_debug_flags": attrs.option(attrs.list(attrs.arg()), default = None),
"strip_non_global_flags": attrs.option(attrs.list(attrs.arg()), default = None),
"target": attrs.option(attrs.string(), default = None),
},
is_toolchain_rule = True,
)
Loading

0 comments on commit 2b12ccb

Please sign in to comment.