Subcomponent Configuration Language
===================================
Subcomponent uses its own configuration language to describe the different
components and the interactions they have with each other. This language, a bit
unusual is loosely based on the Edje_ syntax.
Brief Overview
---------------
It is a descriptive configuration language that allows to store uniquely identified,
typed key-value pairs. It exposes two main concepts:
- *properties*
- and *blocks*.
Before going in the details of properties and blocks, here is a simple code example:
.. code:: javascript
subcomponents {
my-component {
name: "A pretty name";
path: "clone-me-here";
fetch {
git {
url: "git://example.com/repo.git", "https://example2.com/repo.git";
branch: "master";
clone-recursive: false;
}
}
}
}
By writing the above code, you actually described a component named
``my-component``, that you described with ``A pretty name``. It will be
retrieved at ``clone-me-here/``, using the *fetch method* **git**. You provided
two URLs that can be used to retrieve the component. If for some reason the
first URL fails (firewall issue, mirror down, ...), the other URL will be used
to fetch the component. It will be retrieved on branch ``master``, and every
time you update the component, it will re-sync itself with upstream changes. It
will also not clone its submodules, as you explicitely disabled them.
All of this is possible by setting *properties* among some *blocks*. A
*property* is expressed within a block as its name, followed by a column.
``url`` and ``branch`` for instance are two properties. It shall be followed by
the value to be affected to the property, and be terminated by a semicolumn.
A property holds a **typed value**. Passing an invalid type to a propery causes
the subcomponent compiler to throw an error. For instance, if you had written
``clone-recursive: "false";``, subcomponent would have refused you to pass a
string to a property that expects a boolean value. Types of properties are
written in the parser and cannot be bypassed. The supported types are:
- *boolean*: expects ``true`` or ``false``;
- *string*: expects an expression encompassed between two double quotes;
- *composite list*: a finite set of values of the same type. The ``url``
property in the previous example is a list of strings. Two elements are
separated by a comma. Lists of lists are not possible, due to the syntax of
the list itself.
As stated earlier, a *property* can only be contained in a *block*. A *block*
holds its name and a finite set of properties. Blocks cannot be contained by
properties. In the previous example, ``subcomponents``, ``my-component``,
``fetch`` and ``git`` are four blocks. The properties and blocks they contain
are enclosed between one opening and one closing braces. The text written just
before opening the curly brace defines the name of the block. Two properties
that are on the same block hierarchy level cannot share the same name.
Syntactic notes
---------------
Blocks and properties are *identifiers* at a lexical level. An identifier:
- can start with: a letter (lowercase or uppercase) or an underscore;
- contain letters, digits, underscores or hyphens.
Subcomponent support C99 comments: multi-lines and single-line:
- `//` tells to ignore everything until a newline;
- `/*` and `*/` ignore all text in between.
Whitespaces are syntactically equivalent as comments.
Describing a component
----------------------
Components are *blocks* contained in the **subcomponents** block. There is
virtually no limitation on the number of described component. Note that
two components cannot have the same name.
A component can the following properties in its top-level block:
- ``name``, a *string* that describes the component in a short sentence,
- ``path``, a *string* that defines The path where the component will be made
available. If it is not absolute, it will be interpreted as relative to the
working directory of the subcomponent process that retrieved the
component.
A component then **must** define a ``fetch`` block. The ``fetch`` block shall
contain one or more blocks that describe each a different *fetch method*. A
*fetch method* is the subcomponent term to describe the means to make a
component available to the user. If several *fetch methods* are described,
the first one to be tried will be the first described. If it fails, the next
one is tried, on so on, sequentially. ``git`` is currently the only fetch
method that subcomponent implements.
Every single fetch method implement one propery in common: ``url``, which is a
list of strings. Each string element shall contain a URL that can be used by
the fetch method to retrieve the component. It is mandatory to affect this
property with a value. It can be a list of one element (equivalent to a single
string). How the URL will be actually used is up to the underlying fetch
method. If a fetch method fails to retrieve the component using an URL item, it
will try the following one. This mechanism allows subcomponent to offer a
resilient way to retrieve components. Imagine that you want to fetch a
component host on some server, but at this precise instant, you are out of
luck, the server is down... if you provided a fallback URL, set a known mirror,
you can still get your component!
The Git fetch method
~~~~~~~~~~~~~~~~~~~~
Exactly one of the following properties must be defined. They are mutually exclusive:
- ``branch``: a *string* that indicates which git branch shall be fetched,
- ``commit``: a *string* that indicates which git commit shall be checkouted,
- ``tag``: a *string* that indicated which git tag shall be retrieved.
Then, the following properties can be set to obtain better performances:
- ``clone-recusrive``: a *boolean* that is *true* by default. If your component
contains git submodules, they will be automatically fetched if this property
is *true*. Otherwise they will be ignored.
- ``shallow``: a *boolean* that is *false* by default. If enabled, the
component will be shallow-cloned. This option is reserved to fetching
*branches*.
- ``shallow-submodules``: a *boolean* that is *false* by default. If tenabled,
the component can shallow-clone its git submodules.
- ``remote``: a *string* that defaults to *subcomponent*. It is the name of the
git remote that subcomponent expects to have an exclusive access on.
Be aware that using shallowing can be hazardeous if not well-understood.
If you update your component from a first shallow clone to another revision,
subcomponent will be obliged to unshallow the repository, leading to higher
fetching times. Shallowing is great when you know exactly what you are doing
though.
Some implementation details
---------------------------
It is worth noting that subcomponent code is implicitely encompassed inside a
*unique anonymous block*: a block without a name. Only one does exist, and it
contains everything else: blocks and properties.
To uniquely identify a property among others, using just their name is not
sufficient. Once the subcomponent compiler has processed every single file
that was provided to him, it has generated a key-value database that is a bit
hash table. Imagine the following block at the top-level of subcomponent:
.. code:: javascript
a_block {
another_block {
a_boolean_property: true;
}
}
As stated before, this block is actually contained by the unique anonymous
block. The property ``a_boolean_property`` is actually named
``/a_block/another_block/a_boolean_property`` in the database.
.. _Edje: https://www.enlightenment.org/themes/start