Allow choosing the name of the packages subdirectory in repositories (#36643)
Co-authored-by: becker33 <becker33@users.noreply.github.com>
This commit is contained in:
		| @@ -32,11 +32,16 @@ A package repository a directory structured like this:: | |||||||
|           ... |           ... | ||||||
|  |  | ||||||
| The top-level ``repo.yaml`` file contains configuration metadata for the | The top-level ``repo.yaml`` file contains configuration metadata for the | ||||||
| repository, and the ``packages`` directory contains subdirectories for | repository. The packages subdirectory, typically ``packages``, contains | ||||||
| each package in the repository.  Each package directory contains a | subdirectories for each package in the repository.  Each package directory | ||||||
| ``package.py`` file and any patches or other files needed to build the | contains a ``package.py`` file and any patches or other files needed to build the | ||||||
| package. | package. | ||||||
|  |  | ||||||
|  | The ``repo.yaml`` file may also contain a ``subdirectory`` key, | ||||||
|  | which can modify the name of the subdirectory used for packages. As seen above, | ||||||
|  | the default value is ``packages``. An empty string (``subdirectory: ''``) requires | ||||||
|  | a flattened repo structure in which the package names are top-level subdirectories. | ||||||
|  |  | ||||||
| Package repositories allow you to: | Package repositories allow you to: | ||||||
|  |  | ||||||
| 1. Maintain your own packages separately from Spack; | 1. Maintain your own packages separately from Spack; | ||||||
| @@ -373,6 +378,24 @@ You can supply a custom namespace with a second argument, e.g.: | |||||||
|   repo: |   repo: | ||||||
|     namespace: 'llnl.comp' |     namespace: 'llnl.comp' | ||||||
|  |  | ||||||
|  | You can also create repositories with custom structure with the ``-d/--subdirectory`` | ||||||
|  | argument, e.g.: | ||||||
|  |  | ||||||
|  | .. code-block:: console | ||||||
|  |  | ||||||
|  |   $ spack repo create -d applications myrepo apps | ||||||
|  |   ==> Created repo with namespace 'apps'. | ||||||
|  |   ==> To register it with Spack, run this command: | ||||||
|  |     spack repo add ~/myrepo | ||||||
|  |  | ||||||
|  |   $ ls myrepo | ||||||
|  |   applications/  repo.yaml | ||||||
|  |  | ||||||
|  |   $ cat myrepo/repo.yaml | ||||||
|  |   repo: | ||||||
|  |     namespace: apps | ||||||
|  |     subdirectory: applications | ||||||
|  |  | ||||||
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^ | ||||||
| ``spack repo add`` | ``spack repo add`` | ||||||
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^ | ||||||
|   | |||||||
| @@ -32,6 +32,17 @@ def setup_parser(subparser): | |||||||
|         help="namespace to identify packages in the repository. " "defaults to the directory name", |         help="namespace to identify packages in the repository. " "defaults to the directory name", | ||||||
|         nargs="?", |         nargs="?", | ||||||
|     ) |     ) | ||||||
|  |     create_parser.add_argument( | ||||||
|  |         "-d", | ||||||
|  |         "--subdirectory", | ||||||
|  |         action="store", | ||||||
|  |         dest="subdir", | ||||||
|  |         default=spack.repo.packages_dir_name, | ||||||
|  |         help=( | ||||||
|  |             "subdirectory to store packages in the repository." | ||||||
|  |             " Default 'packages'. Use an empty string for no subdirectory." | ||||||
|  |         ), | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|     # List |     # List | ||||||
|     list_parser = sp.add_parser("list", help=repo_list.__doc__) |     list_parser = sp.add_parser("list", help=repo_list.__doc__) | ||||||
| @@ -70,7 +81,7 @@ def setup_parser(subparser): | |||||||
| 
 | 
 | ||||||
| def repo_create(args): | def repo_create(args): | ||||||
|     """Create a new package repository.""" |     """Create a new package repository.""" | ||||||
|     full_path, namespace = spack.repo.create_repo(args.directory, args.namespace) |     full_path, namespace = spack.repo.create_repo(args.directory, args.namespace, args.subdir) | ||||||
|     tty.msg("Created repo with namespace '%s'." % namespace) |     tty.msg("Created repo with namespace '%s'." % namespace) | ||||||
|     tty.msg("To register it with spack, run this command:", "spack repo add %s" % full_path) |     tty.msg("To register it with spack, run this command:", "spack repo add %s" % full_path) | ||||||
| 
 | 
 | ||||||
|   | |||||||
| @@ -935,12 +935,6 @@ def check(condition, msg): | |||||||
|         self.config_file = os.path.join(self.root, repo_config_name) |         self.config_file = os.path.join(self.root, repo_config_name) | ||||||
|         check(os.path.isfile(self.config_file), "No %s found in '%s'" % (repo_config_name, root)) |         check(os.path.isfile(self.config_file), "No %s found in '%s'" % (repo_config_name, root)) | ||||||
| 
 | 
 | ||||||
|         self.packages_path = os.path.join(self.root, packages_dir_name) |  | ||||||
|         check( |  | ||||||
|             os.path.isdir(self.packages_path), |  | ||||||
|             "No directory '%s' found in '%s'" % (packages_dir_name, root), |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         # Read configuration and validate namespace |         # Read configuration and validate namespace | ||||||
|         config = self._read_config() |         config = self._read_config() | ||||||
|         check( |         check( | ||||||
| @@ -961,6 +955,13 @@ def check(condition, msg): | |||||||
|         # Keep name components around for checking prefixes. |         # Keep name components around for checking prefixes. | ||||||
|         self._names = self.full_namespace.split(".") |         self._names = self.full_namespace.split(".") | ||||||
| 
 | 
 | ||||||
|  |         packages_dir = config.get("subdirectory", packages_dir_name) | ||||||
|  |         self.packages_path = os.path.join(self.root, packages_dir) | ||||||
|  |         check( | ||||||
|  |             os.path.isdir(self.packages_path), | ||||||
|  |             "No directory '%s' found in '%s'" % (packages_dir, root), | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|         # These are internal cache variables. |         # These are internal cache variables. | ||||||
|         self._modules = {} |         self._modules = {} | ||||||
|         self._classes = {} |         self._classes = {} | ||||||
| @@ -1150,7 +1151,7 @@ def all_package_names(self, include_virtuals=False): | |||||||
| 
 | 
 | ||||||
|     def package_path(self, name): |     def package_path(self, name): | ||||||
|         """Get path to package.py file for this repo.""" |         """Get path to package.py file for this repo.""" | ||||||
|         return os.path.join(self.root, packages_dir_name, name, package_file_name) |         return os.path.join(self.packages_path, name, package_file_name) | ||||||
| 
 | 
 | ||||||
|     def all_package_paths(self): |     def all_package_paths(self): | ||||||
|         for name in self.all_package_names(): |         for name in self.all_package_names(): | ||||||
| @@ -1287,7 +1288,7 @@ def __contains__(self, pkg_name): | |||||||
| RepoType = Union[Repo, RepoPath] | RepoType = Union[Repo, RepoPath] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def create_repo(root, namespace=None): | def create_repo(root, namespace=None, subdir=packages_dir_name): | ||||||
|     """Create a new repository in root with the specified namespace. |     """Create a new repository in root with the specified namespace. | ||||||
| 
 | 
 | ||||||
|     If the namespace is not provided, use basename of root. |     If the namespace is not provided, use basename of root. | ||||||
| @@ -1318,12 +1319,14 @@ def create_repo(root, namespace=None): | |||||||
| 
 | 
 | ||||||
|     try: |     try: | ||||||
|         config_path = os.path.join(root, repo_config_name) |         config_path = os.path.join(root, repo_config_name) | ||||||
|         packages_path = os.path.join(root, packages_dir_name) |         packages_path = os.path.join(root, subdir) | ||||||
| 
 | 
 | ||||||
|         fs.mkdirp(packages_path) |         fs.mkdirp(packages_path) | ||||||
|         with open(config_path, "w") as config: |         with open(config_path, "w") as config: | ||||||
|             config.write("repo:\n") |             config.write("repo:\n") | ||||||
|             config.write("  namespace: '%s'\n" % namespace) |             config.write(f"  namespace: '{namespace}'\n") | ||||||
|  |             if subdir != packages_dir_name: | ||||||
|  |                 config.write(f"  subdirectory: '{subdir}'\n") | ||||||
| 
 | 
 | ||||||
|     except (IOError, OSError) as e: |     except (IOError, OSError) as e: | ||||||
|         # try to clean up. |         # try to clean up. | ||||||
|   | |||||||
| @@ -11,11 +11,11 @@ | |||||||
| import spack.repo | import spack.repo | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @pytest.fixture() | @pytest.fixture(params=["packages", "", "foo"]) | ||||||
| def extra_repo(tmpdir_factory): | def extra_repo(tmpdir_factory, request): | ||||||
|     repo_namespace = "extra_test_repo" |     repo_namespace = "extra_test_repo" | ||||||
|     repo_dir = tmpdir_factory.mktemp(repo_namespace) |     repo_dir = tmpdir_factory.mktemp(repo_namespace) | ||||||
|     repo_dir.ensure("packages", dir=True) |     repo_dir.ensure(request.param, dir=True) | ||||||
| 
 | 
 | ||||||
|     with open(str(repo_dir.join("repo.yaml")), "w") as f: |     with open(str(repo_dir.join("repo.yaml")), "w") as f: | ||||||
|         f.write( |         f.write( | ||||||
| @@ -24,7 +24,9 @@ def extra_repo(tmpdir_factory): | |||||||
|   namespace: extra_test_repo |   namespace: extra_test_repo | ||||||
| """ | """ | ||||||
|         ) |         ) | ||||||
|     return spack.repo.Repo(str(repo_dir)) |         if request.param != "packages": | ||||||
|  |             f.write(f"  subdirectory: '{request.param}'") | ||||||
|  |     return (spack.repo.Repo(str(repo_dir)), request.param) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_repo_getpkg(mutable_mock_repo): | def test_repo_getpkg(mutable_mock_repo): | ||||||
| @@ -33,13 +35,13 @@ def test_repo_getpkg(mutable_mock_repo): | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_repo_multi_getpkg(mutable_mock_repo, extra_repo): | def test_repo_multi_getpkg(mutable_mock_repo, extra_repo): | ||||||
|     mutable_mock_repo.put_first(extra_repo) |     mutable_mock_repo.put_first(extra_repo[0]) | ||||||
|     mutable_mock_repo.get_pkg_class("a") |     mutable_mock_repo.get_pkg_class("a") | ||||||
|     mutable_mock_repo.get_pkg_class("builtin.mock.a") |     mutable_mock_repo.get_pkg_class("builtin.mock.a") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_repo_multi_getpkgclass(mutable_mock_repo, extra_repo): | def test_repo_multi_getpkgclass(mutable_mock_repo, extra_repo): | ||||||
|     mutable_mock_repo.put_first(extra_repo) |     mutable_mock_repo.put_first(extra_repo[0]) | ||||||
|     mutable_mock_repo.get_pkg_class("a") |     mutable_mock_repo.get_pkg_class("a") | ||||||
|     mutable_mock_repo.get_pkg_class("builtin.mock.a") |     mutable_mock_repo.get_pkg_class("builtin.mock.a") | ||||||
| 
 | 
 | ||||||
| @@ -63,9 +65,9 @@ def test_repo_last_mtime(): | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_repo_invisibles(mutable_mock_repo, extra_repo): | def test_repo_invisibles(mutable_mock_repo, extra_repo): | ||||||
|     with open(os.path.join(extra_repo.root, "packages", ".invisible"), "w"): |     with open(os.path.join(extra_repo[0].root, extra_repo[1], ".invisible"), "w"): | ||||||
|         pass |         pass | ||||||
|     extra_repo.all_package_names() |     extra_repo[0].all_package_names() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @pytest.mark.parametrize("attr_name,exists", [("cmake", True), ("__sphinx_mock__", False)]) | @pytest.mark.parametrize("attr_name,exists", [("cmake", True), ("__sphinx_mock__", False)]) | ||||||
|   | |||||||
| @@ -1606,7 +1606,7 @@ _spack_repo() { | |||||||
| _spack_repo_create() { | _spack_repo_create() { | ||||||
|     if $list_options |     if $list_options | ||||||
|     then |     then | ||||||
|         SPACK_COMPREPLY="-h --help" |         SPACK_COMPREPLY="-h --help -d --subdirectory" | ||||||
|     else |     else | ||||||
|         _repos |         _repos | ||||||
|     fi |     fi | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Greg Becker
					Greg Becker