From ef757995a5e8406b886c80717a24d8147b074846 Mon Sep 17 00:00:00 2001 From: Sean Bryan Date: Tue, 19 Sep 2023 10:45:48 +1000 Subject: [PATCH] Add requested changes from code review Split up the default build function into smaller `pre_build`, `run_build` and `post_build` functions to improve code readability. Simplify unit tests for `*_build` functions. Add `benchcab/utils/fs.py` for defining utility functions for interacting with the file system. --- benchcab/benchcab.py | 19 +++- benchcab/fluxsite.py | 2 +- benchcab/repository.py | 174 +++++++++++++---------------- benchcab/utils/fs.py | 31 ++++++ benchcab/utils/os.py | 16 --- tests/common.py | 2 - tests/conftest.py | 5 + tests/test_repository.py | 230 +++++++++++++++++++++++---------------- 8 files changed, 270 insertions(+), 209 deletions(-) create mode 100644 benchcab/utils/fs.py delete mode 100644 benchcab/utils/os.py diff --git a/benchcab/benchcab.py b/benchcab/benchcab.py index accbfd49..e27f3af2 100644 --- a/benchcab/benchcab.py +++ b/benchcab/benchcab.py @@ -192,7 +192,24 @@ def checkout(self): def build(self): """Endpoint for `benchcab build`.""" for repo in self.repos: - repo.build(modules=self.config["modules"], verbose=self.args.verbose) + if repo.build_script: + print( + "Compiling CABLE using custom build script for " + f"realisation {repo.name}..." + ) + repo.custom_build( + modules=self.config["modules"], verbose=self.args.verbose + ) + else: + print( + f"Compiling CABLE {'with MPI' if internal.MPI else 'serially'} for " + f"realisation {repo.name}..." + ) + repo.pre_build(verbose=self.args.verbose) + repo.run_build( + modules=self.config["modules"], verbose=self.args.verbose + ) + repo.post_build(verbose=self.args.verbose) print(f"Successfully compiled CABLE for realisation {repo.name}") print("") diff --git a/benchcab/fluxsite.py b/benchcab/fluxsite.py index 06a72e1e..921a76a1 100644 --- a/benchcab/fluxsite.py +++ b/benchcab/fluxsite.py @@ -16,7 +16,7 @@ from benchcab.repository import CableRepository from benchcab.comparison import ComparisonTask from benchcab.utils.subprocess import SubprocessWrapperInterface, SubprocessWrapper -from benchcab.utils.os import chdir +from benchcab.utils.fs import chdir # fmt: off diff --git a/benchcab/repository.py b/benchcab/repository.py index a217484c..61e62a5e 100644 --- a/benchcab/repository.py +++ b/benchcab/repository.py @@ -10,7 +10,7 @@ from benchcab import internal from benchcab.environment_modules import EnvironmentModulesInterface, EnvironmentModules from benchcab.utils.subprocess import SubprocessWrapperInterface, SubprocessWrapper -from benchcab.utils.os import chdir +from benchcab.utils.fs import chdir, copy2, rename class CableRepository: @@ -75,101 +75,8 @@ def svn_info_show_item(self, item: str) -> str: ) return proc.stdout.strip() - def build( - self, - modules: list[str], - offline_source_files: Optional[list[str]] = None, - verbose=False, - ) -> None: - """Build CABLE using the default build script or a custom build script.""" - - if self.build_script: - self._custom_build(modules=modules, verbose=verbose) - return - - mpi = internal.MPI - print( - f"Compiling CABLE {'with MPI' if mpi else 'serially'} for " - f"realisation {self.name}..." - ) - - path_to_repo = self.root_dir / internal.SRC_DIR / self.name - tmp_dir = path_to_repo / "offline" / ".tmp" - if not tmp_dir.exists(): - if verbose: - print(f"mkdir {tmp_dir.relative_to(self.root_dir)}") - tmp_dir.mkdir() - - for pattern in ( - offline_source_files - if offline_source_files - else internal.OFFLINE_SOURCE_FILES - ): - for path in path_to_repo.glob(pattern): - if not path.is_file(): - continue - if verbose: - print( - f"cp -p {path.relative_to(self.root_dir)} " - f"{tmp_dir.relative_to(self.root_dir)}" - ) - shutil.copy2(path, tmp_dir) - - src, dest = path_to_repo / "offline" / "Makefile", tmp_dir - if verbose: - print( - f"cp -p {src.relative_to(self.root_dir)} {dest.relative_to(self.root_dir)}" - ) - shutil.copy2(src, dest) - - src, dest = path_to_repo / "offline" / "parallel_cable", tmp_dir - if verbose: - print( - f"cp -p {src.relative_to(self.root_dir)} {dest.relative_to(self.root_dir)}" - ) - shutil.copy2(src, dest) - - src, dest = path_to_repo / "offline" / "serial_cable", tmp_dir - if verbose: - print( - f"cp -p {src.relative_to(self.root_dir)} {dest.relative_to(self.root_dir)}" - ) - shutil.copy2(src, dest) - - with chdir(tmp_dir), self.modules_handler.load(modules, verbose=verbose): - env = os.environ.copy() - env["NCDIR"] = f"{env['NETCDF_ROOT']}/lib/Intel" - env["NCMOD"] = f"{env['NETCDF_ROOT']}/include/Intel" - env["CFLAGS"] = "-O2 -fp-model precise" - env["LDFLAGS"] = f"-L{env['NETCDF_ROOT']}/lib/Intel -O0" - env["LD"] = "-lnetcdf -lnetcdff" - env["FC"] = "mpif90" if mpi else "ifort" - - self.subprocess_handler.run_cmd( - "make -f Makefile", env=env, verbose=verbose - ) - self.subprocess_handler.run_cmd( - f"./{'parallel_cable' if mpi else 'serial_cable'} \"{env['FC']}\" " - f"\"{env['CFLAGS']}\" \"{env['LDFLAGS']}\" \"{env['LD']}\" \"{env['NCMOD']}\"", - env=env, - verbose=verbose, - ) - - src, dest = ( - tmp_dir / internal.CABLE_EXE, - path_to_repo / "offline" / internal.CABLE_EXE, - ) - if verbose: - print( - f"mv {src.relative_to(self.root_dir)} {dest.relative_to(self.root_dir)}" - ) - src.rename(dest) - - def _custom_build(self, modules: list[str], verbose=False): - print( - "Compiling CABLE using custom build script for " - f"realisation {self.name}..." - ) + def custom_build(self, modules: list[str], verbose=False): + """Build CABLE using a custom build script.""" build_script_path = ( self.root_dir / internal.SRC_DIR / self.name / self.build_script @@ -207,6 +114,81 @@ def _custom_build(self, modules: list[str], verbose=False): verbose=verbose, ) + def pre_build(self, verbose=False): + """Runs CABLE pre-build steps.""" + + path_to_repo = self.root_dir / internal.SRC_DIR / self.name + tmp_dir = path_to_repo / "offline" / ".tmp" + if not tmp_dir.exists(): + if verbose: + print(f"mkdir {tmp_dir.relative_to(self.root_dir)}") + tmp_dir.mkdir() + + for pattern in internal.OFFLINE_SOURCE_FILES: + for path in path_to_repo.glob(pattern): + if not path.is_file(): + continue + copy2( + path.relative_to(self.root_dir), + tmp_dir.relative_to(self.root_dir), + verbose=verbose, + ) + + copy2( + (path_to_repo / "offline" / "Makefile").relative_to(self.root_dir), + tmp_dir.relative_to(self.root_dir), + verbose=verbose, + ) + + copy2( + (path_to_repo / "offline" / "parallel_cable").relative_to(self.root_dir), + tmp_dir.relative_to(self.root_dir), + verbose=verbose, + ) + + copy2( + (path_to_repo / "offline" / "serial_cable").relative_to(self.root_dir), + tmp_dir.relative_to(self.root_dir), + verbose=verbose, + ) + + def run_build(self, modules: list[str], verbose=False): + """Runs CABLE build scripts.""" + + path_to_repo = self.root_dir / internal.SRC_DIR / self.name + tmp_dir = path_to_repo / "offline" / ".tmp" + + with chdir(tmp_dir), self.modules_handler.load(modules, verbose=verbose): + env = os.environ.copy() + env["NCDIR"] = f"{env['NETCDF_ROOT']}/lib/Intel" + env["NCMOD"] = f"{env['NETCDF_ROOT']}/include/Intel" + env["CFLAGS"] = "-O2 -fp-model precise" + env["LDFLAGS"] = f"-L{env['NETCDF_ROOT']}/lib/Intel -O0" + env["LD"] = "-lnetcdf -lnetcdff" + env["FC"] = "mpif90" if internal.MPI else "ifort" + + self.subprocess_handler.run_cmd( + "make -f Makefile", env=env, verbose=verbose + ) + self.subprocess_handler.run_cmd( + f"./{'parallel_cable' if internal.MPI else 'serial_cable'} \"{env['FC']}\" " + f"\"{env['CFLAGS']}\" \"{env['LDFLAGS']}\" \"{env['LD']}\" \"{env['NCMOD']}\"", + env=env, + verbose=verbose, + ) + + def post_build(self, verbose=False): + """Runs CABLE post-build steps.""" + + path_to_repo = self.root_dir / internal.SRC_DIR / self.name + tmp_dir = path_to_repo / "offline" / ".tmp" + + rename( + (tmp_dir / internal.CABLE_EXE).relative_to(self.root_dir), + (path_to_repo / "offline" / internal.CABLE_EXE).relative_to(self.root_dir), + verbose=verbose, + ) + def remove_module_lines(file_path: Path) -> None: """Remove lines from `file_path` that call the environment modules package.""" diff --git a/benchcab/utils/fs.py b/benchcab/utils/fs.py new file mode 100644 index 00000000..36027efe --- /dev/null +++ b/benchcab/utils/fs.py @@ -0,0 +1,31 @@ +"""Contains utility functions for interacting with the file system.""" + +import os +import shutil +import contextlib +from pathlib import Path + + +@contextlib.contextmanager +def chdir(newdir: Path): + """Context manager `cd`.""" + prevdir = Path.cwd() + os.chdir(newdir.expanduser()) + try: + yield + finally: + os.chdir(prevdir) + + +def rename(src: Path, dest: Path, verbose=False): + """A wrapper around `pathlib.Path.rename` with optional loggging.""" + if verbose: + print(f"mv {src} {dest}") + src.rename(dest) + + +def copy2(src: Path, dest: Path, verbose=False): + """A wrapper around `shutil.copy2` with optional logging.""" + if verbose: + print(f"cp -p {src} {dest}") + shutil.copy2(src, dest) diff --git a/benchcab/utils/os.py b/benchcab/utils/os.py deleted file mode 100644 index 3ef7947e..00000000 --- a/benchcab/utils/os.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Utility functions which wrap around the os module.""" - -import os -import contextlib -from pathlib import Path - - -@contextlib.contextmanager -def chdir(newdir: Path): - """Context manager `cd`.""" - prevdir = Path.cwd() - os.chdir(newdir.expanduser()) - try: - yield - finally: - os.chdir(prevdir) diff --git a/tests/common.py b/tests/common.py index ac127462..335315f2 100644 --- a/tests/common.py +++ b/tests/common.py @@ -78,7 +78,6 @@ def __init__(self) -> None: self.stdout = "mock standard output" self.error_on_call = False self.env = {} - self.side_effect = lambda: None def run_cmd( self, @@ -89,7 +88,6 @@ def run_cmd( env: Optional[dict] = None, ) -> CompletedProcess: self.commands.append(cmd) - self.side_effect() if self.error_on_call: raise CalledProcessError(returncode=1, cmd=cmd, output=self.stdout) if output_file: diff --git a/tests/conftest.py b/tests/conftest.py index 8b764969..a2f23ea1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,8 @@ """Contains pytest fixtures accessible to all tests in the same directory.""" +import os import shutil +from pathlib import Path import pytest from .common import MOCK_CWD @@ -11,12 +13,15 @@ def run_around_tests(): """`pytest` autouse fixture that runs around each test.""" # Setup: + prevdir = Path.cwd() if MOCK_CWD.exists(): shutil.rmtree(MOCK_CWD) MOCK_CWD.mkdir() + os.chdir(MOCK_CWD.expanduser()) # Run the test: yield # Teardown: + os.chdir(prevdir) shutil.rmtree(MOCK_CWD) diff --git a/tests/test_repository.py b/tests/test_repository.py index dc616712..8f9c734a 100644 --- a/tests/test_repository.py +++ b/tests/test_repository.py @@ -110,61 +110,158 @@ def test_svn_info_show_item(): ) -def test_build(): - """Tests for `CableRepository.build()`.""" +def test_pre_build(): + """Tests for `CableRepository.pre_build()`.""" + repo_dir = MOCK_CWD / internal.SRC_DIR / "trunk" offline_dir = repo_dir / "offline" - custom_build_script_path = repo_dir / "my-custom-build.sh" - mock_modules = ["foo", "bar"] + offline_dir.mkdir(parents=True) + (offline_dir / "Makefile").touch() + (offline_dir / "parallel_cable").touch() + (offline_dir / "serial_cable").touch() + (offline_dir / "foo.f90").touch() + + # Success case: test source files and scripts are copied to .tmp + repo = get_mock_repo() + repo.pre_build() + assert (offline_dir / ".tmp" / "Makefile").exists() + assert (offline_dir / ".tmp" / "parallel_cable").exists() + assert (offline_dir / ".tmp" / "serial_cable").exists() + assert (offline_dir / ".tmp" / "foo.f90").exists() + shutil.rmtree(offline_dir / ".tmp") + + # Success case: test non-verbose standard output + repo = get_mock_repo() + with contextlib.redirect_stdout(io.StringIO()) as buf: + repo.pre_build() + assert not buf.getvalue() + shutil.rmtree(offline_dir / ".tmp") + + # Success case: test verbose standard output + repo = get_mock_repo() + with contextlib.redirect_stdout(io.StringIO()) as buf: + repo.pre_build(verbose=True) + assert buf.getvalue() == ( + "mkdir src/trunk/offline/.tmp\n" + "cp -p src/trunk/offline/foo.f90 src/trunk/offline/.tmp\n" + "cp -p src/trunk/offline/Makefile src/trunk/offline/.tmp\n" + "cp -p src/trunk/offline/parallel_cable src/trunk/offline/.tmp\n" + "cp -p src/trunk/offline/serial_cable src/trunk/offline/.tmp\n" + ) + shutil.rmtree(offline_dir / ".tmp") + + +def test_run_build(): + """Tests for `CableRepository.run_build()`.""" + mock_netcdf_root = "/mock/path/to/root" - default_env = { - "NCDIR": f"{mock_netcdf_root}/lib/Intel", - "NCMOD": f"{mock_netcdf_root}/include/Intel", - "CFLAGS": "-O2 -fp-model precise", - "LDFLAGS": f"-L{mock_netcdf_root}/lib/Intel -O0", - "LD": "-lnetcdf -lnetcdff", - "FC": "ifort", - } - - # Success case: test default build - offline_dir.mkdir(parents=True, exist_ok=True) - (offline_dir / "Makefile").touch(exist_ok=True) - (offline_dir / "parallel_cable").touch(exist_ok=True) - (offline_dir / "serial_cable").touch(exist_ok=True) - (offline_dir / "foo.f90").touch(exist_ok=True) + mock_modules = ["foo", "bar"] + (MOCK_CWD / internal.SRC_DIR / "trunk" / "offline" / ".tmp").mkdir(parents=True) + + # This is required so that we can use the NETCDF_ROOT environment variable + # when running `make`, and `serial_cable` and `parallel_cable` scripts: os.environ["NETCDF_ROOT"] = mock_netcdf_root + + # Success case: test build commands are run mock_subprocess = MockSubprocessWrapper() - mock_subprocess.side_effect = lambda: ( - offline_dir / ".tmp" / internal.CABLE_EXE - ).touch(exist_ok=True) + repo = get_mock_repo(subprocess_handler=mock_subprocess) + repo.run_build(mock_modules) + assert mock_subprocess.commands == [ + "make -f Makefile", + './serial_cable "ifort" "-O2 -fp-model precise"' + f' "-L{mock_netcdf_root}/lib/Intel -O0" "-lnetcdf -lnetcdff" ' + f'"{mock_netcdf_root}/include/Intel"', + ] + + # Success case: test modules are loaded at runtime mock_environment_modules = MockEnvironmentModules() - repo = get_mock_repo(mock_subprocess, mock_environment_modules) - repo.build(mock_modules, offline_source_files=["offline/*90"]) - assert (offline_dir / ".tmp" / "foo.f90").exists() - assert "make -f Makefile" in mock_subprocess.commands - assert ( - f"./serial_cable \"{default_env['FC']}\" \"{default_env['CFLAGS']}\"" - f" \"{default_env['LDFLAGS']}\" \"{default_env['LD']}\" \"{default_env['NCMOD']}\"" - in mock_subprocess.commands - ) - assert all(kv in mock_subprocess.env.items() for kv in default_env.items()) + repo = get_mock_repo(modules_handler=mock_environment_modules) + repo.run_build(mock_modules) assert ( "module load " + " ".join(mock_modules) ) in mock_environment_modules.commands assert ( "module unload " + " ".join(mock_modules) ) in mock_environment_modules.commands + + # Success case: test commands are run with the correct environment variables + mock_subprocess = MockSubprocessWrapper() + repo = get_mock_repo(subprocess_handler=mock_subprocess) + repo.run_build(mock_modules) + assert all( + kv in mock_subprocess.env.items() + for kv in { + "NCDIR": f"{mock_netcdf_root}/lib/Intel", + "NCMOD": f"{mock_netcdf_root}/include/Intel", + "CFLAGS": "-O2 -fp-model precise", + "LDFLAGS": f"-L{mock_netcdf_root}/lib/Intel -O0", + "LD": "-lnetcdf -lnetcdff", + "FC": "ifort", + }.items() + ) + + # Success case: test non-verbose standard output + repo = get_mock_repo() + with contextlib.redirect_stdout(io.StringIO()) as buf: + repo.run_build(mock_modules) + assert not buf.getvalue() + + # Success case: test verbose standard output + repo = get_mock_repo() + with contextlib.redirect_stdout(io.StringIO()) as buf: + repo.run_build(mock_modules, verbose=True) + assert buf.getvalue() == ( + f"Loading modules: {' '.join(mock_modules)}\n" + f"Unloading modules: {' '.join(mock_modules)}\n" + ) + + +def test_post_build(): + """Tests for `CableRepository.post_build()`.""" + + repo_dir = MOCK_CWD / internal.SRC_DIR / "trunk" + offline_dir = repo_dir / "offline" + tmp_dir = offline_dir / ".tmp" + + # Success case: test executable is moved to offline directory + tmp_dir.mkdir(parents=True) + (tmp_dir / internal.CABLE_EXE).touch() + repo = get_mock_repo() + repo.post_build() assert (offline_dir / internal.CABLE_EXE).exists() - shutil.rmtree(offline_dir / ".tmp") + + # Success case: test non-verbose standard output + (tmp_dir / internal.CABLE_EXE).touch() + repo = get_mock_repo() + with contextlib.redirect_stdout(io.StringIO()) as buf: + repo.post_build() + assert not buf.getvalue() + + # Success case: test verbose standard output + (tmp_dir / internal.CABLE_EXE).touch() + repo = get_mock_repo() + with contextlib.redirect_stdout(io.StringIO()) as buf: + repo.post_build(verbose=True) + assert buf.getvalue() == ( + "mv src/trunk/offline/.tmp/cable src/trunk/offline/cable\n" + ) + + +def test_custom_build(): + """Tests for `CableRepository.custom_build()`.""" + + repo_dir = MOCK_CWD / internal.SRC_DIR / "trunk" + custom_build_script_path = repo_dir / "my-custom-build.sh" + custom_build_script_path.parent.mkdir(parents=True) + custom_build_script_path.touch() + mock_modules = ["foo", "bar"] # Success case: execute the build command for a custom build script - custom_build_script_path.parent.mkdir(parents=True, exist_ok=True) - custom_build_script_path.touch(exist_ok=True) mock_subprocess = MockSubprocessWrapper() mock_environment_modules = MockEnvironmentModules() repo = get_mock_repo(mock_subprocess, mock_environment_modules) repo.build_script = str(custom_build_script_path.relative_to(repo_dir)) - repo.build(mock_modules) + repo.custom_build(mock_modules) assert "./tmp-build.sh" in mock_subprocess.commands assert ( "module load " + " ".join(mock_modules) @@ -173,70 +270,19 @@ def test_build(): "module unload " + " ".join(mock_modules) ) in mock_environment_modules.commands - # Success case: test non-verbose standard output - offline_dir.mkdir(parents=True, exist_ok=True) - (offline_dir / "Makefile").touch(exist_ok=True) - (offline_dir / "parallel_cable").touch(exist_ok=True) - (offline_dir / "serial_cable").touch(exist_ok=True) - (offline_dir / "foo.f90").touch(exist_ok=True) - os.environ["NETCDF_ROOT"] = mock_netcdf_root - mock_subprocess = MockSubprocessWrapper() - mock_subprocess.side_effect = lambda: ( - offline_dir / ".tmp" / internal.CABLE_EXE - ).touch(exist_ok=True) - repo = get_mock_repo(subprocess_handler=mock_subprocess) - with contextlib.redirect_stdout(io.StringIO()) as buf: - repo.build(mock_modules, offline_source_files=["offline/*90"]) - assert buf.getvalue() == "Compiling CABLE serially for realisation trunk...\n" - shutil.rmtree(offline_dir / ".tmp") - # Success case: test non-verbose standard output for a custom build script - custom_build_script_path.parent.mkdir(parents=True, exist_ok=True) - custom_build_script_path.touch(exist_ok=True) repo = get_mock_repo() repo.build_script = str(custom_build_script_path.relative_to(repo_dir)) with contextlib.redirect_stdout(io.StringIO()) as buf: - repo.build(mock_modules) - assert buf.getvalue() == ( - "Compiling CABLE using custom build script for realisation trunk...\n" - ) - - # Success case: test verbose standard output - offline_dir.mkdir(parents=True, exist_ok=True) - (offline_dir / "Makefile").touch(exist_ok=True) - (offline_dir / "parallel_cable").touch(exist_ok=True) - (offline_dir / "serial_cable").touch(exist_ok=True) - (offline_dir / "foo.f90").touch(exist_ok=True) - os.environ["NETCDF_ROOT"] = mock_netcdf_root - mock_subprocess = MockSubprocessWrapper() - mock_subprocess.side_effect = lambda: ( - offline_dir / ".tmp" / internal.CABLE_EXE - ).touch(exist_ok=True) - repo = get_mock_repo(subprocess_handler=mock_subprocess) - with contextlib.redirect_stdout(io.StringIO()) as buf: - repo.build(mock_modules, offline_source_files=["offline/*90"], verbose=True) - assert buf.getvalue() == ( - "Compiling CABLE serially for realisation trunk...\n" - "mkdir src/trunk/offline/.tmp\n" - "cp -p src/trunk/offline/foo.f90 src/trunk/offline/.tmp\n" - "cp -p src/trunk/offline/Makefile src/trunk/offline/.tmp\n" - "cp -p src/trunk/offline/parallel_cable src/trunk/offline/.tmp\n" - "cp -p src/trunk/offline/serial_cable src/trunk/offline/.tmp\n" - f"Loading modules: {' '.join(mock_modules)}\n" - f"Unloading modules: {' '.join(mock_modules)}\n" - "mv src/trunk/offline/.tmp/cable src/trunk/offline/cable\n" - ) - shutil.rmtree(offline_dir / ".tmp") + repo.custom_build(mock_modules) + assert not buf.getvalue() # Success case: test verbose standard output for a custom build script - custom_build_script_path.parent.mkdir(parents=True, exist_ok=True) - custom_build_script_path.touch(exist_ok=True) repo = get_mock_repo() repo.build_script = str(custom_build_script_path.relative_to(repo_dir)) with contextlib.redirect_stdout(io.StringIO()) as buf: - repo.build(mock_modules, verbose=True) + repo.custom_build(mock_modules, verbose=True) assert buf.getvalue() == ( - "Compiling CABLE using custom build script for realisation trunk...\n" f"Copying {custom_build_script_path} to {custom_build_script_path.parent}/tmp-build.sh\n" f"chmod +x {custom_build_script_path.parent}/tmp-build.sh\n" "Modifying tmp-build.sh: remove lines that call environment " @@ -246,8 +292,6 @@ def test_build(): ) # Failure case: cannot find custom build script - custom_build_script_path.parent.mkdir(parents=True, exist_ok=True) - custom_build_script_path.touch(exist_ok=True) custom_build_script_path.unlink() repo = get_mock_repo() repo.build_script = str(custom_build_script_path.relative_to(repo_dir)) @@ -257,7 +301,7 @@ def test_build(): "found. Do you need to specify a different build script with the 'build_script' " "option in config.yaml?", ): - repo.build(mock_modules) + repo.custom_build(mock_modules) def test_remove_module_lines():