# Manifest Configuration

Polytropos generates the `__manifest__.py` dynamically for each release based on your
`pyproject.toml` configuration.

## Version vs. Release

For Polytropos we use this specific terminology:

-   **Release** refers to Odoo release (Odoo 18.0, 19.0, etc.)
-   **Version** refers to your addon version (1.0.0, 2.34.1, etc.).

Therefore, a single _version_ of the addon can have different _releases_. Built
artifacts get a combination of release and version.

!!! example

    ```shell
    # Build 2 releases for the conditional_assets example
    cd examples/conditional_assets
    uv build --wheel --config-setting odoo_release=19.0
    uv build --wheel --config-setting odoo_release=18.0

    # Check all releases artifacts
    ls -1 dist/
    conditional_assets-18.0.1.0.0-py3-none-any.whl
    conditional_assets-18.0.1.0.0.tar.gz
    conditional_assets-19.0.1.0.0-py3-none-any.whl
    conditional_assets-19.0.1.0.0.tar.gz
    ```

## Dynamic Version

Your `pyproject.toml` must declare `version` as dynamic:

```toml title="pyproject.toml"
[project]
name = "odoo-addon-my-module"
version = "1.0.0"
dynamic = ["version"]
```

This is required because Polytropos generates a version that combines the Odoo release
with your module version (e.g., `18.0.1.2.3`). Without `dynamic = ["version"]`, tools
like **uv** cannot properly resolve dependencies that depend on the Odoo release.

For example, if your addon depends on `odoo-addon-other==18.0.*`, but your addon is
tagged with version `1.2.3`, uv will fail to solve dependencies because it sees a
mismatch between the static version in `[project]` and the actual wheel version.

Polytropos emits a warning during build if version is not declared as dynamic.

## Manifest values auto-populated from [project]

Several manifest fields are automatically populated from
[the standards-based `[project]` section](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/)
of `pyproject.toml`:

| Manifest Field                 | pyproject.toml Source                                      |
| ------------------------------ | ---------------------------------------------------------- |
| `name`                         | `project.name`                                             |
| `summary`                      | `project.description`                                      |
| `author`                       | `project.authors[*].name`                                  |
| `license`                      | `project.license`                                          |
| `website`                      | `project.urls.Homepage`                                    |
| `depends`                      | `project.dependencies` (see [Dependencies](#dependencies)) |
| `external_dependencies.python` | `project.dependencies` (see [Dependencies](#dependencies)) |

This avoids duplication - you define these once in `[project]` and they automatically
appear in the manifest.

The `name` field is generated from the module name (converted to title case, e.g.,
`my_module` → `My Module`). You can override it explicitly in
`[[tool.polytropos.manifest]]`.

!!! example

    These instructions:

    ```toml title="pyproject.toml"
    [project]
    name = "odoo-addon-my-module"
    version = "1.22.30"
    description = "My awesome Odoo module"
    authors = [
        {name = "My Name", email = "my-email@example.com"},
        {name = "Other Name", email = "other-email@example.com"},
    ]
    license = "LGPL-3"

    [project.urls]
    Homepage = "https://mywebsite.com"

    [tool.polytropos]
    default_odoo_release = "19.0"
    ```

    Produce this manifest:

    ```python title="my_module/__manifest__.py"
    {
        "name": "My Module",
        "summary": "My awesome Odoo module",
        "author": "My Name, Other Name",
        "license": "LGPL-3",
        "website": "https://mywebsite.com",
        "version": "19.0.1.22.30",
    }
    ```

Polytropos will never add any other fields to the manifest automatically.

### Project Name

The project name must start with `odoo-addon-` and use hyphens between words. This makes
your modules compatible with the wider Odoo addon package builders ecosystem.

The conversion follows these rules:

-   Package name (Python): `odoo-addon-module-name` (hyphens between words)
-   Module directory: `odoo/addons/module_name` (underscores between words)
-   Manifest name: `Module Name` (human-readable title case)

You can also override the module name explicitly. See below.

### Overriding Auto-populated Fields

If you need to override an auto-populated field, you can do so in
`[tool.polytropos.manifest]`:

```toml
[tool.polytropos.manifest]
name = "Custom Name"
author = "Custom Author"
```

!!! tip

    A warning will be emitted if you define the same field in both `[project]` and
    `[tool.polytropos.manifest]`. The manifest value takes precedence, but it's cleaner to
    define it only in `[project]`.

## Version-specific Overrides

Use overrides to specify different values per Odoo release using
[PEP 440 specifiers](https://peps.python.org/pep-0440/#version-specifiers):

```toml
[[tool.polytropos.manifest]]
depends = ["base"]
data = [
    "views/res_partner.xml"
]

[[tool.polytropos.manifest]]
releases = ">=18"
application = true

[[tool.polytropos.manifest]]
releases = "<16"
data = [
    "views/res_partner_legacy.xml"
]
```

### Merge Behavior

When merging, Polytropos follows these rules strictly:

1. `releases` key is removed (only used by Polytropos)
1. **Dicts are merged**: Keys are combined recursively
1. **Lists are combined**: Both sets of items are kept
1. **Values are replaced**: The last defined value wins

All manifest fields support overrides: `depends`, `data`, `demo`, `assets`,
`application`, `installable`, etc.

### Assets (CSS/JS)

Odoo uses asset bundles for web files. Define them in `pyproject.toml`:

```toml
[[tool.polytropos.manifest]]
depends = ["base"]
assets."my_module.assets_backend" = [
    # Include from another bundle
    ["include", "my_module.assets_common"],
    # Remove a file
    ["remove", "my_module/static/src/css/base.css"],
    # Add before a reference
    ["prepend", "my_module/static/src/css/ref.css", "my_module/static/src/css/new.css"],
    # Add after a reference
    ["after", "my_module/static/src/css/ref.css", "my_module/static/src/css/new.css"],
    # Direct file
    "my_module/static/src/css/legacy.css",
]

# Override for Odoo 18+
[[tool.polytropos.manifest]]
releases = ">=18"
assets."my_module.assets_backend" = [
    ["remove", "my_module/static/src/css/legacy.css"],
]
```

Polytropos is doing nothing special with this, just following its normal rules. Check
out
[Odoo Asset Documentation](https://www.odoo.com/documentation/19.0/developer/reference/frontend/assets.html)
to know how Odoo will process assets.

## Dependencies

If Polytropos addons don't define the `odoo` dependency, it will be implicitly added and
all releases will be buildable.

If `odoo` is defined, you will not be able to build wheels for incompatible releases.

Other dependencies can be added as `[project].dependencies` (both addons (with
`odoo-addon-` prefix) and other dependencies, for all releases), as
`[[tool.polytropos.manifest]].depends` (only addons (without prefix)) or as
`[[tool.polytropos.manifest]].external_dependencies.python` (both multi-version).

!!! example

    These instructions:

    ```toml title="pyproject.toml"
    [project]
    name = "odoo-addon-example"
    version = "1.0.0"
    dependencies = [
        # Forbid building for 17.0 or earlier
        "odoo>=18.0",

        # Depend on this OCA addon for all releases
        "odoo-addon-partner-fax",

        # Depend on this standard Python dependency on all releases
        "funcy>=1",
    ]

    [tool.polytropos]
    default_odoo_release = "19.0"

    [[tool.polytropos.manifest]]
    depends = [
        # Odoo CE/EE addons are never packaged on PyPi, so you must specify
        # these dependencies always directly here in the manifest section
        "sale",

        # You can also add OCA or other addon dependencies here if you prefer
        "queue_job",
    ]
    external_dependencies.python = [
        # You can add Python dependencies here if you prefer
        "cowsay>6",
    ]

    [[tool.polytropos.manifest]]
    # These overrides are only for Odoo 19+
    releases = ">=19"
    depends = [
        "project", # Odoo CE
        "queue_job_cron", # OCA
    ]
    external_dependencies.python = [
        "cowsay>7",
        "copier>=9.0",
    ]
    ```

    Will build this manifest for Odoo 18:

    ```python title="example/__manifest__.py"
    {
        "name": "Example",
        "version": "18.0.1.0.0",
        "depends": [
            "partner_fax",
            "sale",
            "queue_job",
        ],
        "external_dependencies": {
            "python": [
                "cowsay>6",
            ],
        },
    }
    ```

    And this manifest for Odoo 19:

    ```python title="example/__manifest__.py"
    {
        "name": "Example",
        "version": "19.0.1.0.0",
        "depends": [
            "project",
            "queue_job_cron",
            "sale",
            "queue_job",
        ],
        "external_dependencies": {
            "python": [
                "cowsay>6",
                "cowsay>7",
                "copier>=9.0",
            ],
        },
    }
    ```

    At the same time, the wheel will depend on these Python packages:

    - Odoo 18 release:
        -   `cowsay>6`
        -   `funcy>=1`
        -   `odoo-addon-partner-fax`
        -   `odoo-addon-queue-job`
        -   `odoo==18.0.*`
    - Odoo 19 release:
        -   `copier>=9.0`
        -   `cowsay>7`
        -   `odoo-addon-partner-fax`
        -   `odoo-addon-queue-job-cron`
        -   `odoo-addon-queue-job`
        -   `odoo==19.0.*`

## See Also

-   [Examples Folder](https://gitlab.com/moduon/polytropos/-/tree/main/examples) -
    Real-world module examples
