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.
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.
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.
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:
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.
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.
API_MODULESHORT_NAME Import-responsible Description of why the API is imported. Import if: feature/tweak/define/api-list
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.
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.3module.export
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.
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.
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.