Skip to content

Commit

Permalink
crosscompile: Add an example for cross compiling
Browse files Browse the repository at this point in the history
This adds an example for cross compiling on an ARM M1 Mac to x86_64 Linux.
There are rough edges around this example like it will *only* compile for
Linux and won't work for OSX, but I think that this problem should be discussed
in the better API issue.

Also I realized I missed a bug in the previous cross compilation PR, where when cross compiling,
we probably want to use the linux_wrapper for building otherwise we end up trying to `install_name_tool`
the final Linux binaries, which doesn't work.

```
[nix-shell:~/code/rules_nixpkgs/examples/toolchains/cc_cross_osx_to_linux_amd64]$ bazel build --config=cross :hello
INFO: Invocation ID: f10b420f-2dca-42d3-ab4b-1179862791ff
INFO: Analyzed target //:hello (3 packages loaded, 19 targets configured).
INFO: Found 1 target...
Target //:hello up-to-date:
  bazel-bin/hello
INFO: Elapsed time: 8.212s, Critical Path: 5.59s
INFO: 4 processes: 2 internal, 2 processwrapper-sandbox.
INFO: Build completed successfully, 4 total actions

[nix-shell:~/code/rules_nixpkgs/examples/toolchains/cc_cross_osx_to_linux_amd64]$ file bazel-bin/hello
bazel-bin/hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /nix/store/jvjrf7rqflfx4nf65yrkk697sxggffki-glibc-x86_64-linux-2.35-224/lib/ld-linux-x86-64.so.2, for GNU/Linux 3.10.0, not stripped
```

note that when compiling for the first time, it'll build clang and gcc a bunch of times for cross compilations so the initial build will take a few hours...
  • Loading branch information
DolceTriade committed Aug 16, 2023
1 parent 89626d0 commit ada90a3
Show file tree
Hide file tree
Showing 13 changed files with 316 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
USE_BAZEL_VERSION=6.x
6 changes: 6 additions & 0 deletions examples/toolchains/cc_cross_osx_to_linux_amd64/.bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
build --incompatible_enable_cc_toolchain_resolution
build:nix --host_platform=@io_tweag_rules_nixpkgs//nixpkgs/platforms:host
build:nix --crosstool_top=@nixpkgs_config_cc//:toolchain
build:cross --host_platform=@io_tweag_rules_nixpkgs//nixpkgs/platforms:host
build:cross --host_crosstool_top=@nixpkgs_config_cc//:toolchain
build:cross --cpu=k8 --crosstool_top=@nixpkgs_cross_cc//:toolchain --platforms=//toolchains:linux_x86_64
10 changes: 10 additions & 0 deletions examples/toolchains/cc_cross_osx_to_linux_amd64/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
load("@rules_cc//cc:defs.bzl", "cc_binary")

cc_binary(
name = "hello",
srcs = ["hello.cc"],
deps = [
"@boost.dev//:boost",
"@zlib.dev//:zlib",
],
)
13 changes: 13 additions & 0 deletions examples/toolchains/cc_cross_osx_to_linux_amd64/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
C++ With Dependencies Toolchain Example
=======================================

This is an example C++ project with dependencies that uses `rules_cc`.

This example uses the Nix package manager to provide C++ dependencies, and as such only works with Nix installed. Demonstrating other methods of providing C++ dependencies is out of scope of this example.

# Usage

To run the example with Nix, issue the following command:
```
nix-shell --command 'bazel run --config=nix :hello'
```
111 changes: 111 additions & 0 deletions examples/toolchains/cc_cross_osx_to_linux_amd64/WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Replace with http_archive: https://github.com/tweag/rules_nixpkgs/#setup
local_repository(
name = "io_tweag_rules_nixpkgs",
path = "../../../",
)

load("@io_tweag_rules_nixpkgs//nixpkgs:repositories.bzl", "rules_nixpkgs_dependencies")

rules_nixpkgs_dependencies()

load(
"@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl",
"nixpkgs_cc_configure",
"nixpkgs_local_repository",
)

nixpkgs_local_repository(
name = "nixpkgs",
nix_file = "//:nixpkgs.nix",
nix_file_deps = ["//:nixpkgs.json"],
)

nixpkgs_cc_configure(
name = "nixpkgs_config_cc",
nix_file = "//toolchains:cc.nix",
repository = "@nixpkgs",
)

nixpkgs_cc_configure(
name = "nixpkgs_cross_cc",
cross_cpu = "k8",
exec_constraints = [
"@platforms//os:osx",
"@platforms//cpu:arm64",
],
nix_file = "//toolchains:osxcross_cc.nix",
nixopts = [
"--arg",
"ccPkgs",
"import <nixpkgs> { crossSystem = \"x86_64-linux\";}",
"--show-trace",
],
repository = "@nixpkgs",
target_constraints = [
"@platforms//cpu:x86_64",
"@platforms//os:linux",
"@rules_nixpkgs_core//constraints:support_nix",
],
)

load("@io_tweag_rules_nixpkgs//nixpkgs:nixpkgs.bzl", "nixpkgs_package")

# We would probably want a select or something to make this work with OSX as well, for a more
# production set up.
nixpkgs_package(
name = "boost",
attribute_path = "boost175",
nix_file_content = """import <nixpkgs> { config = {}; overlays = []; system = "x86_64-linux"; }""",
repository = "@nixpkgs",
)

nixpkgs_package(
name = "boost.dev",
attribute_path = "boost175.dev",
build_file_content = """\
load("@rules_cc//cc:defs.bzl", "cc_library")
filegroup(
name = "include",
srcs = glob(["include/**/*.h", "include/**/*.hpp"]),
visibility = ["//visibility:public"],
)
cc_library(
name = "boost",
srcs = ["@boost//:lib"],
hdrs = [":include"],
strip_include_prefix = "include",
visibility = ["//visibility:public"],
)
""",
nix_file_content = """import <nixpkgs> { config = {}; overlays = []; system = "x86_64-linux"; }""",
repository = "@nixpkgs",
)

nixpkgs_package(
name = "zlib",
attribute_path = "zlib",
nix_file_content = """import <nixpkgs> { config = {}; overlays = []; system = "x86_64-linux"; }""",
repository = "@nixpkgs",
)

nixpkgs_package(
name = "zlib.dev",
attribute_path = "zlib.dev",
build_file_content = """\
load("@rules_cc//cc:defs.bzl", "cc_library")
filegroup(
name = "include",
srcs = glob(["include/**/*.h"]),
visibility = ["//visibility:public"],
)
cc_library(
name = "zlib",
srcs = ["@zlib//:lib"],
hdrs = [":include"],
strip_include_prefix = "include",
visibility = ["//visibility:public"],
)
""",
nix_file_content = """import <nixpkgs> { config = {}; overlays = []; system = "x86_64-linux"; }""",
repository = "@nixpkgs",
)
8 changes: 8 additions & 0 deletions examples/toolchains/cc_cross_osx_to_linux_amd64/hello.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <iostream>
#include <boost/filesystem/operations.hpp>
#include <zlib.h>

int main()
{
std::cout << "Hello world!\n";
}
7 changes: 7 additions & 0 deletions examples/toolchains/cc_cross_osx_to_linux_amd64/nixpkgs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"owner": "NixOS",
"repo": "nixpkgs",
"branch": "23.05",
"rev": "3ad64d9e2d5bf80c877286102355b1625891ae9a",
"sha256": "sha256-PuZSAHeq4/9pP/uYH1FcagQ3nLm/DrDrvKi/xC9glvw="
}
8 changes: 8 additions & 0 deletions examples/toolchains/cc_cross_osx_to_linux_amd64/nixpkgs.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
let
spec = builtins.fromJSON (builtins.readFile ./nixpkgs.json);
nixpkgs = fetchTarball {
url = "https://github.com/${spec.owner}/${spec.repo}/archive/${spec.rev}.tar.gz";
sha256 = spec.sha256;
};
in
import nixpkgs
3 changes: 3 additions & 0 deletions examples/toolchains/cc_cross_osx_to_linux_amd64/shell.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{ pkgs ? import ./nixpkgs.nix { } }:

pkgs.mkShellNoCC { nativeBuildInputs = [ pkgs.bazel_6 ]; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
platform(
name = "linux_x86_64",
constraint_values = [
"@platforms//cpu:x86_64",
"@platforms//os:linux",
"@rules_nixpkgs_core//constraints:support_nix",
],
visibility = ["//visibility:public"],
)
72 changes: 72 additions & 0 deletions examples/toolchains/cc_cross_osx_to_linux_amd64/toolchains/cc.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# We need to write a fancy nix file to handle the CC compiler due to OSX: https://github.com/tweag/rules_nixpkgs/issues/368
# What this file is basically saying is: if OSX, change the CXX compiler to use a bunch of Apple frameworks, LLVM libs,
# libc++ instead of libstdc++, and some additional compiler flags to ignore some warnings, otherwise, just use clang11
let
pkgs = import <nixpkgs> {};
in let
clang = pkgs.clang_11;
# The original `postLinkSignHook` from nixpkgs assumes `codesign_allocate` is
# in the PATH which is not the case when using our cc_wrapper. Set
# `CODESIGN_ALLOCATE` to an absolute path here and override the hook for
# `darwinCC` below.
postLinkSignHook = with pkgs;
writeTextFile {
name = "post-link-sign-hook";
executable = true;

text = ''
CODESIGN_ALLOCATE=${darwin.cctools}/bin/codesign_allocate \
${darwin.sigtool}/bin/codesign -f -s - "$linkerOutput"
'';
};
darwinCC =
# Work around https://github.com/NixOS/nixpkgs/issues/42059.
# See also https://github.com/NixOS/nixpkgs/pull/41589.
pkgs.wrapCCWith rec {
cc = clang;
bintools = pkgs.stdenv.cc.bintools.override {inherit postLinkSignHook;};
extraBuildCommands = with pkgs.darwin.apple_sdk.frameworks; ''
echo "-Wno-unused-command-line-argument" >> $out/nix-support/cc-cflags
echo "-Wno-elaborated-enum-base" >> $out/nix-support/cc-cflags
echo "-isystem ${pkgs.llvmPackages_11.libcxx.dev}/include/c++/v1" >> $out/nix-support/cc-cflags
echo "-isystem ${pkgs.llvmPackages_11.clang-unwrapped.lib}/lib/clang/${cc.version}/include" >> $out/nix-support/cc-cflags
echo "-F${CoreFoundation}/Library/Frameworks" >> $out/nix-support/cc-cflags
echo "-F${CoreServices}/Library/Frameworks" >> $out/nix-support/cc-cflags
echo "-F${Security}/Library/Frameworks" >> $out/nix-support/cc-cflags
echo "-F${Foundation}/Library/Frameworks" >> $out/nix-support/cc-cflags
echo "-L${pkgs.llvmPackages_11.libcxx}/lib" >> $out/nix-support/cc-cflags
echo "-L${pkgs.llvmPackages_11.libcxxabi}/lib" >> $out/nix-support/cc-cflags
echo "-L${pkgs.libiconv}/lib" >> $out/nix-support/cc-cflags
echo "-L${pkgs.darwin.libobjc}/lib" >> $out/nix-support/cc-cflags
echo "-resource-dir=${pkgs.stdenv.cc}/resource-root" >> $out/nix-support/cc-cflags
'';
};
linuxCC = pkgs.wrapCCWith rec {
cc = clang;
bintools = pkgs.stdenv.cc.bintools;
extraPackages = [pkgs.glibc.static];
extraBuildCommands = ''
echo "-isystem ${pkgs.llvmPackages_11.clang-unwrapped.lib}/lib/clang/${cc.version}/include" >> $out/nix-support/cc-cflags
echo "-L ${pkgs.glibc.static}/lib" >> $out/nix-support/cc-ldflags
echo "-resource-dir=${cc}/resource-root" >> $out/nix-support/cc-cflags
'';
};
in
pkgs.buildEnv (
let
cc =
if pkgs.stdenv.isDarwin
then darwinCC
else linuxCC;
in {
name = "bazel-nixpkgs-cc";
# XXX: `gcov` is missing in `/bin`.
# It exists in `stdenv.cc.cc` but that collides with `stdenv.cc`.
paths = [cc cc.bintools] ++ pkgs.lib.optional pkgs.stdenv.isDarwin pkgs.darwin.cctools;
pathsToLink = ["/bin"];
passthru = {
inherit (cc) isClang targetPrefix;
orignalName = cc.name;
};
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# This will take many hours to build. Caching this somewhere is recommended.
let
targetSystem = "x86_64-linux";
og = import <nixpkgs> {};
nixpkgs = import <nixpkgs> {
buildSystem = builtins.currentSystem;
hostSystem = targetSystem;
crossSystem = {
config = targetSystem;
};
crossOverlays = [
(self: super: {
# Apparently this is a hacky way to extend llvmPackages, but whatever
llvmPackages_11 = super.llvmPackages_11.extend (final: prev: rec {
libllvm = prev.libllvm.overrideAttrs (old: {
# We need to override LDFLAGS because it puts non-Darwin compatible flags,
# so remove the old flags, and also explicitly tell the compiler where to
# find libcxxabi. Not sure why we need to do this.
LDFLAGS = "-L ${super.llvmPackages_11.libcxxabi}/lib";
# We need to make sure cctools is available because darwin code signing needs it
# in your $PATH.
nativeBuildInputs = (old.nativeBuildInputs or []) ++ [og.darwin.cctools];
});
libclang = prev.libclang.override {
inherit libllvm;
};
libraries = super.llvmPackages_11.libraries;
});
})
];
};
# this will use linux binaries from binary cache, so no need to build those
pkgsLinux = import <nixpkgs> {
config = {};
overlays = [];
system = targetSystem;
};
in let
pkgs = builtins.trace nixpkgs.stdenv.name nixpkgs.buildPackages;
linuxCC = pkgs.wrapCCWith rec {
cc = pkgs.llvmPackages_11.clang-unwrapped;
bintools = pkgs.llvmPackages_11.bintools;
extraPackages = [pkgsLinux.glibc.static pkgs.llvmPackages_11.libraries.libcxxabi pkgs.llvmPackages_11.libraries.libcxx];
extraBuildCommands = ''
echo "-isystem ${pkgs.llvmPackages_11.clang-unwrapped.lib}/lib/clang/${cc.version}/include" >> $out/nix-support/cc-cflags
echo "-isystem ${pkgsLinux.glibc.dev}/include" >> $out/nix-support/cc-cflags
echo "-L ${pkgs.llvmPackages_11.libraries.libcxxabi}/lib" >> $out/nix-support/cc-ldflags
echo "-L ${pkgsLinux.glibc.static}/lib" >> $out/nix-support/cc-ldflags
echo "-resource-dir=${cc}/resource-root" >> $out/nix-support/cc-cflags
'';
};
in
pkgs.buildEnv (
let
cc = linuxCC;
in {
name = "bazel-${cc.name}-cc";
# XXX: `gcov` is missing in `/bin`.
# It exists in `stdenv.cc.cc` but that collides with `stdenv.cc`.
paths = [cc cc.bintools];
pathsToLink = ["/bin"];
passthru = {
inherit (cc) isClang targetPrefix;
orignalName = cc.name;
};
}
)
2 changes: 1 addition & 1 deletion toolchains/cc/cc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def _parse_cc_toolchain_info(content, filename):
def _nixpkgs_cc_toolchain_config_impl(repository_ctx):
host_cpu = get_cpu_value(repository_ctx)
cross_cpu = repository_ctx.attr.cross_cpu or host_cpu
darwin = host_cpu == "darwin" or host_cpu == "darwin_arm64"
darwin = (host_cpu == "darwin" or host_cpu == "darwin_arm64") and cross_cpu == host_cpu

cc_toolchain_info_file = repository_ctx.path(repository_ctx.attr.cc_toolchain_info)
if not cc_toolchain_info_file.exists and not repository_ctx.attr.fail_not_supported:
Expand Down

0 comments on commit ada90a3

Please sign in to comment.