Setting up a build system¶
The recommended way to include the modified asn1c compiler into a Python package is by using a CMake-based build system. While it is possible to use other build systems, they are not officially supported and may require significant manual integration work.
In this small tutorial, we will create a CMakeLists.txt-based build system for a Python
extension module, using hatchling as the project builder
and scikit-build-core to integrate
CMake into the Python packaging process.
Hint
You may also want to take a look at the example module in the repository on GitHub that implements a basic extension and a submodule extension here: example_mod
Overview¶
This build setup relies on several components working together:
asn1cpython-support: A Python package that provides helper utilities for ASN.1 compilation and integration. It offers a CLI interface to retrieve the installation path of the
asn1c-bindingsCMake configuration files.asn1c-bindings: A CMake package that exports helper functions, including
asn1c_add_extension(), which generates ASN.1 C sources at configure time, compiles them into Python extension modules, copies stub files, and ensures correct installation paths.scikit-build-core (or compatible PEP 517 build backend): Used as the Python build backend, facilitating CMake-driven builds integrated into standard Python packaging workflows.
Prerequisites¶
Before you begin, ensure the following:
CMake between 3.15 and 3.27 (inclusive).
The ASN.1 compiler toolchain (
asn1c) is installed and accessible via your system PATH, or configured via environment variables or CMake defines such asA1C_PATH.Python 3.8 or newer.
A working installation of
pipandhatch.
Project Structure Example¶
A minimal project might look like this:
example-mod/
├── CMakeLists.txt
├── example.asn
├── src/
│ ├── example_mod/ # Python sources go here
│ └── generated/ # Populated by the ASN.1 compiler at configure time
├── pyproject.toml
└── README.md
In this example:
example.asnis your ASN.1 schema.src/generatedwill hold generated C and Python files during the configure step.CMakeLists.txtdefines how to build your extension.pyproject.tomlconfigures the Python overall project.
CMake Setup¶
CMake is a cross-platform build system that uses CMakeLists.txt files to define
build rules. In our case, we need a total of three steps to create a new
extension:
1. Finding the CMake package¶
The asn1cpython-support package includes a helper command that tells CMake
where the asn1c-bindings package is installed. The following snippet can be
used to insert the required package into the search path of CMake:
# Retrieve the path to asn1c-bindings CMake package via the Python helper
execute_process(
COMMAND "${Python_EXECUTABLE}" -m asn1cpython_support --cmake-dir
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE asn1c-bindings_ROOT
)
# Load the asn1c-bindings CMake package
find_package(asn1c-bindings CONFIG REQUIRED)
Note
You must list asn1cpython-support in the [build-system] requires
section of your pyproject.toml, otherwise CMake will not be able to find
the helper. For instance:
[build-system]
# just add the python package as a build dependency
requires = ["hatchling", "scikit-build-core~=0.9.0", "asn1cpython-support"]
2. Adding a new extension¶
The asn1c_add_extension() function declares a Python extension
module that will be generated and compiled automatically. This call performs
multiple actions, which include running the asn1c compiler at configure time
to generate C and Python interface files, adds them to a new Python extension
and ensures the generated .pyi type stub file is renamed and installed
correctly.
# Add an ASN.1 extension module.
asn1c_add_extension(
NAME _example_mod
ASN_FILES ${CMAKE_CURRENT_SOURCE_DIR}/example.asn
)
Python Packaging Integration¶
When using scikit-build-core together with hatchling, your
pyproject.toml could look like this:
[build-system]
requires = ["hatchling", "scikit-build-core~=0.9.0", "asn1cpython-support"]
build-backend = "hatchling.build"
[project]
name = "example-mod"
version = "1.0.0"
description = "Example Python extension built from ASN.1"
readme = "README.md"
dependencies = ["bitarray"] # Needed for BIT STRING type
[tool.hatch.build.targets.wheel.hooks.scikit-build]
experimental = true
# Optional: define custom compiler or skeleton paths
# [tool.scikit-build.cmake.define]
# A1C_PATH = "path/to/custom/asn1c"
# A1C_SKELETONS_PATH = "path/to/skeletons"
This configuration allows you to build your package with:
pip install .
# or
hatch build