Skip to content

Commit

Permalink
Updates.
Browse files Browse the repository at this point in the history
  • Loading branch information
rajamukherji committed Oct 28, 2021
1 parent 2e75415 commit a28193c
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 74 deletions.
53 changes: 53 additions & 0 deletions docs/contexts.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,56 @@
Contexts
========

*Contexts* are used in ``rabs`` to provide dynamic hierarchical scoping for symbols. Symbols defined in a context are visible in any child contexts, and can be overridden without affecting the parent context. Each context has an associated path for identification which is also used as a prefix for targets created within the context.

The root :file:`build.rabs` file is executed in the root context. New contexts are created using :mini:`subdir()` or :mini:`scope()`, using the current context as the parent.

.. code-block:: mini
subdir("directory") :> Will load directory/build.rabs in child context
scope("test";) do
:> Inside child context
end
Build Contexts
--------------

Some targets (e.g. file targets) can be anywhere in the project and thus cannot be bound to a specific context. This means that flags or other settings (e.g. compilation flags) cannot be bound to targets.

However, the build function for each target should only be set once. ``rabs`` remembers the context where the build function for each target was set and uses the same context when performing the build.

Example
~~~~~~~

.. code-block:: mini
:< ROOT >:
CFLAGS := ["-pipe"]
fun compile_c(Target) do
let Source := Target % "c"
execute("cc", CFLAGS, Source, "-o", Target)
end
let Objects := [
file("main.o"),
file("test.o")
]
file("main.o") => compile_c
scope("test";) do
CFLAGS := old + ["-O3"]
file("test.o") => compile_c
end
DEFAULT[Objects]
In this example, :file:`main.o` will be compiled using just ``-pipe`` for *CFLAGS* but :file:`test.o` will be compiled using ``-pipe -O3``.

Default Targets
---------------

Each directory context defines a :mini:`DEFAULT` :doc:`meta target </targets/meta>`. If a directory context is not the root context then its :mini:`DEFAULT` target is automatically added as a dependency of the parent context's :mini:`DEFAULT` target.
3 changes: 2 additions & 1 deletion docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ Reference
=========

.. toctree::
:maxdepth: 2
:maxdepth: 1
:caption: Contents:

/contexts
/targets
/vmounts
/library
22 changes: 22 additions & 0 deletions docs/targets/expressions.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
Expression Targets
==================

An expression target represents a computable value. Expression targets can be used for capturing the output of config generation tools (e.g. :file:`pkg-config`) preventing the need to run them unnecessarily at each build.

To create an expression target, call the built-in :mini:`expr(Name)` function with a name that is unique to the current context. Calling :mini:`expr(Name)` with a name that already of an existing expr target in the current context will simply return the existing target.

.. code-block:: mini
var ExprTarget := expr("GTK_CFLAGS")
ExprTarget => fun(Target) do
shell("pkg-config --cflags gtk+-3.0"):trim
end
Build Function
--------------

When an expression target is used as a value (e.g. in a shell command), it is replaced by the value returned by the target's build function. Most common types of value are supported, booleans, numbers, strings, lists and maps are the most important.

Update Detection
----------------

An expression target is considered updated when the value computed by its build function has changed (using the Minilang hash method).
81 changes: 15 additions & 66 deletions docs/targets/files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,85 +3,34 @@ File Targets

File targets correspond to files or directories in the file system. Each file target has a path which is either absolute or relative to the root directory of the project.

To create a file target, call the built-in ``file`` function with the path to the file:
To create a file target, call the built-in :mini:`file(Path)` function with the path to the file or use a method such as :mini:`/`, :mini:`%` or :mini:`dir`.

.. code-block:: mini
var FileTarget := file("filename.txt")
var FileTarget := file("/path/filename.txt")
var DirTarget := FileTarget:dir :> file("/path")
var ObjectTarget := DirTarget / "test.o" :> file("/path/test.o")
var SourceTarget := ObjectTarget % "c" :> file("/path/test.c")
When called with a relative path, the path of the current context is used as the base. See :doc:`/contexts` for more information.

.. note::

If a file target is created with an absolute path that lies within the project directory, the path is converted to a relative path. This ensures that the file target still references the correct location even if the project directory is moved in the file system.

Methods
-------
Methods applicable to file targets can be found :doc:`here </library/file>`. The methods applicable to general targets also apply, they can be found :doc:`here </library/target>`.

.. code-block:: mini
FileTarget / Path
Returns a new file target by appending ``Path`` to ``FileTarget``.

.. code-block:: mini
FileTarget % Extension
Returns a new file target by replacing the extension of ``FileTarget`` with ``Extension``.

.. code-block:: mini
FileTarget:dir
Returns a new file target pointing to the directory containing ``FileTarget``.

.. code-block:: mini
FileTarget:dirname
Returns a string containing the path of the directory containing ``FileTarget``. This can differ from calling ``FileTarget:dir`` due to virtual mounts. See :doc:`/vmounts`.

.. code-block:: mini
FileTarget:basename
Path Resolution
---------------

Returns a string containing the base name of ``FileTarget``.
Where possible, file targets are represented by paths relative to the project root. When required (e.g. use in a shell command), the full path of the file is computed. In the absence of virtual mounts, this simply the relative path appended to the root path. Virtual mounts allow for multiple directories to be overlayed and generating multiple candidate paths for each file target. In this case the first path found to have an existing file is selected. If the file does not exist at any candidate path, the first candidate path is selected. More details about virtual mounts can be found :doc:`here </vmounts>`.

.. code-block:: mini
FileTarget:extension
Returns a string containing the extension of ``FileTarget``.

.. code-block:: mini
FileTarget:map(Source, Dest)
Returns a new file target found by computing a relative path from ``Source`` to ``FileTarget`` and appending it to ``Dest``.

.. code-block:: mini
FileTarget1 - FileTarget2
Returns the relative path from ``FileTarget2`` to ``FileTarget1``. If ``FileTarget1`` is not contained somewhere in ``FileTarget2`` then ``nil`` is returned.

.. code-block:: mini
FileTarget:exists
Returns ``FileTarget`` if ``FileTarget`` corresponds to an existing file in the file system, or if ``FileTarget`` has a build function.

.. code-block:: mini
Build Function
--------------

FileTarget:ls(Pattern, :R)
The build function for a file target should cause the file to be created. The result of the build function is ignored (excluding errors), but ``rabs`` will give a warning if the file does not exist after the build function has completed.

Returns a list of the files contained in ``FileTarget`` that match ``Pattern``. ``Pattern`` may be omitted in which case all files are returned. ``:R`` denotes a recursive listing, omit it to return only one level of files.
Update Detection
----------------

ml_method_by_name("ls", 0, target_file_ls, FileTargetT, NULL);
ml_method_by_name("copy", 0, target_file_copy, FileTargetT, FileTargetT, NULL);
ml_method_by_name("open", 0, target_file_open, FileTargetT, MLStringT, NULL);
ml_method_by_name("mkdir", 0, target_file_mkdir, FileTargetT, NULL);
ml_method_by_name("rmdir", 0, target_file_rmdir, FileTargetT, NULL);
ml_method_by_name("chdir", 0, target_file_chdir, FileTargetT, NULL);
ml_method_by_name("path", 0, target_file_path, FileTargetT, NULL);
When checking for updated file targets, ``rabs`` first checks the file modification time. If the modification time has changed, then ``rabs`` computes the *SHA256* checksum of the file contents. If this has changed then the file is marked as updated.
19 changes: 19 additions & 0 deletions docs/targets/meta.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
Meta Targets
============

A meta target represents a target with no other properties other than a build function and dependencies on other targets.

To create a meta target, call the built-in :mini:`meta(Name)` function with a name that is unique to the current context. Calling :mini:`meta(Name)` with a name that already of an existing meta target in the current context will simply return the existing target.

.. code-block:: mini
var MetaTarget := meta("TESTS")
Each directory :doc:`context </contexts>` defines a :mini:`DEFAULT` meta target. If a directory context is not the root context then its :mini:`DEFAULT` target is automatically added as a dependency of the parent context's :mini:`DEFAULT` target.

Build Function
--------------

The build function for a meta target can do anything. The result of the build function is ignored (excluding errors).

Update Detection
----------------

A meta target is considered updated if any of its dependencies has been updated.
22 changes: 22 additions & 0 deletions docs/targets/scans.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
Scan Targets
============

A scan target represents a computable list of other targets that is updated as required.

To create a scan target, call the :mini:`:scan(Name)` method on another *base* target. Calling :mini:`:scan(Name)` with a name of an existing scan target for the same base target will simply return the existing target.

.. code-block:: mini
var FileTarget := file("test.o")
var ScanTarget := FileTarget:scan("HEADERS")
Build Function
--------------

The build function for a scan target should return a list of targets. Each of the targets returned is then updated if necessary.

.. note::

The targets returned by a scan target's build function are not automatically considered dependencies of the scan target itself. In some cases (e.g. C include files), this behaviour is required in which case the dependencies need to be added explicitly. Since the list of targets is not known until the scan target's build function is run, the only way to do this is to use the :mini:`check(Targets)` which will add :mini:`Targets` as dependencies to the current building target.

Update Detection
----------------

A scan target is considered updated when any of its list of targets has been updated.
27 changes: 27 additions & 0 deletions docs/targets/symbols.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
Symbol Targets
==============

A symbol target represents a context-specific value binding.

Symbol targets are created automatically whenever an undeclared identifier is used. They can be created explicitly using the :mini:`symbol(Name)` function, in cases where the name is created programmatically.

.. code-block:: mini
var SymbolTarget := SYMBOL
When symbols are used in any code, they are resolved in the current context. Thus the same symbol in the same function may resolve to different values depending on the current context when the function is called. This is used to have context specific flags or settings in general purpose build functions. See :doc:`/contexts` for an example.

Using a symbol while building a target also adds that symbol as a dependency to that target. In some cases, it may be necessary to explicitly add a symbol as a dependency to a target even if it is not used within the target's build function. Since using a symbol in any code automatically resolves to the symbols value, the symbol name must be added as a string instead.

.. code-block:: mini
var FileTarget := file("test.o")
FileTarget["SYMBOL"]
Build Function
--------------

Symbols should not have build functions.

Update Detection
----------------

A symbol is considered updated if its value changes. Note that if the value of a symbol is another target, the update status of that target does not affect the symbol's update status.
2 changes: 1 addition & 1 deletion docs/tutorial/example1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Suppose we have a single *C* source file :file:`hello.c`. To build a program :fi

.. code-block:: mini
-- ROOT --
:< ROOT >:
file("hello.o")[file("hello.c")] => fun() do
execute('gcc -c -o{file("hello.o")} {file("hello.c")}')
Expand Down
29 changes: 29 additions & 0 deletions docs/vmounts.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,31 @@
Virtual Mounts
==============

Virtual mounts allow directories to be overlayed, which in turn allows source and build directories to be kept separate without adding a lot of path overhead to the build functions.

Virtual mounts are created using the :mini:`vmount(Path, Source)` function. Both :mini:`Path` and :mini:`Source` should be directory paths. :mini:`Path` must be a relative path to the current context path, :mini:`Source` can be either relative or absolute.

Once created, virtual mounts are visible project wide.

.. code-block:: mini
vmount("build", "source")
Path Resolution
---------------

Whenever a file target is used, it is first resolved to an absolute path. Each virtual mount :mini:`vmount(Path, Source)` adds creates two possible resolutions for the file path, the original and one with :mini:`Path` replaced with :mini:`Source`. Virtual mounts are considered in reverse order, later virtual mounts are applied first.

Resolving a file target to a path involves checking each possible resolution for an existing file or directory. If found, the path of the existing file or directory is returned. Otherwise the first possible resolution, i.e. without any vmount replacements, is returned.

.. code-block:: mini
vmount("a", "b")
vmount("a/c", "d")
file("a/c/test") :> Tries "a/c/test", "d/test", "b/c/test" in that order
.. note:: mini

When using :mini:`subdir` with a virtual mount, the target path should be used, not the source, even though the :file:`build.rabs` file likely in the source directory. ``rabs`` will treat the target path as part of the project and only use the source directory for finding files, including the :file:`build.rabs` file.

18 changes: 13 additions & 5 deletions src/rabs.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,11 +303,19 @@ ML_FUNCTION(Vmount) {
ML_CHECK_ARG_TYPE(1, MLStringT);
const char *Path = ml_string_value(Args[0]);
const char *Target = ml_string_value(Args[1]);
vfs_mount(
concat(CurrentContext->Path, "/", Path, NULL),
concat(CurrentContext->Path, "/", Target, NULL),
Target[0] == '/'
);
if (Target[0] == '/') {
vfs_mount(
concat(CurrentContext->Path, "/", Path, NULL),
Target,
1
);
} else {
vfs_mount(
concat(CurrentContext->Path, "/", Path, NULL),
concat(CurrentContext->Path, "/", Target, NULL),
0
);
}
return MLNil;
}

Expand Down
1 change: 0 additions & 1 deletion src/target_scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ struct target_scan_t {
const char *Name;
target_t *Source;
targetset_t *Scans;
ml_value_t *Rebuild;
};

ML_TYPE(ScanTargetT, (TargetT), "scan-target");
Expand Down

0 comments on commit a28193c

Please sign in to comment.