Skip to content

Support SwiftPM and add Swift bindings for v6#2958

Draft
Lancelotbronner wants to merge 3 commits into
capstone-engine:nextfrom
Lancelotbronner:next
Draft

Support SwiftPM and add Swift bindings for v6#2958
Lancelotbronner wants to merge 3 commits into
capstone-engine:nextfrom
Lancelotbronner:next

Conversation

@Lancelotbronner

Copy link
Copy Markdown

Your checklist for this pull request

  • I've documented or updated the documentation of every API function and struct this PR changes.
  • I've added tests that prove my fix is effective or that my feature works (if possible)

Detailed description

I'm depending on capstone v6 for a macOS app prototype, as such I need Swift bindings and integration into SwiftPM.

While there is already another PR that did so, it seemed extremely intrusive to capstone, attempting to force it into SwiftPM's default convention rather than configuring SwiftPM appropriately.

Here are my changes to support building capstone with SwiftPM:

  • Add Package.swift, the capstone target builds capstone
    • It emits "unknown build rule" for each .inc file, they can be manually added to exclude to avoid them but I didn't feel like doing that yet.
  • Add module.modulemap, this is needed by Clang as part of Xcode's build system

Here are my changes to add (nice, safer) Swift bindings:

  • In Package.swift, the CapstoneKit target wraps capstone where possible.
  • Add Capstone.apinotes, a sidecar file that provides Clang with additional information and renames to make the automatically generated Swift bindings more "Swifty". It's very incomplete and some parts could likely be script-generated. See Clang's documentation. See Swift's extensions.
  • Add bindings/swift/CapstoneKit.swift with a few work-in-progress wrapper types

Test plan

All Capstone.apinotes bindings are automatically generated by Swift.
There is no need to separately test them.

Only the APIs in bindings/swift need to be tested, and I'm using them in my prototype.

@Lancelotbronner Lancelotbronner marked this pull request as draft June 7, 2026 21:47
@Lancelotbronner Lancelotbronner changed the title [Draft] Support SwiftPM and add Swift bindings for v6 Support SwiftPM and add Swift bindings for v6 Jun 7, 2026

@Rot127 Rot127 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the new attempt. It looks not bad at all!
To merge this though we need to add all the generator scripts and testing so we can continue to maintain it as easy as possible.
If those require to install the Swift toolchain, so be it.

But it must be maintainable from Linux (for CI, Linux users and Windows/WSL users).

Only the APIs in bindings/swift need to be tested, and I'm using them in my prototype.

That is not enough I am afraid:

  1. We won't be able to see for each PR if it breaks something. Or if we have forgotten to update the bindings if a PR adds something new.
  2. I really tried hard over the last years to increase the test coverage a lot. So we have a high certainty that Capstone works as intended. I really don't want to give up on this philosophy. Especially because we write C here and Capstone is such a "infrastructure-kinda" tool. It should just work with very few annoyances. So I would make it a hard requirement that the Switt bindings are tested.

Comment thread bindings/swift/CapstoneKit.swift Outdated
Comment thread include/Capstone.apinotes Outdated
# CS_ARCH_EVM, ///< Ethereum architecture
# CS_ARCH_MOS65XX, ///< MOS65XX architecture (including MOS6502)
# CS_ARCH_WASM, ///< WebAssembly architecture
# CS_ARCH_BPF, ///< Berkeley Packet Filter architecture (including eBPF)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of them are missing, do you know why?

@Lancelotbronner Lancelotbronner Jun 11, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes because I've been doing it manually so far, these just rename or precise semantics of the API to make them behave a bit nicer but they're not necessary for the bindings to be available.

Here's how Swift imports and maps the C headers.

Comment thread include/Capstone.apinotes

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All Capstone.apinotes bindings are automatically generated by Swift.

How would be generate them here?

It needs a script in bindings/swift so we can run it before every release or if a PR cahgnes the API, so we can update the bindings.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're not generated manually. As in during the build the Swift compiler will ask Clang to build capstone and will automatically surface types and functions.

There's full two-interoperability so there's no meed for a script.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait I see how what I wrote is confusing, my bad. Hopefully my comment on the other thread clarified it?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the Swift build will generate these .apinotes on the fly? Then we wouldn't need to add them here, or do I miss something?

@Lancelotbronner Lancelotbronner Jun 12, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, here's an example with cs_ac_type:

When Swift builds capstone, it provides a cs_ac_type structure with a single rawValue: UInt32 field and a number of globals for the enumerators, such as let CS_AC_READ = cs_ac_type(rawValue: 1).

The .apinotes file tells the Swift compiler that:

  • cs_ac_type should be renamed to AccessSet
  • cs_ac_type is a flags type, which adds a whole bunch of convenience methods such as var access: AccessSet; access.contains(AccessSet.read); access.union(otherAccess) etc.
  • CS_AC_INVALID should be renamed to AccessSet.invalid (which you can shorten to .invalid when the type is known in context).
  • CS_AC_READ should be renamed to AccessSet.read (same as above).
  • etc.

When a user tries to use cs_ac_type, CS_AC_READ or such, the compiler tells them it was renamed and offers an automatic fix that renames it.

It also helps in structuring the generated documentation, now instead of CS_AC_READ being in the global namespace, it's placed under AccessSet.

This is especially good with architecture enums and structures:

Screenshot 2026-06-12 at 9 54 54 AM

Another example with cs_op_type:

  • cs_op_type is renamed to CapstoneOpKind
  • cs_op_type is a proper enum:
    • it generates as a Swift enum, rather than a wrapper structure
    • any globals of this type are nested under the type's namespace
  • cs_op_type is a closed enum. This allows Swift to do exhaustiveness checking in switch statements.

Let me know if that makes sense?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Is there any reason to strip the cs_ prefix for Swift?
I don't know how they do namespace management, but in my experience it is better to not remove the name prefixes, since it can come to name collisions.

@Lancelotbronner Lancelotbronner Jun 13, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Namespaces are by-module, any definition can be accessed explicitly via capstone::SomeTypeOrFunction.

In Swift prefixes are either removed or spelled out when ambiguous. In terms of ABIs the symbol names are still the C header ones.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Swift prefixes are either removed or spelled out when ambiguous. In terms of ABIs the symbol names are still the C header ones.

I see. Makes sense.

@Lancelotbronner

Copy link
Copy Markdown
Author

I'll fix that next week, got a pretty busy weekend!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants