diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index c3aad07..52cfcfa 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -55,6 +55,131 @@ jobs: if: matrix.llvm == 19 run: go test -v + test-windows: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.22' + - name: Set up MSYS2 + uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 + with: + msystem: CLANG64 + path-type: inherit + update: true + install: curl + - name: Install LLVM 19 + shell: msys2 {0} + env: + LLVM_VERSION: '19.1.7' + MSYS2_LLVM_PACKAGE_VERSION: '19.1.7-1' + run: | + set -euo pipefail + + repo=https://repo.msys2.org/mingw/clang64 + prefix=mingw-w64-clang-x86_64 + # MSYS2 is a rolling repository and can eventually remove these old + # artifacts. If a URL starts returning 404, refresh the transitive + # dependency versions and signatures together while keeping LLVM 19 + # pinned to the version above. + packages=( + "clang-$MSYS2_LLVM_PACKAGE_VERSION" + "clang-libs-$MSYS2_LLVM_PACKAGE_VERSION" + "compiler-rt-$MSYS2_LLVM_PACKAGE_VERSION" + "llvm-$MSYS2_LLVM_PACKAGE_VERSION" + "llvm-libs-$MSYS2_LLVM_PACKAGE_VERSION" + "lld-$MSYS2_LLVM_PACKAGE_VERSION" + "libc++-$MSYS2_LLVM_PACKAGE_VERSION" + "libunwind-$MSYS2_LLVM_PACKAGE_VERSION" + 'gettext-runtime-0.22.5-2' + 'libffi-3.4.6-1' + 'libiconv-1.17-4' + 'libxml2-2.12.9-2' + 'xz-5.6.3-3' + 'zlib-1.3.1-1' + 'zstd-1.5.6-2' + ) + download_dir="$(mktemp -d)" + trap 'rm -rf "$download_dir"' EXIT + archives=() + for package in "${packages[@]}"; do + archive="$prefix-$package-any.pkg.tar.zst" + curl --fail --location --retry 3 \ + --output "$download_dir/$archive" \ + "$repo/$archive" + curl --fail --location --retry 3 \ + --output "$download_dir/$archive.sig" \ + "$repo/$archive.sig" + archives+=("$download_dir/$archive") + done + + # These archived LLVM 19 packages predate MSYS2's rename of the + # compiler-runtime virtual dependency from gcc-libs to cc-libs. + # libc++ 19 still supplies that runtime, so satisfy only the renamed + # package metadata rather than upgrading the pinned LLVM toolchain. + assumed_cc_runtime="$prefix-cc-libs=$LLVM_VERSION" + # pacman discovers each adjacent .sig file and validates it against + # the MSYS2 package keyring before installing the local archive. + pacman --noconfirm -U \ + --assume-installed "$assumed_cc_runtime" \ + "${archives[@]}" + pacman --noconfirm -S --needed \ + --assume-installed "$assumed_cc_runtime" \ + "$prefix-pkgconf" + + for package in clang clang-libs compiler-rt llvm llvm-libs lld libc++ libunwind; do + installed="$(pacman -Q "$prefix-$package" | awk '{print $2}')" + if [[ "$installed" != "$MSYS2_LLVM_PACKAGE_VERSION" ]]; then + echo "expected $package $MSYS2_LLVM_PACKAGE_VERSION, got $installed" >&2 + exit 1 + fi + done + - name: Test LLVM 19 + shell: msys2 {0} + run: | + set -euo pipefail + + llvm_version="$(llvm-config --version)" + if [[ "$llvm_version" != 19.1.7 ]]; then + echo "expected LLVM 19.1.7, got $llvm_version" >&2 + exit 1 + fi + + # Supply the installed LLVM's include and library paths through the + # pkg-config name consumed by the Windows LLVM 19 binding. + pc_dir="$(cygpath -u "$RUNNER_TEMP")/llvm-pkgconfig" + mkdir -p "$pc_dir" + cflags="$(llvm-config --cflags | tr '\r\n' ' ')" + ldflags="$(llvm-config --ldflags --libs all --system-libs | tr '\r\n' ' ')" + printf '%s\n' \ + 'Name: LLVM 19' \ + 'Description: LLVM 19 host compiler and linker flags' \ + "Version: $llvm_version" \ + "Cflags: $cflags" \ + "Libs: $ldflags" \ + > "$pc_dir/llvm-19.pc" + + export CC=clang + export CXX=clang++ + export CGO_ENABLED=1 + PKG_CONFIG_PATH="$(cygpath -m "$pc_dir")" + export PKG_CONFIG_PATH + clang --version + go test -v -tags=llvm19 + - name: Test default LLVM + shell: msys2 {0} + run: | + set -euo pipefail + export CC=clang + export CXX=clang++ + export CGO_ENABLED=1 + pc_dir="$(cygpath -u "$RUNNER_TEMP")/llvm-pkgconfig" + PKG_CONFIG_PATH="$(cygpath -m "$pc_dir")" + export PKG_CONFIG_PATH + go test -v test-linux-fedora: # Fedora uses different paths than other systems, so testing it separately. runs-on: ubuntu-24.04 diff --git a/README.markdown b/README.markdown index e66c391..28cfdc2 100644 --- a/README.markdown +++ b/README.markdown @@ -13,6 +13,7 @@ Currently supported: * LLVM 20, 19, 18, 17, 16, 15 and 14 from [apt.llvm.org](http://apt.llvm.org/) on Debian/Ubuntu. * LLVM 20, 19, 18, 17, 16, 15 and 14 from Homebrew on macOS. + * LLVM 19 in an MSYS2 CLANG64 environment on Windows (see the setup below). * Any of the above versions with a manually built LLVM through the `byollvm` build tag. You need to set up `CFLAGS`/`LDFLAGS` etc yourself in this case. You can select the LLVM version using a build tag, for example `-tags=llvm17` @@ -26,6 +27,31 @@ If you have a supported LLVM installation, you should be able to do a simple `go You can use build tags to select a LLVM version. For example, use `-tags=llvm15` to select LLVM 15. Setting a build tag for a LLVM version that is not supported will be ignored. +### Windows (MSYS2 CLANG64) + +The LLVM 19 binding expects `pkg-config` metadata named `llvm-19`. MSYS2 does +not provide that versioned file, so after installing LLVM 19 and `pkgconf` in a +CLANG64 environment, generate it from `llvm-config`: + + pc_dir="$PWD/.llvm-pkgconfig" + mkdir -p "$pc_dir" + llvm_version="$(llvm-config --version)" + test "$llvm_version" = 19.1.7 + cflags="$(llvm-config --cflags | tr '\r\n' ' ')" + ldflags="$(llvm-config --ldflags --libs all --system-libs | tr '\r\n' ' ')" + printf '%s\n' \ + 'Name: LLVM 19' \ + 'Description: LLVM 19 host compiler and linker flags' \ + "Version: $llvm_version" \ + "Cflags: $cflags" \ + "Libs: $ldflags" \ + > "$pc_dir/llvm-19.pc" + export PKG_CONFIG_PATH="$(cygpath -m "$pc_dir")" + export CC=clang CXX=clang++ CGO_ENABLED=1 + +The Windows CI job in [`.github/workflows/go.yml`](.github/workflows/go.yml) +shows the exact pinned MSYS2 packages used by this project. + ## License These LLVM bindings for Go originally come from LLVM, but they have since been [removed](https://discourse.llvm.org/t/rfc-remove-the-go-bindings/65725). Still, they remain under the same license as they were originally, which is the [Apache License 2.0 (with LLVM exceptions)](http://releases.llvm.org/9.0.0/LICENSE.TXT). Check upstream LLVM for detailed copyright information. diff --git a/archive_test.go b/archive_test.go index df8e876..db1d476 100644 --- a/archive_test.go +++ b/archive_test.go @@ -10,6 +10,7 @@ package llvm import ( "bytes" + "fmt" "os" "path/filepath" "strings" @@ -74,7 +75,7 @@ func TestWriteArchiveRejectsInvalidMembers(t *testing.T) { {name: "empty path", members: []ArchiveMember{{}}, want: "archive path is empty"}, {name: "no members", path: path, want: "archive has no members"}, {name: "empty member", path: path, members: []ArchiveMember{{}}, want: "has no file or memory buffer"}, - {name: "missing file", path: path, members: []ArchiveMember{NewArchiveMemberFromFile(missing)}, want: missing}, + {name: "missing file", path: path, members: []ArchiveMember{NewArchiveMemberFromFile(missing)}, want: fmt.Sprintf("%q", missing)}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/llvm_config_llvm19.go b/llvm_config_llvm19.go index fea3f0b..83a0f1a 100644 --- a/llvm_config_llvm19.go +++ b/llvm_config_llvm19.go @@ -15,6 +15,8 @@ package llvm // #cgo linux CPPFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib64/llvm19/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo linux CXXFLAGS: -std=c++17 // #cgo linux LDFLAGS: -L/usr/lib/llvm-19/lib -L/usr/lib64/llvm19/lib -lLLVM-19 +// #cgo windows pkg-config: llvm-19 +// #cgo windows CXXFLAGS: -std=c++17 import "C" type run_build_sh int