Subject: [bug#54239] [PATCH] gnu: Add cross-clang.

From: Pierre Langlois <[email-masked]>
Date: Thu, 03 Mar 2022 20:51:01 +0000

Hi Julien,

Julien Lepiller <[email-masked]> writes:

> Hi Guix!
>
> This small patch series adds cross-clang, a cross-compiler version of
> clang. Clang doesn't really make a distinction between a native and a
> cross-build, it is already a cross-compiler, but this ensures that:
>
> 1. it actually works
> 2. it targets (%current-target-architecture) by default
>
> The rest of the series ensures that libcxx and libcxxabi can be
> cross-compiled with it.

That's really cool! Out of curiosity, what target are you interested in
that clang supports and GCC doesn't?  For instance I'm interested in
WebAssembly, and I've actually been contemplating how we could improve
support for cross-compilers based on Clang in Guix, to be able to
provide a working WebAssembly toolchain.

For context, such a toolchain will be needed to enable potential use of
WebAssembly in IceCat, for security purposes, see
https://hacks.mozilla.org/2021/12/webassembly-and-back-again-fine-grained-sandboxing-in-firefox-95/

I actually have a toolchain that works already, which targets the
wasm32-wasi-unknown triplet, but I wasn't convinced the way I did it was
the right way to go long term, so it needs more work.  I'm attaching the
patch in case there's anything useful to you in there already!

The approach I took was to create wrappers around clang/clang++ in order
to set the correct include paths, and then provide an isolated sysroot
for wasm32-wasi using union-build. This will work for IceCat, where
their should be a flag to pass a sysroot path, but we should be able to
do better in Guix.

I though I'd share in case that's helpful! I'll take a look at your
patches to see if I can build on top of them for a future WebAssembly
toolchain in Guix.

Thanks,
Pierre

>From 7d3c2a54ad66d5c4b2aa96eb946537fac1a5ae75 Mon Sep 17 00:00:00 2001
From: Pierre Langlois <[email-masked]>
Date: Sun, 16 Jan 2022 16:55:59 +0000
Subject: [PATCH] gnu: Add wasm32-wasi-clang-toolchain.

WIP
---
 gnu/local.mk          |   1 +
 gnu/packages/llvm.scm |   1 +
 gnu/packages/wasm.scm | 273 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 275 insertions(+)
 create mode 100644 gnu/packages/wasm.scm

diff --git a/gnu/local.mk b/gnu/local.mk
index b7339ff472..952786827f 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -603,6 +603,7 @@ GNU_SYSTEM_MODULES =				\
   %D%/packages/vpn.scm				\
   %D%/packages/vulkan.scm			\
   %D%/packages/w3m.scm				\
+  %D%/packages/wasm.scm			\
   %D%/packages/wdiff.scm			\
   %D%/packages/web.scm				\
   %D%/packages/web-browsers.scm			\
diff --git a/gnu/packages/llvm.scm b/gnu/packages/llvm.scm
index eb949bed1b..8821b2b6a7 100644
--- a/gnu/packages/llvm.scm
+++ b/gnu/packages/llvm.scm
@@ -74,6 +74,7 @@ (define-module (gnu packages llvm)
   #:use-module (srfi srfi-1)
   #:use-module (ice-9 match)
   #:export (make-lld-wrapper
+            clang-from-llvm
             system->llvm-target))
 
 (define* (system->llvm-target #:optional
diff --git a/gnu/packages/wasm.scm b/gnu/packages/wasm.scm
new file mode 100644
index 0000000000..f9f73505ef
--- /dev/null
+++ b/gnu/packages/wasm.scm
@@ -0,0 +1,273 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2021 Pierre Langlois <[email-masked]>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu packages wasm)
+  #:use-module (guix gexp)
+  #:use-module (guix packages)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (guix download)
+  #:use-module (guix git-download)
+  #:use-module (guix utils)
+  #:use-module (guix build-system cmake)
+  #:use-module (guix build-system gnu)
+  #:use-module (guix build-system trivial)
+  #:use-module (gnu packages base)
+  #:use-module (gnu packages bash)
+  #:use-module (gnu packages compression)
+  #:use-module (gnu packages llvm)
+  #:use-module (gnu packages python))
+
+(define-public wasi-libc
+  (let ((commit "ad5133410f66b93a2381db5b542aad5e0964db96")
+        (revision "1"))
+    (package
+      (name "wasi-libc")
+      (version (git-version "0.1-alpha" revision commit))
+      (source (origin
+                (method git-fetch)
+                (uri (git-reference
+                      (url "https://github.com/WebAssembly/wasi-libc")
+                      (commit commit)
+                      (recursive? #t)))
+                (file-name (git-file-name name version))
+                (sha256
+                 (base32
+                  "146jamq2q24vxjfpcwlqj84wzc80cbpbg0ns2wimyvzbanah48j6"))))
+      (build-system gnu-build-system)
+      (native-inputs (list clang-13))
+      (arguments
+       (list #:tests? #f ;No test suite
+             #:phases
+             #~(modify-phases %standard-phases
+                 (delete 'configure)
+                 (add-before 'build 'set-sysroot-include
+                   (lambda _
+                     (setenv "C_INCLUDE_PATH"
+                             (string-append (getcwd) "/sysroot/include"))))
+                 (add-before 'install 'set-install-dir
+                   (lambda _
+                     (setenv "INSTALL_DIR"
+                             (string-append #$output "/wasm32-wasi")))))))
+      (home-page "https://wasi.dev")
+      (synopsis "WASI libc implementation for WebAssembly")
+      (description
+       "WASI Libc is a libc for WebAssembly programs built on top of WASI
+system calls.  It provides a wide array of POSIX-compatible C APIs, including
+support for standard I/O, file I/O, filesystem manipulation, memory
+management, time, string, environment variables, program startup, and many
+other APIs.")
+      (license (list
+                ;; For wasi-libc, with LLVM exceptions
+                license:asl2.0
+                ;; For malloc.c.
+                license:cc0
+                ;; For cloudlibc.
+                license:bsd-2
+                ;; For wasi-libc and musl-libc.
+                license:expat)))))
+
+(define-public wasm32-wasi-clang-runtime
+  (package (inherit clang-runtime-13)
+    (native-inputs
+     (list clang-13 wasi-libc))
+    (inputs (list llvm-13))
+    (arguments
+     (list
+      #:build-type "Release"
+      #:tests? #f
+      ;; Stripping binaries breaks wasm linking, resulting in the following
+      ;; error: "archive has no index; run ranlib to add one".
+      #:strip-binaries? #f
+      #:configure-flags
+      #~(list "-DCMAKE_C_COMPILER=clang"
+              "-DCMAKE_C_COMPILER_TARGET=wasm32-wasi"
+              (string-append
+               "-DCMAKE_SYSROOT=" #$wasi-libc "/wasm32-wasi")
+              (string-append
+               "-DCMAKE_C_FLAGS=-I " #$wasi-libc "/wasm32-wasi/include")
+
+              "-DCOMPILER_RT_OS_DIR=wasi"
+
+              "-DCOMPILER_RT_BAREMETAL_BUILD=On"
+              "-DCOMPILER_RT_DEFAULT_TARGET_ONLY=On"
+
+              ;; WASM only needs libclang_rt.builtins-wasm32.a from
+              ;; compiler-rt.
+              (string-append "../compiler-rt-"
+                             #$(package-version clang-runtime-13)
+                             ".src/lib/builtins"))))))
+
+;; FIXME: Ideally we wouldn't need to build a separate compiler because clang
+;; can support multiple targets at runtime.  However Guix patches the default
+;; clang with a specific clang-runtime package.  It would be good to improve
+;; upstream Guix's support for cross-compiling with clang.
+
+(define clang-from-llvm (@@ (gnu packages llvm) clang-from-llvm))
+
+(define-public wasm32-wasi-clang
+  (let ((base (clang-from-llvm llvm-13 wasm32-wasi-clang-runtime
+                               "1j8pr5kk8iqyb4jds3yl7c6x672617h4ngkpl4575j7mk4nrwykq")))
+    (package (inherit base)
+      (name "wasm32-wasi-clang")
+      (inputs
+       (modify-inputs (package-inputs base)
+         (prepend wasi-libc)))
+      (arguments
+       (substitute-keyword-arguments (package-arguments base)
+         ((#:configure-flags flags)
+          #~(list "-DCLANG_INCLUDE_TESTS=True"
+                  ;; Use a sane default include directory.
+                  (string-append "-DC_INCLUDE_DIRS="
+                                 #$wasi-libc
+                                 "/wasm32-wasi/include")))
+         ((#:phases phases)
+          `(modify-phases ,phases
+             (delete 'symlink-cfi_ignorelist))))))))
+
+(define-public wasm32-wasi-libcxx
+  (package
+    (name "wasm32-wasi-libcxx")
+    (version "13.0.0")
+    (source
+     (origin
+       (method git-fetch)
+       (uri (git-reference
+             (url "https://github.com/llvm/llvm-project")
+             (commit (string-append "llvmorg-" version))))
+       (file-name (git-file-name name version))
+       (sha256
+        (base32
+         "0cjl0vssi4y2g4nfr710fb6cdhxmn5r0vis15sf088zsc5zydfhw"))))
+    (build-system cmake-build-system)
+    (arguments
+     (list
+      #:configure-flags
+      #~(list (string-append "-S ../source/runtimes")
+
+              "-DLLVM_ENABLE_RUNTIMES=libcxx;libcxxabi"
+
+              (string-append
+               "-DCMAKE_SYSROOT=" #$wasi-libc "/wasm32-wasi")
+
+              (string-append
+               "-DCMAKE_INCLUDE_PATH=" #$wasi-libc "/wasm32-wasi/include")
+
+              (string-append
+               "-DCMAKE_STAGING_PREFIX=" #$output "/wasm32-wasi")
+
+              "-DCMAKE_C_COMPILER=clang"
+              "-DCMAKE_C_COMPILER_WORKS=ON"
+              "-DCMAKE_CXX_COMPILER=clang++"
+              "-DCMAKE_CXX_COMPILER_WORKS=ON"
+              "-DCMAKE_C_COMPILER_TARGET=wasm32-wasi"
+              "-DCMAKE_CXX_COMPILER_TARGET=wasm32-wasi"
+
+              "-DLIBCXX_LIBDIR_SUFFIX=/wasm32-wasi"
+
+              "-DLIBCXX_ENABLE_EXCEPTIONS=OFF"
+              "-DLIBCXX_ENABLE_SHARED=OFF"
+              "-DLIBCXX_ENABLE_THREADS=OFF"
+              "-DLIBCXX_ENABLE_FILESYSTEM=OFF"
+
+              "-DLIBCXXABI_LIBDIR_SUFFIX=/wasm32-wasi"
+
+              "-DLIBCXXABI_ENABLE_EXCEPTIONS=OFF"
+              "-DLIBCXXABI_ENABLE_SHARED=OFF"
+              "-DLIBCXXABI_ENABLE_THREADS=OFF"
+              "-DLIBCXXABI_ENABLE_FILESYSTEM=OFF")
+      #:tests? #f
+      #:phases
+      #~(modify-phases %standard-phases
+          (add-after 'set-paths 'adjust-CPLUS_INCLUDE_PATH
+            (lambda _
+              (setenv "CPLUS_INCLUDE_PATH"
+                      (string-append #$wasi-libc "/wasm32-wasi/include:"
+                                     (getenv "CPLUS_INCLUDE_PATH"))))))))
+    (native-inputs
+     (list wasm32-wasi-clang lld python))
+    (inputs
+     (list wasi-libc))
+    (home-page "https://libcxx.llvm.org")
+    (synopsis "C++ standard library, for WebAssembly")
+    (description
+     "This package provides an implementation of the C++ standard library for
+use with Clang, targeting C++11, C++14 and above.  This package targets
+WebAssembly with WASI.")
+    (license license:expat)))
+
+(define-public wasm32-wasi-clang-toolchain
+  (package
+    (name "wasm32-wasi-clang-toolchain")
+    (version (package-version wasm32-wasi-clang))
+    (source #f)
+    (build-system trivial-build-system)
+    (arguments
+     (list
+      #:builder
+      (with-imported-modules '((guix build union)
+                               (guix build utils))
+        #~(begin
+            (use-modules (guix build union)
+                         (guix build utils))
+            (union-build #$output
+                         (list #$wasm32-wasi-clang-runtime
+                               #$wasi-libc
+                               #$wasm32-wasi-libcxx))
+            (mkdir-p (string-append #$output "/bin"))
+
+            ;; We provide clang and clang++ via a wrapped program that sets
+            ;; include paths correctly so that it does not include paths from
+            ;; the host.
+
+            ;; FIXME: Review how we can provide better support for
+            ;; cross-compiling with clang in Guix, maybe adding support for
+            ;; the CROSS_C_INCLUDE_PATH and CROSS_CPLUS_INCLUDE_PATH
+            ;; environment variables like GCC.
+
+            (for-each
+             (lambda (bin)
+               (symlink (string-append #$wasm32-wasi-clang bin)
+                        (string-append #$output bin))
+               (wrap-program (string-append #$output bin)
+                 #:sh (string-append #$bash-minimal "/bin/bash")
+                 `("C_INCLUDE_PATH" ":" =
+                   (,(string-append #$output "/wasm32-wasi/include")))
+                 `("CPLUS_INCLUDE_PATH" ":" =
+                   ;; Make sure inclure/c++/v1 comes first for #include_next
+                   ;; to work.
+                   (,(string-append #$output "/wasm32-wasi/include/c++/v1")
+                    ,(string-append #$output "/wasm32-wasi/include")))))
+             '("/bin/clang" "/bin/clang++"))
+
+            (symlink (string-append #$lld "/bin/wasm-ld")
+                     (string-append #$output "/bin/wasm-ld"))))))
+    (inputs
+     (list bash-minimal
+           lld
+           wasm32-wasi-clang
+           wasm32-wasi-clang-runtime
+           wasi-libc
+           wasm32-wasi-libcxx))
+    (license (cons
+              (package-license wasm32-wasi-clang)
+              (package-license wasi-libc)))
+    (home-page "https://clang.llvm.org")
+    (synopsis "Complete Clang toolchain for C/C++ development, for WebAssembly.")
+    (description "This package provides a complete Clang toolchain for C/C++
+development targeting WebAssembly with WASI.  This includes Clang, as well as
+libc, libc++ and wasm-ld.")))
-- 
2.34.0