diff --git a/docs/src/conf.py b/docs/src/conf.py index a67b471..910104e 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -20,7 +20,7 @@ # -- Project information ----------------------------------------------------- project = 'elba' -copyright = '2018, David Cao' +copyright = '2019, David Cao' author = 'David Cao' # The short X.Y version diff --git a/docs/src/index.rst b/docs/src/index.rst index 1eb25c1..aabf8e4 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -9,6 +9,7 @@ to be a mostly comprehensive guide on actually using it. :caption: Usage usage/quick_start + usage/binaries_and_tests usage/installing usage/custom_subcommands usage/publishing diff --git a/docs/src/reference/manifest.rst b/docs/src/reference/manifest.rst index 5101eec..37bf059 100644 --- a/docs/src/reference/manifest.rst +++ b/docs/src/reference/manifest.rst @@ -181,7 +181,6 @@ three types of targets which elba can build: # this corresponds to the first of the following files which exists: # - bin/Whatever/Module.idr # - bin/Whatever/Module.lidr - # - src/bin/Whatever/Module.idr (because of the default `path` value) # Example 2: main uses dots instead of slashes to separate folders, and # includes an idr extension @@ -200,8 +199,6 @@ three types of targets which elba can build: # corresponds to the first of the following files which exists: # - bin/Whatever/Module.idr # - bin/Whatever/Module.lidr - # - src/bin/Whatever/Module.idr (due to the default `path` value) - # - src/bin/Whatever/Module.lidr # in both cases, this file should have a function `Module.custom : IO ()`, # which will be used as the main function diff --git a/docs/src/usage/binaries_and_tests.rst b/docs/src/usage/binaries_and_tests.rst new file mode 100644 index 0000000..658671a --- /dev/null +++ b/docs/src/usage/binaries_and_tests.rst @@ -0,0 +1,203 @@ +Binaries and Tests +================== + +Although specifying a library target is pretty straightforward, trying +to get a binary or a test target working can involve a lot more +finagling; elba supports multiple syntaxes and ways to specify a +binary target (and by extension, a test target, since test targets are +just spruced-up binary targets under-the-hood) in order to ensure +maximum flexibility and compatibility with the existing Idris code out +in the wild. Although there is information about these kinds of targets +:doc:`in the reference <../reference/manifest>`__, this section will +help you build some intuition as to the building blocks of the binary +target specification system, and will provide a "cookbook" of common +usecases to follow. + +Terminology +----------- + +There are a few terms you should know as a prerequisite: + +- A **subpath** is a subdirectory of the project's root folder. By + definition, subpaths cannot refer to parent or absolute directories. + Examples include ``bin/``, ``bin/Whatever/Module``, ``Lightyear.idr``, + etc. + +- A **module path** is the ``A.B.C`` name associated with a given Idris + file ("module"). Names in the module path are separated with periods, + and their precise location is determined by other config keys in the + manifest. Examples include ``Lightyear.Core``, + ``Control.Monad.Tardis``, etc. + +Subpaths and module paths can actually be combined into a **mixed path**, +like in ``src/Lightyear.Core`` or ``tests/one/Tests.Unit.API``. The +parts which are separated with slashes are considered the subpath +portion (``src``, ``tests/one``), while the parts separated with periods +are the module portion (``Lightyear.Core``, ``Tests.Unit.API**). When +mixed, subpaths always precede module paths. These types of strings-- +subpaths, module paths, or mixed paths--will be referred to as +**target strings** or **target specifiers**. + +In order to account for main functions which aren't named ``Main.main``, +elba allows for generation of a main file which points to a function +in another module. This will be referred to as "generating a main file" +for another function. + +Resolution Rules +---------------- + +There are multiple components to any binary or test target; for our +purposes, the relevant parts are the ``path`` and ``main`` specifiers. +``main`` is required and refers to a target string, while ``path`` +refers to a parent directory in which the target string should be +searched for. ``path`` can be omitted: its default value is ``src/`` +for binary targets and ``tests/`` for test targets. + +Given the following binary target: + +.. code-block:: toml + + [[bin]] + path = "$p" + main = "a/.../pqr.xyz.ext" + +elba will go through the following rules to resolve a target specifier +(the first search which works is used): + +1. Attempt to interpret as a subpath: filename ``pqr.xyz``, extension ``ext`` + 1. If there's no file extension or ``ext`` == ``idr`` or ``lidr``: + 1. Search for ``a/.../xyz.idr`` + 2. Search for ``a/.../xyz.lidr`` + 2. Otherwise: + 1. Search for ``a/.../xyz.idr``, generate main for fn ``ext`` + 2. Search for ``a/.../xyz.lidr``, generate main for fn ``ext`` + 3. If either branch failed, continue to next +2. Otherwise, interpret as a mixed or module path: + 1. Search for ``$p/a/.../pqr/xyz/ext.idr`` + 2. Search for ``$p/a/.../pqr/xyz/ext.lidr`` + 3. Search for ``$p/a/.../pqr/xyz.idr``, generate main for fn ``ext`` + (except if ``ext`` == ``idr`` or ``lidr``) + 4. Search for ``$p/a/.../pqr/xyz.lidr``, generate main for fn ``ext`` + (except if ``ext`` == ``idr`` or ``lidr``) + +These rules make more sense in practice. + +Source & Target Paths +--------------------- + +Internally, elba splits a path into two parts: a **source path** and a +**target path**. Any file which is located in the source path will be +included in the Idris build invocation with the ``--include`` flag (i.e. +your source files *will not* be available unless they are located under +the source path). To determine where to divide the source and target +paths, elba uses the following rules: + +1. The source path of a subpath is its immediate parent. +2. The source path of a mixed or module path is the value of the + ``path`` specifier. + +In Practice +----------- + +This section contains a few handy examples of common patterns for +target specifiers. + +Files in src/ directory +~~~~~~~~~~~~~~~~~~~~~~~ + +Let's say your project has a file ``src/Main.idr``, with a function +``Main.main``. You could generate a binary for it in the following +ways (don't use all the ``[[bin]]`` blocks at once!) + +.. code-block:: toml + + [[bin]] + path = "src" # also specified by default + main = "Main.idr" + +.. code-block:: toml + + [[bin]] + # this is a subpath, so path will be ignored: + main = "src/Main.idr" + +src/ directory, custom main +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now, let's say you decide to move your main file to somewhere more +exotic, like ``src/bin/App/Cli.idr``, with a main function +``App.Cli.run``. Which bin target you use depends on which files your +binary will need to import to work: + +.. code-block:: toml + + [[bin]] + # This binary needs files from the `src` directory + # This line is the default, so it isn't necessary + path = "src/" + main = "bin/App.Cli.run" + # this works as long as the file "bin/App.Cli.idr" doesn't exist + # also works: main = "bin/App/Cli.run", so long as + # "bin/App/Cli/run.idr" doesn't exist + +.. code-block:: toml + + [[bin]] + # We only need files from src/bin + path = "src/bin" + main = "App.Cli.run" + # or main = "App/Cli.run" + +.. code-block:: toml + + [[bin]] + # We only need files from src/bin/App + path = "src/bin/App" + main = "Cli.run" + +.. code-block:: toml + + [[bin]] + # Equivalent to above + # Whatever we set path to is irrelevant; elba will resolve main as a + # subpath first + main = "src/bin/App/Cli.run" + +Adding a test +~~~~~~~~~~~~~ + +Because tests and binaries are represented the same way to elba, the +same rules and processes apply here too. Let's add a test function +``runTests`` in the file ``tests/Tests.idr``: + +.. code-block:: toml + + [[test]] + path = "tests" + main = "Tests.runTests" + +``.idr`` and ``.lidr`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +elba has special cases for target specifiers that end in ``idr`` or +``lidr``. If you add a test target like so: + +.. code-block:: toml + + [[test]] + path = "tests" + main = "Tests.idr" + +elba will look for: + +1. ``Tests.idr`` +2. ``tests/Tests/idr.idr`` +3. ``tests/Tests/idr.lidr`` +4. ``tests/Tests.idr`` +5. ``tests/Tests.lidr`` + +elba will never try to generate anything if the target specifier ends +with ``.idr`` or ``.lidr``. + +More examples of these are available in :doc:`the reference +<../reference/manifest>`__. diff --git a/docs/src/usage/publishing.rst b/docs/src/usage/publishing.rst index d00d833..d4353ed 100644 --- a/docs/src/usage/publishing.rst +++ b/docs/src/usage/publishing.rst @@ -37,6 +37,7 @@ have multiple indices specified in your configuration, you can specify the index to use with the ``--index`` flag. For example: .. code-block:: console + $ elba login a67fc893bccfea2141 --index index+git+https://github.com/elba/index Otherwise, elba will use the default index (the first index specified in @@ -80,6 +81,7 @@ your project, under the ``package.ignore`` key. .gitignore files as list elements. An example is provided below: .. code-block:: toml + [package] # snip: other package metadata # ignoring files that end with .out or .dev diff --git a/src/lib/package/manifest.rs b/src/lib/package/manifest.rs index 74cc646..89e9294 100644 --- a/src/lib/package/manifest.rs +++ b/src/lib/package/manifest.rs @@ -369,6 +369,9 @@ impl BinTarget { // part exists, we assume that the last part refers to a function which // should be treated as the main function. Some((src_path, target_path.with_extension(after))) + } else if src_path.join(&target_path).with_extension("lidr").exists() { + // Same, but for literate file + Some((src_path, target_path.with_extension(after))) } else { None }