Skip to content

Commit ecf73ee

Browse files
Added the cmake-config command (#6)
* Added the cmake-config command + also added a defaults.py file that holds default names and paths. ~ Moved the default tag prefix to the defaults file. * Removed the moved universal tag function from _utils.py * src prefix, CMAKE -> CMake, docs changes + Added src_prefix function to defaults. ~ Changed references to CMAKE to instead say CMake ~ Some documentation improvement * Small doc change * Added unit tests for CMake config. + Added a cmake mark. + Added fixtures, functions, and tests for testing CMake config. + Added a CMake and Ninja lock file. * BUGFIX: Unintended removal of init images by testing Normal init images were removed by testing. + Added a function for making init image Dockerfiles. ~ Moved the above funtionality out of setup_init(). ~ Changed the init_image fixture to add a unique directory to the init dockerfile so it will not delete images unintentionally. * Removed src_prefix() --------- Co-authored-by: tyler-g-hudson <[email protected]>
1 parent 4acd125 commit ecf73ee

13 files changed

+515
-53
lines changed

docker_cli/_docker_cmake.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from textwrap import dedent
2+
3+
from ._docker_mamba import micromamba_docker_lines
4+
from .defaults import build_prefix, install_prefix
5+
6+
7+
def cmake_config_dockerfile(base: str, build_type: str, with_cuda: bool = True) -> str:
8+
"""
9+
Creates a Dockerfile for configuring CMake Build.
10+
11+
This function assumes that the working directory on the image is at the
12+
source directory where the top-level CMakeLists.txt file is contained.
13+
14+
Parameters
15+
----------
16+
base : str
17+
The base image tag.
18+
build_type : str
19+
The CMake build type. See
20+
`here <https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html>`_
21+
for possible values.
22+
with_cuda : bool, optional
23+
Whether or not to use CUDA in the build. Defaults to True.
24+
25+
Returns
26+
-------
27+
dockerfile : str
28+
The generated Dockerfile.
29+
"""
30+
# Parsing the additional arguments and then joining them as a list because there may
31+
# be additional cmake arguments to add in the future, and this simplifies the
32+
# process of adding them.
33+
additional_args = []
34+
if with_cuda:
35+
additional_args += ["-D WITH_CUDA=YES"]
36+
else:
37+
additional_args += ["-D WITH_CUDA=NO"]
38+
cmake_extra_args = " ".join(additional_args)
39+
40+
# Begin constructing the dockerfile with the initial FROM line.
41+
dockerfile: str = f"FROM {base}\n\n"
42+
43+
# Activate the micromamba user and environment.
44+
dockerfile += micromamba_docker_lines() + "\n\n"
45+
dockerfile += dedent(
46+
f"""
47+
ENV INSTALL_PREFIX {str(install_prefix())}
48+
ENV BUILD_PREFIX {str(build_prefix())}
49+
50+
RUN cmake \\
51+
-S . \\
52+
-B $BUILD_PREFIX \\
53+
-G Ninja \\
54+
-D ISCE3_FETCH_DEPS=NO \\
55+
-D CMAKE_BUILD_TYPE={build_type} \\
56+
-D CMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \\
57+
-D CMAKE_PREFIX_PATH=$MAMBA_ROOT_PREFIX \\
58+
{cmake_extra_args}
59+
"""
60+
).strip()
61+
62+
return dockerfile

docker_cli/_docker_init.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from textwrap import dedent
2+
3+
4+
def init_dockerfile(base: str, custom_lines: str) -> str:
5+
"""
6+
Set up the initial configuration image.
7+
8+
Parameters
9+
----------
10+
base : str
11+
The name of the image upon this one will be based.
12+
custom_lines : str
13+
Custom installation and configuration lines to be added to the Dockerfile.
14+
15+
Returns
16+
-------
17+
str
18+
The generated Dockerfile.
19+
"""
20+
return (
21+
f"FROM {base}\n\n"
22+
+ custom_lines
23+
+ "\n"
24+
+ dedent(
25+
"""
26+
ENV DEFAULT_GROUP defaultgroup
27+
ENV DEFAULT_USER defaultuser
28+
ENV DEFAULT_GID 1000
29+
ENV DEFAULT_UID 1000
30+
31+
RUN groupadd -g $DEFAULT_GID $DEFAULT_GROUP
32+
RUN useradd -g $DEFAULT_GID -u $DEFAULT_UID -m $DEFAULT_USER
33+
34+
RUN chmod -R 777 /tmp
35+
"""
36+
).strip()
37+
)

docker_cli/_utils.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,7 @@
1515
get_supported_package_managers,
1616
)
1717
from ._url_reader import URLReader, get_supported_url_readers, get_url_reader
18-
19-
20-
def universal_tag_prefix() -> str:
21-
"""
22-
Returns a prefix for tags generated by the system.
23-
24-
Returns
25-
-------
26-
str
27-
The prefix.
28-
"""
29-
return "dcli"
18+
from .defaults import universal_tag_prefix
3019

3120

3221
@contextmanager

docker_cli/cli/build_commands.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from pathlib import Path
33
from typing import List
44

5-
from ..commands import copy_dir, get_archive
5+
from ..commands import configure_cmake, copy_dir, get_archive
66
from ._utils import add_tag_argument, help_formatter
77

88

@@ -13,8 +13,8 @@ def init_build_parsers(subparsers: argparse._SubParsersAction) -> None:
1313
The build commands are the group of commands to be completed after the CUDA and
1414
conda environments have been installed to the image, with the purpose of acquiring
1515
and building the ISCE3 repository and any further repositories.
16-
These commands consist of the "get-archive" and "copydir" commands, and more are
17-
being added.
16+
These commands consist of the "get-archive", "copydir", and "cmake-config" commands,
17+
and more are being added.
1818
1919
Parameters
2020
-----------
@@ -39,6 +39,24 @@ def init_build_parsers(subparsers: argparse._SubParsersAction) -> None:
3939
help="The path to place the contents of the Git archive at on the image.",
4040
)
4141

42+
build_type_choices = ["Release", "Debug", "RelWithDebInfo", "MinSizeRel"]
43+
config_params = argparse.ArgumentParser(add_help=False)
44+
config_params.add_argument(
45+
"--build-type",
46+
type=str,
47+
default="Release",
48+
metavar="CMAKE_BUILD_TYPE",
49+
choices=build_type_choices,
50+
help="The CMAKE_BUILD_TYPE argument for CMake. Valid options are: "
51+
+ f"{', '.join(build_type_choices)}. Defaults to \"Release\".",
52+
)
53+
config_params.add_argument(
54+
"--no-cuda",
55+
action="store_true",
56+
default=False,
57+
help="If used, the build configuration will not use CUDA.",
58+
)
59+
4260
setup_parse = argparse.ArgumentParser(add_help=False)
4361
setup_parse.add_argument(
4462
"--base",
@@ -89,14 +107,29 @@ def init_build_parsers(subparsers: argparse._SubParsersAction) -> None:
89107
help="Run Docker build with no cache if used.",
90108
)
91109

110+
config_parser = subparsers.add_parser(
111+
"cmake-config",
112+
parents=[setup_parse, config_params],
113+
help="Creates an image with a configured compiler.",
114+
formatter_class=help_formatter,
115+
)
116+
add_tag_argument(parser=config_parser, default="configured")
117+
config_parser.add_argument(
118+
"--no-cache",
119+
action="store_true",
120+
help="Run Docker build with no cache if used.",
121+
)
122+
92123

93124
def build_command_names() -> List[str]:
94125
"""Returns a list of all build command names."""
95-
return ["get-archive", "copydir"]
126+
return ["get-archive", "copydir", "cmake-config"]
96127

97128

98129
def run_build(args: argparse.Namespace, command: str) -> None:
99130
if command == "get-archive":
100131
get_archive(**vars(args))
101132
if command == "copydir":
102133
copy_dir(**vars(args))
134+
if command == "cmake-config":
135+
configure_cmake(**vars(args))

docker_cli/cli/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import sys
33
from typing import Sequence
44

5-
from .._utils import universal_tag_prefix
5+
from ..defaults import universal_tag_prefix
66
from ._utils import help_formatter
77
from .build_commands import build_command_names, init_build_parsers, run_build
88
from .setup_commands import init_setup_parsers, run_setup

docker_cli/commands.py

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,14 @@
55
from subprocess import DEVNULL, PIPE, run
66
from typing import Iterable, List
77

8+
from ._docker_cmake import cmake_config_dockerfile
89
from ._docker_git import git_extract_dockerfile
910
from ._docker_insert import insert_dir_dockerfile
1011
from ._docker_mamba import mamba_lockfile_command
1112
from ._image import Image
1213
from ._url_reader import URLReader
13-
from ._utils import (
14-
image_command_check,
15-
is_conda_pkg_name,
16-
temp_image,
17-
universal_tag_prefix,
18-
)
14+
from ._utils import image_command_check, is_conda_pkg_name, temp_image
15+
from .defaults import universal_tag_prefix
1916

2017

2118
def get_archive(
@@ -149,6 +146,47 @@ def copy_dir(
149146
)
150147

151148

149+
def configure_cmake(
150+
tag: str,
151+
base: str,
152+
build_type: str,
153+
no_cuda: bool = False,
154+
no_cache: bool = False,
155+
) -> Image:
156+
"""
157+
Produces an image with CMake configured.
158+
159+
Parameters
160+
----------
161+
tag : str
162+
The image tag.
163+
base : str
164+
The base image tag.
165+
build_type : str
166+
The CMake build type. See
167+
`here <https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html>`_
168+
for possible values.
169+
no_cuda : bool, optional
170+
If True, build without CUDA. Defaults to False.
171+
no_cache : bool, optional
172+
Run Docker build with no cache if True. Defaults to False.
173+
174+
Returns
175+
-------
176+
Image
177+
The generated image.
178+
"""
179+
dockerfile: str = cmake_config_dockerfile(
180+
base=base,
181+
build_type=build_type,
182+
with_cuda=not no_cuda,
183+
)
184+
185+
prefix = universal_tag_prefix()
186+
img_tag = tag if tag.startswith(prefix) else f"{prefix}-{tag}"
187+
return Image.build(tag=img_tag, dockerfile_string=dockerfile, no_cache=no_cache)
188+
189+
152190
def dropin(tag: str) -> None:
153191
"""
154192
Initiates a drop-in session on an image.

docker_cli/defaults.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from pathlib import Path
2+
3+
4+
def universal_tag_prefix() -> str:
5+
"""Returns a prefix for tags generated by the system."""
6+
return "dcli"
7+
8+
9+
def install_prefix() -> Path:
10+
"""Returns the build system's default install prefix path."""
11+
return Path("/app")
12+
13+
14+
def build_prefix() -> Path:
15+
"""Returns the build system's default build prefix path."""
16+
return Path("/tmp/build")

docker_cli/setup_commands.py

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
from __future__ import annotations
22

33
from pathlib import Path
4-
from textwrap import dedent, indent
4+
from textwrap import indent
55
from typing import Dict, Optional, Tuple
66

77
from ._docker_cuda import CUDADockerfileGenerator, get_cuda_dockerfile_generator
8+
from ._docker_init import init_dockerfile
89
from ._docker_mamba import mamba_add_reqs_dockerfile, mamba_install_dockerfile
910
from ._image import Image
1011
from ._package_manager import PackageManager
1112
from ._url_reader import URLReader, get_url_reader
12-
from ._utils import (
13-
image_command_check,
14-
parse_cuda_info,
15-
temp_image,
16-
universal_tag_prefix,
17-
)
13+
from ._utils import image_command_check, parse_cuda_info, temp_image
14+
from .defaults import universal_tag_prefix
1815

1916

2017
def setup_init(
@@ -42,26 +39,9 @@ def setup_init(
4239
The URL Reader present on the image.
4340
"""
4441
with temp_image(base) as temp_img:
45-
package_mgr, url_reader, dockerfile = image_command_check(temp_img, True)
46-
47-
dockerfile = (
48-
f"FROM {base}\n\n"
49-
+ dockerfile
50-
+ "\n"
51-
+ dedent(
52-
"""
53-
ENV DEFAULT_GROUP defaultgroup
54-
ENV DEFAULT_USER defaultuser
55-
ENV DEFAULT_GID 1000
56-
ENV DEFAULT_UID 1000
57-
58-
RUN groupadd -g $DEFAULT_GID $DEFAULT_GROUP
59-
RUN useradd -g $DEFAULT_GID -u $DEFAULT_UID -m $DEFAULT_USER
60-
61-
RUN chmod -R 777 /tmp
62-
"""
63-
).strip()
64-
)
42+
package_mgr, url_reader, initial_lines = image_command_check(temp_img, True)
43+
44+
dockerfile = init_dockerfile(base=base, custom_lines=initial_lines)
6545

6646
prefix = universal_tag_prefix()
6747
img_tag = tag if tag.startswith(prefix) else f"{prefix}-{tag}"

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ log_cli = 0
33
log_cli_level = WARNING
44
markers =
55
build: mark a test as a build command test.
6+
cmake: mark a test as a CMake test.
67
cuda: mark a test as a CUDA test.
78
dockerfiles: mark a test as a dockerfile test.
89
git: mark a test as a Git test.

test/fixtures.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from pytest import fixture
88

99
from docker_cli import Image, PackageManager, URLReader
10+
from docker_cli._docker_init import init_dockerfile
1011
from docker_cli._utils import image_command_check, temp_image
11-
from docker_cli.setup_commands import setup_init
1212

1313
from .utils import determine_scope, generate_tag, remove_docker_image
1414

@@ -92,7 +92,20 @@ def init_image(base_tag: str, init_tag: str) -> Iterator[Image]:
9292
Iterator[Image]
9393
The initialization tag generator.
9494
"""
95-
img, _, _ = setup_init(base=base_tag, tag=init_tag, no_cache=False)
95+
# Get some initial install lines to ensure that the appropriate software is
96+
# installed on the init image.
97+
with temp_image(base_tag) as temp_img:
98+
_, _, initial_lines = image_command_check(temp_img, True)
99+
100+
# Get the init image dockerfile.
101+
dockerfile = init_dockerfile(base=base_tag, custom_lines=initial_lines)
102+
103+
# Creates a unique directory in the image. This causes the image to be unique,
104+
# preventing unintentional deletion of init images that are not for testing.
105+
dockerfile += f"\n\nRUN mkdir {init_tag}"
106+
107+
# Build and yield the image.
108+
img = Image.build(tag=init_tag, dockerfile_string=dockerfile)
96109
yield img
97110
remove_docker_image(init_tag)
98111

0 commit comments

Comments
 (0)