Metadata-Version: 2.4
Name: pfmg
Version: 0.8.0
Summary: Python Flatpak Module Generator — a probe-first replacement for flatpak-pip-generator
Author-email: "Diego C. Sampaio" <slavelix@ymail.com>
License-Expression: AGPL-3.0-or-later
Project-URL: Homepage, https://github.com/kriptolix/pfmg
Project-URL: Issues, https://github.com/kriptolix/pfmg/issues
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: POSIX :: Linux
Classifier: Development Status :: 4 - Beta
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.12
Requires-Dist: rich>=13
Requires-Dist: pyyaml>=6
Requires-Dist: packaging>=24
Requires-Dist: requests>=2.31
Requires-Dist: uv>=0.1.1
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Dynamic: license-file

# pfmg — Python Flatpak Module Generator

> [!WARNING]
> For the sake of honesty, it's important to say that most of the code in this package was written by Claude. Although I reviewed the code in general, I didn't review every single line in detail. Please use it accordingly and report any issues you find.

A modern, probe-first replacement for [flatpak-pip-generator](https://github.com/flatpak/flatpak-builder-tools/tree/master/pip).

Instead of resolving dependencies statically on the host, **pfmg** installs the package inside a real Flatpak build sandbox, runs `ldd` on every compiled extension, checks `pkg-config`, and tests the import. The result is a Flatpak module JSON that you can drop directly into your manifest — with native dependency errors caught before you ever run `flatpak-builder`.

---

## Requirements

- Python 3.11+
- `flatpak` installed on the host (for `generate` and `inspect`)
- `pip3` or `uv` available on the host (for source collection)

---

## Installation

```bash
pip install pfmg
```

---

## How it works

```
pfmg generate numpy
         │
         ├─ 1. Install package inside org.freedesktop.Sdk sandbox
         ├─ 2. Run ldd on every .so file → detect missing native libs
         ├─ 3. Run pkg-config on declared deps → detect missing headers / pc files
         ├─ 4. Test the Python import
         ├─ 5. Build-test the generated module (flatpak-builder dry run)
         │      └─ if a build dep is missing and resolvable on PyPI → probe it
         │         and retry automatically (up to 5 times)
         └─ 6. Write module JSON
```

Errors are matched against a local dataset of SDK profiles, extension profiles, and ready-to-use recipes, so you get actionable suggestions ("add `org.freedesktop.Sdk.Extension.rust-stable`") instead of raw linker output.

---

## The local dataset

pfmg ships with — and continuously grows — a dataset of:

| Directory | Contents |
|-----------|----------|
| `data/sdk-profiles/` | Profiles of base SDKs (libraries, pkgconfig, executables) |
| `data/ext-profiles/` | Profiles of SDK extensions |
| `data/nat-recipes/` | Ready-to-use Flatpak module JSON for native libraries (zlib, openssl, …) |
| `data/pip-recipes/` | Ready-to-use module JSON for Python packages with known native deps |

When `pfmg generate` encounters an error it queries this dataset and suggests concrete actions: which SDK extension to add, which recipe to prepend to the manifest, or which `--build-dep` flag to pass.

Use `pfmg import`, `pfmg inspect`, and `pfmg inspect-all` to grow the dataset for your own extensions or SDK versions.

## Commands

### `pfmg generate`

Probe one or more Python packages and generate Flatpak module JSON files.

```
pfmg generate <package> [<package> ...] [OPTIONS]
```

| Option | Default | Description |
|--------|---------|-------------|
| `--sdk`, `-s` | `org.freedesktop.Sdk` | Base SDK to use |
| `--runtime` | `org.freedesktop.Platform` | Runtime ID |
| `--sdk-version`, `-V` | `24.08` | SDK branch |
| `--extension`, `-e` | — | SDK extension to activate (repeatable) |
| `--build-dep`, `-b` | — | Explicit build-time PyPI dep (repeatable) |
| `--output-dir`, `-o` | — | Directory to write module JSON files |
| `--format`, `-f` | `json` | Output format: `json` or `yaml` |
| `--uv` / `--pip` | `--uv` | Package installer to use inside the sandbox |
| `--keep` | off | Keep the sandbox work directory after probing |
| `--raw-output`, `-r` | off | Print raw sandbox stdout/stderr |
| `--verbose`, `-v` | off | Enable debug logging |

**Examples:**

```bash
# Simplest case — latest numpy
pfmg generate numpy

# Pin a version, save the result
pfmg generate numpy==1.26.4 --output-dir ./modules

# Package that needs the Rust toolchain
pfmg generate cryptography \
  --extension org.freedesktop.Sdk.Extension.rust-stable \
  --output-dir ./modules

# Several packages at once on SDK 24.08
pfmg generate pillow lxml --sdk-version 24.08 --output-dir ./modules

# Manually hint a build-time dep (pfmg tries to auto-resolve these,
# but you can be explicit)
pfmg generate some-package --build-dep meson-python --build-dep ninja
```

When `--output-dir` is omitted the module JSON is printed to stdout. If pfmg detects errors it prints a resolution table with suggestions from the local dataset.

---


### `pfmg search`

Search the local dataset for a library, package name, or executable.

```
pfmg search <query> [OPTIONS]
```

| Option | Default | Description |
|--------|---------|-------------|
| `--kind`, `-k` | all | Filter: `nat`, `pip`, `sdk`, `ext` |
| `--module`, `-m` | off | Print the full module JSON for matching recipes |

```bash
pfmg search openssl
pfmg search numpy --kind pip
pfmg search libz  --kind nat
pfmg search clang --kind ext --module
```

Searches SDK profiles, extension profiles, nat-recipes, and pip-recipes using the same fuzzy matching as the error resolver.

---

### `pfmg list`

List all entries in the local dataset.

```
pfmg list [OPTIONS]
```

| Option | Default | Description |
|--------|---------|-------------|
| `--kind`, `-k` | all | Filter: `nat`, `pip`, `sdk`, `ext` |
| `--limit`, `-n` | `50` | Maximum rows per table |

```bash
pfmg list
pfmg list --kind nat
pfmg list --kind pip --limit 100
```

---

### `pfmg show`

Show the full content of a recipe by its exact ID.

```
pfmg show <recipe-id> [OPTIONS]
```

| Option | Default | Description |
|--------|---------|-------------|
| `--kind`, `-k` | both | Restrict to `nat` or `pip` |

```bash
pfmg show zlib
pfmg show numpy --kind pip
```

---

### `pfmg summarize`

Print counts of recipes and profiles in the local dataset.

```bash
pfmg summarize
```

---

### `pfmg inspect`

Introspect a Flatpak SDK or extension and write a static profile JSON to the local dataset. Profiles are used by the resolver to match errors against known providers.

```
pfmg inspect <sdk-or-extension-id> [OPTIONS]
```

| Option | Default | Description |
|--------|---------|-------------|
| `--sdk-branch`, `-B` | `25.08` | Branch of the base SDK |
| `--ext-branch`, `-X` | — | Branch of the extension when it differs from `--sdk-branch` |
| `--output-dir`, `-o` | — | Override the default profile output directory |
| `--nocleanup` | off | Don't uninstall the extension after probing |
| `--override` | off | Re-probe and overwrite an existing profile |

pfmg auto-detects whether the target is a base SDK or an extension by looking for `.Extension.` in the ID.

**Examples:**

```bash
# Base SDK
pfmg inspect org.freedesktop.Sdk --sdk-branch 24.08

# Extension on the same branch as the SDK
pfmg inspect org.freedesktop.Sdk.Extension.node24 --sdk-branch 25.08

# Extension with its own branch
pfmg inspect org.freedesktop.Sdk.Extension.gcc8 --sdk-branch 24.08 --ext-branch 1.6
```

Profiles are written to:

```
data/sdk-profiles/<shortname>.<branch>.json   # base SDKs
data/ext-profiles/<shortname>.<branch>.json   # extensions
```

---

### `pfmg inspect-all`

Discover every extension available on a Flatpak remote for the given SDK branches and probe them all.

```
pfmg inspect-all [OPTIONS]
```

| Option | Default | Description |
|--------|---------|-------------|
| `--sdk-branch`, `-B` | `24.08`, `25.08` | Branches to probe (repeatable) |
| `--remote`, `-r` | `flathub` | Flatpak remote to query |
| `--output-dir`, `-o` | — | Override profile output directory |
| `--nocleanup` | off | Don't uninstall extensions after probing |
| `--override` | off | Re-probe profiles that already exist |

Intended to be run once a year when a new SDK generation ships:

```bash
# Update profiles for the two current branches
pfmg inspect-all

# When 26.08 ships
pfmg inspect-all -B 25.08 -B 26.08
```

Extensions whose profile already exists are skipped unless `--override` is given.

---

### `pfmg import`

Import native library and Python recipes from a shared-modules directory (e.g. a checkout of [flathub/shared-modules](https://github.com/flathub/shared-modules)).

```
pfmg import <modules-dir> [OPTIONS]
```

| Option | Default | Description |
|--------|---------|-------------|
| `--repo-root`, `-r` | `.` | Root of the pfmg repository |
| `--verbose`, `-v` | off | Enable debug logging |

```bash
pfmg import ~/src/shared-modules
```

---





---
