diff options
author | 2013-08-06 22:09:28 +0200 | |
---|---|---|
committer | 2013-08-06 22:09:28 +0200 | |
commit | 4d158c383f10ccc177b93ffeecaff3fce33b1691 (patch) | |
tree | 36704efe2cafb0e166697924a9e7fddcb0201bf6 /docs | |
parent | README.md: github code highlighting (diff) | |
download | g-sorcery-4d158c383f10ccc177b93ffeecaff3fce33b1691.tar.gz g-sorcery-4d158c383f10ccc177b93ffeecaff3fce33b1691.tar.bz2 g-sorcery-4d158c383f10ccc177b93ffeecaff3fce33b1691.zip |
Developer instructions
Diffstat (limited to 'docs')
-rw-r--r-- | docs/Makefile | 13 | ||||
-rw-r--r-- | docs/developer_instructions.html | 946 | ||||
-rw-r--r-- | docs/developer_instructions.rst | 640 |
3 files changed, 1596 insertions, 3 deletions
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 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="generator" content="Docutils 0.10: http://docutils.sourceforge.net/" /> +<title>Developer Instructions</title> +<style type="text/css"> + +/* +:Author: David Goodger (goodger@python.org) +:Id: $Id: html4css1.css 7514 2012-09-14 14:27:12Z milde $ +:Copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { + overflow: hidden; +} + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title, .code .error { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin: 0 0 0.5em 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left, .figure.align-left, object.align-left { + clear: left ; + float: left ; + margin-right: 1em } + +img.align-right, .figure.align-right, object.align-right { + clear: right ; + float: right ; + margin-left: 1em } + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left } + +.align-center { + clear: both ; + text-align: center } + +.align-right { + text-align: right } + +/* reset inner alignment in figures */ +div.align-right { + text-align: inherit } + +/* div.align-center * { */ +/* text-align: left } */ + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font: inherit } + +pre.literal-block, pre.doctest-block, pre.math, pre.code { + margin-left: 2em ; + margin-right: 2em } + +pre.code .ln { color: grey; } /* line numbers */ +pre.code, code { background-color: #eeeeee } +pre.code .comment, code .comment { color: #5C6576 } +pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } +pre.code .literal.string, code .literal.string { color: #0C5404 } +pre.code .name.builtin, code .name.builtin { color: #352B84 } +pre.code .deleted, code .deleted { background-color: #DEB0A1} +pre.code .inserted, code .inserted { background-color: #A3D289} + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } + +</style> +</head> +<body> +<div class="document" id="developer-instructions"> +<h1 class="title">Developer Instructions</h1> + +<div class="section" id="g-sorcery-overview"> +<h1>g-sorcery overview</h1> +<p><strong>g-sorcery</strong> is a framework aimed to easy development of ebuild +generators.</p> +<p>Some terms used in this guide:</p> +<ul> +<li><dl class="first docutils"> +<dt><strong>3rd party software provider</strong> or <strong>repository</strong></dt> +<dd><p class="first last">A system of software distribution like CTAN or CPAN that +provides packages for some domain (e.g. TeX packages or elisp +packages for emacs).</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt><strong>backend</strong></dt> +<dd><p class="first last">A tool developed using <strong>g-sorcery</strong> framework that provides +support for repositories of a given type.</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt><strong>overlay</strong></dt> +<dd><p class="first last">Usual Gentoo overlay.</p> +</dd> +</dl> +</li> +</ul> +<p><strong>g-sorcery</strong> consists of different parts:</p> +<ul> +<li><dl class="first docutils"> +<dt><strong>package_db.PackageDB</strong></dt> +<dd><p class="first last">A package database. It holds information about all available +packages in a given repository.</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt><strong>package_db.DBGenerator</strong></dt> +<dd><p class="first last">A fabric that creates PackageDB object and fills it with information.</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt><strong>backend.Backend</strong></dt> +<dd><p class="first last">Backend that processes user commands.</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt><strong>ebuild</strong></dt> +<dd><p class="first last">Module with different ebuild generators.</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt><strong>eclass</strong></dt> +<dd><p class="first last">Module with eclass generators.</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt><strong>metadata.MetadataGenerator</strong></dt> +<dd><p class="first last">Metadata generator.</p> +</dd> +</dl> +</li> +</ul> +<p>Also there are other modules and classes that will be described later.</p> +<p>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.</p> +<p>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.</p> +<p>The idea of <strong>g-sorcery</strong> 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 <strong>Gentoo</strong> packages.</p> +<p>So there are two phases of backend operation:</p> +<ul class="simple"> +<li>synchronize with repository database</li> +<li>populate overlay using obtained information</li> +</ul> +<p>There are two ways of using backend:</p> +<ul class="simple"> +<li>run it as a CLI tool manually</li> +<li>use its integration with layman</li> +</ul> +</div> +<div class="section" id="backend-structure"> +<h1>Backend structure</h1> +<p>The only mandatory module in a backend is called <strong>backend</strong>. It should contain +at least one variable called <strong>instance</strong> that has a <strong>__call__</strong> method that +takes 4 arguments. These arguments are:</p> +<ul class="simple"> +<li>self</li> +<li>command line arguments</li> +<li>backend config</li> +<li>g-sorcery config</li> +</ul> +<p>Usually <strong>instance</strong> variable should be an instance of a class g_sorcery.backend.Backend +or derived class.</p> +<p>g_sorcery.backend.Backend constructor takes 8 arguments. They are:</p> +<ul class="simple"> +<li>self</li> +<li>Package database generator class</li> +<li>Two ebuild generator classes</li> +<li>Eclass generator class</li> +<li>Metadata generator class</li> +<li>Package database class</li> +<li>Boolean variable that defines method of database generation</li> +</ul> +<p>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 <strong>git-2</strong> eclass from the main tree or +any eclass from backends provided with g-sorcery.</p> +<p>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.</p> +<p>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:</p> +<ul class="simple"> +<li>install a binary that calls g-sorcery with appropriate backend name (see man g-sorcery)</li> +<li>install a config that allows g-sorcery find appropriate backend module</li> +</ul> +<p>A binary should just pass arguments to g-sorcery. For a backend named gs-elpa it could look like</p> +<pre class="code literal-block"> +#!/bin/bash + +g-sorcery g-elpa $@ +</pre> +<div class="section" id="backend-config"> +<h2>Backend config</h2> +<p>Backend config is just a JSON file with a dictionary. There are two mandatory entries:</p> +<ul> +<li><dl class="first docutils"> +<dt>package</dt> +<dd><p class="first last">Its value should be a string with a package containing backend.</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt>repositories</dt> +<dd><p class="first last">A dictionary describing available repositories. Should have at least one entry.</p> +</dd> +</dl> +</li> +</ul> +<p>Backend config should have a name BACKEND.js and should be installed under <strong>/etc/g-sorcery</strong> +directory. BACKEND here is a backend name which was used in a g-sorcery call.</p> +<p>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 <strong>repo_uri</strong> in case database is +generated using info downloaded from the repository or <strong>db_uri</strong> in case database is +just synced with another already generated database. Also there can be a <strong>masters</strong> entry that +contains a list of overlays this repository depends on. If present it should contain at least +<strong>gentoo</strong> entry.</p> +<p>A simple backend config:</p> +<pre class="code literal-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"] + } + } +} +</pre> +</div> +</div> +<div class="section" id="package-database"> +<h1>Package database</h1> +<div class="section" id="directory-layout"> +<h2>Directory layout</h2> +<p>Package database is a directory tree with JSON files. The layout of this tree looks like:</p> +<pre class="code literal-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 + ... +</pre> +<p>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.</p> +</div> +<div class="section" id="packagedb-class"> +<h2>PackageDB class</h2> +<p>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.</p> +<p>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 <strong>get_real_db_uri</strong> method.</p> +<p>There is a number of hooks that are called after package, category or the whole database is +written/read:</p> +<ul class="simple"> +<li>additional_write_version</li> +<li>additional_write_package</li> +<li>additional_write_category</li> +<li>additional_write</li> +<li>additional_read_version</li> +<li>additional_read_package</li> +<li>additional_read_category</li> +<li>additional_read</li> +</ul> +<p>Note that before add any package you should add a category for it using <strong>add_category</strong>. +Then packages can be added using <strong>add_package</strong>. PackageDB currently does not write changes +automatically, so you should call <strong>write</strong> after changes are done. This is not relevant +for database changing in <strong>process_data</strong> method of database generator as there all changes +are written by other methods it calls internally after <strong>process_data</strong>.</p> +</div> +<div class="section" id="json-serializable-objects"> +<h2>JSON serializable objects</h2> +<p>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:</p> +<ul class="simple"> +<li>usual method <strong>serialize</strong> that returns a JSON serializable object in terms of standard Python +json module</li> +<li>class method <strong>deserialize</strong> that takes a value returned by <strong>serialize</strong> and constructs new instance +of your class using it</li> +</ul> +</div> +<div class="section" id="dependency-handling"> +<h2>Dependency handling</h2> +<p>There is a special class g_sorcery.g_collections.Dependency aimed to handle dependencies. +Its constructor takes two mandatory parameters:</p> +<ul class="simple"> +<li>category</li> +<li>package</li> +</ul> +<p>and two additional parameters:</p> +<ul class="simple"> +<li>version</li> +<li>operator</li> +</ul> +<p>These two are the same as version and operator used in the usual package atom.</p> +<p>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".</p> +<p>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.</p> +</div> +</div> +<div class="section" id="package-database-generator"> +<h1>Package database generator</h1> +<div class="section" id="customizing-dbgenerator"> +<h2>Customizing DBGenerator</h2> +<p>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:</p> +<ul> +<li><dl class="first docutils"> +<dt>get_download_uries</dt> +<dd><p class="first">Get a list with download URI entries. +Each entry has one of the following formats:</p> +<ol class="arabic"> +<li><p class="first">String with URI.</p> +</li> +<li><dl class="first docutils"> +<dt>A dictionary with entries:</dt> +<dd><ul class="first simple"> +<li>uri: URI.</li> +<li>parser: Parser to be applied to downloaded data.</li> +<li>open_file: Whether parser accepts file objects.</li> +<li>open_mode: Open mode for a downloaded file.</li> +</ul> +<p class="last">The only mandatory entry is uri.</p> +</dd> +</dl> +</li> +</ol> +<p class="last">The default implementation returns [backend_config["repositories"][REPOSITORY]["repo_uri"]].</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt>parse_data</dt> +<dd><p class="first last">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.</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt>process_data</dt> +<dd><p class="first last">This method should fill a package database with entries using +already downloaded and parsed data.</p> +</dd> +</dl> +</li> +</ul> +<p>Generally speaking these are all the method you should implement.</p> +</div> +<div class="section" id="value-convertion"> +<h2>Value convertion</h2> +<p>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 <strong>convert</strong> function. To understand how it works see its sources in +g_sorcery.package_db.DBGenerator and as an example CTAN backend.</p> +<p>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 <strong>common_config</strong> entry to backend config which +looks like:</p> +<pre class="code literal-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" + } +} +</pre> +<p>And then call in your <strong>process_data</strong> method</p> +<pre class="code literal-block"> +license = self.convert([common_config, config], "licenses", repo_license) +</pre> +<p>Where <strong>common_config</strong>, <strong>config</strong> are config provided as arguments to your <strong>process_data</strong> method +and <strong>repo_license</strong> is a license name used by the repository.</p> +<p>There is a special conversion function used for dependencies: <strong>convert_dependency</strong>. To use it you should +usually redefine <strong>convert_internal_dependency</strong> and <strong>convert_external_dependency</strong>. To decide whether +a dependency is external database generator uses <strong>external</strong> entry in config.</p> +<p>You may want to test whether there is a given value in given entry in config. To do it use +<strong>in_config</strong> function.</p> +</div> +</div> +<div class="section" id="eclass-generator"> +<h1>Eclass generator</h1> +<p>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</p> +<pre class="code literal-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')) +</pre> +<p>There is no common eclass currently. I plan to change it in the future, so your eclass code can +inherit any common functionality.</p> +</div> +<div class="section" id="ebuild-generator"> +<h1>Ebuild generator</h1> +<p>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.</p> +<p>Layout has entries for vars and inherited eclasses. Each entry is a list. +Entries are processed in the following order:</p> +<ul class="simple"> +<li>vars_before_inherit</li> +<li>inherit</li> +<li>vars_after_inherit</li> +<li>vars_after_description</li> +<li>vars_after_keywords</li> +</ul> +<p><strong>inherit</strong> entry is just a list of eclass names.</p> +<p><strong>vars*</strong> entries are lists of variables in two possible formats:</p> +<ol class="arabic simple"> +<li>A string with variable name</li> +<li>A tuple (varname, value)</li> +</ol> +<p>Variable names are automatically transformed to the upper-case during ebuild generation.</p> +<p>An example of ebuild generator:</p> +<pre class="code literal-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) +</pre> +</div> +<div class="section" id="metadata-generator"> +<h1>Metadata generator</h1> +<p>To use metadata generator you should just define some variables in ebuild data.</p> +<div class="section" id="xml-schema-format"> +<h2>XML schema format</h2> +<p>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:</p> +<ul> +<li><dl class="first docutils"> +<dt><strong>name</strong></dt> +<dd><p class="first last">Name of a tag</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt><strong>multiple</strong></dt> +<dd><p class="first last">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.</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt><strong>required</strong></dt> +<dd><p class="first last">Boolean that defines if a given tag is required.</p> +</dd> +</dl> +</li> +<li><dl class="first docutils"> +<dt><strong>subtags</strong></dt> +<dd><p class="first last">List of subtags.</p> +</dd> +</dl> +</li> +</ul> +</div> +<div class="section" id="data-dictinonary-format"> +<h2>Data dictinonary format</h2> +<p>The part of ebuild data used for metadata generation should have data dictionary format +also defined in g_sorcery.metadata.</p> +<p>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 <strong>text</strong> 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.</p> +</div> +<div class="section" id="metadata-xml-schema"> +<h2>Metadata XML schema</h2> +<p>Metadata XML schema looks like</p> +<pre class="code literal-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' : []}, + ] + }, + ] +</pre> +<p>So to have metadata.xml filled with e.g. maintainer info you should add to ebuild data +something like</p> +<pre class="code literal-block"> +{'maintainer' : [{'email' : 'piatlicki@gmail.com', + 'name' : 'Jauhien Piatlicki'}]} +</pre> +</div> +</div> +<div class="section" id="layman-integration"> +<h1>Layman integration</h1> +<p>There is a <strong>layman</strong> integration for <strong>g-sorcery</strong> (thanks to Brian Dolbec and Auke Booij here). +To use it you just need to install and xml file describing your repositories in +<strong>/etc/layman/overlays</strong> directory. For our example of backend config we could write an xml file +that looks like</p> +<pre class="code literal-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> +</pre> +<p>In entries <strong><source type="g-sorcery">gs-elpa melpa</source></strong> the source type +should always be <strong>g-sorcery</strong>, <strong>gs-elpa</strong> is backend name and <strong>melpa</strong> is repository name.</p> +<p>For full description of format of this file see <strong>layman</strong> documentation.</p> +<p>Note: at the moment layman-9999 does not support <strong>g-sorcery</strong> overlay type and you should +patch it with <a class="reference external" href="https://raw.github.com/jauhien/g-sorcery/master/layman-git-g-sorcery.patch">https://raw.github.com/jauhien/g-sorcery/master/layman-git-g-sorcery.patch</a></p> +</div> +<div class="section" id="summary"> +<h1>Summary</h1> +<p>So to create your own backend you should write a module named <strong>backend</strong> and define there +a variable named <strong>instance</strong> that is an instance of g_sorcery.backend.Backend class. Or something +that quacks like this class.</p> +<p>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.</p> +<p>Also you should write an executable that calls g-sorcery and some configs.</p> +</div> +</div> +</body> +</html> 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. |