Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ jobs:
"$RUNNER_TEMP/prik-release-check/bin/prik" --version
"$RUNNER_TEMP/prik-release-check/bin/python" -c \
'import importlib.metadata as m, prik; assert prik.__version__ == m.version("prik")'
"$RUNNER_TEMP/prik-release-check/bin/python" -c \
'from prik.cmake import cmake_module_dir; assert (cmake_module_dir() / "UsePRIK.cmake").is_file()'
"$RUNNER_TEMP/prik-release-check/bin/prik" --help
"$RUNNER_TEMP/prik-release-check/bin/python" -m prik --help
- name: Upload distributions
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ release tags add a leading `v` to the package version.

## Unreleased

- Added CMake integration through the packaged `UsePRIK.cmake` helper and a
`prik generate --cmake` standalone-project mode. CMake generates PRIK wrapper
sources as build outputs and owns native compilation, linking, external
targets, and incremental rebuilds while preserving per-source flags,
compiler-required ABI options, preprocessing dependencies, and linker
language from PRIK's completed build plan.

- CMake contract modules now distinguish semantic `NATIVE_LANGUAGE` from the
final `LINKER_LANGUAGE`, keep native compilation flags target-local, and
report a clear error when CMake's C language is not enabled.

- `prik-build.json` schema 5 records generated/native compilation-unit ABI
flags and explicit native linker-language requirements.

- Array handles support allocatable and pointer arguments, results, module
variables, derived fields, optional arguments, and matching ordinary-array
parameters. Numeric and character arrays accept supported forward and
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
include CHANGELOG.md
include CITATION.cff
include .artifacts/.gitignore
recursive-include cmake *.cmake
541 changes: 541 additions & 0 deletions cmake/UsePRIK.cmake

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions docs/developer/packages/pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ prik/pipeline/

`build.py` is the orchestration hub. Its public records describe inputs and
results without executing a build: `NativeCompilationUnit`,
`GeneratedCompilationUnit`,
`NativePrebuiltArtifact`, `NativeLinkItem`, `NativeBuildPlan`, and
`WrapperBuildResult`. Its three public entrypoints are source-first builds,
contract-first builds, and replay of a saved contract-build manifest.
Expand Down Expand Up @@ -114,6 +115,16 @@ source collections stay distinct, a source-free `.pyi` build states its native
language instead of deriving it from a compiler or ABI decorator, and prebuilt
objects, archives, and libraries stay ordered `NativeLinkItem` records.

Build integrations consume the completed result rather than compiler command
logs. Native and generated compilation units retain their own requested flags,
required ABI flags, and include directories; the result also records every
semantic/preprocessing dependency and the final linker language. This is the
lossless boundary used by `UsePRIK.cmake` for regeneration, linker-driver
selection, and target-local compilation. CMake places native units in a
private per-module object target so source properties cannot leak between
PRIK extension targets; generated sources remain on the Python extension
target.

`WrapperBuildResult` and saved `.pyi` manifests report each generated native
group's kind, language, member keys, and source paths, so zero-source,
adapter-only, support-only, and mixed output stay factual across direct builds,
Expand Down
169 changes: 169 additions & 0 deletions docs/user/guide/cmake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
---
title: CMake Builds
description: Build PRIK Python extensions from an existing or generated CMake project
audience: users
prerequisites: building the shared library, CMake, Python development files
related: building-shared-library.md, ../reference/cli-commands.md
status: maintained
publication: reviewed
---

# CMake Builds

Use CMake when its toolchain, dependency targets, and build scheduling should
own compilation and linking. PRIK still parses the native inputs, completes
wrapper policy, and generates the wrapper and any Fortran bridge sources. By
default, PRIK uses CMake's selected C or Fortran compiler for preprocessing,
source analysis, and ABI probes. CMake mode does not accept a separate
`--compiler` override because the analyzed and compiled toolchains must agree.

## Existing CMake project

Install PRIK, make its `cmake` directory available through
`CMAKE_MODULE_PATH`, and include the packaged helper:

```cmake
cmake_minimum_required(VERSION 3.20)

project(MyPhysics LANGUAGES C Fortran)

find_package(
Python
COMPONENTS Interpreter Development.Module
REQUIRED
)

# Ask PRIK's Python environment for its packaged CMake helper.
execute_process(
COMMAND "${Python_EXECUTABLE}" -c "from prik.cmake import cmake_module_dir; print(cmake_module_dir().as_posix())"
RESULT_VARIABLE PRIK_CMAKE_MODULE_RESULT
OUTPUT_VARIABLE PRIK_CMAKE_MODULE_DIR
ERROR_VARIABLE PRIK_CMAKE_MODULE_ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT PRIK_CMAKE_MODULE_RESULT EQUAL 0)
message(FATAL_ERROR "Cannot locate UsePRIK.cmake: ${PRIK_CMAKE_MODULE_ERROR}")
endif()
list(APPEND CMAKE_MODULE_PATH "${PRIK_CMAKE_MODULE_DIR}")
include(UsePRIK)

prik_add_module(
physics
FORTRAN_SOURCES
solver.f90
matrix.f90
)
```

Then configure and build the extension:

```bash
cmake -S . -B build
cmake --build build
```

`prik_add_module()` also accepts `SOURCES` for source-first input, `CONTRACT`
with `FORTRAN_SOURCES` or `C_SOURCES` for an authored semantic `.pyi`,
`INCLUDE_DIRS`, `MODULE_DIRS`, native and generated-source compile flag groups,
`LINK_LIBRARIES`, `LINK_OPTIONS`, and additional generation-only `PRIK_ARGS`.
For a contract backed only by opaque native inputs, use `NATIVE_LANGUAGE` to
state the contract ABI language and `LINKER_LANGUAGE` to state the final CMake
linker driver independently:

```cmake
prik_add_module(
c_api
CONTRACT api.pyi
NATIVE_LANGUAGE C
LINKER_LANGUAGE Fortran
LINK_LIBRARIES native_fortran_archive
)
```

When native source files use one language, PRIK infers `NATIVE_LANGUAGE` from
`FORTRAN_SOURCES` or `C_SOURCES`. Set it explicitly when the contract ABI
differs from the implementation source language or when both source languages
are present. A source-free contract must state it explicitly. CMake's C
language must be enabled because every PRIK extension contains generated C
binding code.

The flag groups remain separate:

- `FORTRAN_FLAGS` and `C_FLAGS` apply only to user-owned native sources.
- `WRAPPER_FORTRAN_FLAGS` applies only to generated Fortran bridge sources.
- `WRAPPER_C_FLAGS` applies to generated C sources and the extension link,
matching PRIK's normal build behavior.

PRIK adds compiler-profile flags required by its ABI plan to the affected
Fortran sources. `NO_STANDARD_LOGICALS` disables PRIK's Intel/NVIDIA logical
interoperability option when compatibility with prebuilt objects requires it.
Only mandatory ABI flags are exported from PRIK's plan; recommended compiler
profile options remain the CMake toolchain's responsibility.
CMake build type, debug, and interprocedural-optimization settings remain
normal CMake target properties; `PRIK_ARGS` rejects compiler and compilation
options that would bypass those target settings.

Use `NO_COMPILE_INPUT_SOURCES` when `SOURCES` supplies only the public
interface. Its implementation may come from `FORTRAN_SOURCES`, `C_SOURCES`, a
prebuilt library, or a target in `LINK_LIBRARIES`:

```cmake
add_library(native_math STATIC implementation.f90)

prik_add_module(
python_api
SOURCES interface.f90
NO_COMPILE_INPUT_SOURCES
LINK_LIBRARIES native_math
)
```

The generated wrapper sources are custom-command outputs. Changing a semantic
source, contract, included C header, or Fortran `INCLUDE` file regenerates them
before CMake compiles the target. CMake recompiles contract-first native
implementations independently.

External dependencies remain CMake dependencies. For example, CMake can find
BLAS and pass its target to the PRIK extension:

```cmake
find_package(BLAS REQUIRED)

prik_add_module(
blas_example
FORTRAN_SOURCES blas_example.f90
LINK_LIBRARIES BLAS::BLAS
)
```

The same form accepts normal project targets such as `native_math` and
`OpenMP::OpenMP_Fortran`; they remain target-oriented CMake link inputs.
Normal Fortran sources and targets carry their link-language requirements
through CMake. For a raw archive or shared library whose language is otherwise
opaque, add `LINKER_LANGUAGE Fortran`; PRIK records that requirement in its
plan and the extension uses CMake's Fortran linker driver. This is independent
of `NATIVE_LANGUAGE`, which controls semantic-contract interpretation.

## Standalone generated project

Generate a small CMake project from native sources:

```bash
python3 -m prik generate --cmake solver.f90 --out-dir build/solver
cmake -S build/solver -B build/solver/cmake-build
cmake --build build/solver/cmake-build
```

For an authored contract, provide its implementation sources as usual:

```bash
python3 -m prik generate --cmake contracts/solver.pyi \
--native-fortran-sources solver.f90 \
--out-dir build/solver
```

The generated `CMakeLists.txt` loads `UsePRIK.cmake` and calls
`prik_add_module()`. `UsePRIK.cmake` integrates PRIK into an existing CMake
project; `prik generate --cmake` creates a standalone CMake project that uses
that same helper. `--native-linker-language fortran` emits the explicit raw
library annotation when standalone input requires the Fortran linker.
4 changes: 3 additions & 1 deletion docs/user/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ workflow that matches how you run PRIK.
## Build Workflows

- [Building the Shared Library](building-shared-library.md) — compilers,
source sets, output placement, and Makefiles
source sets, output placement, Makefiles, and CMake
- [CMake Builds](cmake.md) — integrate PRIK into an existing CMake project or
generate a standalone one
- [IPython and Jupyter Notebooks](notebooks.md) — compile Fortran and C cells
and edit semantic contracts interactively

Expand Down
24 changes: 18 additions & 6 deletions docs/user/reference/cli-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ python3 -m prik {parse,semantics,generate,probe} [OPTIONS] ...
| no subcommand | Builds one importable extension from Fortran source, a supported C source, or a semantic `.pyi` contract. |
| `parse` | Prints parser facts and diagnostics. |
| `semantics` | Prints a human-readable semantic-IR report; `--json` selects the complete JSON record. |
| `generate` | Writes `.pyi` contracts, wrapper sources, or a Makefile without compiling. |
| `generate` | Writes `.pyi` contracts, wrapper sources, a Makefile, or a CMake project without compiling. |
| `probe` | Prints compiler-target datatype and ABI facts. |

## Getting help
Expand Down Expand Up @@ -98,6 +98,7 @@ least one explicit native input: `--native-fortran-sources`, `--native-c-sources
| `--native-objects PATH ...` | Links object files, static archives, or shared libraries. |
| `--native-library NAME ...` | Links system libraries by name — `--native-library openblas` passes `-lopenblas`. |
| `--native-link-item KIND:VALUE ...` | Ordered link items. `KIND` is `object`, `archive`, `shared-library`, `library`, or `arg`. |
| `--native-linker-language {c,fortran}` | Requires the named final linker language when prebuilt inputs do not carry it. |
| `--native-library-dir DIR ...` | Library search directories and runtime paths. |
| `--lto` | Enables link-time optimization for Fortran and C builds by adding `-flto` to generated and native compilation and to the extension link. |
| `--collision-adapter NAME ...` | Calls native symbol `NAME` through a forwarder defined in a separate translation unit, so the binding never declares an identifier its own headers already declare. |
Expand Down Expand Up @@ -197,7 +198,7 @@ Support](../language-support/c-support.md) before building a C API.
`generate` requires exactly one output mode:

```bash
python3 -m prik generate (--pyi | --sources | --makefile) INPUT [INPUT ...] [OPTIONS]
python3 -m prik generate (--pyi | --sources | --makefile | --cmake) INPUT [INPUT ...] [OPTIONS]
python3 -m prik generate (--sources | --makefile) --build-manifest PATH [OVERRIDES]
```

Expand All @@ -206,11 +207,14 @@ python3 -m prik generate (--sources | --makefile) --build-manifest PATH [OVERRID
| `--pyi` | Writes the editable semantic `.pyi` contract. |
| `--sources` | Writes wrapper sources without compiling. |
| `--makefile` | Writes wrapper sources, the replay manifest when applicable, and `Makefile.prik`. |
| `--cmake` | Writes a standalone `CMakeLists.txt` that uses `UsePRIK.cmake`. |
| `--module-name NAME` | Sets the Python module name used by generated wrapper sources; `--cmake` requires an ASCII C target name. |

```bash
python3 -m prik generate --pyi points.f90 --out contracts/points
python3 -m prik generate --sources points.f90 --out-dir build
python3 -m prik generate --makefile points.f90 --out-dir build
python3 -m prik generate --cmake points.f90 --out-dir build/points
```

For a C source contract, `--language c` is valid with `--pyi`:
Expand All @@ -219,14 +223,22 @@ For a C source contract, `--language c` is valid with `--pyi`:
python3 -m prik generate --pyi --language c path/to/api.c --out api.pyi
```

`--sources` and `--makefile` still run preprocessing and semantic policy to
produce a valid wrapper plan; they skip object compilation and linking, and
use `--out-dir`. With no `--out`, `generate --pyi` prints every generated
`--sources` and `--makefile` run preprocessing and semantic policy to produce
a valid wrapper plan; they skip object compilation and linking, and use
`--out-dir`. `--cmake` writes the CMake project; its CMake configuration later
runs PRIK's wrapper-generation step, while CMake owns compilation and linking.
In CMake mode, `--compiler` and `--wrapper-compiler-debug` are rejected:
CMake's selected compiler and build configuration own those choices. Native
and generated-wrapper flag options remain distinct in the generated helper
call, `--no-standard-logicals` maps to PRIK's CMake compilation plan, and
`--lto` maps to CMake's interprocedural-optimization target property.
With no `--out`, `generate --pyi` prints every generated
contract. For Fortran, `--out PATH` names a package directory containing
`__init__.pyi` and any module leaves. For C, it names the single output `.pyi`
file. Bare `--out` writes beside the inputs. The [source-to-contract
layouts](pyi-format.md#source-to-contract-layout) show both forms.
`--compiler` and `-I` affect only preprocessing and datatype measurement.
Outside CMake mode, `--compiler` and `-I` affect preprocessing and datatype
measurement as documented by the selected command.

In `.pyi` Makefile mode, PRIK writes `<out-dir>/prik-build.json` first, then
generates `<out-dir>/Makefile.prik` from that manifest.
Expand Down
32 changes: 24 additions & 8 deletions docs/user/reference/configuration-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ A representative manifest has this structure:
"requested_name": null
},
"generated_wrapper": {
"compilation_units": [
{
"abi_flags": [],
"flags": [],
"include_dirs": [
"."
],
"language": "c",
"source": "module_wrapper.c"
}
],
"native_code_groups": [],
"sources": [
"module_wrapper.c"
Expand All @@ -105,6 +116,7 @@ A representative manifest has this structure:
"native_build_plan": {
"compilation_units": [
{
"abi_flags": [],
"flags": [],
"include_dirs": [],
"language": "fortran",
Expand All @@ -121,6 +133,7 @@ A representative manifest has this structure:
"path": "module.o"
}
],
"linker_language": null,
"module_dirs": [
"."
],
Expand All @@ -134,18 +147,21 @@ A representative manifest has this structure:
"shared_library": "module.cpython-<platform>.so",
"strict_wrapper_names": false
},
"schema_version": 4
"schema_version": 5
}
```

The values and array contents vary by build. In particular,
`generated_wrapper.native_code_groups` records any generated Fortran adapters
or support sources, while `native_build_plan.link_items` preserves the exact
order of objects, archives, shared libraries, named libraries, and linker
arguments. Paths are stored relative to the manifest directory when possible
and resolved from that directory during replay.

Replay reads the current schema version, `4`. Regenerate the manifest with the
`generated_wrapper.compilation_units` and
`native_build_plan.compilation_units` keep source-specific flags, required ABI
flags, and include directories. `generated_wrapper.native_code_groups` records
any generated Fortran adapters or support sources, while
`native_build_plan.link_items` preserves the exact order of objects, archives,
shared libraries, named libraries, and linker arguments. Paths are stored
relative to the manifest directory when possible and resolved from that
directory during replay.

Replay reads the current schema version, `5`. Regenerate the manifest with the
current PRIK version when upgrading from an older schema.

## `Makefile.prik`
Expand Down
2 changes: 2 additions & 0 deletions docs/user/reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ you have not.
- [Python API](python-api.md) — the build entrypoints and advanced package imports.
- [Build manifests and Makefiles](configuration-files.md) — how both files are
generated, what they contain, and how to build or replay them.
- [CMake Builds](../guide/cmake.md) — the packaged CMake helper and standalone
CMake project generation.

## Contracts

Expand Down
2 changes: 1 addition & 1 deletion docs/user/reference/python-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Reach past the root facade when you need a single stage rather than a build.
| Semantic conversion | `prik.semantics.fortran2ir`, `prik.semantics.pyi2ir` | Fortran conversion helpers, `convert_pyi_to_ir` |
| C semantic conversion | `prik.semantics.c2ir` | `CToIRConverter`, `c_file_to_semantic_module`, `c_file_to_semantic_modules` |
| `.pyi` loading and stub emission | `prik.pipeline.pyi` | `pyi_*_to_semantic_module`, `emit_module_stubs` |
| Build records and results | `prik.pipeline.build` | `WrapperBuildResult`, `NativeBuildPlan`, `NativeCompilationUnit`, `NativePrebuiltArtifact`, `NativeLinkItem` |
| Build records and results | `prik.pipeline.build` | `WrapperBuildResult`, `NativeBuildPlan`, `NativeCompilationUnit`, `GeneratedCompilationUnit`, `NativePrebuiltArtifact`, `NativeLinkItem` |
| IPython/Jupyter integration | `prik.jupyter` | `%load_ext prik.jupyter`, then `%%fortran`, `%%c`, or `%%pyi` |
| Target type probing | `prik.preprocessing.probes.fortran_types` | probe source, requirements, expressions, report and error types |
| C target type probing | `prik.preprocessing.probes.c_types` | `probe_c_standard_types`, `probe_c_standard_types_cached`, and C probe records/error type |
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ nav:
- Symbols, Headers, and Dependencies: user/guide/c/symbols-headers-and-dependencies.md
- Build Workflows:
- Building the Shared Library: user/guide/building-shared-library.md
- CMake Builds: user/guide/cmake.md
- IPython and Jupyter Notebooks: user/guide/notebooks.md
- Tutorials:
- Run PRIK in a Notebook: user/tutorials/notebook-quickstart.md
Expand Down
Loading
Loading