From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id 1EF44138200 for ; Tue, 6 Aug 2013 20:09:05 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id A011CE0B74; Tue, 6 Aug 2013 20:09:04 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 1A82AE0B74 for ; Tue, 6 Aug 2013 20:09:03 +0000 (UTC) Received: from hornbill.gentoo.org (hornbill.gentoo.org [94.100.119.163]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 07E4233EAD8 for ; Tue, 6 Aug 2013 20:09:02 +0000 (UTC) Received: from localhost.localdomain (localhost [127.0.0.1]) by hornbill.gentoo.org (Postfix) with ESMTP id 8DD5FE468F for ; Tue, 6 Aug 2013 20:09:00 +0000 (UTC) From: "Jauhien Piatlicki" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Jauhien Piatlicki" Message-ID: <1375819768.4d158c383f10ccc177b93ffeecaff3fce33b1691.jauhien@gentoo> Subject: [gentoo-commits] proj/g-sorcery:master commit in: /, docs/ X-VCS-Repository: proj/g-sorcery X-VCS-Files: README.md docs/Makefile docs/developer_instructions.html docs/developer_instructions.rst X-VCS-Directories: / docs/ X-VCS-Committer: jauhien X-VCS-Committer-Name: Jauhien Piatlicki X-VCS-Revision: 4d158c383f10ccc177b93ffeecaff3fce33b1691 X-VCS-Branch: master Date: Tue, 6 Aug 2013 20:09:00 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: 96f48a5b-7a34-4ede-a62b-c98cb3f38430 X-Archives-Hash: 3522168c1d6b11899b0e6a21e840c09b commit: 4d158c383f10ccc177b93ffeecaff3fce33b1691 Author: Jauhien Piatlicki (jauhien) gmail com> AuthorDate: Tue Aug 6 20:09:28 2013 +0000 Commit: Jauhien Piatlicki gmail com> CommitDate: Tue Aug 6 20:09:28 2013 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/g-sorcery.git;a=commit;h=4d158c38 Developer instructions --- README.md | 4 +- docs/Makefile | 13 +- docs/developer_instructions.html | 946 +++++++++++++++++++++++++++++++++++++++ docs/developer_instructions.rst | 640 ++++++++++++++++++++++++++ 4 files changed, 1599 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index caa3a7e..dcd8553 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Installation and using ====================== At the moment upstream layman does not support g-sorcery overlay type. -You should patch it with `https://raw.github.com/jauhien/g-sorcery/master/layman-git-g-sorcery.patch`. +You should [patch it](https://raw.github.com/jauhien/g-sorcery/master/layman-git-g-sorcery.patch). To do it download above mentioned patch, place it in **/etc/portage/patches/app-portage/layman-9999/** directory and @@ -120,3 +120,5 @@ all in one overlay. Note, that if you call **generate-tree** command your overla will be wiped and overlay tree for a given repository will be generated. Be careful! See man pages of **gs-elpa** and **gs-ctan** for further information. + +If you want to develop a new backend see [developer's instructions](./docs/developer_instructions.html). diff --git a/docs/Makefile b/docs/Makefile index c42c78d..19b6458 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,9 +1,16 @@ -SOURCES=g-sorcery gs-elpa gs-ctan -MANS=$(SOURCES:=.8) +HTML_SOURCES=developer_instructions +HTML_DOCS=$(HTML_SOURCES:=.html) +MAN_SOURCES=g-sorcery gs-elpa gs-ctan +MANS=$(MAN_SOURCES:=.8) + +RST2HTML=rst2html.py RST2MAN=rst2man.py -all: ${MANS} +all: ${MANS} ${HTML_DOCS} %.8: %.8.rst $(RST2MAN) $< $@ + +%.html: %.rst + $(RST2HTML) $< $@ diff --git a/docs/developer_instructions.html b/docs/developer_instructions.html new file mode 100644 index 0000000..db574e6 --- /dev/null +++ b/docs/developer_instructions.html @@ -0,0 +1,946 @@ + + + + + + +Developer Instructions + + + +
+

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

+
+#!/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:

+
+ {
+   "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:

+
+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 "nt".

+

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. +
    3. +
      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.

      +
      +
      +
    4. +
    +

    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:

+
+"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

+
+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

+
+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. +
  3. A tuple (varname, value)
  4. +
+

Variable names are automatically transformed to the upper-case during ebuild generation.

+

An example of ebuild generator:

+
+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

+
+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

+
+{'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

+
+<?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.

+
+
+ + 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:: + + + + + + gnu-elpa + packages for emacs + http://elpa.gnu.org/ + + piatlicki@gmail.com + Jauhien Piatlicki + + gs-elpa gnu-elpa + + + marmalade + packages for emacs + http://marmalade-repo.org/ + + piatlicki@gmail.com + Jauhien Piatlicki + + gs-elpa marmalade + + + melpa + packages for emacs + http://melpa.milkbox.net + + piatlicki@gmail.com + Jauhien Piatlicki + + gs-elpa melpa + + + +In entries **gs-elpa melpa** 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.