Repository: snek5000/snek5000 (language: python)

Issue:
Nek5000 libraries rebuild in case of compiler change is not triggered
Sometimes a manual edit of the nek5000_make_config.yml is required to rebuild Nek5000
```py
~/Sources/snek5000/snek5000/lib/Nek5000 @140c4fd9* 10s
snek ❯ snakemake --configfile nek5000_make_config.yml -j1 --snakefile ../../src/snek5000/resources/nek5000.smk  3rd_party/gslib/lib/libgs.a 3rd_party/blasLapack/libblasLapack.a
```



Discussion / hints:
We can either
- add a command to manually do this
- find a way to re-trigger this.
Just a question: do we really need that for 0.8.0 ? Could we postpone this issue to 0.9.0 ?
Okay it is not that crucial anyway
Just need to improve https://snek5000.readthedocs.io/en/latest/how-to/rebuild-nek.html

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index af1f9d47..77f53fdb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,24 @@ Security in case of vulnerabilities.
 
 ## [Unreleased]
 
+## [0.9.1] - 2023-05-01
+
+## Added
+
+- New command `snek-make-nek` to clean and rebuild Nek5000 tools
+- JOSS paper draft
+
+Documentation
+
+- How to use adaptive time step
+- How to configure C and Fortran compilers
+- Document the new copier template
+
+## Changed
+
+- Use `remaining_clock_time` by default
+- Replace isort and flake8 with ruff, upgrade hooks
+
 ## [0.9.0] - 2023-01-11
 
 ### Added
@@ -378,5 +396,6 @@ lists are a very incomplete lists of the additions, changes and deprecations.
 [0.7.0b0]: https://github.com/snek5000/snek5000/compare/0.6.1b0...0.7.0b0
 [0.8.0]: https://github.com/snek5000/snek5000/compare/0.7.0b0...0.8.0
 [0.9.0]: https://github.com/snek5000/snek5000/compare/0.8.0...0.9.0
+[0.9.1]: https://github.com/snek5000/snek5000/compare/0.9.0...0.9.1
 [@paugier]: https://github.com/paugier
-[unreleased]: https://github.com/snek5000/snek5000/compare/0.9.0...HEAD
+[unreleased]: https://github.com/snek5000/snek5000/compare/0.9.1...HEAD
diff --git a/setup.cfg b/setup.cfg
index 2d8e5ce9..14a1739e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -49,6 +49,7 @@ console_scripts =
   snek-ipy-load = snek5000.util.console:start_ipython_load_sim
   snek-make = snek5000.make:snek_make
   snek-restart = snek5000.util.restart:main
+  snek-make-nek = snek5000.make:snek_make_nek
 
 [options.extras_require]
 docs =
diff --git a/src/snek5000/make.py b/src/snek5000/make.py
index a8158aeb..15b28ab4 100644
--- a/src/snek5000/make.py
+++ b/src/snek5000/make.py
@@ -3,6 +3,7 @@
 
 """
 import argparse
+import subprocess
 import sys
 from pathlib import Path
 from typing import Iterable
@@ -29,16 +30,19 @@ class Make:
 
     """
 
-    def __init__(self, sim=None, path_run=None):
+    def __init__(self, sim=None, path_run=None, snakefile=None):
         if (sim is None and path_run is None) or (
             sim is not None and path_run is not None
         ):
             raise ValueError("Either sim of path_run has to be given.")
 
+        if snakefile:
+            snakefile = Path(snakefile)
+
         if path_run is None:
             self.path_run = sim.output.path_run
             try:
-                self.file = next(
+                self.file = snakefile or next(
                     f for f in sim.output.get_paths() if f.name == "Snakefile"
                 )
             except AttributeError:
@@ -49,13 +53,13 @@ def __init__(self, sim=None, path_run=None):
             if not path_run.exists():
                 raise FileNotFoundError(f"{path_run} does not exist.")
             self.path_run = Path(path_run)
-            self.file = self.path_run / "Snakefile"
+            self.file = snakefile or self.path_run / "Snakefile"
             if not self.file.exists():
                 raise FileNotFoundError(f"No Snakefile in {path_run}")
 
         self.log_handler = []
 
-    def list(self):
+    def list(self, **kwargs):
         """List rules.
 
         Equivalent to::
@@ -64,7 +68,9 @@ def list(self):
 
         """
         with change_dir(self.path_run):
-            return snakemake(self.file, listrules=True, log_handler=self.log_handler)
+            return snakemake(
+                self.file, listrules=True, log_handler=self.log_handler, **kwargs
+            )
 
     def exec(
         self,
@@ -332,3 +338,57 @@ def snek_make():
         keep_incomplete=not args.clean_after_fail,
         nproc=args.nproc,
     )
+
+
+def snek_make_nek():
+    """Used for the command snek-make-nek"""
+    parser = argparse.ArgumentParser(
+        prog="snek-make-nek",
+        description="Execute Snakemake rules to build Nek5000 tools and libraries",
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+    )
+
+    parser.add_argument(
+        "rule",
+        nargs="?",
+        default=None,
+        help="Snakemake rule to be executed.",
+    )
+    parser.add_argument(
+        "-l",
+        "--list-rules",
+        action="store_true",
+        help="List rules and exit.",
+    )
+    parser.add_argument(
+        "-c",
+        "--clean-git",
+        action="store_true",
+        help="Apply git-clean on Nek5000 repository before building.",
+    )
+
+    args = parser.parse_args()
+
+    # make = Make(path_run=Path(snek5000.get_nek_source_root()), snakefile=snek5000.get_snek_resource("nek5000.smk"))
+    #
+    make = _Nek5000Make()
+
+    from snek5000.output.base import Output
+
+    with Output.find_configfile().open() as fp:
+        config = yaml.safe_load(fp)
+
+    Output.update_snakemake_config(config, "snek-make-nek", env_sensitive=True)
+
+    if args.list_rules:
+        make.list(config=config)
+        sys.exit(0)
+
+    if args.clean_git:
+        with change_dir(snek5000.get_nek_source_root()):
+            subprocess.run(["git", "clean", "-xdf"])
+
+    if args.rule is None:
+        make.build(config)
+    else:
+        make.exec(args.rule, nproc=4, config=config)


Test patch:
diff --git a/tests/test_make.py b/tests/test_make.py
index c40a003a..35769191 100644
--- a/tests/test_make.py
+++ b/tests/test_make.py
@@ -6,7 +6,7 @@
 
 import pytest
 
-from snek5000.make import _Nek5000Make, snek_make
+from snek5000.make import _Nek5000Make, snek_make, snek_make_nek
 
 
 def nek5000_build(config):
@@ -51,3 +51,24 @@ def test_snek_make_clean(sim_data, monkeypatch):
     monkeypatch.chdir(sim_data)
     with patch.object(sys, "argv", ["snek-make", "clean"]):
         snek_make()
+
+
+def test_snek_make_nek_list():
+    with patch.object(sys, "argv", ["snek-make-nek", "--list"]):
+        with pytest.raises(SystemExit):
+            snek_make_nek()
+
+
+def test_snek_make_nek_default():
+    with patch.object(sys, "argv", ["snek-make-nek"]):
+        snek_make_nek()
+
+
+def test_snek_make_nek_clean_nek():
+    with patch.object(sys, "argv", ["snek-make-nek", "--clean-git"]):
+        snek_make_nek()
+
+
+def test_snek_make_nek_genmap():
+    with patch.object(sys, "argv", ["snek-make-nek", "bin/genmap"]):
+        snek_make_nek()