Skip to content

Add CLANG_VERSION macro check#57

Merged
edsko merged 1 commit into
mainfrom
tcard/opt-version
Apr 17, 2026
Merged

Add CLANG_VERSION macro check#57
edsko merged 1 commit into
mainfrom
tcard/opt-version

Conversation

@TravisCardwell

Copy link
Copy Markdown
Collaborator

From the API documentation:

Check the compile-time Clang version against the Clang version specified in the CLANG_VERSION macro

Users may optionally specify a Clang version in the CLANG_VERSION macro. When set, this function is used to check that the compile-time Clang version matches, failing if not. When not set, no check is performed.

Only the version number is checked. The following checks are supported:

  • MAJOR to just check the major version (example: 21)
  • MAJOR.MINOR to check the major and minor versions (example: 21.1)
  • MAJOR.MINOR.PATCH to check the major, minor, and patch versions (example: 21.1.8)

The primary motivation for this is to distinguish the Clang version in cached builds in the Cabal store. Cabal does not consider system dependencies to determine when a package needs to be rebuilt. This is particularly problematic for libclang-bindings because the LLVM/Clang project has frequent releases, and it is easy to run into a situation where a library cached in the Cabal store no longer works. This can be solved by clearing the Cabal store, but doing so may result in time-consuming recompilation of many other packages. The CLANG_VERSION macro provides a workaround: changing GHC options forces Cabal to rebuild the library.

The CLANG_VERSION macro is generally set in a cabal.project.local file, by configuring -optc in ghc-options. Note that cc-options is not sufficient to force Cabal to rebuild the library. Example:

package libclang-bindings
  ghc-options: -optc=-DCLANG_VERSION=21.1

Note that this will also be documented in the hs-bindgen manual.

Implementation notes:

  • We are unable to stringify macros using the GHC preprocessor. As a workaround, we stringify the macro in C and implement a function that returns the string. Internal function getUserClangVersion gets and parses the string.
  • Function checkUserClangVersion (which has the above documentation) is a TH function that performs the check and fails if a user-specified version does not match the version that is being linked against, as determined using the configure script.
  • Internal module Clang.Version.Check just calls checkUserClangVersion. It has to be separate because of the stage restriction, and it has no exports.

This workaround is built using ugly hacks, but at least it provides a way for users to work around the Cabal store issue (well-typed/hs-bindgen#1244) without having to clear their Cabal store.

@TravisCardwell
TravisCardwell requested a review from edsko April 17, 2026 00:56
@TravisCardwell TravisCardwell self-assigned this Apr 17, 2026
@TravisCardwell
TravisCardwell removed the request for review from edsko April 17, 2026 01:09
@TravisCardwell

Copy link
Copy Markdown
Collaborator Author

This is failing to build the Clang.Version.Check module on Windows with errors like the following:

[22 of 35] Compiling Clang.Version.Check ( src\Clang\Version\Check.hs, dist-newstyle\build\x86_64-windows\ghc-9.14.1\libclang-bindings-0.1.0\build\Clang\Version\Check.o )
ghc-9.14.1.exe:  | D:\a\libclang\libclang\dist-newstyle\build\x86_64-windows\ghc-9.14.1\libclang-bindings-0.1.0\build\Clang\LowLevel\FFI.o: unknown symbol `__imp_clang_EvalResult_dispose'
ghc-9.14.1.exe:  | D:\a\libclang\libclang\dist-newstyle\build\x86_64-windows\ghc-9.14.1\libclang-bindings-0.1.0\build\Clang\Internal\CXString.o: unknown symbol `ghczuwrapperZC117ZClibclangzmbindingszm0zi1zi0zminplaceZCClangziLowLevelziFFIZCwrapzudisposeString'
ghc-9.14.1.exe: Could not load Object Code D:\a\libclang\libclang\dist-newstyle\build\x86_64-windows\ghc-9.14.1\libclang-bindings-0.1.0\build\Clang\Internal\CXString.o.

<no location info>: error:

Perhaps this is happening because I implemented checkUserClangVersion in the Clang.Version module, which also imports the low-level FFI bindings. I initially implemented it in a separate module, actually, but I moved it into Clang.Version after checking that doing so works just to minimize the number of (internal) modules added. I will try putting it back into a separate module and see if that resolves the Windows issue. 🤞

@TravisCardwell
TravisCardwell marked this pull request as draft April 17, 2026 01:09
@TravisCardwell

Copy link
Copy Markdown
Collaborator Author

I tried putting the new code into a Clang.Version.Internal module, and calling checkUserClangVersion in Clang.Version.Internal.Check. Note that this requires defining ClangVersion and parseClangVersion in the internal module and re-exporting it from Clang.Version. The internal module does not have any dependencies on the libclang FFI, and indeed this works (even on Windows).

@TravisCardwell
TravisCardwell marked this pull request as ready for review April 17, 2026 01:29
@TravisCardwell
TravisCardwell requested a review from edsko April 17, 2026 01:29
@edsko
edsko added this pull request to the merge queue Apr 17, 2026
Merged via the queue into main with commit cdbf817 Apr 17, 2026
10 checks passed
@edsko
edsko deleted the tcard/opt-version branch April 17, 2026 07:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants