2010-09-12, 23:11
I know everyone's busy with the pending release but I wanted to give you an overview and status of my multi-language addon effort.
I) Overview.
The refactored Addon API is split into two parts, native functionality (C++) and language bindings. The Native API is a set of C++ classes and methods that has no dependency on a particular (potential) Addon language. This API mirrors the existing Python API (though I hope this will one day be deprecated) by module (namespace) and classes.
A "description" of this API is captured in an XML file. Originally I had looked at potentially automating this by using something like gcc2xml or an C++ variation of java's XDoclet or annotations, but it was too complicated to get something to show quickly and nothing prevents it from being done in the future.
This XML description drives a code generator (written in groovy) which applies the description to set of templates. This results in the generation of a set of C++ files that represent the binding to various languages (currently Python).
II) Some Details:
a) The Native API:
The Native functionality is a C++ set of modules and classes. These have no dependence on any particular Addon language. The current set is a transfer of the existing Python functionality into a Python-independent C++ implementation.
All native API classes and modules are in the namespace "XBMCAddon" and all of the code currently sits in the directory xbmc/lib/libAddon/module. All files that are part of the API are named Addon*.h/cpp.
i) Modules: In the native API Modules are represented as namespaces. The four existing (legacy) modules: xbmc, xbmcgui, xbmcaddon, xbmcplugin, are a set of global methods within their respective namespace, and nested within 'XBMCAddon'. For example, the 'sleep' method of the module "xbmc" can be accessed natively with "XBMCAddon::xbmc:leep()." Modules are in source files that are named AddonModule[Modulename].h/cpp.
ii) Classes: Classes are always within modules and are therefore named accordingly. For example, the Player class, which is in the "xbmc" module is "XBMCAddon::xbmc:layer."
There are several classes that contain objects that represent what are standard types (dictionaries, lists, strings) in some languages, but are not native to C++. These are also in files Addon*.h/cpp. So far these include (only):
AddonDictionary.h -> This is defined as template <class T> class Dictionary : public std::map<CStdString,T> and defines a "nullDictionary"
AddonString.h -> This is a CStdString but defines a "nullString" to use as a default value.
I'm not particularly thrilled with this solution but it works for now. Any suggestions on what might accomplish this better let me know.
b) The API specification
The specification contains the API definitions captured in a set of XML files and located at xbmc/lib/libAddon/module/specs. There is a separate XML file for each module. An example from the current xbmc.xml file is:
Another single file contains type metadata for parameters and return types. This file tells the generator how to handle the three different 'kinds' of 'types': 1) native C++ types (bool, int, long, etc). 2) Classes that are part of the API (String, Dictionary, etc), and 3) Classes that are defined in the API spec (xbmc.Player, xbmcgui.ListItem, etc). The file looks like:
c) Code Generation.
The XML specification drives the execution of the generator over multiple templates. The entire process is driven by an XML file that contains meta data for the bindings. It currently looks like this:
The generator will apply the XML spec to the template file within the given scope and produce the given output file. For example, given the above, the generator will apply each XML module definition spec to the template contained in the file PythonBindings.cpp.template and will produce an output file for each resulting pass called {modulename}_PythonBindings.cpp (obviously the actual module name will be substituted). The templates and this metadata file are all found at xbmc/lib/libAddon/module/bindings
III) Current status.
I have the entire xbmc module now working, and some of the xbmcgui module. This included callbacks (The "Player" is the only class with callbacks in the xbmc module). I still need to:
1) Move the other modules into this system.
2) Remove the Python specifics from some of the code that bootstraps and calls into the Addon system
3) I'm pretty sure I can improve the callback system (it currently works the same way the offical Dharma one works) by having both synchronous calls and better asynchronous calling.
4) Add bindings (templates) for another language (groovy would be my first choice).
I hope this will lead to a redesign of the API itself (a version 2.0 perhaps) and perhaps a merging of some other portions of the system into a uniform API (c-pluff stuff and the new JSON-RPC functionality).
Let me know what you think. The git repository is:
http://github.com/jimfcarroll/xbmc-multi...addons.git
You'll need to switch to the 'Dharma' branch.
All of the code is in xbmc/lib/libAddon/module with the exception of some hooks in other places.
As always, feedback is welcome.
I) Overview.
The refactored Addon API is split into two parts, native functionality (C++) and language bindings. The Native API is a set of C++ classes and methods that has no dependency on a particular (potential) Addon language. This API mirrors the existing Python API (though I hope this will one day be deprecated) by module (namespace) and classes.
A "description" of this API is captured in an XML file. Originally I had looked at potentially automating this by using something like gcc2xml or an C++ variation of java's XDoclet or annotations, but it was too complicated to get something to show quickly and nothing prevents it from being done in the future.
This XML description drives a code generator (written in groovy) which applies the description to set of templates. This results in the generation of a set of C++ files that represent the binding to various languages (currently Python).
II) Some Details:
a) The Native API:
The Native functionality is a C++ set of modules and classes. These have no dependence on any particular Addon language. The current set is a transfer of the existing Python functionality into a Python-independent C++ implementation.
All native API classes and modules are in the namespace "XBMCAddon" and all of the code currently sits in the directory xbmc/lib/libAddon/module. All files that are part of the API are named Addon*.h/cpp.
i) Modules: In the native API Modules are represented as namespaces. The four existing (legacy) modules: xbmc, xbmcgui, xbmcaddon, xbmcplugin, are a set of global methods within their respective namespace, and nested within 'XBMCAddon'. For example, the 'sleep' method of the module "xbmc" can be accessed natively with "XBMCAddon::xbmc:leep()." Modules are in source files that are named AddonModule[Modulename].h/cpp.
ii) Classes: Classes are always within modules and are therefore named accordingly. For example, the Player class, which is in the "xbmc" module is "XBMCAddon::xbmc:layer."
There are several classes that contain objects that represent what are standard types (dictionaries, lists, strings) in some languages, but are not native to C++. These are also in files Addon*.h/cpp. So far these include (only):
AddonDictionary.h -> This is defined as template <class T> class Dictionary : public std::map<CStdString,T> and defines a "nullDictionary"
AddonString.h -> This is a CStdString but defines a "nullString" to use as a default value.
I'm not particularly thrilled with this solution but it works for now. Any suggestions on what might accomplish this better let me know.
b) The API specification
The specification contains the API definitions captured in a set of XML files and located at xbmc/lib/libAddon/module/specs. There is a separate XML file for each module. An example from the current xbmc.xml file is:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<module name="xbmc">
<method name="log">
<doc><![CDATA[... method documentation ...]]></doc>
<parameter modifier="const" type="string">msg</parameter>
...
</method>
...
<class name="Player" >
<doc><![CDATA[ ... class documentation ...]]></doc>
<constructor>
<parameter type="int" default="EPC_NONE">playerCore</parameter>
</constructor>
<method name="playStream" > ...
Another single file contains type metadata for parameters and return types. This file tells the generator how to handle the three different 'kinds' of 'types': 1) native C++ types (bool, int, long, etc). 2) Classes that are part of the API (String, Dictionary, etc), and 3) Classes that are defined in the API spec (xbmc.Player, xbmcgui.ListItem, etc). The file looks like:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<meta>
<parameter type="int" meta="native" />
<parameter type="bool" meta="native" />
...
<parameter type="string" meta="native" alias="char" modifier="*" />
<parameter type="wstring" meta="object" alias="String" namespace="XBMCAddon" />
<return type="string" meta="object" alias="String" namespace="XBMCAddon" />
<parameter type="Dictionary<CStdString>" meta="object" namespace="XBMCAddon" />
</meta>
c) Code Generation.
The XML specification drives the execution of the generator over multiple templates. The entire process is driven by an XML file that contains meta data for the bindings. It currently looks like this:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<bindings>
<binding name="python">
<param name="override" module="xbmc" class="Player" method="play" />
<param name="override" module="xbmcgui" class="ListItem" method="setInfo" />
<template in="PythonBindings.cpp.template"
scope="permodule"
out="${module}_PythonBindings.cpp"/>
<template in="python.cpp.template"
scope="allmodules"
out="python.cpp"/>
</binding>
<template in="addons.cpp.template"
scope="allbindings"
out="addons.cpp"/>
</bindings>
The generator will apply the XML spec to the template file within the given scope and produce the given output file. For example, given the above, the generator will apply each XML module definition spec to the template contained in the file PythonBindings.cpp.template and will produce an output file for each resulting pass called {modulename}_PythonBindings.cpp (obviously the actual module name will be substituted). The templates and this metadata file are all found at xbmc/lib/libAddon/module/bindings
III) Current status.
I have the entire xbmc module now working, and some of the xbmcgui module. This included callbacks (The "Player" is the only class with callbacks in the xbmc module). I still need to:
1) Move the other modules into this system.
2) Remove the Python specifics from some of the code that bootstraps and calls into the Addon system
3) I'm pretty sure I can improve the callback system (it currently works the same way the offical Dharma one works) by having both synchronous calls and better asynchronous calling.
4) Add bindings (templates) for another language (groovy would be my first choice).
I hope this will lead to a redesign of the API itself (a version 2.0 perhaps) and perhaps a merging of some other portions of the system into a uniform API (c-pluff stuff and the new JSON-RPC functionality).
Let me know what you think. The git repository is:
http://github.com/jimfcarroll/xbmc-multi...addons.git
You'll need to switch to the 'Dharma' branch.
All of the code is in xbmc/lib/libAddon/module with the exception of some hooks in other places.
As always, feedback is welcome.