aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJauhien Piatlicki (jauhien) <piatlicki@gmail.com>2013-08-06 22:09:28 +0200
committerJauhien Piatlicki (jauhien) <piatlicki@gmail.com>2013-08-06 22:09:28 +0200
commit4d158c383f10ccc177b93ffeecaff3fce33b1691 (patch)
tree36704efe2cafb0e166697924a9e7fddcb0201bf6 /docs/developer_instructions.rst
parentREADME.md: github code highlighting (diff)
downloadg-sorcery-4d158c383f10ccc177b93ffeecaff3fce33b1691.tar.gz
g-sorcery-4d158c383f10ccc177b93ffeecaff3fce33b1691.tar.bz2
g-sorcery-4d158c383f10ccc177b93ffeecaff3fce33b1691.zip
Developer instructions
Diffstat (limited to 'docs/developer_instructions.rst')
-rw-r--r--docs/developer_instructions.rst640
1 files changed, 640 insertions, 0 deletions
diff --git a/docs/developer_instructions.rst b/docs/developer_instructions.rst
new file mode 100644
index 0000000..cd95a2f
--- /dev/null
+++ b/docs/developer_instructions.rst
@@ -0,0 +1,640 @@
+======================
+Developer Instructions
+======================
+
+g-sorcery overview
+==================
+
+**g-sorcery** is a framework aimed to easy development of ebuild
+generators.
+
+Some terms used in this guide:
+
+* **3rd party software provider** or **repository**
+ A system of software distribution like CTAN or CPAN that
+ provides packages for some domain (e.g. TeX packages or elisp
+ packages for emacs).
+
+* **backend**
+ A tool developed using **g-sorcery** framework that provides
+ support for repositories of a given type.
+
+* **overlay**
+ Usual Gentoo overlay.
+
+**g-sorcery** consists of different parts:
+
+* **package_db.PackageDB**
+ A package database. It holds information about all available
+ packages in a given repository.
+
+* **package_db.DBGenerator**
+ A fabric that creates PackageDB object and fills it with information.
+
+* **backend.Backend**
+ Backend that processes user commands.
+
+* **ebuild**
+ Module with different ebuild generators.
+
+* **eclass**
+ Module with eclass generators.
+
+* **metadata.MetadataGenerator**
+ Metadata generator.
+
+Also there are other modules and classes that will be described later.
+
+Usually repositories of a given type provide some kind of database. It can
+be just a plain ASCII file, xmlrpc interface or just a joint set of web-pages.
+This database describes what packages are available and how to install them.
+
+Also usually there is an upstream tool for repositories of a given type that
+allows installation of available packages. The main problem when using
+such tools is that package mangler you use is not aware of them and they are
+not aware of your package manager.
+
+The idea of **g-sorcery** is to convert a database provided by a repository
+into well defined format and then generate an overlay with ebuilds.
+Then available packages can be installed as usual **Gentoo** packages.
+
+So there are two phases of backend operation:
+
+- synchronize with repository database
+
+- populate overlay using obtained information
+
+There are two ways of using backend:
+
+- run it as a CLI tool manually
+
+- use its integration with layman
+
+
+Backend structure
+=================
+
+The only mandatory module in a backend is called **backend**. It should contain
+at least one variable called **instance** that has a **__call__** method that
+takes 4 arguments. These arguments are:
+
+* self
+
+* command line arguments
+
+* backend config
+
+* g-sorcery config
+
+Usually **instance** variable should be an instance of a class g_sorcery.backend.Backend
+or derived class.
+
+g_sorcery.backend.Backend constructor takes 8 arguments. They are:
+
+* self
+
+* Package database generator class
+
+* Two ebuild generator classes
+
+* Eclass generator class
+
+* Metadata generator class
+
+* Package database class
+
+* Boolean variable that defines method of database generation
+
+There are two ebuild generator classes as there are two scenarios of using backend on user
+side: generate the entire overlay tree (possibly by layman) or generate a given ebuild
+and its dependencies. In a first case it would be very bad idea to have sources in ebuild's
+SRC_URI as during manifest generation for an overlay all the sources would be downloaded
+to the user's comuter that inevitably would made user really happy. So one ebuild generator
+generates ebuild with empty SRC_URI. Note that a mechanism for downloading of sources during
+ebuild merging should be provided. For an example see **git-2** eclass from the main tree or
+any eclass from backends provided with g-sorcery.
+
+Usually downloading and parsing of a database from a repository is an easy operation. But sometimes
+there could exist some problems. Hence exists the last parameter in Backend constructor that
+allows syncing with already generated database available somewhere in Internet.
+
+To do something usefull backend should customize any classes from g-sorcery it needs
+and define backend.instance variable using those classes. Other two things backend should do are:
+
+* install a binary that calls g-sorcery with appropriate backend name (see man g-sorcery)
+
+* install a config that allows g-sorcery find appropriate backend module
+
+A binary should just pass arguments to g-sorcery. For a backend named gs-elpa it could look like
+
+.. code-block::
+
+ #!/bin/bash
+
+ g-sorcery g-elpa $@
+
+Backend config
+~~~~~~~~~~~~~~
+
+Backend config is just a JSON file with a dictionary. There are two mandatory entries:
+
+* package
+ Its value should be a string with a package containing backend.
+
+* repositories
+ A dictionary describing available repositories. Should have at least one entry.
+
+Backend config should have a name BACKEND.js and should be installed under **/etc/g-sorcery**
+directory. BACKEND here is a backend name which was used in a g-sorcery call.
+
+An entry in repositories dictionary as key should have a repository name and should be a dictionary
+with repository properties. The only mandatory property is **repo_uri** in case database is
+generated using info downloaded from the repository or **db_uri** in case database is
+just synced with another already generated database. Also there can be a **masters** entry that
+contains a list of overlays this repository depends on. If present it should contain at least
+**gentoo** entry.
+
+A simple backend config:
+
+.. code-block::
+
+ {
+ "package": "gs_elpa",
+ "repositories": {
+ "gnu-elpa": {
+ "repo_uri": "http://elpa.gnu.org/packages/"
+ },
+ "marmalade": {
+ "repo_uri": "http://marmalade-repo.org/packages/",
+ "masters": ["gentoo", "gnu-elpa"]
+ },
+ "melpa": {
+ "repo_uri": "http://melpa.milkbox.net/packages/",
+ "masters": ["gentoo", "gnu-elpa"]
+ }
+ }
+ }
+
+Package database
+================
+
+Directory layout
+~~~~~~~~~~~~~~~~
+
+Package database is a directory tree with JSON files. The layout of this tree looks like:
+
+.. code-block::
+
+ db dir
+ manifest.json: database manifest
+ categories.json: information about categories
+ category1
+ packages.json: list of packages
+ package1
+ versions.json: list of versions
+ version1.json: description of a package
+ version2.json: description of a package
+ ...
+ package2
+ ...
+ category2
+ ...
+
+Files named version.json contain ebuild data which is just a dictionary with information
+relevant for ebuild, eclass and metadata generation for a given package.
+
+PackageDB class
+~~~~~~~~~~~~~~~
+
+PackageDB class is aimed for interaction with package database. It has methods that allow
+to add categories and packages and to do queries on them. Usually you do not want to customize this
+class. But in case you want there is number of methods that can be redifend.
+
+First of all if you have a database that should be synced with another already generate database
+you can redifine URI to be used for syncing using **get_real_db_uri** method.
+
+There is a number of hooks that are called after package, category or the whole database is
+written/read:
+
+* additional_write_version
+
+* additional_write_package
+
+* additional_write_category
+
+* additional_write
+
+* additional_read_version
+
+* additional_read_package
+
+* additional_read_category
+
+* additional_read
+
+Note that before add any package you should add a category for it using **add_category**.
+Then packages can be added using **add_package**. PackageDB currently does not write changes
+automatically, so you should call **write** after changes are done. This is not relevant
+for database changing in **process_data** method of database generator as there all changes
+are written by other methods it calls internally after **process_data**.
+
+JSON serializable objects
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you need to store an object in a database it should be JSON serializable in terms of
+g_sorcery.serialization module. It means it should define two methods:
+
+* usual method **serialize** that returns a JSON serializable object in terms of standard Python
+ json module
+
+* class method **deserialize** that takes a value returned by **serialize** and constructs new instance
+ of your class using it
+
+Dependency handling
+~~~~~~~~~~~~~~~~~~~
+
+There is a special class g_sorcery.g_collections.Dependency aimed to handle dependencies.
+Its constructor takes two mandatory parameters:
+
+* category
+
+* package
+
+and two additional parameters:
+
+* version
+
+* operator
+
+These two are the same as version and operator used in the usual package atom.
+
+For storing dependency lists in a database you should use a collection
+g_sorcery.g_collections.serializable_elist. Its constructor takes an iterable and a
+separator that will be used to separate items when this collection is printed. In case of
+storing dependencies for using them in ebuild's DEPEND variable a separator should be "\n\t".
+
+Ebuild data for every package version must have a "dependencies" entry. This entry is used
+by backend during deciding which ebuilds should be generated. So make sure it does not have
+any external dependencies.
+
+
+Package database generator
+==========================
+
+Customizing DBGenerator
+~~~~~~~~~~~~~~~~~~~~~~~
+
+To do something usefull you should customize package_db.DBGenerator class.
+With this aim you should subclass it and define some methods. Here they are:
+
+* get_download_uries
+ Get a list with download URI entries.
+ Each entry has one of the following formats:
+
+ 1. String with URI.
+
+ 2. A dictionary with entries:
+ - uri: URI.
+
+ - parser: Parser to be applied to downloaded data.
+
+ - open_file: Whether parser accepts file objects.
+
+ - open_mode: Open mode for a downloaded file.
+
+ The only mandatory entry is uri.
+
+ The default implementation returns [backend_config["repositories"][REPOSITORY]["repo_uri"]].
+
+* parse_data
+ This method parses a file downloaded from a repository
+ and returns its content in any form you think useful.
+ There is no useful default implementation of this method.
+
+* process_data
+ This method should fill a package database with entries using
+ already downloaded and parsed data.
+
+Generally speaking these are all the method you should implement.
+
+Value convertion
+~~~~~~~~~~~~~~~~
+
+During database generation you may need to convert some values provided by repository
+(e.g license names that can not coincide with those used in Gentoo). With this aim
+you can use **convert** function. To understand how it works see its sources in
+g_sorcery.package_db.DBGenerator and as an example CTAN backend.
+
+Here is a very short example. If you want to convert licenses in the same way for all
+repositories of this type you just add **common_config** entry to backend config which
+looks like:
+
+.. code-block::
+
+ "common_config": {
+ "licenses": {
+ "apache2": "Apache-2.0",
+ "artistic": "Artistic",
+ "Artistic2": "Artistic-2",
+ "gpl": "GPL-1",
+ "gpl2": "GPL-2",
+ "gpl3": "GPL-3",
+ "knuth": "TeX",
+ "lgpl": "LGPL-2",
+ "lgpl2.1": "LGPL-2.1",
+ "lppl": "LPPL-1.2",
+ "lppl1": "LPPL-1.2",
+ "lppl1.2": "LPPL-1.2",
+ "lppl1.3": "LPPL-1.3c"
+ }
+ }
+
+And then call in your **process_data** method
+
+.. code-block::
+
+ license = self.convert([common_config, config], "licenses", repo_license)
+
+Where **common_config**, **config** are config provided as arguments to your **process_data** method
+and **repo_license** is a license name used by the repository.
+
+There is a special conversion function used for dependencies: **convert_dependency**. To use it you should
+usually redefine **convert_internal_dependency** and **convert_external_dependency**. To decide whether
+a dependency is external database generator uses **external** entry in config.
+
+You may want to test whether there is a given value in given entry in config. To do it use
+**in_config** function.
+
+Eclass generator
+================
+
+Usualy you do not want to modify eclass generator. Currently it is very simple: it just returns eclasses
+from a given directory. So all you should do is populating a directory with eclasses and then
+inheriting g_sorcery.eclass.EclassGenerator and defining a directory in constructor. It should look
+like
+
+.. code-block::
+
+ class ElpaEclassGenerator(EclassGenerator):
+ """
+ Implementation of eclass generator. Only specifies a data directory.
+ """
+ def __init__(self):
+ super(ElpaEclassGenerator, self).__init__(os.path.join(get_pkgpath(__file__), 'data'))
+
+There is no common eclass currently. I plan to change it in the future, so your eclass code can
+inherit any common functionality.
+
+Ebuild generator
+================
+
+There is a number of ebuild generators in g_sorcery.ebuild module. The DefaultEbuildGenerator
+is a recommended one. To use it you should inherit it and define an ebuild layout in constructor.
+
+Layout has entries for vars and inherited eclasses. Each entry is a list.
+Entries are processed in the following order:
+
+* vars_before_inherit
+
+* inherit
+
+* vars_after_inherit
+
+* vars_after_description
+
+* vars_after_keywords
+
+**inherit** entry is just a list of eclass names.
+
+**vars*** entries are lists of variables in two possible formats:
+
+1. A string with variable name
+2. A tuple (varname, value)
+
+Variable names are automatically transformed to the upper-case during ebuild generation.
+
+An example of ebuild generator:
+
+.. code-block::
+
+ Layout = collections.namedtuple("Layout",
+ ["vars_before_inherit", "inherit",
+ "vars_after_description", "vars_after_keywords"])
+
+ class ElpaEbuildWithoutDigestGenerator(DefaultEbuildGenerator):
+ """
+ Implementation of ebuild generator without sources digesting.
+ """
+ def __init__(self, package_db):
+
+ vars_before_inherit = \
+ ["repo_uri", "source_type", "realname"]
+
+ inherit = ["g-elpa"]
+
+ vars_after_description = \
+ ["homepage"]
+
+ vars_after_keywords = \
+ ["depend", "rdepend"]
+
+ layout = Layout(vars_before_inherit, inherit,
+ vars_after_description, vars_after_keywords)
+
+ super(ElpaEbuildWithoutDigestGenerator, self).__init__(package_db, layout)
+
+Metadata generator
+==================
+
+To use metadata generator you should just define some variables in ebuild data.
+
+XML schema format
+~~~~~~~~~~~~~~~~~
+
+Metadata generator uses a XML schema in format defined in g_sorcery.metadata module.
+Schema is a list of entries. Each entry describes one XML tag.
+Entry is a dictionary. Dictionary keys are:
+
+* **name**
+ Name of a tag
+
+* **multiple**
+ Defines if a given tag can be used more then one time. It is a tuple. First element
+ of a tuple is boolean. If it is set a tag can be repeated. Second element is a string.
+ If it is not empty, it defines a name for an attribute
+ that will distinguish different entries of a tag.
+
+* **required**
+ Boolean that defines if a given tag is required.
+
+* **subtags**
+ List of subtags.
+
+Data dictinonary format
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The part of ebuild data used for metadata generation should have data dictionary format
+also defined in g_sorcery.metadata.
+
+Keys correspond to tags from a schema with the same name.
+If a tag is not multiple without subkeys value is just a
+string with text for the tag.
+If tag is multiple value is a list with entries
+corresponding to a single tag.
+If tag has subtags value is a dictionary with entries
+corresponding to subkeys and **text** entry corresponding
+to text for the tag.
+If tag should have attributes value is a tuple or list with
+0 element containing an attribute and 1 element containing
+a value for the tag as described previously.
+
+Metadata XML schema
+~~~~~~~~~~~~~~~~~~~
+
+Metadata XML schema looks like
+
+.. code-block::
+
+ default_schema = [{'name' : 'herd',
+ 'multiple' : (True, ""),
+ 'required' : False,
+ 'subtags' : []},
+
+ {'name' : 'maintainer',
+ 'multiple' : (True, ""),
+ 'required' : False,
+ 'subtags' : [{'name' : 'email',
+ 'multiple' : (False, ""),
+ 'required' : True,
+ 'subtags' : []},
+ {'name' : 'name',
+ 'multiple' : (False, ""),
+ 'required' : False,
+ 'subtags' : []},
+ {'name' : 'description',
+ 'multiple' : (False, ""),
+ 'required' : False,
+ 'subtags' : []},
+ ]
+ },
+
+ {'name' : 'longdescription',
+ 'multiple' : (False, ""),
+ 'required' : False,
+ 'subtags' : []},
+
+ {'name' : 'use',
+ 'multiple' : (False, ""),
+ 'required' : False,
+ 'subtags' : [{'name' : 'flag',
+ 'multiple' : (True, "name"),
+ 'required' : True,
+ 'subtags' : []}]
+ },
+
+ {'name' : 'upstream',
+ 'multiple' : (False, ""),
+ 'required' : False,
+ 'subtags' : [{'name' : 'maintainer',
+ 'multiple' : (True, ""),
+ 'required' : False,
+ 'subtags' : [{'name' : 'name',
+ 'multiple' : (False, ""),
+ 'required' : True,
+ 'subtags' : []},
+ {'name' : 'email',
+ 'multiple' : (False, ""),
+ 'required' : False,
+ 'subtags' : []}]},
+ {'name' : 'changelog',
+ 'multiple' : (False, ""),
+ 'required' : False,
+ 'subtags' : []},
+ {'name' : 'doc',
+ 'multiple' : (False, ""),
+ 'required' : False,
+ 'subtags' : []},
+ {'name' : 'bugs-to',
+ 'multiple' : (False, ""),
+ 'required' : False,
+ 'subtags' : []},
+ {'name' : 'remote-id',
+ 'multiple' : (False, ""),
+ 'required' : False,
+ 'subtags' : []},
+ ]
+ },
+ ]
+
+So to have metadata.xml filled with e.g. maintainer info you should add to ebuild data
+something like
+
+.. code-block::
+
+ {'maintainer' : [{'email' : 'piatlicki@gmail.com',
+ 'name' : 'Jauhien Piatlicki'}]}
+
+Layman integration
+==================
+
+There is a **layman** integration for **g-sorcery** (thanks to Brian Dolbec and Auke Booij here).
+To use it you just need to install and xml file describing your repositories in
+**/etc/layman/overlays** directory. For our example of backend config we could write an xml file
+that looks like
+
+.. code-block::
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE repositories SYSTEM "/dtd/repositories.dtd">
+ <repositories xmlns="" version="1.0">
+ <repo quality="experimental" status="unofficial">
+ <name>gnu-elpa</name>
+ <description>packages for emacs</description>
+ <homepage>http://elpa.gnu.org/</homepage>
+ <owner>
+ <email>piatlicki@gmail.com</email>
+ <name>Jauhien Piatlicki</name>
+ </owner>
+ <source type="g-sorcery">gs-elpa gnu-elpa</source>
+ </repo>
+ <repo quality="experimental" status="unofficial">
+ <name>marmalade</name>
+ <description>packages for emacs</description>
+ <homepage>http://marmalade-repo.org/</homepage>
+ <owner>
+ <email>piatlicki@gmail.com</email>
+ <name>Jauhien Piatlicki</name>
+ </owner>
+ <source type="g-sorcery">gs-elpa marmalade</source>
+ </repo>
+ <repo quality="experimental" status="unofficial">
+ <name>melpa</name>
+ <description>packages for emacs</description>
+ <homepage>http://melpa.milkbox.net</homepage>
+ <owner>
+ <email>piatlicki@gmail.com</email>
+ <name>Jauhien Piatlicki</name>
+ </owner>
+ <source type="g-sorcery">gs-elpa melpa</source>
+ </repo>
+ </repositories>
+
+In entries **<source type="g-sorcery">gs-elpa melpa</source>** the source type
+should always be **g-sorcery**, **gs-elpa** is backend name and **melpa** is repository name.
+
+For full description of format of this file see **layman** documentation.
+
+Note: at the moment layman-9999 does not support **g-sorcery** overlay type and you should
+patch it with https://raw.github.com/jauhien/g-sorcery/master/layman-git-g-sorcery.patch
+
+Summary
+=======
+
+So to create your own backend you should write a module named **backend** and define there
+a variable named **instance** that is an instance of g_sorcery.backend.Backend class. Or something
+that quacks like this class.
+
+Before doing it you should have defined classes you pass to it as parameters. They should be database
+generator, two ebuild generators, eclass and metadata generators.
+
+Also you should write an executable that calls g-sorcery and some configs.