From a14ab58cc80baba2aa8cc07f3b0f528adc576f54 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Tue, 25 Nov 2025 11:36:03 +0100 Subject: [PATCH 01/12] Reorganize manual files --- manual/DesignGoals.md | 3 +- .../01-GeneratedNames.md} | 0 .../{Structs.md => Translation/02-Structs.md} | 0 .../{Enums.md => Translation/03-Enums.md} | 0 .../{Unions.md => Translation/04-Unions.md} | 0 .../05-Functions.md} | 0 .../{Globals.md => Translation/06-Globals.md} | 0 .../{Macros.md => Translation/07-Macros.md} | 0 ...ibility.md => Uncategorized-Visibility.md} | 0 ...{ZeroCopy.md => Uncategorized-ZeroCopy.md} | 0 .../{Invocation.md => Usage/01-Invocation.md} | 0 .../{Tracing.md => Usage/02-Tracing.md} | 0 .../03-ClangOptions.md} | 0 .../{Includes.md => Usage/04-Includes.md} | 0 .../05-ParsingSelectingAndProgramSlicing.md} | 0 .../06-BindingSpecifications.md} | 0 .../07-TestGeneration.md} | 0 .../08-CrossCompilation.md} | 0 manual/README.md | 34 +++++++++---------- 19 files changed, 18 insertions(+), 19 deletions(-) rename manual/LowLevel/{GeneratedNames.md => Translation/01-GeneratedNames.md} (100%) rename manual/LowLevel/{Structs.md => Translation/02-Structs.md} (100%) rename manual/LowLevel/{Enums.md => Translation/03-Enums.md} (100%) rename manual/LowLevel/{Unions.md => Translation/04-Unions.md} (100%) rename manual/LowLevel/{Functions.md => Translation/05-Functions.md} (100%) rename manual/LowLevel/{Globals.md => Translation/06-Globals.md} (100%) rename manual/LowLevel/{Macros.md => Translation/07-Macros.md} (100%) rename manual/LowLevel/{Visibility.md => Uncategorized-Visibility.md} (100%) rename manual/LowLevel/{ZeroCopy.md => Uncategorized-ZeroCopy.md} (100%) rename manual/LowLevel/{Invocation.md => Usage/01-Invocation.md} (100%) rename manual/LowLevel/{Tracing.md => Usage/02-Tracing.md} (100%) rename manual/LowLevel/{ClangOptions.md => Usage/03-ClangOptions.md} (100%) rename manual/LowLevel/{Includes.md => Usage/04-Includes.md} (100%) rename manual/LowLevel/{ParsingSelectingAndProgramSlicing.md => Usage/05-ParsingSelectingAndProgramSlicing.md} (100%) rename manual/LowLevel/{BindingSpecifications.md => Usage/06-BindingSpecifications.md} (100%) rename manual/LowLevel/{TestGeneration.md => Usage/07-TestGeneration.md} (100%) rename manual/LowLevel/{CrossCompilation.md => Usage/08-CrossCompilation.md} (100%) diff --git a/manual/DesignGoals.md b/manual/DesignGoals.md index 74a4d6fc1..33a2889b9 100644 --- a/manual/DesignGoals.md +++ b/manual/DesignGoals.md @@ -1,4 +1,3 @@ # Design goals -Discuss https://github.com/well-typed/hs-bindgen/issues/168 - +Discuss . diff --git a/manual/LowLevel/GeneratedNames.md b/manual/LowLevel/Translation/01-GeneratedNames.md similarity index 100% rename from manual/LowLevel/GeneratedNames.md rename to manual/LowLevel/Translation/01-GeneratedNames.md diff --git a/manual/LowLevel/Structs.md b/manual/LowLevel/Translation/02-Structs.md similarity index 100% rename from manual/LowLevel/Structs.md rename to manual/LowLevel/Translation/02-Structs.md diff --git a/manual/LowLevel/Enums.md b/manual/LowLevel/Translation/03-Enums.md similarity index 100% rename from manual/LowLevel/Enums.md rename to manual/LowLevel/Translation/03-Enums.md diff --git a/manual/LowLevel/Unions.md b/manual/LowLevel/Translation/04-Unions.md similarity index 100% rename from manual/LowLevel/Unions.md rename to manual/LowLevel/Translation/04-Unions.md diff --git a/manual/LowLevel/Functions.md b/manual/LowLevel/Translation/05-Functions.md similarity index 100% rename from manual/LowLevel/Functions.md rename to manual/LowLevel/Translation/05-Functions.md diff --git a/manual/LowLevel/Globals.md b/manual/LowLevel/Translation/06-Globals.md similarity index 100% rename from manual/LowLevel/Globals.md rename to manual/LowLevel/Translation/06-Globals.md diff --git a/manual/LowLevel/Macros.md b/manual/LowLevel/Translation/07-Macros.md similarity index 100% rename from manual/LowLevel/Macros.md rename to manual/LowLevel/Translation/07-Macros.md diff --git a/manual/LowLevel/Visibility.md b/manual/LowLevel/Uncategorized-Visibility.md similarity index 100% rename from manual/LowLevel/Visibility.md rename to manual/LowLevel/Uncategorized-Visibility.md diff --git a/manual/LowLevel/ZeroCopy.md b/manual/LowLevel/Uncategorized-ZeroCopy.md similarity index 100% rename from manual/LowLevel/ZeroCopy.md rename to manual/LowLevel/Uncategorized-ZeroCopy.md diff --git a/manual/LowLevel/Invocation.md b/manual/LowLevel/Usage/01-Invocation.md similarity index 100% rename from manual/LowLevel/Invocation.md rename to manual/LowLevel/Usage/01-Invocation.md diff --git a/manual/LowLevel/Tracing.md b/manual/LowLevel/Usage/02-Tracing.md similarity index 100% rename from manual/LowLevel/Tracing.md rename to manual/LowLevel/Usage/02-Tracing.md diff --git a/manual/LowLevel/ClangOptions.md b/manual/LowLevel/Usage/03-ClangOptions.md similarity index 100% rename from manual/LowLevel/ClangOptions.md rename to manual/LowLevel/Usage/03-ClangOptions.md diff --git a/manual/LowLevel/Includes.md b/manual/LowLevel/Usage/04-Includes.md similarity index 100% rename from manual/LowLevel/Includes.md rename to manual/LowLevel/Usage/04-Includes.md diff --git a/manual/LowLevel/ParsingSelectingAndProgramSlicing.md b/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md similarity index 100% rename from manual/LowLevel/ParsingSelectingAndProgramSlicing.md rename to manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md diff --git a/manual/LowLevel/BindingSpecifications.md b/manual/LowLevel/Usage/06-BindingSpecifications.md similarity index 100% rename from manual/LowLevel/BindingSpecifications.md rename to manual/LowLevel/Usage/06-BindingSpecifications.md diff --git a/manual/LowLevel/TestGeneration.md b/manual/LowLevel/Usage/07-TestGeneration.md similarity index 100% rename from manual/LowLevel/TestGeneration.md rename to manual/LowLevel/Usage/07-TestGeneration.md diff --git a/manual/LowLevel/CrossCompilation.md b/manual/LowLevel/Usage/08-CrossCompilation.md similarity index 100% rename from manual/LowLevel/CrossCompilation.md rename to manual/LowLevel/Usage/08-CrossCompilation.md diff --git a/manual/README.md b/manual/README.md index d5a57a00b..60f65ff53 100644 --- a/manual/README.md +++ b/manual/README.md @@ -9,30 +9,30 @@ ### Usage -* [Invocation](LowLevel/Invocation.md) -* [Tracing](LowLevel/Tracing.md) -* [Clang options](LowLevel/ClangOptions.md) -* [Includes](LowLevel/Includes.md) -* [Parsing, selecing and program slicing](LowLevel/ParsingSelectingAndProgramSlicing.md) -* [Binding specifications](LowLevel/BindingSpecifications.md) -* [Test generation](LowLevel/TestGeneration.md) -* [CrossCompilation](LowLevel/CrossCompilation.md) +* [Invocation](LowLevel/Usage/01-Invocation.md) +* [Tracing](LowLevel/Usage/02-Tracing.md) +* [Clang options](LowLevel/Usage/03-ClangOptions.md) +* [Includes](LowLevel/Usage/04-Includes.md) +* [Parsing, selecing and program slicing](LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md) +* [Binding specifications](LowLevel/Usage/06-BindingSpecifications.md) +* [Test generation](LowLevel/Usage/07-TestGeneration.md) +* [CrossCompilation](LowLevel/Usage/08-CrossCompilation.md) ### Translation -* [Generated names](LowLevel/GeneratedNames.md) -* [Structs](LowLevel/Structs.md) -* [Enums](LowLevel/Enums.md) -* [Unions](LowLevel/Unions.md) -* [Functions](LowLevel/Functions.md) -* [Global variables and constants](LowLevel/Globals.md) -* [Macros](LowLevel/Macros.md) +* [Generated names](LowLevel/Translation/01-GeneratedNames.md) +* [Structs](LowLevel/Translation/02-Structs.md) +* [Enums](LowLevel/Translation/03-Enums.md) +* [Unions](LowLevel/Translation/04-Unions.md) +* [Functions](LowLevel/Translation/05-Functions.md) +* [Global variables and constants](LowLevel/Translation/06-Globals.md) +* [Macros](LowLevel/Translation/07-Macros.md) ## Handwriting high-level bindings This section of the manual is not yet written. It should include at least a -chapter with examples on how to write high-level bindings, and chapter on memory -management. +chapter with examples on how to write high-level bindings, and a chapter on +memory management. In addition, we will have combinators that will make writing the high-level bindings easier. This is currently under active development. From fc013428a264670888f6b83912407a47d3246536 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Tue, 25 Nov 2025 11:39:40 +0100 Subject: [PATCH 02/12] Run `markdownlint-cli -f` --- README.md | 1 - alternatives/README.md | 22 +++++----- dev/binding-spec.md | 6 +-- dev/dev-environment.md | 7 ++++ dev/troubleshooting.md | 3 ++ examples/README.md | 17 +++++--- examples/botan/README.md | 2 + examples/c-minisat/README.md | 1 + examples/c-qrcode/README.md | 1 + examples/libpcap/README.md | 1 + hs-bindgen/clang-ast-dump/README.md | 6 +-- manual/LowLevel/Introduction.md | 4 +- .../LowLevel/Translation/01-GeneratedNames.md | 11 +++-- manual/LowLevel/Translation/02-Structs.md | 2 + manual/LowLevel/Translation/03-Enums.md | 4 +- manual/LowLevel/Translation/04-Unions.md | 7 ++-- manual/LowLevel/Translation/05-Functions.md | 2 +- manual/LowLevel/Translation/06-Globals.md | 40 ++++++++++++------ manual/LowLevel/Uncategorized-Visibility.md | 2 + manual/LowLevel/Uncategorized-ZeroCopy.md | 2 +- manual/LowLevel/Usage/01-Invocation.md | 42 +++++++++++-------- manual/LowLevel/Usage/03-ClangOptions.md | 12 +++--- manual/LowLevel/Usage/04-Includes.md | 28 ++++++------- .../05-ParsingSelectingAndProgramSlicing.md | 2 + 24 files changed, 134 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 54fe19c67..df581984e 100644 --- a/README.md +++ b/README.md @@ -47,4 +47,3 @@ that can be extended by users for specific applications. You can find the (currently still very incomplete) manual in [manual/](manual/README.md). - diff --git a/alternatives/README.md b/alternatives/README.md index ef78f64e0..fc8d73905 100644 --- a/alternatives/README.md +++ b/alternatives/README.md @@ -81,7 +81,7 @@ It also supports writing some inline-C using `#def`, as some form of poor-man's You get no support for declaring function signatures, this piggy-backs on the regular FFI (CAPI or otherwise). -See https://ghc.gitlab.haskell.org/ghc/doc/users_guide/utils.html#writing-haskell-interfaces-to-c-code-hsc2hs for more information. +See for more information. ## `c2hs` @@ -116,7 +116,7 @@ cShowStruct $ HaskellStruct 1234 5678 ``` However, the syntax for these is rather arcane, and not all common patterns can -be expressed (see https://well-typed.com/blog/2023/03/purgatory/ for some +be expressed (see for some examples). The `Storable` story here is similar to the one in `hsc2hs`, albeit with @@ -124,7 +124,7 @@ slightly different syntax. Unlike `hsc2hs`, however, `c2hs` does not use the C compiler, but does its own calculations; these have been known to be unreliable at times, especially with cross compilation. -See https://github.com/haskell/c2hs/wiki/User-Guide for more information. +See for more information. ## `inline-c` @@ -200,33 +200,33 @@ Haskell files, but from richer API descriptions. In this section we mention a few of these; perhaps we can take some inspiration from these API descriptions in the kind of customization options that `hs-bindgen` should offer. -### https://github.com/gtk2hs/gtk2hs/tree/master/tools/apiGen +### > It works by extracting an api description from the C source files and > extracting documentation from the docbook documentation produced by gtk-doc. -### https://github.com/haskell-gi/haskell-gi +### > Generate Haskell bindings for GObject Introspection capable libraries. This is essential next version of `apiGen`. -### https://github.com/brendanhay/amazonka +### > The gen package contain a code generator for synthesising Haskell data types, > packages, and configuration from the botocore service definitions. -where https://github.com/boto/botocore is +where is > A low-level interface to a growing number of Amazon Web Services. The botocore > package is the foundation for the AWS CLI as well as boto3. Botocore is > maintained and published by Amazon Web Services. -### https://hackage.haskell.org/package/godot-haskell +### Generates bindings from the Godot documentation, not from general header files. -### https://github.com/ekmett/gl +### > The `gl` package supplies low level bindings to all of the OpenGL > specification for Haskell. This package, `glgen`, is used to build the `gl` @@ -235,7 +235,7 @@ Generates bindings from the Godot documentation, not from general header files. Generates bindings from the [Khronos XML specification of OpenGL](https://registry.khronos.org/OpenGL/). -### https://hackage.haskell.org/package/vulkan +### Generates bindings from the Vulkan docs. @@ -243,7 +243,7 @@ The `README` [goes into some detail](https://github.com/expipiplus1/vulkan?tab=readme-ov-file#how-the-c-types-relate-to-haskell-types) on the chosen relation between C types and Haskell types. -### https://hackage.haskell.org/package/vulkan-api +### Generate bindings from the Vulkan XML spec. Makes some different choices for the mappings; see discussion in diff --git a/dev/binding-spec.md b/dev/binding-spec.md index 4ac05da30..693fb0452 100644 --- a/dev/binding-spec.md +++ b/dev/binding-spec.md @@ -23,9 +23,9 @@ To serve as an example, consider a point in time when we have the following modules: - * Module `V1` has version `1.8`. - * Module `V2` has version `2.3`. - * Module `V3` has version `3.1`. + * Module `V1` has version `1.8`. + * Module `V2` has version `2.3`. + * Module `V3` has version `3.1`. * We support two types of compatibility. diff --git a/dev/dev-environment.md b/dev/dev-environment.md index da5996c87..446a8d43c 100644 --- a/dev/dev-environment.md +++ b/dev/dev-environment.md @@ -6,6 +6,7 @@ Linux, NixOS, and macOS. ## General Prerequisites All platforms require (last updated October 9, 2025): + - GHC 9.4.8 or greater (or compatible version) - Cabal (latest version) - LLVM/Clang (version 14 - 21) @@ -55,6 +56,7 @@ macOS setup is similar to Linux but with platform-specific considerations. ### Prerequisites Install LLVM and Clang: + ```bash brew install llvm@16 ``` @@ -62,6 +64,7 @@ brew install llvm@16 ### Environment Setup 1. **Set LLVM paths**: + ```bash export LLVM_PATH=/opt/homebrew/opt/llvm@16 # Adjust for your installation export LLVM_CONFIG=$LLVM_PATH/bin/llvm-config @@ -69,11 +72,13 @@ brew install llvm@16 ``` 2. **SDK configuration**: + ```bash export SDKROOT=$(xcrun --show-sdk-path --sdk macosx) ``` 3. **Library paths**: + ```bash export DYLD_LIBRARY_PATH=/path/to/your/c/libs:$DYLD_LIBRARY_PATH ``` @@ -88,6 +93,7 @@ and Clang, so no separate LLVM installation is needed. ### Environment Setup 1. Set LLVM paths (PowerShell): + ```powershell $env:LLVM_PATH = "C:\ghcup\ghc\9.12.2\mingw" $env:LLVM_CONFIG = "$env:LLVM_PATH\bin\llvm-config.exe" @@ -97,6 +103,7 @@ and Clang, so no separate LLVM installation is needed. 2. Library paths: Windows uses `PATH` for finding DLLs: + ```powershell $env:PATH = "C:\path\to\your\c\libs;" + $env:PATH ``` diff --git a/dev/troubleshooting.md b/dev/troubleshooting.md index 8f6cca2f4..60b6906be 100644 --- a/dev/troubleshooting.md +++ b/dev/troubleshooting.md @@ -20,6 +20,7 @@ Missing dependency on a foreign library: Solution: 1. Build the C libraries first: + ```bash cd manual/c make @@ -27,6 +28,7 @@ Solution: ``` 2. Create a `cabal.project.local` file with proper paths: + ``` package hs-game extra-include-dirs: /path/to/hs-bindgen/manual/c @@ -115,6 +117,7 @@ Solution: - Linux: Usually works - macOS/Windows/LLVM: Avoid Unicode characters in C code - Use CPP macros to conditionally handle Unicode: + ```c #if defined(SUPPORTS_UNICODE) #else diff --git a/examples/README.md b/examples/README.md index 218b8ae4a..5c44cc6cb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -60,22 +60,25 @@ as the following requirements are met: * The package should have its own project file at `REPOSITORY_ROOT/examples/libfoo/hs-project/cabal.project`, which should contain at least: + ``` import: ../../../cabal.project.base packages: . ../../../c-expr-runtime ../../../hs-bindgen-runtime ``` + If other `hs-bindgen` packages are required, add them to `packages`. -* Add a script at `REPOSITORY_ROOT/examples/libfoo/generate-and-run.sh` - * The script should install the `libfoo` C package (locally) - * The script should run `hs-bindgen-cli` on `libfoo`'s header files and put +* Add a script at `REPOSITORY_ROOT/examples/libfoo/generate-and-run.sh` +* The script should install the `libfoo` C package (locally) +* The script should run `hs-bindgen-cli` on `libfoo`'s header files and put the generated modules into the Haskell project - * The script should make sure that the Haskell package can find the installed +* The script should make sure that the Haskell package can find the installed `libfoo` package. For locally installed packages, this probably means setting `LD_LIBRARY_PATH` and updating the `REPOSITORY_ROOT/examples/libfoo/hs-project/cabal.project.local` file so that it includes: + ``` package libfoo extra-include-dirs: @@ -83,10 +86,11 @@ as the following requirements are met: extra-lib-dirs: -- insert absolute path to installation directory for dll files here ``` + If a `cabal.project.local` file already exists, then the file should be updated to include the lines above. Otherwise, it should create the file with the lines above. - * The script should run the Haskell executable +* The script should run the Haskell executable * Add a composite action by creating a new file at `REPOSITORY_ROOT/.github/actions/examples/libfoo.action.yml` * The composite action should install example-specific prerequisites, such as @@ -96,6 +100,7 @@ as the following requirements are met: * Update the workflow file at `REPOSITORY_ROOT/.github/workflows/examples.yml` * Add `'libfoo'` to the `example` array of the workflow matrix * Towards the end of the file, add a step that calls the `libfoo` composite action: + ```yml - name: ๐Ÿงช Build and run libfoo example if: ${{ matrix.example == 'libfoo' }} @@ -103,4 +108,4 @@ as the following requirements are met: ``` Now create a PR with these changs and (hopefully) you will observe that a new -job is run that tests the new example project. \ No newline at end of file +job is run that tests the new example project. diff --git a/examples/botan/README.md b/examples/botan/README.md index 8401bdb44..08378346e 100644 --- a/examples/botan/README.md +++ b/examples/botan/README.md @@ -12,6 +12,7 @@ nix-shell -p ccache ``` Or, for example, on Ubuntu: + ```bash apt-get install ccache ``` @@ -25,6 +26,7 @@ TODO: on Windows, use sccache instead ``` This script will: + 1. Build the botan-3 C++ library 2. Generate Haskell bindings using `hs-bindgen` 3. Create `cabal.project.local` with the necessary configuration diff --git a/examples/c-minisat/README.md b/examples/c-minisat/README.md index a1833bd60..ff8be2bc4 100644 --- a/examples/c-minisat/README.md +++ b/examples/c-minisat/README.md @@ -19,6 +19,7 @@ nix-shell -p minisat ``` This script will: + 1. Build the minisat-c-bindings C library 2. Create symbolic links for the shared library (see quirks below) 3. Generate Haskell bindings using `hs-bindgen` diff --git a/examples/c-qrcode/README.md b/examples/c-qrcode/README.md index 5525c5d50..350e901da 100644 --- a/examples/c-qrcode/README.md +++ b/examples/c-qrcode/README.md @@ -11,6 +11,7 @@ for generating QR codes. ``` This script will: + 1. Build the QR Code generator C library 2. Generate Haskell bindings using `hs-bindgen` 3. Create `cabal.project.local` with the necessary configuration diff --git a/examples/libpcap/README.md b/examples/libpcap/README.md index 8af6e6bea..f21ec0b77 100644 --- a/examples/libpcap/README.md +++ b/examples/libpcap/README.md @@ -28,6 +28,7 @@ nix-shell -p cmake flex bison ``` This script will: + 1. Build `libpcap` 2. Create symbolic links for the shared library 3. Generate Haskell bindings using `hs-bindgen` diff --git a/hs-bindgen/clang-ast-dump/README.md b/hs-bindgen/clang-ast-dump/README.md index 994808431..47522fdf5 100644 --- a/hs-bindgen/clang-ast-dump/README.md +++ b/hs-bindgen/clang-ast-dump/README.md @@ -11,13 +11,13 @@ All of the following commands are to be run in the project root. Display options: ``` -$ cabal run clang-ast-dump -- --help +cabal run clang-ast-dump -- --help ``` Run: ``` -$ cabal run clang-ast-dump -- hs-bindgen/examples/comments.h +cabal run clang-ast-dump -- hs-bindgen/examples/comments.h ``` ## Alternatives @@ -25,5 +25,5 @@ $ cabal run clang-ast-dump -- hs-bindgen/examples/comments.h Official: ``` -$ clang -Xclang -ast-dump foo.c +clang -Xclang -ast-dump foo.c ``` diff --git a/manual/LowLevel/Introduction.md b/manual/LowLevel/Introduction.md index 22e15ce16..d4a66a9d3 100644 --- a/manual/LowLevel/Introduction.md +++ b/manual/LowLevel/Introduction.md @@ -104,7 +104,7 @@ declared [complete][ghc-manual:complete]. > [!NOTE] > The `CENum` class will provide an alternative interface to `Enum` and > `Bounded`. -> https://github.com/well-typed/hs-bindgen/pull/552 +> > [!NOTE] > Generating a Haskell ADT to correspond to this enum will be the responsibility @@ -253,8 +253,6 @@ foreign import capi safe "some_header.h getYear" getYear :: Ptr Date -> IO YEAR ``` - - [^1]: Slightly edited for layout. [ghc-manual:complete]: https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/pragmas.html#complete-pragmas diff --git a/manual/LowLevel/Translation/01-GeneratedNames.md b/manual/LowLevel/Translation/01-GeneratedNames.md index aa0f6c428..1a6edd7b0 100644 --- a/manual/LowLevel/Translation/01-GeneratedNames.md +++ b/manual/LowLevel/Translation/01-GeneratedNames.md @@ -59,7 +59,7 @@ data Triple = Triple { > [!NOTE] > Alternatively we could take advantage of `DuplicateRecordFields` or > `OverloadedRecordDot`/`OverloadedRecordUpdate`. -> https://github.com/well-typed/hs-bindgen/issues/69 +> ### Accessors @@ -105,7 +105,7 @@ set_occupation_employee :: Employee -> Occupation > [!NOTE] > Here too we could in principle take advantage of overloaded record syntax. -> https://github.com/well-typed/hs-bindgen/issues/557 +> ### Anonymous types @@ -154,7 +154,7 @@ data Rect = Rect { > [!NOTE] > With external bindings it might be possible to avoid this duplication, and use > a _single_ struct definition with two fields `x` and `y`. -> https://github.com/well-typed/hs-bindgen/issues/536 +> #### Pointer `typedef`s @@ -211,14 +211,14 @@ which `isAlphaNum` returns `False`. After escaping, this name becomes > For this _particular_ example we could generate a better name if we > transformed it to [NFC][unicode:NFC] first. Indeed, `gcc` (but not `clang`) > will issue a warning that this name is not in NFC. -> https://github.com/well-typed/hs-bindgen/issues/560 +> > [!NOTE] > C _functions_ with names that contain characters that are invalid in Haskell > identifiers can currently not be imported, because `ghc` applies Haskell > naming rules to C identifiers. This is unlikely to be an issue, but if it is, > we'd need to either patch ghc or generate a C wrapper. -> https://github.com/well-typed/hs-bindgen/issues/569 +> ### Capitalization @@ -307,6 +307,5 @@ newtype Data = Data { } ``` - [hackage:base:isAlphaNum]: https://hackage.haskell.org/package/base/docs/Data-Char.html#v:isAlphaNum [unicode:NFC]: https://unicode.org/reports/tr15/ diff --git a/manual/LowLevel/Translation/02-Structs.md b/manual/LowLevel/Translation/02-Structs.md index 00d1976ce..e90861a0c 100644 --- a/manual/LowLevel/Translation/02-Structs.md +++ b/manual/LowLevel/Translation/02-Structs.md @@ -196,6 +196,7 @@ are used, provides some helper functions. For example, peekBitOffWidth :: Bitfield a => Ptr b -> Int -> Int -> IO a peekBitOffWidth pointer offset width = ... ``` + obtains the bitfield member of type `a` at the destination of the provided `pointer` with `offset`, and `width`. @@ -268,6 +269,7 @@ data WithFlexibleArrayMember element struct = WithFlexibleArrayMember ``` For example, + ```haskell bracket (withCString "Rich" $ \cstr -> surname_init cstr) surname_free $ \ptr -> do diff --git a/manual/LowLevel/Translation/03-Enums.md b/manual/LowLevel/Translation/03-Enums.md index 0a9bcb082..58adae72c 100644 --- a/manual/LowLevel/Translation/03-Enums.md +++ b/manual/LowLevel/Translation/03-Enums.md @@ -262,7 +262,7 @@ showCEnum :: forall a. CEnum a => a -> String > [!NOTE] > It is not yet possible to prevent `hs-bindgen` from generating instances. -> https://github.com/well-typed/hs-bindgen/issues/307 +> ## Read instance @@ -322,7 +322,7 @@ readEitherCEnum :: forall a. CEnum a => String -> a > [!NOTE] > It is not yet possible to prevent `hs-bindgen` from generating instances. -> https://github.com/well-typed/hs-bindgen/issues/307 +> ## Deriving-via support diff --git a/manual/LowLevel/Translation/04-Unions.md b/manual/LowLevel/Translation/04-Unions.md index 50904468a..b6bef9c4c 100644 --- a/manual/LowLevel/Translation/04-Unions.md +++ b/manual/LowLevel/Translation/04-Unions.md @@ -2,7 +2,7 @@ > [!NOTE] > We are considering changing the approach for dealing with unions. -> https://github.com/well-typed/hs-bindgen/pull/567 +> Suppose we have @@ -98,7 +98,7 @@ It is worth taking a look at this though because it is a common pattern in C. > Recognizing _particular_ union patterns like this where we can tell which > alternative is used, and representing these as proper Haskell ADTs, can be > done in the high-level API generation. -> https://github.com/well-typed/hs-bindgen/issues/18 +> Since we cannot provide a `Show` instance for `Occupation`, we also cannot provide a `Show` instance for `Person`. We _could_ derive a dummy instance for @@ -110,7 +110,6 @@ for `Person` either. > [!NOTE] > We currently _do_ try to derive `Show` for `Person`. -> This is a bug: https://github.com/well-typed/hs-bindgen/issues/558 - +> This is a bug: [hackage:base:ByteArray]: https://hackage.haskell.org/package/base/docs/Data-Array-Byte.html#t:ByteArray diff --git a/manual/LowLevel/Translation/05-Functions.md b/manual/LowLevel/Translation/05-Functions.md index 6ab4231d0..ef70d06fe 100644 --- a/manual/LowLevel/Translation/05-Functions.md +++ b/manual/LowLevel/Translation/05-Functions.md @@ -113,6 +113,7 @@ typedef int int2int(int); extern int apply1_pointer_arg (int2int *, int); extern int apply1_nopointer_arg (int2int, int); ``` + ```hs newtype Int2int = Int2int { un_Int2int :: CInt -> IO CInt } foreign import {-# details elided #-} apply1_pointer_arg @@ -191,7 +192,6 @@ apply1_union :: Apply1Union ``` [creference:fun-decl]: https://en.cppreference.com/w/c/language/function_declaration.html#Explanation -[creference:fun-ptr-conv]: https://en.cppreference.com/w/c/language/conversion.html#Function_to_pointer_conversion ## Userland CAPI diff --git a/manual/LowLevel/Translation/06-Globals.md b/manual/LowLevel/Translation/06-Globals.md index 509f76e73..18ac56673 100644 --- a/manual/LowLevel/Translation/06-Globals.md +++ b/manual/LowLevel/Translation/06-Globals.md @@ -101,7 +101,7 @@ will result in a warning, and not produce any bindings. ## Constants -Global variables can also represenent global *constants* if they use +Global variables can also represenent global _constants_ if they use `const`-qualified types. ```c @@ -156,8 +156,8 @@ The approach to generating foreign imports for global variables is as follows: and returns the pointer. * If the type of the global variable is `const`-qualified, then it is actually a - global *constant*. If there is a `Storable` instance in scope for the variable - type, we generate a *pure* Haskell function that returns the value of the + global _constant_. If there is a `Storable` instance in scope for the variable + type, we generate a _pure_ Haskell function that returns the value of the global constant, using a combination of `unsafePerformIO` and `peek` on the variable pointer. This is safe, since the value of the global constant should not change. @@ -170,16 +170,19 @@ We include examples of generated bindings for a variety of types below. ### Simple value: `int` Global: + ```c int x; ``` Stub: + ```c /* get_x_ptr */ __attribute__ ((const)) int* fe8f4js8(void) { return &x; } ``` Import: + ```hs foreign import ccall unsafe "fe8f4js8" fe8f4js8 :: IO (Ptr CInt) @@ -197,6 +200,7 @@ Memory layout: | int* ; Ptr CInt | x_ptr | 2000 | 1000 | Constant: + ```hs {-# NOINLINE x #-} -- If the type of the global were @const int x@, we would also generate the following @@ -214,16 +218,19 @@ Of course, this is only safe if the user knows that it is pointing to a sequence of `int`s. Global: + ```c int* x; ``` Stub: + ```c /* get_x_ptr */ int** ae8fae8() { return &x; } ``` Import: + ```hs foreign import ccall unsafe "ae8fae8" ae8fae8 :: IO (Ptr (Ptr CInt)) @@ -255,6 +262,7 @@ Memory layout: | int**; Ptr (Ptr CInt) | x_ptr | 3000 | 2000 | Constant: + ```hs {-# NOINLINE x #-} -- If the type of the global were @int * const x@, we would also generate the following. @@ -276,25 +284,28 @@ We have a subtle choice here of what to generate a binding for. The options are: For uniformity, we use the latter option. -Note that the *value* of the pointer is the same regardless of which approach we +Note that the _value_ of the pointer is the same regardless of which approach we pick. A pointer to the first element of the array points to the start of the -array, and a pointer to the array as a whole *also* points to the start of the +array, and a pointer to the array as a whole _also_ points to the start of the array. The difference is only in the type of the pointer. As such, a user of the generated bindings can safely cast the pointer to the whole array to a pointer to the first element of the array. Global: + ```c typedef int triplet[3]; triplet x; ``` Stub: + ```c /* get_x_ptr */ __attribute__ ((const)) triplet *f94u3030(void) { return &x; } ``` Import: + ```hs newtype Triplet = Triplet (ConstantArray 3 CInt) foreign import ccall unsafe "f94u3030" f94u3030 :: IO (Ptr Triplet) @@ -313,15 +324,16 @@ x_elem_ptr = snd $ ConstantArray.toFirstElemPtr x_ptr Memory layout: -| type | name | address | value | -| -------------------------------------- | ------------- | ------- | ------- | -| int[3] | x | 1000 | 1 | -| | | 1004 | 2 | -| | | 1008 | 3 | -| | | ... | | -| (*int)[3] ; Ptr (ConstantArray 3 CInt) | x_ptr | 2000 | 1000 | +| type | name | address | value | +|------------------------------------------|-------|---------|-------| +| `int[3]` | x | 1000 | 1 | +| | | 1004 | 2 | +| | | 1008 | 3 | +| | | ... | | +| `(*int)[3] ; Ptr (ConstantArray 3 CInt)` | x_ptr | 2000 | 1000 | Constant: + ```hs {-# NOINLINE x #-} -- If the type of the global were @const triplet x@, we would also generate the following @@ -336,17 +348,20 @@ difference is only in the types: we use `IncompleteArray` instead of `ConstantArray`. Global: + ```c typedef int list[]; list x; ``` Stub: + ```c /* get_x_ptr */ __attribute__ ((const)) list *poeyrb8a(void) { return &x; } ``` Import: + ```hs newtype List = List (IncompleteArray CInt) foreign import ccall unsafe "poeyrb8a" poeyrb8a :: IO (Ptr List) @@ -374,6 +389,7 @@ Memory layout: | (*int)[] ; Ptr (IncompleteArray CInt) | x_ptr | 2000 | 1000 | Constant: + ```hs {-# NOINLINE x #-} -- If the type of the global were @const list x@, we would /not/ generate the following. diff --git a/manual/LowLevel/Uncategorized-Visibility.md b/manual/LowLevel/Uncategorized-Visibility.md index d74c22a71..946768282 100644 --- a/manual/LowLevel/Uncategorized-Visibility.md +++ b/manual/LowLevel/Uncategorized-Visibility.md @@ -161,6 +161,7 @@ Note that tentative definitions can also act as definitions if there are no full definitions elsewhere in the header, in which case requirement 3 is satisfied. Examples: + ```c extern int __attribute__ ((visibility ("hidden"))) foo (void); int __attribute__ ((visibility ("hidden"))) bar (void); @@ -168,6 +169,7 @@ extern int __attribute__ ((visibility ("hidden"))) i; ``` Non-examples: + ```c int __attribute__ ((visibility ("hidden"))) foo (void) {return 1;}; static int __attribute__ ((visibility ("hidden"))) bar (void) {return 2;}; diff --git a/manual/LowLevel/Uncategorized-ZeroCopy.md b/manual/LowLevel/Uncategorized-ZeroCopy.md index ca4c7f6f3..cf0e4a182 100644 --- a/manual/LowLevel/Uncategorized-ZeroCopy.md +++ b/manual/LowLevel/Uncategorized-ZeroCopy.md @@ -1,3 +1,3 @@ # Zero-copy bindings -TODO: write text. See issue #1288. \ No newline at end of file +TODO: write text. See issue #1288. diff --git a/manual/LowLevel/Usage/01-Invocation.md b/manual/LowLevel/Usage/01-Invocation.md index bbf883d99..78a589dfd 100644 --- a/manual/LowLevel/Usage/01-Invocation.md +++ b/manual/LowLevel/Usage/01-Invocation.md @@ -91,23 +91,27 @@ should be set in the `.cabal` file. Properly setting environment variables is crucial for `hs-bindgen` to find the necessary tools and libraries in Linux. - * `LD_LIBRARY_PATH`: To ensure that the C libraries you build can be linked +* `LD_LIBRARY_PATH`: To ensure that the C libraries you build can be linked at runtime, you need to add their location to this variable. - * Example: If your shared library `libexample.so` is in `/path/to/your/c/libs`, you would run: + * Example: If your shared library `libexample.so` is in `/path/to/your/c/libs`, you would run: + ```bash export LD_LIBRARY_PATH=/path/to/your/c/libs:$LD_LIBRARY_PATH ``` - * `BINDGEN_EXTRA_CLANG_ARGS`: This variable allows you to pass extra +* `BINDGEN_EXTRA_CLANG_ARGS`: This variable allows you to pass extra arguments to `libclang`. This is particularly useful for specifying include directories. - * To find your system's default include paths, you can run: + * To find your system's default include paths, you can run: + ```bash clang -v -E -xc /dev/null ``` - * You can then set the variable with these paths: + + * You can then set the variable with these paths: + ```bash export BINDGEN_EXTRA_CLANG_ARGS="-nostdinc -I/usr/lib/clang/14/include -I/usr/include" ``` @@ -117,11 +121,11 @@ Properly setting environment variables is crucial for `hs-bindgen` to find the n ultimately used depends on the order of directories in the include path. - * Note that the common use of this environment variable is to set + * Note that the common use of this environment variable is to set preprocessor flags. So only overwrite the include paths if absolutely necessary. - * `LLVM_PATH`, `LLVM_CONFIG`: `hs-bindgen` may need to know where to find +* `LLVM_PATH`, `LLVM_CONFIG`: `hs-bindgen` may need to know where to find your LLVM installation. ```bash @@ -134,18 +138,18 @@ Properly setting environment variables is crucial for `hs-bindgen` to find the n #### Common Errors and Solutions - * Missing headers (`stddef.h`, etc.): If you encounter errors about missing +* Missing headers (`stddef.h`, etc.): If you encounter errors about missing standard headers, it's a sign that `libclang` cannot find the system's include directories. Setting `BINDGEN_EXTRA_CLANG_ARGS` as described above is the solution. - * Missing shared libraries (`libexample.so`): If you see an error like +* Missing shared libraries (`libexample.so`): If you see an error like `cannot open shared object file: No such file or directory`, it means the dynamic linker can't find your C library. Adding the library's directory to `LD_LIBRARY_PATH` or making sure your cabal.project.local points to the right folder will resolve this. - * Unicode character issues with LLVM backend: When using the LLVM backend, +* Unicode character issues with LLVM backend: When using the LLVM backend, you might encounter issues with Unicode characters in your C code. This can sometimes manifest as errors at the assembler level. Ensure your source files do not include Unicode. @@ -159,15 +163,16 @@ sure to avoid using Unicode-specific characters in C function definitions. #### Environment Variables - * `DYLD_LIBRARY_PATH`: This is the macOS equivalent of `LD_LIBRARY_PATH`. It +* `DYLD_LIBRARY_PATH`: This is the macOS equivalent of `LD_LIBRARY_PATH`. It tells the dynamic linker where to find dynamic libraries (`.dylib` files). - * Example: + * Example: + ```bash export DYLD_LIBRARY_PATH=/path/to/your/c/libs:$DYLD_LIBRARY_PATH ``` - * `BINDGEN_EXTRA_CLANG_ARGS`: On MacOS, setting the include paths like we +* `BINDGEN_EXTRA_CLANG_ARGS`: On MacOS, setting the include paths like we suggest to do in Linux is not required. If you need to, see the section for Linux on how to set it up. @@ -190,15 +195,16 @@ On Windows, the primary way the system finds DLLs at runtime is by searching the directories listed in the `PATH` environment variable. This is a crucial difference from Linux and MacOS. - * To ensure your application can find its required DLLs, add the corresponding +* To ensure your application can find its required DLLs, add the corresponding directories to the `PATH`: + ```powershell $env:PATH = "C:\path\to\your\c\libs;" + $env:PATH ``` #### Environment Variables - * `LLVM_PATH`, `LLVM_CONFIG`, `LIBCLANG_PATH`: You need to point `hs-bindgen` +* `LLVM_PATH`, `LLVM_CONFIG`, `LIBCLANG_PATH`: You need to point `hs-bindgen` to the LLVM/Clang installation that comes with GHC. Make sure these flags aren't already set to the right paths. @@ -208,18 +214,18 @@ difference from Linux and MacOS. $env:LIBCLANG_PATH = "$env:LLVM_PATH\lib" ``` - * `BINDGEN_EXTRA_CLANG_ARGS`: On Windows, setting the include paths like we +* `BINDGEN_EXTRA_CLANG_ARGS`: On Windows, setting the include paths like we suggest to do in Linux is not required. If you need to, see the section for Linux on how to set it up. #### Common Errors and Solutions - * Dynamic-link library loading order: If your application fails silently +* Dynamic-link library loading order: If your application fails silently or with an `ExitFailure` and a cryptic error code like `(-1073741515)`, it is very likely a DLL loading issue. Adding the directory containing your C libraries' DLLs to the system `PATH` is the solution. - * Resolving issues with underlying type mismatches (`FC.CInt` vs. +* Resolving issues with underlying type mismatches (`FC.CInt` vs. `FC.CUInt`): You might encounter Haskell type errors where, for example, a C `int` is being interpreted as a `CUInt` instead of a `CInt`. This is often due to how different compilers and platforms define basic types. Carefully diff --git a/manual/LowLevel/Usage/03-ClangOptions.md b/manual/LowLevel/Usage/03-ClangOptions.md index c26210451..3ed70b039 100644 --- a/manual/LowLevel/Usage/03-ClangOptions.md +++ b/manual/LowLevel/Usage/03-ClangOptions.md @@ -70,12 +70,12 @@ Template Haskell API take precedence. The following targets are supported: - - `x86_64-pc-linux` - - `i386-pc-linux` - - `aarch64-pc-linux` - - `x86_64-pc-windows` - - `x86_64-apple-macosx` - - `aarch64-apple-macosx` + - `x86_64-pc-linux` + - `i386-pc-linux` + - `aarch64-pc-linux` + - `x86_64-pc-windows` + - `x86_64-apple-macosx` + - `aarch64-apple-macosx` This behavior is consistent with that of [`rust-bindgen`][]. diff --git a/manual/LowLevel/Usage/04-Includes.md b/manual/LowLevel/Usage/04-Includes.md index 2d4593b5a..7f89dccc8 100644 --- a/manual/LowLevel/Usage/04-Includes.md +++ b/manual/LowLevel/Usage/04-Includes.md @@ -248,7 +248,7 @@ arguments should be relative to it. The following command runs the preprocessor on header `foo.h` in the `include` directory: ``` -$ hs-bindgen-cli preprocess -I include foo.h +hs-bindgen-cli preprocess -I include foo.h ``` This translates to include directive `#include `. Header resolution @@ -298,19 +298,19 @@ When you encounter an include issue, the first thing to do is understand where the issue is occurring. Possibilities include: * `hs-bindgen` translation of C headers - * `hs-bindgen` options are relevant, including `BINDGEN_EXTRA_CLANG_ARGS`. - * Clang options are relevant, including `C_INCLUDE_PATH`. - * Run `hs-bindgen-cli --version` to confirm which version of `libclang` is + * `hs-bindgen` options are relevant, including `BINDGEN_EXTRA_CLANG_ARGS`. + * Clang options are relevant, including `C_INCLUDE_PATH`. + * Run `hs-bindgen-cli --version` to confirm which version of `libclang` is being used. * Compilation of C libraries to shared objects - * `hs-bindgen` options are not relevant. - * The compiler options are relevant. `C_INCLUDE_PATH` is used by both GCC + * `hs-bindgen` options are not relevant. + * The compiler options are relevant. `C_INCLUDE_PATH` is used by both GCC and Clang. * Compilation of translated Haskell modules - * `hs-bindgen` options are not relevant. - * The compiler options are relevant. `C_INCLUDE_PATH` is used by both GCC + * `hs-bindgen` options are not relevant. + * The compiler options are relevant. `C_INCLUDE_PATH` is used by both GCC and Clang. - * Run `ghc --info` to confirm which compiler is being used. + * Run `ghc --info` to confirm which compiler is being used. When using the Template Haskell API, you can use the `-ddump-splices` GHC option to dump the generated Haskell source, including CAPI C source. This allows you @@ -322,7 +322,7 @@ options and environment variables into account, so you can test configuration and confirm precedence. ``` -$ clang -E -v - ``` -$ hs-bindgen-cli info include-graph stdint.h +hs-bindgen-cli info include-graph stdint.h ``` The `hs-bindgen-cli info libclang` command may be used to run `libclang` with @@ -356,5 +356,5 @@ Clang options such as `-v`, to confirm the `libclang` version, C include search paths, etc. ``` -$ hs-bindgen-cli info libclang --clang-option=-v +hs-bindgen-cli info libclang --clang-option=-v ``` diff --git a/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md b/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md index 424e64743..3e368912e 100644 --- a/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md +++ b/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md @@ -49,6 +49,7 @@ parsed to use an external binding for them, however. They must be named in order to be specified in an external binding specification. We use parse predicates mostly because: + - We seek to avoid repetitive parsing and reification. We can do so by using binding specifications. For example, the standard library need not be parsed every time, and we provide excellent external bindings covering the standard @@ -63,6 +64,7 @@ containing the declaration being matched. By default `hs-bindgen` only parses and reifies declarations in headers from sub-directories of the main headers, and avoids parsing and reifying all declarations. This default behavior can be changed; in particular, the command line options are: + - `--parse-all`: Parse all declarations provided by Clang to `hs-bindgen`. - `--parse-from-main-headers`: Only parse declarations in main headers. - `--parse-from-main-header-dirs`: Parse declarations in main headers and From 48d9f1c39ebdf116f2b2273edc0cfeed6e820208 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Tue, 25 Nov 2025 12:03:58 +0100 Subject: [PATCH 03/12] Markdown linter, manual changes --- ansi-diff/CHANGELOG.md | 2 + dev/building-manual.md | 10 +-- dev/ci.md | 2 + dev/code-structure.md | 2 + dev/project-structure.md | 2 +- dev/testing.md | 2 +- dev/troubleshooting.md | 12 +-- examples/README.md | 8 +- hs-bindgen/clang-ast-dump/README.md | 6 +- manual/LowLevel/Translation/03-Enums.md | 8 +- manual/LowLevel/Translation/05-Functions.md | 9 ++- manual/LowLevel/Translation/06-Globals.md | 22 +++--- manual/LowLevel/Uncategorized-Visibility.md | 12 +-- manual/LowLevel/Usage/01-Invocation.md | 75 +++++++++---------- manual/LowLevel/Usage/03-ClangOptions.md | 16 ++-- manual/LowLevel/Usage/04-Includes.md | 32 ++++---- .../05-ParsingSelectingAndProgramSlicing.md | 4 +- manual/Roadmap.md | 4 +- 18 files changed, 117 insertions(+), 111 deletions(-) diff --git a/ansi-diff/CHANGELOG.md b/ansi-diff/CHANGELOG.md index 6d1755331..05bdd4052 100644 --- a/ansi-diff/CHANGELOG.md +++ b/ansi-diff/CHANGELOG.md @@ -1,3 +1,5 @@ +# Changelog + ## 0.1.0 -- YYYY-mm-dd * First version. Released on an unsuspecting world. diff --git a/dev/building-manual.md b/dev/building-manual.md index db123325d..82eb4b66a 100644 --- a/dev/building-manual.md +++ b/dev/building-manual.md @@ -14,7 +14,7 @@ for your platform as described in [dev-environment.md](dev-environment.md). On the [`manual`](../manual) directory, create the following `cabal.project.local` file: -``` +```cabal package manual extra-include-dirs: /path/to/hs-bindgen/manual/c @@ -37,7 +37,7 @@ package hs-vector On Linux, since it supports Unicode characters, also add the following to the file: -``` +```cabal package * ghc-options: -optc-DSUPPORTS_UNICODE @@ -90,8 +90,8 @@ cabal run manual export BINDGEN_EXTRA_CLANG_ARGS="-nostdinc $CLANG_ARGS" ``` -- Missing shared libraries: If you see `cannot open shared object file: No such file or directory`, - add the library's directory to `LD_LIBRARY_PATH` on Linux, +- Missing shared libraries: If you see `cannot open shared object file: No such + file or directory`, add the library's directory to `LD_LIBRARY_PATH` on Linux, `DYLD_LIBRARY_PATH` on MacOS and `PATH` on Windows. - Unicode-related compilation errors: Verify that `SUPPORTS_UNICODE` is only @@ -110,4 +110,4 @@ cabal run manual ### More troubleshooting -See [here](./troubleshooting.md) +See the [section on troubleshooting](./troubleshooting.md). diff --git a/dev/ci.md b/dev/ci.md index 06affff5c..d6ab542dd 100644 --- a/dev/ci.md +++ b/dev/ci.md @@ -1,3 +1,5 @@ +# Continues development, continuous integration + We have a two tiered CI. In particular - We do not automatically run CI on pushes. diff --git a/dev/code-structure.md b/dev/code-structure.md index e6708f51f..b7d0a2242 100644 --- a/dev/code-structure.md +++ b/dev/code-structure.md @@ -1,3 +1,5 @@ +# Code structure + - `HsBindgen.Language` contains data structures that are more general; that is, not directly related to binding generation, and that could live in separate packages. diff --git a/dev/project-structure.md b/dev/project-structure.md index 464dbbf76..ccec355b3 100644 --- a/dev/project-structure.md +++ b/dev/project-structure.md @@ -5,7 +5,7 @@ help new developers understand how the codebase is organized. ## Repository Layout -``` +```text hs-bindgen/ โ”œโ”€โ”€ alternatives/ โ”œโ”€โ”€ ansi-diff/ diff --git a/dev/testing.md b/dev/testing.md index 3eb213f65..3d58638b4 100644 --- a/dev/testing.md +++ b/dev/testing.md @@ -26,7 +26,7 @@ cabal run -- test-hs-bindgen --accept A very useful thing to debug failing CI is to add: -``` +```yaml - name: Setup tmate session uses: mxschmitt/action-tmate@v3 ``` diff --git a/dev/troubleshooting.md b/dev/troubleshooting.md index 60b6906be..6c7b5df35 100644 --- a/dev/troubleshooting.md +++ b/dev/troubleshooting.md @@ -10,7 +10,7 @@ with `hs-bindgen`. Error: -``` +```text Error: [Cabal-4345] Missing dependency on a foreign library: * Missing (or bad) C library: game @@ -29,7 +29,7 @@ Solution: 2. Create a `cabal.project.local` file with proper paths: - ``` + ```cabal package hs-game extra-include-dirs: /path/to/hs-bindgen/manual/c extra-lib-dirs: /path/to/hs-bindgen/manual/c @@ -47,7 +47,7 @@ Solution: Error: -``` +```text fatal error: manual_examples.h: No such file or directory #include "manual_examples.h" ``` @@ -62,14 +62,14 @@ Solution: Error: -``` +```text undefined reference to 'hs_bindgen_c_example_helloworld' ``` Solution: Add the C library to your `.cabal` file: -``` +```cabal extra-libraries: hs-bindgen-c-example ``` @@ -84,7 +84,7 @@ for `lib.so` binary file name. Error: -``` +```text error while loading shared libraries: libhs-bindgen-c-example.so: cannot open shared object file: No such file or directory ``` diff --git a/examples/README.md b/examples/README.md index 5c44cc6cb..7f7591d8d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -13,8 +13,8 @@ examples include bindings and toy programs for the following C libraries: The examples are only tested (in CI) on Ubuntu. They might work on other distributions such as Windows or MacOS as well, but we offer no guarantees. -The following software should be installed before trying to build and run one of the -examples: +The following software should be installed before trying to build and run one of +the examples: * A version of GHC that is compatible with `hs-bindgen`, such as `9.6.x` * A version of Cabal that is compatible with `hs-bindgen`, such as `3.16.x` @@ -61,7 +61,7 @@ as the following requirements are met: `REPOSITORY_ROOT/examples/libfoo/hs-project/cabal.project`, which should contain at least: - ``` + ```cabal import: ../../../cabal.project.base packages: . ../../../c-expr-runtime @@ -79,7 +79,7 @@ as the following requirements are met: `REPOSITORY_ROOT/examples/libfoo/hs-project/cabal.project.local` file so that it includes: - ``` + ```cabal package libfoo extra-include-dirs: -- insert absolute path to installation directory for header files here diff --git a/hs-bindgen/clang-ast-dump/README.md b/hs-bindgen/clang-ast-dump/README.md index 47522fdf5..5f920b2c8 100644 --- a/hs-bindgen/clang-ast-dump/README.md +++ b/hs-bindgen/clang-ast-dump/README.md @@ -10,13 +10,13 @@ All of the following commands are to be run in the project root. Display options: -``` +```console cabal run clang-ast-dump -- --help ``` Run: -``` +```console cabal run clang-ast-dump -- hs-bindgen/examples/comments.h ``` @@ -24,6 +24,6 @@ cabal run clang-ast-dump -- hs-bindgen/examples/comments.h Official: -``` +```console clang -Xclang -ast-dump foo.c ``` diff --git a/manual/LowLevel/Translation/03-Enums.md b/manual/LowLevel/Translation/03-Enums.md index 58adae72c..165f741cb 100644 --- a/manual/LowLevel/Translation/03-Enums.md +++ b/manual/LowLevel/Translation/03-Enums.md @@ -347,7 +347,7 @@ putStrLn $ "Possible votes: " ++ show ([minBound .. maxBound] :: [Vote]) will result in -``` +```text Possible votes: [Infavour,Against,Abstain] ``` @@ -361,7 +361,7 @@ The `CENum` class insists if `a` has both a `CEnum` instance and an `Ord` instance, the `Ord` instance on `a` must be compatible with the `Ord` instance on the underlying integral type: -``` +```text (x <= y) if and only if (fromCEnum x <= fromCEnum y) ``` @@ -384,7 +384,7 @@ instance CEnum a => Enum (AsCEnum a) where Provided that `fromIntegral` doesn't do anything very strange, this means that we must also have -``` +```text (x <= y) if and only if (fromEnum x <= fromEnum y) ``` @@ -401,7 +401,7 @@ instance SequentialCEnum a => Enum (AsSequentialCEnum a) where In other words, `succ` and `pred` are just inherited from the underlying integral type, so that we have for all `x` -``` +```text pred x < x < succ x ``` diff --git a/manual/LowLevel/Translation/05-Functions.md b/manual/LowLevel/Translation/05-Functions.md index ef70d06fe..2ecde94b6 100644 --- a/manual/LowLevel/Translation/05-Functions.md +++ b/manual/LowLevel/Translation/05-Functions.md @@ -8,7 +8,8 @@ TODO TODO: introduction to function pointers -1. For every C function, generate an additional binding for the address of that C function. +1. For every C function, generate an additional binding for the address of that + C function. In theory every C function is a candidate for being passed to other functions as a function pointer. For example, consider the following two (contrived) @@ -122,9 +123,9 @@ foreign import {-# details elided #-} apply1_nopointer_arg :: FunPtr Int2int -> CInt -> IO CInt ``` -Similarly, the address stubs `apply1_pointer_arg_ptr` and `apply1_nopointer_arg_ptr` that -we generate (see the ["Function pointers" section](#function-pointers)) would -get the exact same type as the other. +Similarly, the address stubs `apply1_pointer_arg_ptr` and +`apply1_nopointer_arg_ptr` that we generate (see the ["Function pointers" +section](#function-pointers)) would get the exact same type as the other. Note that parameters of function type can occur almost anywhere where types normally can occur, with some minor restrictions. For example: variables, diff --git a/manual/LowLevel/Translation/06-Globals.md b/manual/LowLevel/Translation/06-Globals.md index 18ac56673..c1cde13b5 100644 --- a/manual/LowLevel/Translation/06-Globals.md +++ b/manual/LowLevel/Translation/06-Globals.md @@ -251,19 +251,19 @@ x_incomplete_array_ptr = IncompleteArray.toIncompleteArrayPtr <$> peek x_ptr Memory layout: -| type | name | address | value | -| ------------------------ | ------------- | ------- | ------- | -| int | | 1000 | 1 | -| | | 1004 | 2 | -| | | 1008 | 3 | -| | | ... | | -| int* | x | 2000 | 1000 | -| | | ... | | -| int**; Ptr (Ptr CInt) | x_ptr | 3000 | 2000 | +| type | name | address | value | +|-------------------------|-------|---------|-------| +| `int` | | 1000 | 1 | +| | | 1004 | 2 | +| | | 1008 | 3 | +| | | ... | | +| `int*` | x | 2000 | 1000 | +| | | ... | | +| `int**; Ptr (Ptr CInt)` | x_ptr | 3000 | 2000 | Constant: -```hs +```haskell {-# NOINLINE x #-} -- If the type of the global were @int * const x@, we would also generate the following. -- Note that we do this for const-pointer-to-int, not for pointer-to-const-int. The "outer" @@ -390,7 +390,7 @@ Memory layout: Constant: -```hs +```haskell {-# NOINLINE x #-} -- If the type of the global were @const list x@, we would /not/ generate the following. -- It would fail to compile because 'IncompleteArray' does not have a 'Storable' instance, diff --git a/manual/LowLevel/Uncategorized-Visibility.md b/manual/LowLevel/Uncategorized-Visibility.md index 946768282..f227cf66e 100644 --- a/manual/LowLevel/Uncategorized-Visibility.md +++ b/manual/LowLevel/Uncategorized-Visibility.md @@ -87,9 +87,9 @@ void __attribute__ ((visibility ("hidden"))) foo (void); Let's also assume `A.c` includes a definition for this `foo` function. We can compile this into a shared library `libA.so` like so: -``` -โฏ gcc -Wall -o A.o -c -fPIC A.c -โฏ gcc -Wall --shared -o libA.so A.o +```console +gcc -Wall -o A.o -c -fPIC A.c +gcc -Wall --shared -o libA.so A.o ``` Now we create a separate file `B.c` that uses the `foo` function from @@ -103,9 +103,9 @@ void bar (void) { foo(); } If we try to compile the following code and link it against `libA.so`, we would get linker errors. -``` -โฏ gcc -Wall -o B.o -c -fPIC B.c -โฏ gcc -Wall --shared -o libB.so B.o libA.so +```console +$ gcc -Wall -o B.o -c -fPIC B.c +$ gcc -Wall --shared -o libB.so B.o libA.so /usr/bin/ld: B.o: in function `bar': B.c:(.text+0x9): undefined reference to `foo' /usr/bin/ld: libB.so: hidden symbol `foo' isn't defined diff --git a/manual/LowLevel/Usage/01-Invocation.md b/manual/LowLevel/Usage/01-Invocation.md index 78a589dfd..0a1d7e3a6 100644 --- a/manual/LowLevel/Usage/01-Invocation.md +++ b/manual/LowLevel/Usage/01-Invocation.md @@ -63,7 +63,7 @@ files and headers. This can be done, amongst other ways, by creating a For example, in order to build the manual you will need the following: -``` +```cabal package manual extra-include-dirs: /manual/c @@ -89,41 +89,40 @@ should be set in the `.cabal` file. #### Setting environment variables -Properly setting environment variables is crucial for `hs-bindgen` to find the necessary tools and libraries in Linux. +Properly setting environment variables is crucial for `hs-bindgen` to find the +necessary tools and libraries in Linux. -* `LD_LIBRARY_PATH`: To ensure that the C libraries you build can be linked - at runtime, you need to add their location to this variable. +* `LD_LIBRARY_PATH`: To ensure that the C libraries you build can be linked at + runtime, you need to add their location to this variable. - * Example: If your shared library `libexample.so` is in `/path/to/your/c/libs`, you would run: + * Example: If your shared library `libexample.so` is in + `/path/to/your/c/libs`, you would run: - ```bash - export LD_LIBRARY_PATH=/path/to/your/c/libs:$LD_LIBRARY_PATH - ``` + ```bash + export LD_LIBRARY_PATH=/path/to/your/c/libs:$LD_LIBRARY_PATH + ``` -* `BINDGEN_EXTRA_CLANG_ARGS`: This variable allows you to pass extra - arguments to `libclang`. This is particularly useful for specifying include - directories. +* `BINDGEN_EXTRA_CLANG_ARGS`: This variable allows you to pass extra arguments + to `libclang`. This is particularly useful for specifying include directories. * To find your system's default include paths, you can run: - ```bash - clang -v -E -xc /dev/null - ``` + ```bash + clang -v -E -xc /dev/null + ``` * You can then set the variable with these paths: - ```bash - export BINDGEN_EXTRA_CLANG_ARGS="-nostdinc -I/usr/lib/clang/14/include -I/usr/include" - ``` + ```bash + export BINDGEN_EXTRA_CLANG_ARGS="-nostdinc -I/usr/lib/clang/14/include -I/usr/include" + ``` - **NOTE**: Without `--no-stdinc`, there are likely multiple directories - in the C include path that provide the same headers. Which header is - ultimately used depends on the order of directories in the include - path. + **NOTE**: Without `--no-stdinc`, there are likely multiple directories in + the C include path that provide the same headers. Which header is ultimately + used depends on the order of directories in the include path. - * Note that the common use of this environment variable is to set - preprocessor flags. So only overwrite the include paths if absolutely - necessary. + * Note that the common use of this environment variable is to set preprocessor + flags. So only overwrite the include paths if absolutely necessary. * `LLVM_PATH`, `LLVM_CONFIG`: `hs-bindgen` may need to know where to find your LLVM installation. @@ -166,11 +165,11 @@ sure to avoid using Unicode-specific characters in C function definitions. * `DYLD_LIBRARY_PATH`: This is the macOS equivalent of `LD_LIBRARY_PATH`. It tells the dynamic linker where to find dynamic libraries (`.dylib` files). - * Example: + Example: - ```bash - export DYLD_LIBRARY_PATH=/path/to/your/c/libs:$DYLD_LIBRARY_PATH - ``` + ```bash + export DYLD_LIBRARY_PATH=/path/to/your/c/libs:$DYLD_LIBRARY_PATH + ``` * `BINDGEN_EXTRA_CLANG_ARGS`: On MacOS, setting the include paths like we suggest to do in Linux is not required. If you need to, see the section for @@ -196,11 +195,11 @@ the directories listed in the `PATH` environment variable. This is a crucial difference from Linux and MacOS. * To ensure your application can find its required DLLs, add the corresponding - directories to the `PATH`: + directories to the `PATH`: - ```powershell - $env:PATH = "C:\path\to\your\c\libs;" + $env:PATH - ``` + ```powershell + $env:PATH = "C:\path\to\your\c\libs;" + $env:PATH + ``` #### Environment Variables @@ -208,11 +207,11 @@ difference from Linux and MacOS. to the LLVM/Clang installation that comes with GHC. Make sure these flags aren't already set to the right paths. - ```powershell - $env:LLVM_PATH = "C:\ghcup\ghc\\mingw" - $env:LLVM_CONFIG = "$env:LLVM_PATH\bin\llvm-config.exe" - $env:LIBCLANG_PATH = "$env:LLVM_PATH\lib" - ``` + ```powershell + $env:LLVM_PATH = "C:\ghcup\ghc\\mingw" + $env:LLVM_CONFIG = "$env:LLVM_PATH\bin\llvm-config.exe" + $env:LIBCLANG_PATH = "$env:LLVM_PATH\lib" + ``` * `BINDGEN_EXTRA_CLANG_ARGS`: On Windows, setting the include paths like we suggest to do in Linux is not required. If you need to, see the section for @@ -239,7 +238,7 @@ difference from Linux and MacOS. targets people use Hackage with, perhaps all generated bindings uploaded to Hackage should include appropriate gates. Minimal example: - ``` + ```cabal if !(os(linux) && arch(x86_64)) buildable: false ``` diff --git a/manual/LowLevel/Usage/03-ClangOptions.md b/manual/LowLevel/Usage/03-ClangOptions.md index 3ed70b039..3b0ff140c 100644 --- a/manual/LowLevel/Usage/03-ClangOptions.md +++ b/manual/LowLevel/Usage/03-ClangOptions.md @@ -35,14 +35,14 @@ managed by `hs-bindgen`. Example: -``` -$ hs-bindgen-cli preprocess \ - --standard c23 \ - -I include \ - --clang-option="-idirafter/opt/acme-0.1.0/include" \ - --module Foo \ - --output Foo.hs \ - foo.h +```console +hs-bindgen-cli preprocess \ + --standard c23 \ + -I include \ + --clang-option="-idirafter/opt/acme-0.1.0/include" \ + --module Foo \ + --output Foo.hs \ + foo.h ``` ## Environment variables diff --git a/manual/LowLevel/Usage/04-Includes.md b/manual/LowLevel/Usage/04-Includes.md index 7f89dccc8..58d412883 100644 --- a/manual/LowLevel/Usage/04-Includes.md +++ b/manual/LowLevel/Usage/04-Includes.md @@ -101,13 +101,13 @@ does the same. Linux example: -``` +```sh C_INCLUDE_PATH=/some/dep/include:/other/dep/include ``` Windows example: -``` +```sh C_INCLUDE_PATH=C:\some\dep\include;C:\other\dep\include ``` @@ -202,14 +202,14 @@ Clang has many more include options, which may be passed via For example, Clang option `-idirafter` may be used to add a directory to the end of the bracket C include search path. -``` -$ hs-bindgen-cli preprocess \ - --standard c23 \ - -I include \ - --clang-option="-idirafter/opt/acme-0.1.0/include" \ - --module Foo \ - --output Foo.hs \ - foo.h +```console +hs-bindgen-cli preprocess \ + --standard c23 \ + -I include \ + --clang-option="-idirafter/opt/acme-0.1.0/include" \ + --module Foo \ + --output Foo.hs \ + foo.h ``` ### `hs-bindgen` environment variables @@ -247,7 +247,7 @@ directory should be added to the bracket C include search path, and header arguments should be relative to it. The following command runs the preprocessor on header `foo.h` in the `include` directory: -``` +```console hs-bindgen-cli preprocess -I include foo.h ``` @@ -321,7 +321,7 @@ includes the C include search paths used by Clang. It takes command-line options and environment variables into account, so you can test configuration and confirm precedence. -``` +```console clang -E -v - -``` +```console hs-bindgen-cli info include-graph stdint.h ``` @@ -355,6 +355,6 @@ The `hs-bindgen-cli info libclang` command may be used to run `libclang` with Clang options such as `-v`, to confirm the `libclang` version, C include search paths, etc. -``` +```console hs-bindgen-cli info libclang --clang-option=-v ``` diff --git a/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md b/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md index 3e368912e..b0bc63164 100644 --- a/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md +++ b/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md @@ -109,7 +109,7 @@ select for translation. For example, select predicates allow matching against declaration names. In particular, the command-line arguments to specify select predicates are (excerpt of `hs-bindgen-cli preprocess --help`): -``` +```text --select-all Select all declarations --select-from-main-headers Select declarations in main headers (default) @@ -178,7 +178,7 @@ external binding for `FileOperationStatus`. Assume we generate bindings for a library `simple` with main header `simple/main.h`. We use the following command line -``` +```console hs-bindgen-cli preprocess -I /path/to/simple main.h ``` diff --git a/manual/Roadmap.md b/manual/Roadmap.md index 66e2ecf59..9bdeb962e 100644 --- a/manual/Roadmap.md +++ b/manual/Roadmap.md @@ -1,7 +1,7 @@ # `hs-bindgen` roadmap -The project is split up into three major milestones, each of which are useful in their -own right and can be released as version +The project is split up into three major milestones, each of which are useful in +their own right and can be released as version [0.1](https://github.com/well-typed/hs-bindgen/issues/29), [0.2](https://github.com/well-typed/hs-bindgen/issues/30) and [0.3](https://github.com/well-typed/hs-bindgen/issues/31). From 8eea824c3affd33ac6e04ac3c4f4b20eb8c99d2e Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Tue, 25 Nov 2025 12:53:38 +0100 Subject: [PATCH 04/12] Manual: Introduction --- manual/LowLevel/Introduction.md | 82 +++++++++++++++------------------ 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/manual/LowLevel/Introduction.md b/manual/LowLevel/Introduction.md index d4a66a9d3..e0b807f2b 100644 --- a/manual/LowLevel/Introduction.md +++ b/manual/LowLevel/Introduction.md @@ -17,7 +17,8 @@ void mk_triple(int a, int b, int c, triple* triple); When we run this through `hs-bindgen`, we get[^1] ```haskell -foreign import capi safe "some_header.h mk_triple" +$(addCSource "... void hs_bindgen_532be147b0ffdb88 ...") +foreign import ccall safe "hs_bindgen_532be147b0ffdb88" mk_triple :: CInt -> CInt -> CInt -> Ptr Triple -> IO () data Triple = Triple { @@ -25,12 +26,10 @@ data Triple = Triple { , triple_b :: CInt , triple_c :: CInt } + deriving stock (Eq, Show) instance Storable Triple where -- implementation omitted for brevity - -deriving stock instance Show Triple -deriving stock instance Eq Triple ``` Which we might use as follows: @@ -44,7 +43,12 @@ mkTriple a b c = unsafePerformIO $ ``` > [!NOTE] -> Conceivably a more high-level API like this could be constructed +> `addCSource` is a Template Haskell splice that adds the C source code at +> compile time, allowing `hs-bindgen` to provide C-native functionality and +> directly interface with the original C code. + +> [!NOTE] +> Conceivably, a more high-level API like this could be constructed > automatically, but this will be the responsibility of the `hs-bindgen` > _high-level_ interface. @@ -65,21 +69,20 @@ int index_triple(triple* triple, index ix); results in ```haskell -foreign import capi safe "some_header.h index_triple" +$(addCSource "... signed int hs_bindgen_c4886273ec5abcf5 ...") +foreign import ccall safe "hs_bindgen_c4886273ec5abcf5" index_triple :: Ptr Triple -> Index -> IO CInt newtype Index = Index { - unIndex :: CUInt + un_Index :: CUInt } + deriving stock (Eq, Ord) -instance Storable Index where - -- implementation omitted for brevity - -instance Show Index where .. -instance Read Index where .. -instance Eq Index where .. -instance Ord Index where .. -instance CEnum Index where .. +instance Storable Index where -- ... +instance Show Index where -- ... +instance Read Index where -- ... +instance CEnum Index where -- ... +instance SequentialCEnum Index where -- ... pattern A, B, C :: Index pattern A = Index 0 @@ -96,7 +99,7 @@ indexTriple triple ix = unsafePerformIO $ ``` The reason that `Index` is defined as a `newtype` around `CUInt` rather than a -Haskell ADT is that a C enum declaration only defines _values_; it does not +Haskell ADT is that a C `enum` declaration only defines _values_; it does not limit the range of the corresponding type. For the same reason we do not derive `Enum` or `Bounded` for `Index`, and the patterns `A`, `B` and `C` are _not_ declared [complete][ghc-manual:complete]. @@ -107,8 +110,8 @@ declared [complete][ghc-manual:complete]. > > [!NOTE] -> Generating a Haskell ADT to correspond to this enum will be the responsibility -> of the `hs-bindgen` high-level interface. +> Generating a Haskell ADT to correspond to this `enum` will be the +> responsibility of the `hs-bindgen` high-level interface. ## Typedefs @@ -124,25 +127,13 @@ becomes ```haskell newtype Sum = Sum { - unSum :: CInt + un_Sum :: CInt } + deriving stock (Eq, Ord, Read, Show) + deriving newtype (Storable, Bits, Bounded, Enum, FiniteBits, Integral, Ix.Ix, Num, Real) -deriving stock instance Eq Sum -deriving stock instance Ord Sum -deriving stock instance Read Sum -deriving stock instance Show Sum - -deriving newtype instance Storable Sum -deriving newtype instance Enum Sum -deriving newtype instance Ix.Ix Sum -deriving newtype instance Bounded Sum -deriving newtype instance Bits.Bits Sum -deriving newtype instance FiniteBits Sum -deriving newtype instance Integral Sum -deriving newtype instance Num Sum -deriving newtype instance Real Sum - -foreign import capi safe "some_header.h sum_triple" +$(addCSource "... sum hs_bindgen_94482b93bc4c5bc9 ...") +foreign import ccall safe "hs_bindgen_94482b93bc4c5bc9" sum_triple :: Ptr Triple -> IO Sum ``` @@ -153,7 +144,7 @@ underlying type, but this can be overridden. ## Macros -For macros we generate different Haskell code depending on the kind of macro. +For macros we generate different Haskell code depending on the kind of the macro. ### Values @@ -190,15 +181,15 @@ macro functions: #define PTR_TO_FIELD(ptr) ptr + 4 ``` -C macros don't have type annotations, and we cannot tell from just looking at -the macro body how it's intended to be used. Therefore `hs-bindgen` uses type +C macros lack type annotations, and we cannot tell from just looking at the +macro body how it is intended to be used. Therefore `hs-bindgen` uses type inference to generate a Haskel function with the most general type possible: ```haskell -pTR_TO_FIELD :: CExpr.Add a CInt => a -> CExpr.AddRes a CInt +pTR_TO_FIELD :: forall a. (C.Add a) CInt => a -> (C.AddRes a) CInt ``` -`Add` and `AddRes` are defined in a `hs-bindgen` companion library called +`Add` and `AddRes` are defined in an `hs-bindgen` companion library called `c-expr`, which essentially allows us to mirror C expressions in Haskell, respecting the typing rules for C. Two examples: @@ -234,14 +225,12 @@ YEAR getYear(date* d); This results in ```haskell --- .. similarly for MONTH and DAY +-- ... similarly for MONTH and DAY newtype YEAR = YEAR { un_YEAR :: CInt } - -instance Storable YEAR where .. -instance Eq YEAR where .. --- and more instances + deriving stock (Eq, Ord, Read, Show) + deriving newtype (Storable, Bits, Bounded, Enum, FiniteBits, Integral, Ix.Ix, Num, Real) data Date = Date { date_year :: YEAR @@ -249,7 +238,8 @@ data Date = Date { , date_day :: DAY } -foreign import capi safe "some_header.h getYear" +$(addCSource "... YEAR hs_bindgen_1c549fc082857e68 ...") +foreign import ccall safe "hs_bindgen_1c549fc082857e68" getYear :: Ptr Date -> IO YEAR ``` From 76eb77c0c5b1109b0ec09c3d3f36279bb846a562 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Tue, 25 Nov 2025 14:19:41 +0100 Subject: [PATCH 05/12] Manual: Invocation --- manual/LowLevel/Usage/01-Invocation.md | 145 ++++++++++++------------- 1 file changed, 71 insertions(+), 74 deletions(-) diff --git a/manual/LowLevel/Usage/01-Invocation.md b/manual/LowLevel/Usage/01-Invocation.md index 0a1d7e3a6..42b1fcd59 100644 --- a/manual/LowLevel/Usage/01-Invocation.md +++ b/manual/LowLevel/Usage/01-Invocation.md @@ -18,50 +18,52 @@ TODO TODO -## Setting up an environment that is able to build and run `hs-bindgen` +## Preparation of system environment for `hs-bindgen` -Depending on the platform, there are specific steps and considerations to -ensure a smooth experience with `hs-bindgen`. This guide will walk you through -the setup process for Linux, macOS, and Windows, highlighting the nuances of -each operating system. +Depending on the platform, specific steps ensure a smooth experience with +`hs-bindgen`. This guide will walk you through the setup process for Linux, +macOS, and Windows. ### Linux -Setting up on Linux, such as Ubuntu, involves making a choice on your -compiler, installing necessary tools like LLVM and Clang, and correctly -configuring your environment variables. +Setup on Linux involves choosing between the GCC and Clang compiler toolchains. -#### Compiler choices (GCC vs. Clang) +>[!NOTE] +> +> We also provide and maintain [Nix Flake](../../../flake.nix), and a +> [Nix-focused tutorial](https://github.com/well-typed/hs-bindgen-tutorial-nix). + +#### Compiler choice (GCC vs. Clang) GCC is the default compiler on many Linux distributions and is also the compiler -used by GHC. `hs-bindgen` uses `libclang` internally to parse C code. -Differences in how GCC and Clang handle aspects like memory layout can lead to -incorrect bindings. Unfortunately it is not straightforward to make GHC use -Clang without effectively, building GHC from scratch. There are flags like -`-pgmc` and `-pgml` ([see reference -here](https://downloads.haskell.org/ghc/latest/docs/users_guide/phases.html#replacing-the-program-for-one-or-more-phases)) -which allow one to tell GHC which C compiler to use, however experiments showed -that just setting these flags was not enough. For more details see the +used by GHC. However, `hs-bindgen` uses `libclang` internally to parse C code. +GCC and Clang will mostly be compatible, but there are some exceptions. For +example, GCC and Clang may exhibit differences in memory layout. Unfortunately, +it is not straightforward to make GHC use Clang without effectively building GHC +from scratch. There are flags like `-pgmc` and `-pgml` ([see the GHC user +guide](https://downloads.haskell.org/ghc/latest/docs/users_guide/phases.html#replacing-the-program-for-one-or-more-phases)) +which allow specification of the C compiler used by GHC; however, experiments +showed that just setting these flags was not enough. For more details see the discussions at issues [#846](https://github.com/well-typed/hs-bindgen/issues/846), and [#847](https://github.com/well-typed/hs-bindgen/issues/847). -All in all, using Clang for both your C code and the code generated by `hs-bindgen` -would ensure consistency and correctness, but we currently don't thoroughly -test with `clang`. +Using Clang for both, generation of and compilation of the bindings ensures +consistency and correctness, but we currently do not thoroughly test compilation +of code using the generated bindings with Clang. #### Installation of LLVM and Clang -You'll need to install LLVM and Clang. The specific package names may vary -slightly depending on your distribution. +You need to install LLVM and Clang. The specific package names may vary +depending on your distribution. #### Adding `cabal.project.local` -Your program might need some extra help figuring out where to find the C source -files and headers. This can be done, amongst other ways, by creating a -`cabal.project.local` file defines `extra-include-dirs` and `extra-lib-dirs`. +We need to inform the program about the location of the C sources. We can do so +by creating a `cabal.project.local` file that defines `extra-include-dirs` and +`extra-lib-dirs`. -For example, in order to build the manual you will need the following: +For example, to build the manual you will need the following: ```cabal package manual @@ -89,40 +91,39 @@ should be set in the `.cabal` file. #### Setting environment variables -Properly setting environment variables is crucial for `hs-bindgen` to find the -necessary tools and libraries in Linux. - -* `LD_LIBRARY_PATH`: To ensure that the C libraries you build can be linked at - runtime, you need to add their location to this variable. +* `LD_LIBRARY_PATH`: Ensures that the C libraries you build can be linked at + runtime, you need to add their locations to this variable. * Example: If your shared library `libexample.so` is in - `/path/to/your/c/libs`, you would run: + `/path/to/your/c/libs`, run: ```bash export LD_LIBRARY_PATH=/path/to/your/c/libs:$LD_LIBRARY_PATH ``` -* `BINDGEN_EXTRA_CLANG_ARGS`: This variable allows you to pass extra arguments - to `libclang`. This is particularly useful for specifying include directories. +* `BINDGEN_EXTRA_CLANG_ARGS`: Contains extra arguments passed to `libclang`. + This is particularly useful for specifying include directories. - * To find your system's default include paths, you can run: + * Find your system's default include paths with ```bash clang -v -E -xc /dev/null ``` - * You can then set the variable with these paths: + * Set the variable accordingly, ```bash export BINDGEN_EXTRA_CLANG_ARGS="-nostdinc -I/usr/lib/clang/14/include -I/usr/include" ``` - **NOTE**: Without `--no-stdinc`, there are likely multiple directories in - the C include path that provide the same headers. Which header is ultimately - used depends on the order of directories in the include path. + > [!NOTE] + > Without `-no-stdinc`, there are likely multiple directories in the C + > include path that provide the same headers. Which header is ultimately + > used depends on the order of directories in the include path. * Note that the common use of this environment variable is to set preprocessor - flags. So only overwrite the include paths if absolutely necessary. + flags. Include paths can be directly set with the `-I` flag of + `hs-bindgen-cli`. * `LLVM_PATH`, `LLVM_CONFIG`: `hs-bindgen` may need to know where to find your LLVM installation. @@ -135,32 +136,30 @@ necessary tools and libraries in Linux. This is only needed when you want to use a version of LLVM/Clang that is not in your current `PATH`. -#### Common Errors and Solutions +#### Common errors and solutions * Missing headers (`stddef.h`, etc.): If you encounter errors about missing - standard headers, it's a sign that `libclang` cannot find the system's - include directories. Setting `BINDGEN_EXTRA_CLANG_ARGS` as described above - is the solution. + standard headers, `libclang` cannot find the system include directories. Try + setting `BINDGEN_EXTRA_CLANG_ARGS` as described above. -* Missing shared libraries (`libexample.so`): If you see an error like - `cannot open shared object file: No such file or directory`, it means the - dynamic linker can't find your C library. Adding the library's directory to - `LD_LIBRARY_PATH` or making sure your cabal.project.local points to the - right folder will resolve this. +* Missing shared libraries (`libexample.so`): If you see an error like `cannot + open shared object file: No such file or directory`, it means the dynamic + linker cannot find your C library. Try adding the directory of the library to + `LD_LIBRARY_PATH` or adapting your `cabal.project.local`. * Unicode character issues with LLVM backend: When using the LLVM backend, you might encounter issues with Unicode characters in your C code. This can sometimes manifest as errors at the assembler level. Ensure your source files do not include Unicode. -### MacOS +### macOS On macOS, the setup is similar to Linux, but with its own specific environment variables and considerations, especially concerning the system's default Clang -installation. Note that Apple's assembler does not support Unicode so make -sure to avoid using Unicode-specific characters in C function definitions. +installation. Note that Apple's assembler does not support Unicode, so avoid +using Unicode-specific characters in C function definitions. -#### Environment Variables +#### Environment variables * `DYLD_LIBRARY_PATH`: This is the macOS equivalent of `LD_LIBRARY_PATH`. It tells the dynamic linker where to find dynamic libraries (`.dylib` files). @@ -171,30 +170,28 @@ sure to avoid using Unicode-specific characters in C function definitions. export DYLD_LIBRARY_PATH=/path/to/your/c/libs:$DYLD_LIBRARY_PATH ``` -* `BINDGEN_EXTRA_CLANG_ARGS`: On MacOS, setting the include paths like we - suggest to do in Linux is not required. If you need to, see the section for - Linux on how to set it up. +* `BINDGEN_EXTRA_CLANG_ARGS`: On macOS, setting the include paths like we + suggest for Linux is not required. If you need to, see the corresponding + section for Linux. ### Windows -Setting up on Windows has its own set of unique challenges, particularly -around how Dynamic-Link Libraries (DLLs) are handled and the configuration of -the compiler environment. +Setup on Windows involves handling of Dynamic-Link Libraries (DLLs) and the +configuration of the compiler environment. #### Built-in Clang with GHC installation When you install GHC on Windows using GHCup, for example, it comes with a MinGW -environment that includes LLVM and Clang. Note that Windows' assembler does not -support Unicode so make sure to avoid using Unicode-specific characters in C -function definitions. +environment that includes LLVM and Clang. Windows' assembler does not support +Unicode, so avoid using Unicode-specific characters in C function definitions. #### Explicit handling of `PATH` for DLLs -On Windows, the primary way the system finds DLLs at runtime is by searching -the directories listed in the `PATH` environment variable. This is a crucial -difference from Linux and MacOS. +At runtime, Windows primarily finds DLLs by searching the directories listed in +the `PATH` environment variable. This is a crucial difference from Linux and +MacOS. -* To ensure your application can find its required DLLs, add the corresponding +* To ensure your application can find the required DLLs, add the corresponding directories to the `PATH`: ```powershell @@ -204,8 +201,7 @@ difference from Linux and MacOS. #### Environment Variables * `LLVM_PATH`, `LLVM_CONFIG`, `LIBCLANG_PATH`: You need to point `hs-bindgen` - to the LLVM/Clang installation that comes with GHC. Make sure these flags - aren't already set to the right paths. + to the LLVM/Clang installation that comes with GHC. ```powershell $env:LLVM_PATH = "C:\ghcup\ghc\\mingw" @@ -213,16 +209,17 @@ difference from Linux and MacOS. $env:LIBCLANG_PATH = "$env:LLVM_PATH\lib" ``` + * `BINDGEN_EXTRA_CLANG_ARGS`: On Windows, setting the include paths like we - suggest to do in Linux is not required. If you need to, see the section for - Linux on how to set it up. + suggest for Linux is not required. If you need to, see the corresponding + section for Linux. #### Common Errors and Solutions -* Dynamic-link library loading order: If your application fails silently - or with an `ExitFailure` and a cryptic error code like `(-1073741515)`, it - is very likely a DLL loading issue. Adding the directory containing your C - libraries' DLLs to the system `PATH` is the solution. +* Dynamic-link library loading order: If your application fails silently or with + an `ExitFailure` and a cryptic error code like `(-1073741515)`, it is very + likely a DLL loading issue. Try adding the directory containing the C DLLs of + your library to the system `PATH`. * Resolving issues with underlying type mismatches (`FC.CInt` vs. `FC.CUInt`): You might encounter Haskell type errors where, for example, a C From f5923f44e406ef1989ea5280a33fff8c01b46946 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Tue, 25 Nov 2025 14:43:11 +0100 Subject: [PATCH 06/12] Manual: Clang options and Includes --- manual/LowLevel/Usage/03-ClangOptions.md | 9 +++++---- manual/LowLevel/Usage/04-Includes.md | 14 +++++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/manual/LowLevel/Usage/03-ClangOptions.md b/manual/LowLevel/Usage/03-ClangOptions.md index 3b0ff140c..b4cfc2345 100644 --- a/manual/LowLevel/Usage/03-ClangOptions.md +++ b/manual/LowLevel/Usage/03-ClangOptions.md @@ -19,7 +19,8 @@ well. When using `hs-bindgen-cli preprocess`, Clang options may be specified as command-line options. Common options are exposed as `hs-bindgen-cli` command-line options, while arbitrary Clang options may be passed using -`--clang-option-before`, `--clang-option`, and `--clang-option-after`. +`--clang-option-before`, `--clang-option`, `--clang-option-after`, or +the environment variable `BINDGEN_EXTRA_CLANG_ARGS`. Options are passed to Clang in the following order: @@ -41,7 +42,7 @@ hs-bindgen-cli preprocess \ -I include \ --clang-option="-idirafter/opt/acme-0.1.0/include" \ --module Foo \ - --output Foo.hs \ + --hs-output-dir src \ foo.h ``` @@ -49,8 +50,8 @@ hs-bindgen-cli preprocess \ Clang options may also be set using environment variables. This is particularly useful when setting environment-specific configuration that may -not be hard-coded in the source code. Options specified via the CLI or the -Template Haskell API take precedence. +not be hard-coded in the source code. Most options specified via the CLI or the +Template Haskell API take precedence (see above). - __When compiling natively (i.e., without specifying a target)__, `hs-bindgen` reads `BINDGEN_EXTRA_CLANG_ARGS` and splits its string value into command-line diff --git a/manual/LowLevel/Usage/04-Includes.md b/manual/LowLevel/Usage/04-Includes.md index 58d412883..d3ed497d5 100644 --- a/manual/LowLevel/Usage/04-Includes.md +++ b/manual/LowLevel/Usage/04-Includes.md @@ -82,7 +82,7 @@ The following options are used to add a directory to a C include search path: These options can be passed multiple times, and the directories are added to the C include search paths in the same order. -The following are used to disable default include directories: +The following options are used to disable default include directories: * `-nostdinc` disables the default include directories (both the standard system directories and the compiler builtin directories). @@ -154,14 +154,14 @@ the CAPI section below. environment variables, described below. In addition, `libclang` process the Clang environment variables, described above. -[Clang options]: +[Clang options]: <03-ClangOptions.md> `libclang` constructs C include search paths like Clang, but it is generally unable to correctly determine the builtin include directory. When the builtin include directory is not configured correctly, one often gets "`stddef.h` not -found" or similar include resolution errors. `hs-bindgen` can attempt to -determine and configure the builtin include directory automatically so that it -does not have to be done manually. +found" or similar include resolution errors. By default, `hs-bindgen` attempts +to determine and configure the builtin include directory automatically so that +it does not have to be done manually. `hs-bingden` has two modes for configuring the builtin include directory: @@ -208,7 +208,7 @@ hs-bindgen-cli preprocess \ -I include \ --clang-option="-idirafter/opt/acme-0.1.0/include" \ --module Foo \ - --output Foo.hs \ + --hs-output-dir src \ foo.h ``` @@ -232,7 +232,7 @@ or Template Haskell `hashInclude` function calls. In addition, headers are specified in [Binding specifications][]. All of these headers are resolved via `libclang` using bracket includes. -[Binding specifications]: +[Binding specifications]: <06-BindingSpecifications.md> Headers must be specified as they would be in an include directive. They are generally relative to a directory in a C include search path, but note that they From bde887e369fc477e911aa64049f1518cc62876be Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Tue, 25 Nov 2025 15:05:17 +0100 Subject: [PATCH 07/12] Manual: Parsing, selecting and program slicing --- .../05-ParsingSelectingAndProgramSlicing.md | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md b/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md index b0bc63164..02321efd8 100644 --- a/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md +++ b/manual/LowLevel/Usage/05-ParsingSelectingAndProgramSlicing.md @@ -9,10 +9,10 @@ The determination of which C declarations to translate is a complex problem. In 2. We do not parse and reify all declarations read and provided by Clang. Instead, we match declarations against [_parse - predicates_](#parse-predicates). We parse and reify matching declarations, - and exclude declarations not matching the parse predicate. That is, the parse - predicate dictates which declarations `hs-bindgen` reifies into - `hs-bindgen`-specific data structures. + predicates_](#parse-predicates). The parse predicate dictates which + declarations `hs-bindgen` reifies into `hs-bindgen`-specific data structures. + We parse and reify declarations matching the parse predicate, and do not + attempt to parse and reify declarations not matching the parse predicate. 3. Given the set of declarations parsed and reified by `hs-bindgen`, [_select predicates_](#select-predicates) dictate which declarations to generate @@ -34,7 +34,7 @@ these main headers including all declarations therein, and transitive dependencies. > [!NOTE] -> The manual section [Includes](./Includes.md) describes how headers are +> The manual section [Includes](./04-Includes.md) describes how headers are > resolved. ## Parse predicates @@ -42,19 +42,20 @@ dependencies. Parse predicates control if `hs-bindgen` parses and reifies a declaration read by Clang into `hs-bindgen`-specific data structures. `hs-bindgen` knows the definitions of parsed declarations. However, `hs-bindgen` does not know much -about non-parsed declarations. `hs-bindgen` registers those declarations as -_non-parsed_, and these declarations can be made available to `hs-bindgen` using -[external bindings](./BindingSpecifications.md). Anonymous declarations must be -parsed to use an external binding for them, however. They must be named in order -to be specified in an external binding specification. +about declarations it does not attempt to parse, or fails to parse. `hs-bindgen` +registers those declarations as _parse-not-attempted_ and _parse-failed_, +respectively. These declarations can be made available to `hs-bindgen` using +[external bindings](./06-BindingSpecifications.md). Anonymous declarations must +be parsed to use an external binding for them, however. They must be named in +order to be specified in an external binding specification. We use parse predicates mostly because: - We seek to avoid repetitive parsing and reification. We can do so by using - binding specifications. For example, the standard library need not be parsed - every time, and we provide excellent external bindings covering the standard - library. Also, declarations may be in external libraries for which the user - has external bindings. + binding specifications. For example, we avoid parsing the standard library + every time, and provide external bindings covering the standard library. Also, + declarations may be in external libraries for which the user has external + bindings. - Not all declarations can be parsed and reified by `hs-bindgen`. We may want to exclude such declarations and instead provide manual external bindings for them. @@ -70,8 +71,8 @@ changed; in particular, the command line options are: - `--parse-from-main-header-dirs`: Parse declarations in main headers and transitively included headers from sub-directories of main headers (default). - `--parse-by-header-path PCRE` and `--parse-except-by-header-path PCRE`: Parse - or exclude declarations in headers with paths matching the regular expression - `PCRE`, respectively. + or do not attempt to parse declarations in headers with paths matching the + regular expression `PCRE`, respectively. Also the `hs-bindgen` library and the Template Haskell interface allow direct specification of the parse predicate using the data type `ParsePredicate`. @@ -82,27 +83,28 @@ specification of the parse predicate using the data type `ParsePredicate`. > definitions. These declarations will be filtered out during selection. > [!NOTE] -> Note that parse predicates match against header _paths_, and not just header -> filenames. The paths are determined by Clang, dependent on the C include path +> Parse predicates match against header _paths_, and not just header filenames. +> The paths are determined by Clang, dependent on the C include path > directories. They may be absolute or relative. The manual section -> [Includes](./Includes.md) describes how headers are resolved. +> [Includes](./04-Includes.md) describes how headers are resolved. Parse predicates do not support matching on declaration names because names are -only determined later in the translation process, in particular, after name +only determined later in the translation process; in particular, after name mangling and resolving and applying binding specifications. Use [_select predicates_](#select-predicates) to match on declaration names. > [!NOTE] -> Note that path separators (forward slash on POSIX platforms and backslash on -> Windows) can be tricky when used in regular expressions. +> Path separators (forward slash on POSIX platforms and backslash on Windows) +> can be tricky when used in regular expressions. ## Select predicates After parsing the declarations, the frontend of `hs-bindgen` sorts the parsed declarations, handles macros, [provides names to -declarations](GeneratedNames.md) and resolves and applies [binding -specifications](BindingSpecifications.md). Then, it matches a _select -predicate_, further reducing the number of declarations to be translated. +declarations](../Translation/01-GeneratedNames.md) and resolves and applies +[binding specifications](06-BindingSpecifications.md). Then, it matches a +_select predicate_, further reducing the number of declarations to be +translated. _Select predicates_ allow fine-grained control about which declarations to select for translation. For example, select predicates allow matching against @@ -129,10 +131,10 @@ predicates are (excerpt of `hs-bindgen-cli preprocess --help`): ``` > [!NOTE] -> Select predicates will support selection of anonymous types. This is not -> implemented yet; see issues -> [#844](https://github.com/well-typed/hs-bindgen/issues/844) and -> [#960](https://github.com/well-typed/hs-bindgen/pull/960). +> For anonymous declarations, the select predicate matches against the +> _use-sites_ of the anonymous declarations. For example, to select an anonymous +> inner `struct` together with the named outer `struct`, match against the name +> of the outer `struct`. > [!NOTE] > We match select predicates before handling C `typedef`s. That is, when @@ -169,7 +171,7 @@ external binding for `FileOperationStatus`. > [!NOTE] > Program slicing can cause declarations to be included even if they are -> explicitly excluded by a select predicate. +> explicitly deselected by a select predicate. ## Notes and examples @@ -211,7 +213,7 @@ declarations `hs-bindgen` reifies into `hs-bindgen`-specific data structures, the select predicate dictates which declarations `hs-bindgen` generates bindings for. -Example 1: Assume program slicing is enabled. All of the following scenarios +Example: Assume program slicing is enabled. All of the following scenarios are different: - Parse `library/*.h`, select `library/main.h`. From af201483418ec032e57ebcfdbf833cd68afa9371 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Tue, 25 Nov 2025 15:48:14 +0100 Subject: [PATCH 08/12] Manual: Binding specifications --- .../Usage/06-BindingSpecifications.md | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/manual/LowLevel/Usage/06-BindingSpecifications.md b/manual/LowLevel/Usage/06-BindingSpecifications.md index a1ed2ee8b..468798119 100644 --- a/manual/LowLevel/Usage/06-BindingSpecifications.md +++ b/manual/LowLevel/Usage/06-BindingSpecifications.md @@ -69,29 +69,38 @@ bindings. When we generate bindings for `vector.h`, we can ask `hs-bindgen` to produce external bindings in addition to the Haskell module (command line flag -`--gen-external-bindings`). This will result in a file that looks like this: +`--gen-binding-spec`). This will result in a file that looks like this: ```yaml -types: +version: + hs_bindgen: 0.1.0 + binding_specification: '1.0' +hsmodule: Vector +ctypes: - headers: vector.h cname: vector - package: hs-vector - module: Vector - identifier: Vector + hsname: Vector +hstypes: +- hsname: Vector + instances: + - Eq + - Show + - Storable ``` This says that the C type called `vector`, defined in `vector.h`, should be -mapped to the type called `Vector` defined in module `Vector` from the -`hs-vector` package (rather than _generating_ a definition for it). +mapped to the type called `Vector` defined in module `Vector` (rather than +_generating_ a definition for it). We can then use these external bindings when processing `vector_rotate.h` (command line flag `--external-binding-spec`). This will result in something -like this: +like ```haskell import qualified Vector -foreign import capi safe "vector_rotate.h vector_rotate" +$(addCSource "...") +foreign import ccall safe "vector_rotate_interface_function" vector_rotate :: Ptr Vector.Vector -> CDouble -> IO (Ptr Vector.Vector) ``` @@ -127,12 +136,20 @@ pattern Length x <- (unwrap -> x) We can do this by handwriting an external bindings file: ```yaml -types: +version: + hs_bindgen: 0.1.0 + binding_specification: '1.0' +hsmodule: Vector.Types +ctypes: - headers: vector_length.h cname: len - package: hs-vector - module: Vector.Types - identifier: Length + hsname: Length +hstypes: +- hsname: Length + instances: + - Eq + - Show + - Storable ``` If we then use `--external-binding-spec` _twice_ when processing @@ -143,7 +160,8 @@ the external bindings for `Length`), we get import qualified Vector import qualified Vector.Types -foreign import capi safe "vector_length.h vector_length" +$(addCSource "...") +foreign import ccall safe "vector_length_interface_function" vector_length :: Ptr Vector.Vector -> IO Vector.Types.Length ``` @@ -181,14 +199,16 @@ For this reason, external bindings can mention more than one header for a given C name: ```yaml -types: +version: + hs_bindgen: 0.1.0 + binding_specification: '1.0' +hsmodule: Game.State +ctypes: - headers: - - game_world.h - game_player.h + - game_world.h cname: game_state - package: hs-game - module: Game.State - identifier: Game_state + hsname: Game_state ``` The C name `game_state` is then considered to be defined "in" `game_world.h` or From dbfe2474dceb1c9e092165b8bab37518f1d1bddb Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Tue, 25 Nov 2025 17:35:28 +0100 Subject: [PATCH 09/12] Manual: Translation part --- .../LowLevel/Translation/01-GeneratedNames.md | 2 +- manual/LowLevel/Translation/02-Structs.md | 24 ++++++++----------- manual/LowLevel/Translation/03-Enums.md | 8 +++---- manual/LowLevel/Translation/06-Globals.md | 24 +++++++++---------- .../Usage/06-BindingSpecifications.md | 2 +- manual/c/structs.h | 13 ++++++++++ manual/hs/manual/app/Manual/Types/Structs.hs | 3 --- 7 files changed, 41 insertions(+), 35 deletions(-) diff --git a/manual/LowLevel/Translation/01-GeneratedNames.md b/manual/LowLevel/Translation/01-GeneratedNames.md index 1a6edd7b0..837d528d3 100644 --- a/manual/LowLevel/Translation/01-GeneratedNames.md +++ b/manual/LowLevel/Translation/01-GeneratedNames.md @@ -19,7 +19,7 @@ Haskell name will be, and also is less likely to result in name clashes. By default, we use the name in the C header wherever one exists. An obvious example is function definitions, where the candidate name for a function `foo` -is simply `foo`. Similarly, if a struct is given a tag or a typedef, +is simply `foo`. Similarly, if a `struct` is given a tag or a `typedef`, ```c struct foo { .. }; diff --git a/manual/LowLevel/Translation/02-Structs.md b/manual/LowLevel/Translation/02-Structs.md index e90861a0c..ef32722c2 100644 --- a/manual/LowLevel/Translation/02-Structs.md +++ b/manual/LowLevel/Translation/02-Structs.md @@ -22,18 +22,16 @@ data Triple = Triple , triple_b :: CInt , triple_c :: CInt } + deriving stock (Eq, Show) instance F.Storable Triple where ... -deriving stock instance Show Triple -deriving stock instance Eq Triple ``` ## Structures with and without `typedef` Adding a `typedef` matching the name of the structure, or using a `typedef` in place of a structure name, does not change the generated bindings. For more -details, see the [section on name -generation](/manual/LowLevel/GeneratedNames.md). +details, see the [section on name generation](01-GeneratedNames.md). ```c typedef struct triple triple; @@ -47,10 +45,8 @@ typedef struct triple triple_t; ```haskell newtype Triple_t = Triple_t { un_Triple_t :: Triple } - -deriving newtype instance F.Storable Triple_t -deriving stock instance Eq Triple_t -deriving stock instance Show Triple_t + deriving stock (Eq, Show) + deriving newtype (Storable) ``` ## Nested structures @@ -82,7 +78,7 @@ creates the following bindings (instances omitted for brevity): ```haskell data Door = Door { door_height :: CFloat - , door_width :: CFloat + , door_width :: CFloat } data Room = Room @@ -135,7 +131,7 @@ struct aula2 { ```haskell data Aula2_door = Aula2_door { aula2_door_height :: CFloat - , aula2_door_width :: CFloat + , aula2_door_width :: CFloat } data Aula2 = Aula2 @@ -166,11 +162,11 @@ structure in their `Storable` instance: ```haskell data Aula_setup = Aula_setup - { aula_setup_window_id :: CChar - , aula_setup_tilt :: CInt + { aula_setup_window_id :: CChar + , aula_setup_tilt :: CInt , aula_setup_close_blinds :: CInt , aula_setup_projector_id :: CChar - , aula_setup_power_mode :: CInt + , aula_setup_power_mode :: CInt } instance F.Storable Aula_setup where @@ -304,5 +300,5 @@ foreign import ccall safe "Structs_create_square" create_square :: CDouble -> IO (Ptr Square) ``` -Note that opaque types do not get a Storable instance, and therefore can not be +Note that opaque types do not get a `Storable` instance, and therefore can not be used by value. diff --git a/manual/LowLevel/Translation/03-Enums.md b/manual/LowLevel/Translation/03-Enums.md index 165f741cb..2f4d9b1d9 100644 --- a/manual/LowLevel/Translation/03-Enums.md +++ b/manual/LowLevel/Translation/03-Enums.md @@ -124,8 +124,8 @@ class Integral (CEnumZ a) => CEnum a where toCEnum :: CEnumZ a -> a fromCEnum :: a -> CEnumZ a - declaredValues :: proxy a -> DeclaredValues a - showsUndeclared :: proxy a -> Int -> CEnumZ a -> ShowS + declaredValues :: proxy a -> DeclaredValues a + showsUndeclared :: proxy a -> Int -> CEnumZ a -> ShowS readPrecUndeclared :: ReadPrec a ``` @@ -143,7 +143,7 @@ instance CEnum Index where , (1, NonEmpty.singleton "B") , (2, NonEmpty.singleton "C") ] - showsUndeclared = showsWrappedUndeclared "Index" + showsUndeclared = showsWrappedUndeclared "Index" readPrecundeclared = readPrecWrappedUndeclared "Index" ``` @@ -250,7 +250,7 @@ showCursorKind :: CXCursorKind -> String showCursorKind = \case CXCursor_UnexposedExpr -> "CXCursor_UnexposedExpr" CXCursor_UnexposedStmt -> "CXCursor_UnexposedStmt" - kind -> show kind + kind -> show kind ``` We also provide a helper function `showCEnum` for C enums without a specialized diff --git a/manual/LowLevel/Translation/06-Globals.md b/manual/LowLevel/Translation/06-Globals.md index c1cde13b5..620f86d29 100644 --- a/manual/LowLevel/Translation/06-Globals.md +++ b/manual/LowLevel/Translation/06-Globals.md @@ -193,11 +193,11 @@ x_ptr = unsafePerformIO fe8f4js8 Memory layout: -| type | name | address | value | -| ------------------------ | ------------- | ------- | ------- | -| int | x | 1000 | 17 | -| | | ... | | -| int* ; Ptr CInt | x_ptr | 2000 | 1000 | +| type | name | address | value | +|-------------------|-------|---------|-------| +| `int` | x | 1000 | 17 | +| | | ... | | +| `int* ; Ptr CInt` | x_ptr | 2000 | 1000 | Constant: @@ -380,13 +380,13 @@ x_elem_ptr = IncompleteArray.toFirstElemPtr x_ptr Memory layout: -| type | name | address | value | -| ------------------------------------- | ------------- | ------- | ------- | -| int[] | x | 1000 | 1 | -| | | 1004 | 2 | -| | | 1008 | 3 | -| | | ... | | -| (*int)[] ; Ptr (IncompleteArray CInt) | x_ptr | 2000 | 1000 | +| type | name | address | value | +|-----------------------------------------|-------|---------|-------| +| `int[]` | x | 1000 | 1 | +| | | 1004 | 2 | +| | | 1008 | 3 | +| | | ... | | +| `(*int)[] ; Ptr (IncompleteArray CInt)` | x_ptr | 2000 | 1000 | Constant: diff --git a/manual/LowLevel/Usage/06-BindingSpecifications.md b/manual/LowLevel/Usage/06-BindingSpecifications.md index 468798119..a48c951ef 100644 --- a/manual/LowLevel/Usage/06-BindingSpecifications.md +++ b/manual/LowLevel/Usage/06-BindingSpecifications.md @@ -123,7 +123,7 @@ module Vector.Types where newtype Length = UnsafeWrap { unwrap :: Double } -instance Show Length where .. +instance Show Length where ... pattern Length :: Double -> Length pattern Length x <- (unwrap -> x) diff --git a/manual/c/structs.h b/manual/c/structs.h index b193fc974..568b49f5d 100644 --- a/manual/c/structs.h +++ b/manual/c/structs.h @@ -1,5 +1,18 @@ #include +/* -------------------------------------------------------------------------- */ +/* Structures with and without typedef. */ + +typedef struct triple { + int a; + int b; + int c; +} triple; + +typedef struct triple triple_t; + +void mk_triple(int a, int b, int c, triple *triple); + /* -------------------------------------------------------------------------- */ /* Nested structures. */ diff --git a/manual/hs/manual/app/Manual/Types/Structs.hs b/manual/hs/manual/app/Manual/Types/Structs.hs index 063a140a9..f85413b1a 100644 --- a/manual/hs/manual/app/Manual/Types/Structs.hs +++ b/manual/hs/manual/app/Manual/Types/Structs.hs @@ -14,9 +14,6 @@ import HsBindgen.Runtime.FlexibleArrayMember qualified as FLAM import HsBindgen.Runtime.IncompleteArray qualified as IA import Manual.Tools - -import Example -import Example.Unsafe import Structs import Structs.Safe From dbf9108910dec9dd9cb8d7fc2af7d382f8b1954c Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Wed, 26 Nov 2025 08:05:17 +0100 Subject: [PATCH 10/12] Manual: Move large parts of Invocation to Installation --- manual/Installation.md | 212 ++++++++++++++++++++++- manual/LowLevel/Usage/01-Invocation.md | 222 ------------------------- 2 files changed, 207 insertions(+), 227 deletions(-) diff --git a/manual/Installation.md b/manual/Installation.md index 3ff706efd..1d3bd00ec 100644 --- a/manual/Installation.md +++ b/manual/Installation.md @@ -1,19 +1,221 @@ # Installation -TODO +Depending on the system, specific steps ensure a smooth experience with +`hs-bindgen`. This guide will walk you through the setup process for Linux, +macOS, and Windows. ## Linux -TODO +Setup on Linux involves choosing between the GCC and Clang compiler toolchains. + +### Compiler choice (GCC vs. Clang) + +GCC is the default compiler on many Linux distributions and is also the compiler +used by GHC. However, `hs-bindgen` uses `libclang` internally to parse C code. +GCC and Clang will mostly be compatible, but there are some exceptions. For +example, GCC and Clang may exhibit differences in memory layout. Unfortunately, +it is not straightforward to make GHC use Clang without effectively building GHC +from scratch. There are flags like `-pgmc` and `-pgml` ([see the GHC user +guide](https://downloads.haskell.org/ghc/latest/docs/users_guide/phases.html#replacing-the-program-for-one-or-more-phases)) +which allow specification of the C compiler used by GHC; however, experiments +showed that just setting these flags was not enough. For more details see the +discussions at issues +[#846](https://github.com/well-typed/hs-bindgen/issues/846), and +[#847](https://github.com/well-typed/hs-bindgen/issues/847). + +Using Clang for both, generation of and compilation of the bindings ensures +consistency and correctness, but we currently do not thoroughly test compilation +of code using the generated bindings with Clang. + +### Installation of LLVM and Clang + +You need to install LLVM and Clang. The specific package names may vary +depending on your distribution. + +### Adding `cabal.project.local` + +We need to inform the program about the location of the C sources. We can do so +by creating a `cabal.project.local` file that defines `extra-include-dirs` and +`extra-lib-dirs`. + +For example, to build the manual you will need the following: + +```cabal +package manual + extra-include-dirs: + /manual/c + extra-lib-dirs: + /manual/c + +package hs-game + extra-include-dirs: + /manual/c + extra-lib-dirs: + /manual/c + +package hs-vector + extra-include-dirs: + /manual/c + extra-lib-dirs: + /manual/c +``` + +Note that the example above enables compilation of the manual from this +repository. For a custom project, `extra-include-dirs` and `extra-lib-dirs` +should be set in the `.cabal` file. + +### Setting environment variables + +* `LD_LIBRARY_PATH`: Ensures that the C libraries you build can be linked at + runtime, you need to add their locations to this variable. + + * Example: If your shared library `libexample.so` is in + `/path/to/your/c/libs`, run: + + ```bash + export LD_LIBRARY_PATH=/path/to/your/c/libs:$LD_LIBRARY_PATH + ``` + +* `BINDGEN_EXTRA_CLANG_ARGS`: Contains extra arguments passed to `libclang`. + This is particularly useful for specifying include directories. + + * Find your system's default include paths with + + ```bash + clang -v -E -xc /dev/null + ``` + + * Set the variable accordingly, + + ```bash + export BINDGEN_EXTRA_CLANG_ARGS="-nostdinc -I/usr/lib/clang/14/include -I/usr/include" + ``` + + > [!NOTE] + > Without `-no-stdinc`, there are likely multiple directories in the C + > include path that provide the same headers. Which header is ultimately + > used depends on the order of directories in the include path. + + * Note that the common use of this environment variable is to set preprocessor + flags. Include paths can be directly set with the `-I` flag of + `hs-bindgen-cli`. + +* `LLVM_PATH`, `LLVM_CONFIG`: `hs-bindgen` may need to know where to find + your LLVM installation. + + ```bash + export LLVM_PATH=/usr/lib/llvm-14 + export LLVM_CONFIG=$LLVM_PATH/bin/llvm-config + ``` + + This is only needed when you want to use a version of LLVM/Clang that is + not in your current `PATH`. + +### Common errors and solutions + +* Missing headers (`stddef.h`, etc.): If you encounter errors about missing + standard headers, `libclang` cannot find the system include directories. Try + setting `BINDGEN_EXTRA_CLANG_ARGS` as described above. + +* Missing shared libraries (`libexample.so`): If you see an error like `cannot + open shared object file: No such file or directory`, it means the dynamic + linker cannot find your C library. Try adding the directory of the library to + `LD_LIBRARY_PATH` or adapting your `cabal.project.local`. + +* Unicode character issues with LLVM backend: When using the LLVM backend, + you might encounter issues with Unicode characters in your C code. This can + sometimes manifest as errors at the assembler level. Ensure your source + files do not include Unicode. ## macOS -TODO +On macOS, the setup is similar to Linux, but with its own specific environment +variables and considerations, especially concerning the system's default Clang +installation. Note that Apple's assembler does not support Unicode, so avoid +using Unicode-specific characters in C function definitions. + +### Environment variables + +* `DYLD_LIBRARY_PATH`: This is the macOS equivalent of `LD_LIBRARY_PATH`. It + tells the dynamic linker where to find dynamic libraries (`.dylib` files). + + Example: + + ```bash + export DYLD_LIBRARY_PATH=/path/to/your/c/libs:$DYLD_LIBRARY_PATH + ``` + +* `BINDGEN_EXTRA_CLANG_ARGS`: On macOS, setting the include paths like we + suggest for Linux is not required. If you need to, see the corresponding + section for Linux. ## Windows -TODO +Setup on Windows involves handling of Dynamic-Link Libraries (DLLs) and the +configuration of the compiler environment. + +### Built-in Clang with GHC installation + +When you install GHC on Windows using GHCup, for example, it comes with a MinGW +environment that includes LLVM and Clang. Windows' assembler does not support +Unicode, so avoid using Unicode-specific characters in C function definitions. + +### Explicit handling of `PATH` for DLLs + +At runtime, Windows primarily finds DLLs by searching the directories listed in +the `PATH` environment variable. This is a crucial difference from Linux and +MacOS. + +* To ensure your application can find the required DLLs, add the corresponding + directories to the `PATH`: + + ```powershell + $env:PATH = "C:\path\to\your\c\libs;" + $env:PATH + ``` + +### Environment Variables + +* `LLVM_PATH`, `LLVM_CONFIG`, `LIBCLANG_PATH`: You need to point `hs-bindgen` + to the LLVM/Clang installation that comes with GHC. + + ```powershell + $env:LLVM_PATH = "C:\ghcup\ghc\\mingw" + $env:LLVM_CONFIG = "$env:LLVM_PATH\bin\llvm-config.exe" + $env:LIBCLANG_PATH = "$env:LLVM_PATH\lib" + ``` + + +* `BINDGEN_EXTRA_CLANG_ARGS`: On Windows, setting the include paths like we + suggest for Linux is not required. If you need to, see the corresponding + section for Linux. + +### Common Errors and Solutions + +* Dynamic-link library loading order: If your application fails silently or with + an `ExitFailure` and a cryptic error code like `(-1073741515)`, it is very + likely a DLL loading issue. Try adding the directory containing the C DLLs of + your library to the system `PATH`. + +* Resolving issues with underlying type mismatches (`FC.CInt` vs. + `FC.CUInt`): You might encounter Haskell type errors where, for example, a C + `int` is being interpreted as a `CUInt` instead of a `CInt`. This is often + due to how different compilers and platforms define basic types. Carefully + check your C and Haskell type definitions to ensure they match. + + Bindings generated by `hs-bindgen` are not portable. In order to create a + portable API, one must do so at a higher level, using CPP to create an + abstraction layer over the low-level, platform-specific bindings. + + Given that `hs-bindgen` only supports generating bindings for a subset of + targets people use Hackage with, perhaps all generated bindings uploaded to + Hackage should include appropriate gates. Minimal example: + + ```cabal + if !(os(linux) && arch(x86_64)) + buildable: false + ``` ## Nix -TODO +We provide and maintain a [Nix Flake](../../../flake.nix), and a [Nix-focused +tutorial](https://github.com/well-typed/hs-bindgen-tutorial-nix). diff --git a/manual/LowLevel/Usage/01-Invocation.md b/manual/LowLevel/Usage/01-Invocation.md index 42b1fcd59..8bb2eea4a 100644 --- a/manual/LowLevel/Usage/01-Invocation.md +++ b/manual/LowLevel/Usage/01-Invocation.md @@ -17,225 +17,3 @@ TODO ## Haskell library TODO - -## Preparation of system environment for `hs-bindgen` - -Depending on the platform, specific steps ensure a smooth experience with -`hs-bindgen`. This guide will walk you through the setup process for Linux, -macOS, and Windows. - -### Linux - -Setup on Linux involves choosing between the GCC and Clang compiler toolchains. - ->[!NOTE] -> -> We also provide and maintain [Nix Flake](../../../flake.nix), and a -> [Nix-focused tutorial](https://github.com/well-typed/hs-bindgen-tutorial-nix). - -#### Compiler choice (GCC vs. Clang) - -GCC is the default compiler on many Linux distributions and is also the compiler -used by GHC. However, `hs-bindgen` uses `libclang` internally to parse C code. -GCC and Clang will mostly be compatible, but there are some exceptions. For -example, GCC and Clang may exhibit differences in memory layout. Unfortunately, -it is not straightforward to make GHC use Clang without effectively building GHC -from scratch. There are flags like `-pgmc` and `-pgml` ([see the GHC user -guide](https://downloads.haskell.org/ghc/latest/docs/users_guide/phases.html#replacing-the-program-for-one-or-more-phases)) -which allow specification of the C compiler used by GHC; however, experiments -showed that just setting these flags was not enough. For more details see the -discussions at issues -[#846](https://github.com/well-typed/hs-bindgen/issues/846), and -[#847](https://github.com/well-typed/hs-bindgen/issues/847). - -Using Clang for both, generation of and compilation of the bindings ensures -consistency and correctness, but we currently do not thoroughly test compilation -of code using the generated bindings with Clang. - -#### Installation of LLVM and Clang - -You need to install LLVM and Clang. The specific package names may vary -depending on your distribution. - -#### Adding `cabal.project.local` - -We need to inform the program about the location of the C sources. We can do so -by creating a `cabal.project.local` file that defines `extra-include-dirs` and -`extra-lib-dirs`. - -For example, to build the manual you will need the following: - -```cabal -package manual - extra-include-dirs: - /manual/c - extra-lib-dirs: - /manual/c - -package hs-game - extra-include-dirs: - /manual/c - extra-lib-dirs: - /manual/c - -package hs-vector - extra-include-dirs: - /manual/c - extra-lib-dirs: - /manual/c -``` - -Note that the example above enables compilation of the manual from this -repository. For a custom project, `extra-include-dirs` and `extra-lib-dirs` -should be set in the `.cabal` file. - -#### Setting environment variables - -* `LD_LIBRARY_PATH`: Ensures that the C libraries you build can be linked at - runtime, you need to add their locations to this variable. - - * Example: If your shared library `libexample.so` is in - `/path/to/your/c/libs`, run: - - ```bash - export LD_LIBRARY_PATH=/path/to/your/c/libs:$LD_LIBRARY_PATH - ``` - -* `BINDGEN_EXTRA_CLANG_ARGS`: Contains extra arguments passed to `libclang`. - This is particularly useful for specifying include directories. - - * Find your system's default include paths with - - ```bash - clang -v -E -xc /dev/null - ``` - - * Set the variable accordingly, - - ```bash - export BINDGEN_EXTRA_CLANG_ARGS="-nostdinc -I/usr/lib/clang/14/include -I/usr/include" - ``` - - > [!NOTE] - > Without `-no-stdinc`, there are likely multiple directories in the C - > include path that provide the same headers. Which header is ultimately - > used depends on the order of directories in the include path. - - * Note that the common use of this environment variable is to set preprocessor - flags. Include paths can be directly set with the `-I` flag of - `hs-bindgen-cli`. - -* `LLVM_PATH`, `LLVM_CONFIG`: `hs-bindgen` may need to know where to find - your LLVM installation. - - ```bash - export LLVM_PATH=/usr/lib/llvm-14 - export LLVM_CONFIG=$LLVM_PATH/bin/llvm-config - ``` - - This is only needed when you want to use a version of LLVM/Clang that is - not in your current `PATH`. - -#### Common errors and solutions - -* Missing headers (`stddef.h`, etc.): If you encounter errors about missing - standard headers, `libclang` cannot find the system include directories. Try - setting `BINDGEN_EXTRA_CLANG_ARGS` as described above. - -* Missing shared libraries (`libexample.so`): If you see an error like `cannot - open shared object file: No such file or directory`, it means the dynamic - linker cannot find your C library. Try adding the directory of the library to - `LD_LIBRARY_PATH` or adapting your `cabal.project.local`. - -* Unicode character issues with LLVM backend: When using the LLVM backend, - you might encounter issues with Unicode characters in your C code. This can - sometimes manifest as errors at the assembler level. Ensure your source - files do not include Unicode. - -### macOS - -On macOS, the setup is similar to Linux, but with its own specific environment -variables and considerations, especially concerning the system's default Clang -installation. Note that Apple's assembler does not support Unicode, so avoid -using Unicode-specific characters in C function definitions. - -#### Environment variables - -* `DYLD_LIBRARY_PATH`: This is the macOS equivalent of `LD_LIBRARY_PATH`. It - tells the dynamic linker where to find dynamic libraries (`.dylib` files). - - Example: - - ```bash - export DYLD_LIBRARY_PATH=/path/to/your/c/libs:$DYLD_LIBRARY_PATH - ``` - -* `BINDGEN_EXTRA_CLANG_ARGS`: On macOS, setting the include paths like we - suggest for Linux is not required. If you need to, see the corresponding - section for Linux. - -### Windows - -Setup on Windows involves handling of Dynamic-Link Libraries (DLLs) and the -configuration of the compiler environment. - -#### Built-in Clang with GHC installation - -When you install GHC on Windows using GHCup, for example, it comes with a MinGW -environment that includes LLVM and Clang. Windows' assembler does not support -Unicode, so avoid using Unicode-specific characters in C function definitions. - -#### Explicit handling of `PATH` for DLLs - -At runtime, Windows primarily finds DLLs by searching the directories listed in -the `PATH` environment variable. This is a crucial difference from Linux and -MacOS. - -* To ensure your application can find the required DLLs, add the corresponding - directories to the `PATH`: - - ```powershell - $env:PATH = "C:\path\to\your\c\libs;" + $env:PATH - ``` - -#### Environment Variables - -* `LLVM_PATH`, `LLVM_CONFIG`, `LIBCLANG_PATH`: You need to point `hs-bindgen` - to the LLVM/Clang installation that comes with GHC. - - ```powershell - $env:LLVM_PATH = "C:\ghcup\ghc\\mingw" - $env:LLVM_CONFIG = "$env:LLVM_PATH\bin\llvm-config.exe" - $env:LIBCLANG_PATH = "$env:LLVM_PATH\lib" - ``` - - -* `BINDGEN_EXTRA_CLANG_ARGS`: On Windows, setting the include paths like we - suggest for Linux is not required. If you need to, see the corresponding - section for Linux. - -#### Common Errors and Solutions - -* Dynamic-link library loading order: If your application fails silently or with - an `ExitFailure` and a cryptic error code like `(-1073741515)`, it is very - likely a DLL loading issue. Try adding the directory containing the C DLLs of - your library to the system `PATH`. - -* Resolving issues with underlying type mismatches (`FC.CInt` vs. - `FC.CUInt`): You might encounter Haskell type errors where, for example, a C - `int` is being interpreted as a `CUInt` instead of a `CInt`. This is often - due to how different compilers and platforms define basic types. Carefully - check your C and Haskell type definitions to ensure they match. - - Bindings generated by `hs-bindgen` are not portable. In order to create a - portable API, one must do so at a higher level, using CPP to create an - abstraction layer over the low-level, platform-specific bindings. - - Given that `hs-bindgen` only supports generating bindings for a subset of - targets people use Hackage with, perhaps all generated bindings uploaded to - Hackage should include appropriate gates. Minimal example: - - ```cabal - if !(os(linux) && arch(x86_64)) - buildable: false - ``` From c3a087e2eef0d93a5a48f8c926920defcec54877 Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Wed, 26 Nov 2025 13:15:21 +0100 Subject: [PATCH 11/12] Manual: Categorize all sections --- .../01-Visibility.md} | 0 manual/LowLevel/Translation/02-Structs.md | 2 +- .../05-ZeroCopy.md} | 0 .../Translation/{05-Functions.md => 06-Functions.md} | 2 +- .../Translation/{06-Globals.md => 07-Globals.md} | 0 .../Translation/{07-Macros.md => 08-Macros.md} | 0 manual/README.md | 11 ++++++++--- 7 files changed, 10 insertions(+), 5 deletions(-) rename manual/LowLevel/{Uncategorized-Visibility.md => Appendix/01-Visibility.md} (100%) rename manual/LowLevel/{Uncategorized-ZeroCopy.md => Translation/05-ZeroCopy.md} (100%) rename manual/LowLevel/Translation/{05-Functions.md => 06-Functions.md} (99%) rename manual/LowLevel/Translation/{06-Globals.md => 07-Globals.md} (100%) rename manual/LowLevel/Translation/{07-Macros.md => 08-Macros.md} (100%) diff --git a/manual/LowLevel/Uncategorized-Visibility.md b/manual/LowLevel/Appendix/01-Visibility.md similarity index 100% rename from manual/LowLevel/Uncategorized-Visibility.md rename to manual/LowLevel/Appendix/01-Visibility.md diff --git a/manual/LowLevel/Translation/02-Structs.md b/manual/LowLevel/Translation/02-Structs.md index ef32722c2..e324b1772 100644 --- a/manual/LowLevel/Translation/02-Structs.md +++ b/manual/LowLevel/Translation/02-Structs.md @@ -5,7 +5,7 @@ structures](https://en.wikipedia.org/wiki/Struct_(C_programming_language)) (`struct`s). The examples are available in the [C header file `structs.h`](/manual/c/structs.h). -In the [Introduction](/manual/LowLevel/Introduction.md), we have seen bindings +In the [Introduction](../Introduction.md), we have seen bindings created for a named C `struct` (structure) storing a triple of integers: ```c diff --git a/manual/LowLevel/Uncategorized-ZeroCopy.md b/manual/LowLevel/Translation/05-ZeroCopy.md similarity index 100% rename from manual/LowLevel/Uncategorized-ZeroCopy.md rename to manual/LowLevel/Translation/05-ZeroCopy.md diff --git a/manual/LowLevel/Translation/05-Functions.md b/manual/LowLevel/Translation/06-Functions.md similarity index 99% rename from manual/LowLevel/Translation/05-Functions.md rename to manual/LowLevel/Translation/06-Functions.md index 2ecde94b6..41855bc8d 100644 --- a/manual/LowLevel/Translation/05-Functions.md +++ b/manual/LowLevel/Translation/06-Functions.md @@ -62,7 +62,7 @@ main = do print y -- prints 16 ``` -[globals]:./Globals.md#Guidelines-for-binding-generation +[globals]:./07-Globals.md#Guidelines-for-binding-generation ## Implicit function to pointer conversion diff --git a/manual/LowLevel/Translation/06-Globals.md b/manual/LowLevel/Translation/07-Globals.md similarity index 100% rename from manual/LowLevel/Translation/06-Globals.md rename to manual/LowLevel/Translation/07-Globals.md diff --git a/manual/LowLevel/Translation/07-Macros.md b/manual/LowLevel/Translation/08-Macros.md similarity index 100% rename from manual/LowLevel/Translation/07-Macros.md rename to manual/LowLevel/Translation/08-Macros.md diff --git a/manual/README.md b/manual/README.md index 60f65ff53..3f93e62e3 100644 --- a/manual/README.md +++ b/manual/README.md @@ -24,9 +24,14 @@ * [Structs](LowLevel/Translation/02-Structs.md) * [Enums](LowLevel/Translation/03-Enums.md) * [Unions](LowLevel/Translation/04-Unions.md) -* [Functions](LowLevel/Translation/05-Functions.md) -* [Global variables and constants](LowLevel/Translation/06-Globals.md) -* [Macros](LowLevel/Translation/07-Macros.md) +* [Zero-Copy](LowLevel/Translation/05-ZeroCopy.md) +* [Functions](LowLevel/Translation/06-Functions.md) +* [Global variables and constants](LowLevel/Translation/07-Globals.md) +* [Macros](LowLevel/Translation/08-Macros.md) + +### Appendix + +* [Visibility](LowLevel/Appendix/01-Visibility.md) ## Handwriting high-level bindings From 83bc1c85d21385396c68ffb0c6e0ebcb2abf475d Mon Sep 17 00:00:00 2001 From: Dominik Schrempf Date: Wed, 26 Nov 2025 08:32:58 +0100 Subject: [PATCH 12/12] [WIP] enums --- manual/LowLevel/Translation/03-Enums.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/manual/LowLevel/Translation/03-Enums.md b/manual/LowLevel/Translation/03-Enums.md index 2f4d9b1d9..0c557cba9 100644 --- a/manual/LowLevel/Translation/03-Enums.md +++ b/manual/LowLevel/Translation/03-Enums.md @@ -5,7 +5,7 @@ ### Simple `enum`s We already saw the simplest form of `enum` in the -[Introduction](Introduction.md): given +[Introduction](../Introduction.md): given ```c typedef enum index { @@ -22,10 +22,7 @@ newtype Index = Index { un_Index :: CUInt } -pattern A :: Index -pattern B :: Index -pattern C :: Index - +pattern A, B, C :: Index pattern A = Index 0 pattern B = Index 1 pattern C = Index 2 @@ -95,11 +92,11 @@ enum vote { } __attribute__((packed)); ``` -we use `CSChar` (corresponding to `signed char`): +we use `CUChar` (corresponding to `unsigned char`): ```haskell newtype Vote = Vote { - un_Vote :: CSChar + un_Vote :: CUChar } ``` @@ -107,7 +104,7 @@ newtype Vote = Vote { At first glance it would seem obvious that the Haskell type defined for C enums should be given an `Enum` instance; if `Enum` corresponds to enumeration types, -then surely C `enum`s qualify. However, as mentioned above, C enums merely +then surely C `enum`s qualify. However, as mentioned above, C `enum`s merely identify some values; they do not restrict the range of the corresponding type. Take `HTTP_status` (see above), and consider `Moved` (with value 301); should `succ Moved` be `Bad_request` (value 400), the next _declared_ value, or should @@ -115,7 +112,7 @@ it be value 302? `Bounded` is problematic for the same reason: should `minBound` be `ok` (value 200) or 0 (`minBound` for `CUInt`)? The `hs-bindgen-runtime` library therefore defines an alternative class called -`CENum`, the most important members of which are: +`CEnum`, the most important members of which are: ```haskell class Integral (CEnumZ a) => CEnum a where