diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index c3aad07..cbc734c 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -55,6 +55,133 @@ jobs: if: matrix.llvm == 19 run: go test -v + test-windows: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - llvm: 14 + version: '14.0.6' + sha256: a46a843b0899d284f2a1c76e15aa9a6f3e939925ed008fd4b3b68c5f2f0c8231 + - llvm: 15 + version: '15.0.7' + sha256: 8c55ba91a041507bd927c5f82348c36ef3696485afb71c51bbcc35a014d179de + - llvm: 16 + version: '16.0.6' + sha256: 8082a28d7ff50e5dfdace530935e3e59c37d11f1f8b52f43b7dc3b2fa130be66 + - llvm: 17 + version: '17.0.6' + sha256: 773f122cd79a54ecf3cbf1b1aff47543e95b12b4f91278b29c70cade37e3018c + - llvm: 18 + version: '18.1.8' + sha256: 7cb37bafd78874e707d0c323f8e16e81503bc76867638ef1c634ee0451fac0be + - llvm: 19 + version: '19.1.7' + sha256: d39689f23e1143a710a9120741ac3a1621fc4e2a81a143959bc870c60f9b85d1 + - llvm: 20 + version: '20.1.8' + sha256: 0944367aff9c3619f98dd0db78a1489018af9a8501b2533d17cc143a9624d853 + - llvm: 21 + version: '21.1.4' + sha256: f18f364b3aea5e4d09a4ed58d1c34b7220883912662e39935af96fabd603a912 + - llvm: 22 + version: '22.1.0' + sha256: 00199342f0cedfae60421d5d4ce0f1e217a06d7617611c8541372c47269c7084 + 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@v2 + with: + msystem: MINGW64 + path-type: inherit + update: true + install: >- + mingw-w64-x86_64-gcc + mingw-w64-x86_64-pkgconf + - name: Install LLVM ${{ matrix.llvm }} + shell: pwsh + run: | + $archiveName = 'llvm-${{ matrix.version }}-windows-gnu.zip' + $archive = Join-Path $env:RUNNER_TEMP $archiveName + $url = "https://github.com/yasuo-ozu/llvm-full/releases/download/v${{ matrix.version }}/$archiveName" + curl.exe --fail --location --retry 3 --output $archive $url + if ($LASTEXITCODE -ne 0) { + throw "failed to download $url" + } + + $actualSha256 = (Get-FileHash -Algorithm SHA256 $archive).Hash.ToLowerInvariant() + if ($actualSha256 -ne '${{ matrix.sha256 }}') { + throw "expected SHA-256 ${{ matrix.sha256 }}, got $actualSha256" + } + + $prefix = Join-Path $env:RUNNER_TEMP 'llvm-${{ matrix.version }}' + New-Item -ItemType Directory -Force -Path $prefix | Out-Null + 7z x $archive "-o$prefix" -y + if ($LASTEXITCODE -ne 0) { + throw "failed to extract $archive" + } + Remove-Item $archive + + if (-not (Test-Path (Join-Path $prefix 'bin\llvm-config.exe'))) { + throw "llvm-config.exe is missing from $prefix" + } + "LLVM_PREFIX=$prefix" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + - name: Test LLVM ${{ matrix.llvm }} + shell: msys2 {0} + run: | + set -euo pipefail + + llvm_bin="$(cygpath -u "$LLVM_PREFIX")/bin" + export PATH="$llvm_bin:$PATH" + llvm_version="$(llvm-config --version)" + if [[ "$llvm_version" != '${{ matrix.version }}' ]]; then + echo "expected LLVM ${{ matrix.version }}, got $llvm_version" >&2 + exit 1 + fi + + # Supply the installed LLVM's include and library paths through the + # versioned pkg-config name consumed by the Windows binding. + pc_dir="$(cygpath -u "$RUNNER_TEMP")/llvm-pkgconfig" + mkdir -p "$pc_dir" + cflags="$(llvm-config --cflags | tr '\r\n' ' ')" + # The release archives can embed their build machine's zstd import + # library path, and their static LLVM libraries require winpthread. + ldflags="$(llvm-config --ldflags --libs all --system-libs | tr '\r\n' ' ' | sed -E 's#[^[:space:]]*/mingw64/lib/libzstd\.dll\.a#-lzstd#g') -lwinpthread" + printf '%s\n' \ + 'Name: LLVM ${{ matrix.llvm }}' \ + 'Description: LLVM ${{ matrix.llvm }} host compiler and linker flags' \ + "Version: $llvm_version" \ + "Cflags: $cflags" \ + "Libs: $ldflags" \ + > "$pc_dir/llvm-${{ matrix.llvm }}.pc" + + export CC=gcc + export CXX=g++ + export CGO_ENABLED=1 + PKG_CONFIG_PATH="$(cygpath -m "$pc_dir")" + export PKG_CONFIG_PATH + gcc --version + go test -v -tags=llvm${{ matrix.llvm }} + - name: Test default LLVM + if: matrix.llvm == 19 + shell: msys2 {0} + run: | + set -euo pipefail + llvm_bin="$(cygpath -u "$LLVM_PREFIX")/bin" + export PATH="$llvm_bin:$PATH" + export CC=gcc + export CXX=g++ + 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..3884324 100644 --- a/README.markdown +++ b/README.markdown @@ -11,8 +11,9 @@ This library provides bindings to a system-installed LLVM. 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 22, 21, 20, 19, 18, 17, 16, 15 and 14 from [apt.llvm.org](http://apt.llvm.org/) on Debian/Ubuntu. + * LLVM 22, 21, 20, 19, 18, 17, 16, 15 and 14 from Homebrew on macOS. + * LLVM 22, 21, 20, 19, 18, 17, 16, 15 and 14 in an MSYS2 MINGW64 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,38 @@ 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 MINGW64) + +The Windows bindings expect `pkg-config` metadata named after the selected LLVM +major version, such as `llvm-19`. After installing a MinGW-compatible LLVM, +`mingw-w64-x86_64-gcc`, and `mingw-w64-x86_64-pkgconf` in a MINGW64 +environment, generate that file from `llvm-config`: + + pc_dir="$PWD/.llvm-pkgconfig" + mkdir -p "$pc_dir" + llvm_version="$(llvm-config --version)" + llvm_major="${llvm_version%%.*}" + case "$llvm_major" in + 14|15|16|17|18|19|20|21|22) ;; + *) echo "unsupported LLVM version: $llvm_version" >&2; exit 1 ;; + esac + cflags="$(llvm-config --cflags | tr '\r\n' ' ')" + # Normalize the release build machine's zstd path and link winpthread. + ldflags="$(llvm-config --ldflags --libs all --system-libs | tr '\r\n' ' ' | sed -E 's#[^[:space:]]*/mingw64/lib/libzstd\.dll\.a#-lzstd#g') -lwinpthread" + printf '%s\n' \ + "Name: LLVM $llvm_major" \ + "Description: LLVM $llvm_major host compiler and linker flags" \ + "Version: $llvm_version" \ + "Cflags: $cflags" \ + "Libs: $ldflags" \ + > "$pc_dir/llvm-$llvm_major.pc" + export PKG_CONFIG_PATH="$(cygpath -m "$pc_dir")" + export CC=gcc CXX=g++ CGO_ENABLED=1 + go test -tags="llvm$llvm_major" + +The Windows CI job in [`.github/workflows/go.yml`](.github/workflows/go.yml) +shows the exact LLVM versions and SHA-256 checksums 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_llvm14.go b/llvm_config_llvm14.go index 9d38a5b..0178894 100644 --- a/llvm_config_llvm14.go +++ b/llvm_config_llvm14.go @@ -11,6 +11,8 @@ package llvm // #cgo linux CPPFLAGS: -I/usr/lib/llvm-14/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo linux CXXFLAGS: -std=c++14 // #cgo linux LDFLAGS: -L/usr/lib/llvm-14/lib -lLLVM-14 +// #cgo windows pkg-config: llvm-14 +// #cgo windows CXXFLAGS: -std=c++14 import "C" type run_build_sh int diff --git a/llvm_config_llvm15.go b/llvm_config_llvm15.go index 34aebc0..a049045 100644 --- a/llvm_config_llvm15.go +++ b/llvm_config_llvm15.go @@ -11,6 +11,8 @@ package llvm // #cgo linux CPPFLAGS: -I/usr/lib/llvm-15/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo linux CXXFLAGS: -std=c++14 // #cgo linux LDFLAGS: -L/usr/lib/llvm-15/lib -lLLVM-15 +// #cgo windows pkg-config: llvm-15 +// #cgo windows CXXFLAGS: -std=c++14 import "C" type run_build_sh int diff --git a/llvm_config_llvm16.go b/llvm_config_llvm16.go index 936ca0f..35cc410 100644 --- a/llvm_config_llvm16.go +++ b/llvm_config_llvm16.go @@ -11,6 +11,8 @@ package llvm // #cgo linux CPPFLAGS: -I/usr/lib/llvm-16/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-16/lib -lLLVM-16 +// #cgo windows pkg-config: llvm-16 +// #cgo windows CXXFLAGS: -std=c++17 import "C" type run_build_sh int diff --git a/llvm_config_llvm17.go b/llvm_config_llvm17.go index 6a00b39..815b687 100644 --- a/llvm_config_llvm17.go +++ b/llvm_config_llvm17.go @@ -11,6 +11,8 @@ package llvm // #cgo linux CPPFLAGS: -I/usr/include/llvm-17 -I/usr/include/llvm-c-17 -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-17/lib -lLLVM-17 +// #cgo windows pkg-config: llvm-17 +// #cgo windows CXXFLAGS: -std=c++17 import "C" type run_build_sh int diff --git a/llvm_config_llvm18.go b/llvm_config_llvm18.go index c41fea0..27dd760 100644 --- a/llvm_config_llvm18.go +++ b/llvm_config_llvm18.go @@ -11,6 +11,8 @@ package llvm // #cgo linux CPPFLAGS: -I/usr/include/llvm-18 -I/usr/include/llvm-c-18 -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-18/lib -lLLVM-18 +// #cgo windows pkg-config: llvm-18 +// #cgo windows CXXFLAGS: -std=c++17 import "C" type run_build_sh int 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 diff --git a/llvm_config_llvm20.go b/llvm_config_llvm20.go index d67281a..add4a07 100644 --- a/llvm_config_llvm20.go +++ b/llvm_config_llvm20.go @@ -15,6 +15,8 @@ package llvm // #cgo linux CPPFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib64/llvm20/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-20/lib -L/usr/lib64/llvm20/lib64 -lLLVM-20 +// #cgo windows pkg-config: llvm-20 +// #cgo windows CXXFLAGS: -std=c++17 import "C" type run_build_sh int diff --git a/llvm_config_llvm21.go b/llvm_config_llvm21.go index 263c2fc..bff0dba 100644 --- a/llvm_config_llvm21.go +++ b/llvm_config_llvm21.go @@ -14,6 +14,8 @@ package llvm // #cgo linux CPPFLAGS: -I/usr/include/llvm-21 -I/usr/include/llvm-c-21 -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-21/lib -lLLVM-21 +// #cgo windows pkg-config: llvm-21 +// #cgo windows CXXFLAGS: -std=c++17 import "C" type run_build_sh int diff --git a/llvm_config_llvm22.go b/llvm_config_llvm22.go index 103e930..3ae122f 100644 --- a/llvm_config_llvm22.go +++ b/llvm_config_llvm22.go @@ -14,6 +14,8 @@ package llvm // #cgo linux CPPFLAGS: -I/usr/include/llvm-22 -I/usr/include/llvm-c-22 -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-22/lib -lLLVM-22 +// #cgo windows pkg-config: llvm-22 +// #cgo windows CXXFLAGS: -std=c++17 import "C" type run_build_sh int diff --git a/llvm_config_windows_llvm20.go b/llvm_config_windows_llvm20.go deleted file mode 100644 index 15d578a..0000000 --- a/llvm_config_windows_llvm20.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build !byollvm && windows && llvm20 - -package llvm - -// #cgo CPPFLAGS: -I/usr/include/llvm -I/usr/include/llvm-c -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -// #cgo CXXFLAGS: -std=c++17 -// #cgo LDFLAGS: -Lllvm-20 -lLLVM-20.dll -import "C" - -type run_build_sh int