# API system

- What is the API system
- Usecase
- Why an API system when there are FEATURES and TWEAKS?
- Where are the APIs described?
- What is the format of module.export?
- What is the format of module.import?
- How do I change or introduce an API in different mainline configurations
- How do I import a new non-released API?
- Can API's depend on each other?

## What is the API system?

The API system allows modules to export API's, to be used by other modules. When importing an API it means a module wants to make use of some functionality exported by another module (or the same module). For example one module might export a utility-function, which is enabled by importing an API.

Each module present in readme.txt in modules/, adjunct/ or platforms/ can have a module.export file where the API's exported by the module are described, and a module.import file where the API's imported from other modules are stated. These files are put in the root of the module.

An API being imported basically means that a preprocessor define is turned on. The define can be used in all source files and is turned on right after features and tweaks are turned on. This means that the API system will be included from core/pch.h.

If an API is exported, but never imported, it will not be enabled and its preprocessor define will not be turned on.

## Usecase

Kurt wants to take a screenshot and store it as a PNG image on his hard drive.

Here the problem is to find a function which can encode an image as a PNG. It turns out that the ogp module has such a function: `EncodeImagePNG`.

To use this function Kurt needs to enable FEATURE_CANVAS. This might not be what he wanted. A better solution is:

ogp moves `EncodeImagePNG` and use a new define, PNG_ENCODE_SUPPORT for example, to protect the code. ogp exports the new API as: API_ENCODE_PNG. ogp will import this API if FEATURE_CANVAS is turned on and Kurt will import the API from his code whenever he wants to without having to enable FEATURE_CANVAS.

This way both the ogp module owner and Kurt will be happy. ogp doesn't need to #ifdef `EncodeImagePNG` on both CANVAS_SUPPORT and KURT_SUPPORT.

## Why an API system when there are FEATURES and TWEAKS?

Features and tweaks work on the product level. Modules have needs to export API's used by other modules where the product does not need to be asked. The API system work between modules on the module level. Product's may however find the need to import certain API's from the core code, and are free to do so. Some utility code in core may be exported as an API and the products can import it if they like.

## Where are the APIs described?

Each API is described as part of its definition in the `module.export` file of the module that defines it.

The script that processes tweaks and APIs and generates the various related header files also generates an XML file containing the information in the `module.export` files. This generated XML file is named `modules/hardcore/documentation/apis.version.xml` and uses an XSLT stylesheet to render in a semi-useful format.

See:

## What is the format of module.export?

    API_MODULESHORT_NAME				API-owner

    	Description of API.

    	Defines       : define-list
    	Depends on    : feature/tweak/define/api-list
            Conflicts with: tweak/api-list

Examples of API-names: API_HC_MESSAGEHANDLER and API_UTIL_ADVANCED_OPVECTOR.

API-owner
  The API owner is the name of the developer responsible for the API. If this value is set to "deprecated" it means the API is no longer available. The API cannot be imported by any other modules, and an error will be produced if tried. Mandatory.
Description
  Text describing the API. It must state clearly what the API does and when it can and shall be imported. This description will be read by both module owners and branch managers for products. Can span several lines. Mandatory.
Defines
  A list of preprocessor defines separated by "," and/or "and". These will be defined if the API is imported. Mandatory.
Depends on
  List of features/tweaks/defines/apis which this API depends on. The features/tweaks/defines/apis must be turned on to be able to import this API. If they are not and someone tries to import the API, an error is produced. The items are separated by "," and/or "and". "nothing" is a valid value. Not mandatory, if left out it means "nothing".
Conflicts with
  List of tweaks and apis which cannot be tweaked/imported if this API is imported. The items are separated by "," and/or "and". "nothing" is a valid value. Not mandatory, if left out it means "nothing".

Example of an exported API:

    API_UTIL_ADVANCED_OPVECTOR			markus

    	Enables an extended API to OpVector which allows
    	Sort(), Search(), Add(), Subtract() and Intersect().

    	Defines   : ADVANCED_OPVECTOR
    	Depends on: nothing

Here the util module exports the API API_UTIL_ADVANCED_OPVECTOR which turns on the define ADVANCED_OPVECTOR. There are no dependencies for this API, which means it can always be imported.

## What is the format of module.import?

    API_MODULESHORT_NAME				Import-responsible

    	Description of why the API is imported.

    	Import if: feature/tweak/define/api-list

Import-responsible
  The person responsible for the import of the API. This is different from the API owner (but may ofcourse be the same person). Mandatory.
Description
  Text describing why the API is imported. Can span several lines. Mandatory.
Import if
  List of features/tweak/defines/apis which are required to be turned on for the API to be imported. Several "Import if"-lines can be used to describe an OR-condition for when to import the API. Can be set to "always" if this API for some reason always shall be imported. Mandatory.

Example of API-import with several import-rules:

    API_UTIL_ADVANCED_OPVECTOR			someone

    	The advanced OpVector functions Sort() and Subtract() are used by search_engine.
    	Ecmascript uses the advanced OpVector function Add().

    	Import if: FEATURE_SEARCH_ENGINE, FEATURE_DISK
    	Import if: FEATURE_ECMASCRIPT

In the example, the preprocessor rule to when to include the API becomes:

    #if (FEATURE_SEARCH_ENGINE == YES && FEATURE_DISK == YES) || FEATURE_ECMASCRIPT == YES

The same effect can be created by duplicating the whole API-section several times, like this:

    API_UTIL_ADVANCED_OPVECTOR			someone

    	The advanced OpVector functions Sort() and Subtract() are used by search_engine.

    	Import if: FEATURE_SEARCH_ENGINE, FEATURE_DISK

    API_UTIL_ADVANCED_OPVECTOR			someone

    	Ecmascript uses the advanced OpVector function Add().

    	Import if: FEATURE_ECMASCRIPT

The last method might sometimes be preferable since it makes it possible to easier document the reason for the import and the owner can be different.

## How do I change or introduce an API in different mainline configurations

The build-system can be told compile the "current" or "next" mainline configuration. Each mainline configuration has an associated version number. If the setup script finds the file `module.export.version` or `module.import.version`, it parses that file instead of the `module.export` or `module.import`. Thus an API can be added or changed for only one mainline configuration.

It is recommended to only keep the versiond file `module.export.version` or `module.import.version` for the "current" mainline configuration and use the default file for the "next" mainline configuration. Thus on switching the mainline version numbers, the versioned file can be removed and only the not-versioned file remains.

Example: If the "current" mainline version is 2.3 and the "next" mainline version is 2.4, and one module needs to export different APIs for the different mainline configurations, it can use two files:

`module.export.2.3`
  exports all APIs for the "current" = 2.3 mainline configuration.
`module.export`
  exports all APIs for the "next" = 2.4 mainline configuration.

So if you want to add or change an API for the "next" mainline configuration, look for the version number of the "current" mainline configuration in `modules/hardcore/version.ini`. If the file `module.export.current_version` exists, you can edit `module.export`.
Otherwise copy `module.export` to `module.export.current_version`, add it to the repository and modify `module.export`. Thus the "current" mainline configuration remains unchanged.

The same applies if you want to add or change an API for the "current" mainline configuration: look for the version number of the "current" mainline configuration in `modules/hardcore/version.ini`. If the file `module.export.current_version` exists, you can edit it
Otherwise copy `module.export` to `module.export.current_version`, add it to the repository and modify it. Thus the "next" mainline configuration remains unchanged.

## How do I import a new non-released API?

You may at some point want to release a version of your module that uses some new API in another module, which has not yet been introduced.

The easiest thing is to import the API as if it existed. It will generate a warning from the script and the API will not be imported until it has been exported.

## Can API's depend on each other?

You can not have circular dependencies. If you have, it will be detected at compile time and generate an error. If you find yourself in a situation that you might need this, feel free to contact the module owner. Maybe we can implement support for circular dependencies if there are good reasons for it.