From d3d87ea190460819120a29459426fdd233b3000c Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Mon, 12 Dec 2016 00:54:20 -0800 Subject: Add documentation for repositories and namespaces. (#2474) * Add documentation for repositories and namespaces. * Update and extend repository documentation per review. - Also add `-N` argument for `spack spec` --- lib/spack/docs/config_yaml.rst | 2 +- lib/spack/docs/configuration.rst | 1 + lib/spack/docs/index.rst | 15 +- lib/spack/docs/packaging_guide.rst | 6 +- lib/spack/docs/repositories.rst | 456 +++++++++++++++++++++++++++++++++++++ lib/spack/docs/tutorial_sc16.rst | 2 +- lib/spack/spack/cmd/repo.py | 2 +- lib/spack/spack/cmd/spec.py | 9 +- 8 files changed, 476 insertions(+), 17 deletions(-) create mode 100644 lib/spack/docs/repositories.rst (limited to 'lib') diff --git a/lib/spack/docs/config_yaml.rst b/lib/spack/docs/config_yaml.rst index 7b1a89bc8a..56aa6ed0a1 100644 --- a/lib/spack/docs/config_yaml.rst +++ b/lib/spack/docs/config_yaml.rst @@ -1,7 +1,7 @@ .. _config-yaml: ==================================== -Basics settings +Basic settings in ``config.yaml`` ==================================== Spack's basic configuration options are set in ``config.yaml``. You can diff --git a/lib/spack/docs/configuration.rst b/lib/spack/docs/configuration.rst index 48a4686b84..32e1a8c170 100644 --- a/lib/spack/docs/configuration.rst +++ b/lib/spack/docs/configuration.rst @@ -12,6 +12,7 @@ case you want to skip directly to specific docs: * :ref:`mirrors.yaml ` * :ref:`modules.yaml ` * :ref:`packages.yaml ` +* :ref:`repos.yaml ` ------------------------- YAML Format diff --git a/lib/spack/docs/index.rst b/lib/spack/docs/index.rst index ed2ff96ed0..4dffe6f091 100644 --- a/lib/spack/docs/index.rst +++ b/lib/spack/docs/index.rst @@ -46,35 +46,30 @@ or refer to the full manual below. getting_started basic_usage workflows + tutorial_sc16 .. toctree:: :maxdepth: 2 - :caption: Reference Manual + :caption: Reference configuration config_yaml build_settings mirrors module_file_support - package_list + repositories command_index + package_list .. toctree:: :maxdepth: 2 - :caption: Tutorial - - tutorial_sc16 - -.. toctree:: - :maxdepth: 2 - :caption: Contributing to Spack + :caption: Contributing contribution_guide packaging_guide developer_guide API Docs - ================== Indices and tables ================== diff --git a/lib/spack/docs/packaging_guide.rst b/lib/spack/docs/packaging_guide.rst index aa9a3874bb..bf3b6af3f4 100644 --- a/lib/spack/docs/packaging_guide.rst +++ b/lib/spack/docs/packaging_guide.rst @@ -93,8 +93,10 @@ always choose to download just one tarball initially, and run $ spack create --name cmake http://www.cmake.org/files/v2.8/cmake-2.8.12.1.tar.gz If it fails entirely, you can get minimal boilerplate by using - :ref:`spack edit --force `, or you can manually create a directory and - ``package.py`` file for the package in ``var/spack/repos/builtin/packages``. + :ref:`spack edit --force `, or you can manually create a + directory and ``package.py`` file for the package in + ``var/spack/repos/builtin/packages``, or within your own :ref:`package + repository `. .. note:: diff --git a/lib/spack/docs/repositories.rst b/lib/spack/docs/repositories.rst new file mode 100644 index 0000000000..f4eb60fac1 --- /dev/null +++ b/lib/spack/docs/repositories.rst @@ -0,0 +1,456 @@ +.. _repositories: + +============================= +Package Repositories +============================= + +Spack comes with over 1,000 built-in package recipes in +``var/spack/repos/builtin/``. This is a **package repository** -- a +directory that Spack searches when it needs to find a package by name. +You may need to maintain packages for restricted, proprietary or +experimental software separately from the built-in repository. Spack +allows you to configure local repositories using either the +``repos.yaml`` or the ``spack repo`` command. + +A package repository a directory structured like this:: + + repo/ + repo.yaml + packages/ + hdf5/ + package.py + mpich/ + package.py + mpich-1.9-bugfix.patch + trilinos/ + package.py + ... + +The top-level ``repo.yaml`` file contains configuration metadata for the +repository, and the ``packages`` directory contains subdirectories for +each package in the repository. Each package directory contains a +``package.py`` file and any patches or other files needed to build the +package. + +Package repositories allow you to: + +1. Maintain your own packages separately from Spack; + +2. Share your packages (e.g. by hosting them in a shared file system), + without committing them to the built-in Spack package repository; and + +3. Override built-in Spack packages with your own implementation. + +Packages in a separate repository can also *depend on* built-in Spack +packages. So, you can leverage existing recipes without re-implementing +them in your own repository. + +--------------------- +``repos.yaml`` +--------------------- + +Spack uses the ``repos.yaml`` file in ``~/.spack`` (and :ref:`elsewhere +`) to find repositories. Note that the ``repos.yaml`` +configuration file is distinct from the ``repo.yaml`` file in each +repository. For more on the YAML format, and on how configuration file +precedence works in Spack, see :ref:`configuration `. + +The default ``etc/spack/defaults/repos.yaml`` file looks like this: + +.. code-block:: yaml + + repos: + - $spack/var/spack/repos/builtin + +The file starts with ``repos:`` and contains a single ordered list of +paths to repositories. Each path is on a separate line starting with +``-``. You can add a repository by inserting another path into the list: + +.. code-block:: yaml + + repos: + - /opt/local-repo + - $spack/var/spack/repos/builtin + +When Spack interprets a spec, e.g. ``mpich`` in ``spack install mpich``, +it searches these repositories in order (first to last) to resolve each +package name. In this example, Spack will look for the following +packages and use the first valid file: + +1. ``/opt/local-repo/packages/mpich/package.py`` +2. ``$spack/var/spack/repos/builtin/packages/mpich/package.py`` + +.. note:: + + Currently, Spack can only use repositories in the file system. We plan + to eventually support URLs in ``repos.yaml``, so that you can easily + point to remote package repositories, but that is not yet implemented. + +--------------------- +Namespaces +--------------------- + +Every repository in Spack has an associated **namespace** defined in its +top-level ``repo.yaml`` file. If you look at +``var/spack/repos/builtin/repo.yaml`` in the built-in repository, you'll +see that its namespace is ``builtin``: + +.. code-block:: console + + $ cat var/spack/repos/builtin/repo.yaml + repo: + namespace: builtin + +Spack records the repository namespace of each installed package. For +example, if you install the ``mpich`` package from the ``builtin`` repo, +Spack records its fully qualified name as ``builtin.mpich``. This +accomplishes two things: + +1. You can have packages with the same name from different namespaces + installed at once. + +1. You can easily determine which repository a package came from after it + is installed (more :ref:`below `). + +.. note:: + + It may seem redundant for a repository to have both a namespace and a + path, but repository *paths* may change over time, or, as mentioned + above, a locally hosted repository path may eventually be hosted at + some remote URL. + + Namespaces are designed to allow *package authors* to associate a + unique identifier with their packages, so that the package can be + identified even if the repository moves. This is why the namespace is + determined by the ``repo.yaml`` file in the repository rather than the + local ``repos.yaml`` configuration: the *repository maintainer* sets + the name. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Uniqueness +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You should choose a namespace that uniquely identifies your package +repository. For example, if you make a repository for packages written +by your organization, you could use your organization's name. You can +also nest namespaces using periods, so you could identify a repository by +a sub-organization. For example, LLNL might use a namespace for its +internal repositories like ``llnl``. Packages from the Physical & Life +Sciences directorate (PLS) might use the ``llnl.pls`` namespace, and +packages created by the Computation directorate might use ``llnl.comp``. + +Spack cannot ensure that every repository is named uniquely, but it will +prevent you from registering two repositories with the same namespace at +the same time. If you try to add a repository that has the same name as +an existing one, e.g. ``builtin``, Spack will print a warning message. + +.. _namespace-example: + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Namespace example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Suppose that LLNL maintains its own version of ``mpich``, separate from +Spack's built-in ``mpich`` package, and suppose you've installed both +LLNL's and Spack's ``mpich`` packages. If you just use ``spack find``, +you won't see a difference between these two packages: + +.. code-block:: console + + $ spack find + ==> 2 installed packages. + -- linux-rhel6-x86_64 / gcc@4.4.7 ------------- + mpich@3.2 mpich@3.2 + +However, if you use ``spack find -N``, Spack will display the packages +with their namespaces: + +.. code-block:: console + + $ spack find -N + ==> 2 installed packages. + -- linux-rhel6-x86_64 / gcc@4.4.7 ------------- + builtin.mpich@3.2 llnl.comp.mpich@3.2 + +Now you know which one is LLNL's special version, and which one is the +built-in Spack package. As you might guess, packages that are identical +except for their namespace will still have different hashes: + +.. code-block:: console + + $ spack find -lN + ==> 2 installed packages. + -- linux-rhel6-x86_64 / gcc@4.4.7 ------------- + c35p3gc builtin.mpich@3.2 itoqmox llnl.comp.mpich@3.2 + +All Spack commands that take a package :ref:`spec ` can also +accept a fully qualified spec with a namespace. This means you can use +the namespace to be more specific when designating, e.g., which package +you want to uninstall: + +.. code-block:: console + + spack uninstall llnl.comp.mpich + +---------------------------- +Overriding built-in packages +---------------------------- + +Spack's search semantics mean that you can make your own implementation +of a built-in Spack package (like ``mpich``), put it in a repository, and +use it to override the built-in package. As long as the repository +containing your ``mpich`` is earlier any other in ``repos.yaml``, any +built-in package that depends on ``mpich`` will be use the one in your +repository. + +Suppose you have three repositories: the builtin Spack repo +(``builtin``), a shared repo for your institution (e.g., ``llnl``), and a +repo containing your own prototype packages (``proto``). Suppose they +contain packages as follows: + + +--------------+------------------------------------+-----------------------------+ + | Namespace | Path to repo | Packages | + +==============+====================================+=============================+ + | ``proto`` | ``~/proto`` | ``mpich`` | + +--------------+------------------------------------+-----------------------------+ + | ``llnl`` | ``/usr/local/llnl`` | ``hdf5`` | + +--------------+------------------------------------+-----------------------------+ + | ``builtin`` | ``$spack/var/spack/repos/builtin`` | ``mpich``, ``hdf5``, others | + +--------------+------------------------------------+-----------------------------+ + +Suppose that ``hdf5`` depends on ``mpich``. You can override the +built-in ``hdf5`` by adding the ``llnl`` repo to ``repos.yaml``: + +.. code-block:: yaml + + repos: + - /usr/local/llnl + - $spack/var/spack/repos/builtin + +``spack install hdf5`` will install ``llnl.hdf5 ^builtin.mpich``. + +If, instead, ``repos.yaml`` looks like this: + +.. code-block:: yaml + + repos: + - ~/proto + - /usr/local/llnl + - $spack/var/spack/repos/builtin + +``spack install hdf5`` will install ``llnl.hdf5 ^proto.mpich``. + +Any unqualified package name will be resolved by searching ``repos.yaml`` +from the first entry to the last. You can force a particular +repository's package by using a fully qualified name. For example, if +your ``repos.yaml`` is as above, and you want ``builtin.mpich`` instead +of ``proto.mpich``, you can write:: + + spack install hdf5 ^builtin.mpich + +which will install ``llnl.hdf5 ^builtin.mpich``. + +Similarly, you can force the ``builtin.hdf5`` like this:: + + spack install builtin.hdf5 ^builtin.mpich + +This will not search ``repos.yaml`` at all, as the ``builtin`` repo is +specified in both cases. It will install ``builtin.hdf5 +^builtin.mpich``. + +If you want to see which repositories will be used in a build *before* +you install it, you can use ``spack spec -N``: + +.. code-block:: console + + $ spack spec -N hdf5 + Input spec + -------------------------------- + hdf5 + + Normalized + -------------------------------- + hdf5 + ^zlib@1.1.2: + + Concretized + -------------------------------- + builtin.hdf5@1.10.0-patch1%clang@7.0.2-apple+cxx~debug+fortran+mpi+shared~szip~threadsafe arch=darwin-elcapitan-x86_64 + ^builtin.openmpi@2.0.1%clang@7.0.2-apple~mxm~pmi~psm~psm2~slurm~sqlite3~thread_multiple~tm~verbs+vt arch=darwin-elcapitan-x86_64 + ^builtin.hwloc@1.11.4%clang@7.0.2-apple arch=darwin-elcapitan-x86_64 + ^builtin.libpciaccess@0.13.4%clang@7.0.2-apple arch=darwin-elcapitan-x86_64 + ^builtin.libtool@2.4.6%clang@7.0.2-apple arch=darwin-elcapitan-x86_64 + ^builtin.m4@1.4.17%clang@7.0.2-apple+sigsegv arch=darwin-elcapitan-x86_64 + ^builtin.libsigsegv@2.10%clang@7.0.2-apple arch=darwin-elcapitan-x86_64 + ^builtin.pkg-config@0.29.1%clang@7.0.2-apple+internal_glib arch=darwin-elcapitan-x86_64 + ^builtin.util-macros@1.19.0%clang@7.0.2-apple arch=darwin-elcapitan-x86_64 + ^builtin.zlib@1.2.8%clang@7.0.2-apple+pic arch=darwin-elcapitan-x86_64 + +.. warning:: + + You *can* use a fully qualified package name in a ``depends_on`` + directive in a ``package.py`` file, like so:: + + depends_on('proto.hdf5') + + This is *not* recommended, as it makes it very difficult for + multiple repos to be composed and shared. A ``package.py`` like this + will fail if the ``proto`` repository is not registered in + ``repos.yaml``. + +.. _cmd-spack-repo: + +-------------------------- +``spack repo`` +-------------------------- + +Spack's :ref:`configuration system ` allows repository +settings to come from ``repos.yaml`` files in many locations. If you +want to see the repositories registered as a result of all configuration +files, use ``spack repo list``. + +^^^^^^^^^^^^^^^^^^^ +``spack repo list`` +^^^^^^^^^^^^^^^^^^^ + +.. code-block:: console + + $ spack repo list + ==> 2 package repositories. + myrepo /Users/gamblin2/myrepo + builtin /Users/gamblin2/src/spack/var/spack/repos/builtin + +Each repository is listed with its associated namespace. To get the raw, +merged YAML from all configuration files, use ``spack config get repos``: + +.. code-block:: console + + $ spack config get repos + repos:srepos: + - /Users/gamblin2/myrepo + - $spack/var/spack/repos/builtin + +mNote that, unlike ``spack repo list``, this does not include the +namespace, which is read from each repo's ``repo.yaml``. + +^^^^^^^^^^^^^^^^^^^^^ +``spack repo create`` +^^^^^^^^^^^^^^^^^^^^^ + +To make your own repository, you don't need to construct a directory +yourself; you can use the ``spack repo create`` command. + +.. code-block:: console + + $ spack repo create myrepo + ==> Created repo with namespace 'myrepo'. + ==> To register it with spack, run this command: + spack repo add /Users/gamblin2/myrepo + + $ ls myrepo + packages/ repo.yaml + + $ cat myrepo/repo.yaml + repo: + namespace: 'myrepo' + +By default, the namespace of a new repo matches its directory's name. +You can supply a custom namespace with a second argument, e.g.: + +.. code-block:: console + + $ spack repo create myrepo llnl.comp + ==> Created repo with namespace 'llnl.comp'. + ==> To register it with spack, run this command: + spack repo add /Users/gamblin2/myrepo + + $ cat myrepo/repo.yaml + repo: + namespace: 'llnl.comp' + +^^^^^^^^^^^^^^^^^^ +``spack repo add`` +^^^^^^^^^^^^^^^^^^ + +Once your repository is created, you can register it with Spack with +``spack repo add``: + +.. code-block:: console + + $ spack repo add ./myrepo + ==> Added repo with namespace 'llnl.comp'. + + $ spack repo list + ==> 2 package repositories. + llnl.comp /Users/gamblin2/myrepo + builtin /Users/gamblin2/src/spack/var/spack/repos/builtin + +This simply adds the repo to your ``repos.yaml`` file. + +Once a repository is registered like this, you should be able to see its +packages' names in the output of ``spack list``, and you should be able +to build them using ``spack install `` as you would with any +built-in package. + +^^^^^^^^^^^^^^^^^^^^^ +``spack repo remove`` +^^^^^^^^^^^^^^^^^^^^^ + +You can remove an already-registered repository with ``spack repo rm``. +This will work whether you pass the repository's namespace *or* its +path. + +By namespace: + +.. code-block:: console + + $ spack repo rm llnl.comp + ==> Removed repository /Users/gamblin2/myrepo with namespace 'llnl.comp'. + + $ spack repo list + ==> 1 package repository. + builtin /Users/gamblin2/src/spack/var/spack/repos/builtin + +By path: + +.. code-block:: console + + $ spack repo rm ~/myrepo + ==> Removed repository /Users/gamblin2/myrepo + + $ spack repo list + ==> 1 package repository. + builtin /Users/gamblin2/src/spack/var/spack/repos/builtin + +-------------------------------- +Repo namespaces and Python +-------------------------------- + +You may have noticed that namespace notation for repositories is similar +to the notation for namespaces in Python. As it turns out, you *can* +treat Spack repositories like Python packages; this is how they are +implemented. + +You could, for example, extend a ``builtin`` package in your own +repository: + +.. code-block:: python + + from spack.pkg.builtin.mpich import Mpich + + class MyPackage(Mpich): + ... + +Spack repo namespaces are actually Python namespaces tacked on under +``spack.pkg``. The search semantics of ``repos.yaml`` are actually +implemented using Python's built-in `sys.path +`_ search. The +:py:mod:`spack.repository` module implements a custom `Python importer +`_. + +.. warning:: + + The mechanism for extending packages is not yet extensively tested, + and extending packages across repositories imposes inter-repo + dependencies, which may be hard to manage. Use this feature at your + own risk, but let us know if you have a use case for it. diff --git a/lib/spack/docs/tutorial_sc16.rst b/lib/spack/docs/tutorial_sc16.rst index 6a3cebe20d..a95eee989c 100644 --- a/lib/spack/docs/tutorial_sc16.rst +++ b/lib/spack/docs/tutorial_sc16.rst @@ -1,7 +1,7 @@ .. _spack-101: ============================= -Spack 101 +Tutorial: Spack 101 ============================= This is a 3-hour introduction to Spack with lectures and live demos. It diff --git a/lib/spack/spack/cmd/repo.py b/lib/spack/spack/cmd/repo.py index 5ab2ac0833..79df63ce8d 100644 --- a/lib/spack/spack/cmd/repo.py +++ b/lib/spack/spack/cmd/repo.py @@ -106,7 +106,7 @@ def repo_add(args): repos.insert(0, canon_path) spack.config.update_config('repos', repos, args.scope) - tty.msg("Created repo with namespace '%s'." % repo.namespace) + tty.msg("Added repo with namespace '%s'." % repo.namespace) def repo_remove(args): diff --git a/lib/spack/spack/cmd/spec.py b/lib/spack/spack/cmd/spec.py index 0a6fb330ac..4dd4474bd4 100644 --- a/lib/spack/spack/cmd/spec.py +++ b/lib/spack/spack/cmd/spec.py @@ -40,6 +40,9 @@ def setup_parser(subparser): '-c', '--cover', action='store', default='nodes', choices=['nodes', 'edges', 'paths'], help='How extensively to traverse the DAG. (default: nodes).') + subparser.add_argument( + '-N', '--namespaces', action='store_true', default=False, + help='Show fully qualified package names.') subparser.add_argument( '-I', '--install-status', action='store_true', default=False, help='Show install status of packages. Packages can be: ' @@ -50,11 +53,13 @@ def setup_parser(subparser): def spec(parser, args): + name_fmt = '$.' if args.namespaces else '$_' kwargs = {'color': True, 'cover': args.cover, - 'install_status': args.install_status, + 'format': name_fmt + '$@$%@+$+$=', 'hashes': args.long or args.very_long, - 'hashlen': None if args.very_long else 7} + 'hashlen': None if args.very_long else 7, + 'install_status': args.install_status} for spec in spack.cmd.parse_specs(args.specs): # With -y, just print YAML to output. -- cgit v1.2.3-60-g2f50