holoscan-cli 2.9.0__py3-none-any.whl → 3.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- holoscan_cli/__main__.py +0 -9
- holoscan_cli/common/argparse_types.py +18 -3
- holoscan_cli/common/artifact_sources.py +14 -3
- holoscan_cli/common/constants.py +20 -7
- holoscan_cli/common/dockerutils.py +19 -3
- holoscan_cli/common/enum_types.py +9 -1
- holoscan_cli/common/sdk_utils.py +46 -38
- holoscan_cli/nics/nics.py +1 -1
- holoscan_cli/packager/arguments.py +6 -2
- holoscan_cli/packager/container_builder.py +58 -19
- holoscan_cli/packager/package_command.py +42 -27
- holoscan_cli/packager/packager.py +1 -1
- holoscan_cli/packager/parameters.py +36 -12
- holoscan_cli/packager/platforms.py +10 -4
- holoscan_cli/packager/templates/Dockerfile-cu12.jinja2 +399 -0
- holoscan_cli/packager/templates/Dockerfile.jinja2 +113 -181
- holoscan_cli/packager/templates/tools.sh +1 -0
- holoscan_cli/runner/run_command.py +8 -0
- holoscan_cli/runner/runner.py +3 -6
- holoscan_cli/version/version.py +7 -0
- {holoscan_cli-2.9.0.dist-info → holoscan_cli-3.8.0.dist-info}/METADATA +24 -18
- holoscan_cli-3.8.0.dist-info/RECORD +40 -0
- {holoscan_cli-2.9.0.dist-info → holoscan_cli-3.8.0.dist-info}/WHEEL +1 -1
- holoscan_cli-2.9.0.dist-info/RECORD +0 -39
- {holoscan_cli-2.9.0.dist-info → holoscan_cli-3.8.0.dist-info}/entry_points.txt +0 -0
- {holoscan_cli-2.9.0.dist-info → holoscan_cli-3.8.0.dist-info/licenses}/LICENSE +0 -0
holoscan_cli/__main__.py
CHANGED
|
@@ -20,7 +20,6 @@ import os
|
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from typing import Optional, Union
|
|
22
22
|
|
|
23
|
-
from .common.enum_types import Platform, PlatformConfiguration
|
|
24
23
|
|
|
25
24
|
logging.getLogger("docker.api.build").setLevel(logging.WARNING)
|
|
26
25
|
logging.getLogger("docker.auth").setLevel(logging.WARNING)
|
|
@@ -97,14 +96,6 @@ def parse_args(argv: Optional[list[str]] = None) -> argparse.Namespace:
|
|
|
97
96
|
parser.print_help()
|
|
98
97
|
parser.exit()
|
|
99
98
|
|
|
100
|
-
if args.command == "package":
|
|
101
|
-
if args.platform[0] == Platform.X64Workstation:
|
|
102
|
-
args.platform_config = PlatformConfiguration.dGPU
|
|
103
|
-
elif args.platform_config is None:
|
|
104
|
-
parser.error(
|
|
105
|
-
f"'--platform-config' is required for '{args.platform[0].value}'"
|
|
106
|
-
)
|
|
107
|
-
|
|
108
99
|
return args
|
|
109
100
|
|
|
110
101
|
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
import argparse
|
|
16
16
|
import os
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
|
|
19
18
|
from .constants import SDK
|
|
20
19
|
from .enum_types import Platform, PlatformConfiguration, SdkType
|
|
21
20
|
|
|
@@ -107,6 +106,7 @@ def valid_platforms(platforms_str: str) -> list[Platform]:
|
|
|
107
106
|
platforms = platforms_str.lower().split(",")
|
|
108
107
|
platform_enums = []
|
|
109
108
|
for platform in platforms:
|
|
109
|
+
platform = platform.strip()
|
|
110
110
|
if platform not in SDK.PLATFORMS:
|
|
111
111
|
raise argparse.ArgumentTypeError(
|
|
112
112
|
f"{platform} is not a valid option for --platforms."
|
|
@@ -128,7 +128,7 @@ def valid_platform_config(platform_config_str: str) -> PlatformConfiguration:
|
|
|
128
128
|
Otherwise, raises argparse.ArgumentTypeError.
|
|
129
129
|
"""
|
|
130
130
|
|
|
131
|
-
platform_config_str = platform_config_str.lower()
|
|
131
|
+
platform_config_str = platform_config_str.lower().strip()
|
|
132
132
|
if platform_config_str not in SDK.PLATFORM_CONFIGS:
|
|
133
133
|
raise argparse.ArgumentTypeError(
|
|
134
134
|
f"{platform_config_str} is not a valid option for --platform-config."
|
|
@@ -149,8 +149,23 @@ def valid_sdk_type(sdk_str: str) -> SdkType:
|
|
|
149
149
|
Otherwise, raises argparse.ArgumentTypeError.
|
|
150
150
|
"""
|
|
151
151
|
|
|
152
|
-
sdk_str = sdk_str.lower()
|
|
152
|
+
sdk_str = sdk_str.lower().strip()
|
|
153
153
|
if sdk_str not in SDK.SDKS:
|
|
154
154
|
raise argparse.ArgumentTypeError(f"{sdk_str} is not a valid option for --sdk.")
|
|
155
155
|
|
|
156
156
|
return SdkType(sdk_str)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def valid_host_ip(host_ip: str) -> str:
|
|
160
|
+
"""Helper type checking and type converting method for ArgumentParser.add_argument
|
|
161
|
+
to convert check valid host:ip format.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
host_ip: host ip string
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
host, ip = host_ip.split(":")
|
|
168
|
+
if host == "" or ip == "":
|
|
169
|
+
raise argparse.ArgumentTypeError(f"Invalid valid for --add-host: '{host_ip}'")
|
|
170
|
+
|
|
171
|
+
return host_ip
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
import json
|
|
16
16
|
import logging
|
|
17
|
+
import os
|
|
17
18
|
from typing import Any, Optional
|
|
18
19
|
|
|
19
20
|
import requests
|
|
@@ -36,8 +37,9 @@ class ArtifactSources:
|
|
|
36
37
|
HoloscanVersion = None
|
|
37
38
|
ManifestFileUrl = None
|
|
38
39
|
|
|
39
|
-
def __init__(self) -> None:
|
|
40
|
+
def __init__(self, cuda_version: int) -> None:
|
|
40
41
|
self._logger = logging.getLogger("common")
|
|
42
|
+
self._cuda_version = cuda_version
|
|
41
43
|
try:
|
|
42
44
|
ArtifactSources.HoloscanVersion = ".".join(
|
|
43
45
|
str(i) for i in Version(__version__).release[0:3]
|
|
@@ -48,7 +50,10 @@ class ArtifactSources:
|
|
|
48
50
|
"a Holoscan SDK version to use."
|
|
49
51
|
) from ex
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
if self._cuda_version == 12:
|
|
54
|
+
ArtifactSources.ManifestFileUrl = f"https://raw.githubusercontent.com/nvidia-holoscan/holoscan-cli/refs/heads/main/releases/{ArtifactSources.HoloscanVersion}/artifacts-cu12.json" # noqa: E501
|
|
55
|
+
else:
|
|
56
|
+
ArtifactSources.ManifestFileUrl = f"https://raw.githubusercontent.com/nvidia-holoscan/holoscan-cli/refs/heads/main/releases/{ArtifactSources.HoloscanVersion}/artifacts.json" # noqa: E501
|
|
52
57
|
|
|
53
58
|
@property
|
|
54
59
|
def holoscan_versions(self) -> list[str]:
|
|
@@ -82,6 +87,12 @@ class ArtifactSources:
|
|
|
82
87
|
"Downloading manifest files from non-HTTPS servers is not supported."
|
|
83
88
|
)
|
|
84
89
|
else:
|
|
90
|
+
if os.path.isdir(uri):
|
|
91
|
+
if self._cuda_version == 12:
|
|
92
|
+
uri = os.path.join(uri, "artifacts-cu12.json")
|
|
93
|
+
else:
|
|
94
|
+
uri = os.path.join(uri, "artifacts.json")
|
|
95
|
+
|
|
85
96
|
self._logger.info(f"Using CLI manifest file from {uri}...")
|
|
86
97
|
with open(uri) as file:
|
|
87
98
|
temp = json.load(file)
|
|
@@ -116,7 +127,7 @@ class ArtifactSources:
|
|
|
116
127
|
)
|
|
117
128
|
|
|
118
129
|
def _download_manifest_internal(self, url, headers=None):
|
|
119
|
-
self._logger.info("Downloading CLI manifest file...")
|
|
130
|
+
self._logger.info(f"Downloading CLI manifest file from {url}...")
|
|
120
131
|
manifest = requests.get(url, headers=headers)
|
|
121
132
|
|
|
122
133
|
try:
|
holoscan_cli/common/constants.py
CHANGED
|
@@ -69,6 +69,9 @@ class Constants:
|
|
|
69
69
|
|
|
70
70
|
RESOURCE_SHARED_MEMORY_KEY = "sharedMemory"
|
|
71
71
|
|
|
72
|
+
# Include "holoscan" for backward compatibility; remove post 3.7.0 release
|
|
73
|
+
PYPI_PACKAGE_NAMES = ["holoscan-cu12", "holoscan-cu13", "holoscan"]
|
|
74
|
+
|
|
72
75
|
|
|
73
76
|
class SDK:
|
|
74
77
|
"""
|
|
@@ -77,19 +80,21 @@ class SDK:
|
|
|
77
80
|
"""
|
|
78
81
|
|
|
79
82
|
# Platform to architecture mappings
|
|
80
|
-
|
|
81
|
-
Platform.
|
|
82
|
-
Platform.
|
|
83
|
+
PLATFORM_ARCH_MAPPINGS = {
|
|
84
|
+
Platform.Jetson: Arch.arm64,
|
|
85
|
+
Platform.IGX_iGPU: Arch.arm64,
|
|
86
|
+
Platform.IGX_dGPU: Arch.arm64,
|
|
83
87
|
Platform.SBSA: Arch.arm64,
|
|
84
|
-
Platform.
|
|
88
|
+
Platform.x86_64: Arch.amd64,
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
# Values of all platforms supported by the Packager
|
|
88
92
|
PLATFORMS = [
|
|
89
|
-
Platform.
|
|
90
|
-
Platform.
|
|
93
|
+
Platform.Jetson.value,
|
|
94
|
+
Platform.IGX_iGPU.value,
|
|
95
|
+
Platform.IGX_dGPU.value,
|
|
91
96
|
Platform.SBSA.value,
|
|
92
|
-
Platform.
|
|
97
|
+
Platform.x86_64.value,
|
|
93
98
|
]
|
|
94
99
|
|
|
95
100
|
# Values of all platform configurations supported by the Packager
|
|
@@ -98,6 +103,14 @@ class SDK:
|
|
|
98
103
|
PlatformConfiguration.dGPU.value,
|
|
99
104
|
]
|
|
100
105
|
|
|
106
|
+
INTERNAL_PLATFORM_MAPPINGS = {
|
|
107
|
+
Platform.Jetson: (Platform.JetsonAgxOrinDevKit, PlatformConfiguration.iGPU),
|
|
108
|
+
Platform.IGX_iGPU: (Platform.IGXOrinDevIt, PlatformConfiguration.iGPU),
|
|
109
|
+
Platform.IGX_dGPU: (Platform.IGXOrinDevIt, PlatformConfiguration.dGPU),
|
|
110
|
+
Platform.SBSA: (Platform.SBSA, PlatformConfiguration.dGPU),
|
|
111
|
+
Platform.x86_64: (Platform.X64Workstation, PlatformConfiguration.dGPU),
|
|
112
|
+
}
|
|
113
|
+
|
|
101
114
|
# Values of SDKs supported by the Packager
|
|
102
115
|
SDKS = [SdkType.Holoscan.value, SdkType.MonaiDeploy.value]
|
|
103
116
|
|
|
@@ -172,6 +172,7 @@ def docker_run(
|
|
|
172
172
|
platform_config: str,
|
|
173
173
|
shared_memory_size: str = "1GB",
|
|
174
174
|
is_root: bool = False,
|
|
175
|
+
remove: bool = False,
|
|
175
176
|
):
|
|
176
177
|
"""Creates and runs a Docker container
|
|
177
178
|
|
|
@@ -351,6 +352,7 @@ def docker_run(
|
|
|
351
352
|
ulimits,
|
|
352
353
|
devices,
|
|
353
354
|
group_adds,
|
|
355
|
+
remove,
|
|
354
356
|
)
|
|
355
357
|
else:
|
|
356
358
|
_start_container(
|
|
@@ -368,6 +370,7 @@ def docker_run(
|
|
|
368
370
|
ulimits,
|
|
369
371
|
devices,
|
|
370
372
|
group_adds,
|
|
373
|
+
remove,
|
|
371
374
|
)
|
|
372
375
|
|
|
373
376
|
|
|
@@ -386,6 +389,7 @@ def _start_container(
|
|
|
386
389
|
ulimits,
|
|
387
390
|
devices,
|
|
388
391
|
group_adds,
|
|
392
|
+
remove,
|
|
389
393
|
):
|
|
390
394
|
container = docker.container.create(
|
|
391
395
|
image_name,
|
|
@@ -394,7 +398,7 @@ def _start_container(
|
|
|
394
398
|
hostname=name,
|
|
395
399
|
name=name,
|
|
396
400
|
networks=[network],
|
|
397
|
-
remove=
|
|
401
|
+
remove=False,
|
|
398
402
|
shm_size=shared_memory_size,
|
|
399
403
|
user=user,
|
|
400
404
|
volumes=volumes,
|
|
@@ -441,7 +445,18 @@ def _start_container(
|
|
|
441
445
|
except Exception:
|
|
442
446
|
print(str(log[1]))
|
|
443
447
|
|
|
444
|
-
|
|
448
|
+
exit_code = container.state.exit_code
|
|
449
|
+
logger.info(
|
|
450
|
+
f"Container '{container_name}'({container_id}) exited with code {exit_code}."
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
if remove:
|
|
454
|
+
container.remove()
|
|
455
|
+
|
|
456
|
+
if exit_code != 0:
|
|
457
|
+
raise RuntimeError(
|
|
458
|
+
f"Container '{container_name}'({container_id}) exited with code {exit_code}."
|
|
459
|
+
)
|
|
445
460
|
|
|
446
461
|
|
|
447
462
|
def _enter_terminal(
|
|
@@ -457,6 +472,7 @@ def _enter_terminal(
|
|
|
457
472
|
ulimits,
|
|
458
473
|
devices,
|
|
459
474
|
group_adds,
|
|
475
|
+
remove,
|
|
460
476
|
):
|
|
461
477
|
print("\n\nEntering terminal...")
|
|
462
478
|
print(
|
|
@@ -475,7 +491,7 @@ def _enter_terminal(
|
|
|
475
491
|
interactive=True,
|
|
476
492
|
name=name,
|
|
477
493
|
networks=[network],
|
|
478
|
-
remove=
|
|
494
|
+
remove=remove,
|
|
479
495
|
shm_size=shared_memory_size,
|
|
480
496
|
tty=True,
|
|
481
497
|
user=user,
|
|
@@ -37,10 +37,18 @@ class Arch(Enum):
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class Platform(Enum):
|
|
40
|
+
# User values - when a new value is added, please also update SDK.PLATFORMS
|
|
41
|
+
Jetson = "jetson"
|
|
42
|
+
IGX_iGPU = "igx-igpu"
|
|
43
|
+
IGX_dGPU = "igx-dgpu"
|
|
44
|
+
SBSA = "sbsa"
|
|
45
|
+
x86_64 = "x86_64"
|
|
46
|
+
|
|
47
|
+
# Internal use only - for mapping actual platform with platform configuration
|
|
48
|
+
# as defined in SDK.INTERNAL_PLATFORM_MAPPINGS.
|
|
40
49
|
IGXOrinDevIt = "igx-orin-devkit"
|
|
41
50
|
JetsonAgxOrinDevKit = "jetson-agx-orin-devkit"
|
|
42
51
|
X64Workstation = "x64-workstation"
|
|
43
|
-
SBSA = "sbsa"
|
|
44
52
|
|
|
45
53
|
|
|
46
54
|
class PlatformConfiguration(Enum):
|
holoscan_cli/common/sdk_utils.py
CHANGED
|
@@ -21,6 +21,7 @@ from typing import Optional
|
|
|
21
21
|
from packaging.version import Version
|
|
22
22
|
|
|
23
23
|
from .artifact_sources import ArtifactSources
|
|
24
|
+
from .constants import Constants
|
|
24
25
|
from .enum_types import SdkType
|
|
25
26
|
from .exceptions import FailedToDetectSDKVersionError, InvalidSdkError
|
|
26
27
|
|
|
@@ -72,19 +73,7 @@ def detect_sdk(sdk: Optional[SdkType] = None) -> SdkType:
|
|
|
72
73
|
|
|
73
74
|
command = None
|
|
74
75
|
try:
|
|
75
|
-
|
|
76
|
-
# with the Debian package.
|
|
77
|
-
# Since the Debian package bundles with 3.10, we don't need to handle 3.9 but
|
|
78
|
-
# we still need to check if `orig_argv` is supported to avoid breaking unit test.
|
|
79
|
-
if (
|
|
80
|
-
getattr(sys, "orig_argv", None)
|
|
81
|
-
and len(sys.orig_argv) >= 3
|
|
82
|
-
and sys.orig_argv[0] == "python3"
|
|
83
|
-
and sys.orig_argv[2] == "holoscan_cli"
|
|
84
|
-
):
|
|
85
|
-
command = "holoscan"
|
|
86
|
-
else:
|
|
87
|
-
command = Path(sys.argv[0]).name.lower()
|
|
76
|
+
command = Path(sys.argv[0]).name.lower()
|
|
88
77
|
return SdkType(command)
|
|
89
78
|
except Exception as ex:
|
|
90
79
|
raise InvalidSdkError(f"Invalid SDK value provided: {command}") from ex
|
|
@@ -137,32 +126,37 @@ def detect_holoscan_version(sdk_version: Optional[Version] = None) -> str:
|
|
|
137
126
|
if sdk_version is not None:
|
|
138
127
|
return sdk_version.base_version
|
|
139
128
|
else:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
elif (
|
|
148
|
-
len(ver.release) == 2
|
|
149
|
-
and ver.major == ver.release[0]
|
|
150
|
-
and ver.minor == ver.release[1]
|
|
151
|
-
):
|
|
152
|
-
ver_str = ver_str + ".0"
|
|
153
|
-
elif (
|
|
154
|
-
len(ver.release) == 4
|
|
155
|
-
and ver.major == ver.release[0]
|
|
156
|
-
and ver.minor == ver.release[1]
|
|
157
|
-
and ver.micro == ver.release[2]
|
|
158
|
-
):
|
|
159
|
-
ver_str = f"{ver.release[0]}.{ver.release[1]}.{ver.release[2]}"
|
|
160
|
-
|
|
161
|
-
return ver_str
|
|
162
|
-
except Exception as ex:
|
|
129
|
+
# Scan for installed packages with prefix "holoscan"
|
|
130
|
+
holoscan_pkgs = [
|
|
131
|
+
dist.metadata["Name"]
|
|
132
|
+
for dist in importlib.metadata.distributions()
|
|
133
|
+
if dist.metadata["Name"].lower() in Constants.PYPI_PACKAGE_NAMES
|
|
134
|
+
]
|
|
135
|
+
if not holoscan_pkgs:
|
|
163
136
|
raise FailedToDetectSDKVersionError(
|
|
164
|
-
"
|
|
165
|
-
)
|
|
137
|
+
"No installed Holoscan PyPI package found."
|
|
138
|
+
)
|
|
139
|
+
ver_str = importlib.metadata.version(holoscan_pkgs[0]).title()
|
|
140
|
+
ver = Version(ver_str)
|
|
141
|
+
ver_str = ".".join(str(i) for i in ver.release)
|
|
142
|
+
|
|
143
|
+
if len(ver.release) == 1 and ver.major == ver.release[0]:
|
|
144
|
+
ver_str = ver_str + ".0.0"
|
|
145
|
+
elif (
|
|
146
|
+
len(ver.release) == 2
|
|
147
|
+
and ver.major == ver.release[0]
|
|
148
|
+
and ver.minor == ver.release[1]
|
|
149
|
+
):
|
|
150
|
+
ver_str = ver_str + ".0"
|
|
151
|
+
elif (
|
|
152
|
+
len(ver.release) == 4
|
|
153
|
+
and ver.major == ver.release[0]
|
|
154
|
+
and ver.minor == ver.release[1]
|
|
155
|
+
and ver.micro == ver.release[2]
|
|
156
|
+
):
|
|
157
|
+
ver_str = f"{ver.release[0]}.{ver.release[1]}.{ver.release[2]}"
|
|
158
|
+
|
|
159
|
+
return ver_str
|
|
166
160
|
|
|
167
161
|
|
|
168
162
|
def detect_monaideploy_version(sdk_version: Optional[Version] = None) -> str:
|
|
@@ -193,3 +187,17 @@ def detect_monaideploy_version(sdk_version: Optional[Version] = None) -> str:
|
|
|
193
187
|
raise FailedToDetectSDKVersionError(
|
|
194
188
|
"Failed to detect installed MONAI Deploy App SDK PyPI version.", ex
|
|
195
189
|
) from ex
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def detect_holoscan_cli_version() -> str:
|
|
193
|
+
"""
|
|
194
|
+
Detects Holoscan CLI version to use based on installed PyPI package.
|
|
195
|
+
"""
|
|
196
|
+
try:
|
|
197
|
+
ver_str = importlib.metadata.version("holoscan-cli").title()
|
|
198
|
+
ver = Version(ver_str)
|
|
199
|
+
return ver.base_version
|
|
200
|
+
except Exception as ex:
|
|
201
|
+
raise FailedToDetectSDKVersionError(
|
|
202
|
+
"Failed to detect installed Holoscan CLI PyPI version.", ex
|
|
203
|
+
) from ex
|
holoscan_cli/nics/nics.py
CHANGED
|
@@ -28,6 +28,6 @@ def execute_nics_command(args: Namespace):
|
|
|
28
28
|
strs = [f"\n\t{item[0]:<15} : {item[1]}" for item in ip_addresses]
|
|
29
29
|
print(f"Available network interface cards/IP addresses: \n{''.join(strs)}")
|
|
30
30
|
except Exception as ex:
|
|
31
|
-
|
|
31
|
+
logger.error("Error executing nics command.")
|
|
32
32
|
logger.debug(ex)
|
|
33
33
|
sys.exit(4)
|
|
@@ -54,7 +54,7 @@ class PackagingArguments:
|
|
|
54
54
|
|
|
55
55
|
self._platforms: list[PlatformParameters]
|
|
56
56
|
self._build_parameters = PackageBuildParameters()
|
|
57
|
-
self._artifact_sources = ArtifactSources()
|
|
57
|
+
self._artifact_sources = ArtifactSources(args.cuda)
|
|
58
58
|
|
|
59
59
|
if args.source is not None:
|
|
60
60
|
self._artifact_sources.load(args.source)
|
|
@@ -76,6 +76,8 @@ class PackagingArguments:
|
|
|
76
76
|
self.build_parameters.cmake_args = args.cmake_args
|
|
77
77
|
self.build_parameters.includes = args.includes
|
|
78
78
|
self.build_parameters.additional_libs = args.additional_libs
|
|
79
|
+
self.build_parameters.add_hosts = args.add_hosts
|
|
80
|
+
self.build_parameters.input_data = args.input_data
|
|
79
81
|
|
|
80
82
|
models = Models()
|
|
81
83
|
platform = Platform(self._artifact_sources)
|
|
@@ -127,7 +129,9 @@ class PackagingArguments:
|
|
|
127
129
|
self.build_parameters.monai_deploy_app_sdk_version
|
|
128
130
|
)
|
|
129
131
|
|
|
130
|
-
self._package_manifest.platform_config =
|
|
132
|
+
self._package_manifest.platform_config = self._platforms[
|
|
133
|
+
0
|
|
134
|
+
].platform_config.value
|
|
131
135
|
|
|
132
136
|
def _read_application_config_file(self, config_file_path: Path):
|
|
133
137
|
self._logger.info(
|
|
@@ -27,7 +27,10 @@ from ..common.dockerutils import (
|
|
|
27
27
|
create_and_get_builder,
|
|
28
28
|
docker_export_tarball,
|
|
29
29
|
)
|
|
30
|
-
from ..common.exceptions import
|
|
30
|
+
from ..common.exceptions import (
|
|
31
|
+
IncompatiblePlatformConfigurationError,
|
|
32
|
+
WrongApplicationPathError,
|
|
33
|
+
)
|
|
31
34
|
from .parameters import PackageBuildParameters, PlatformBuildResults, PlatformParameters
|
|
32
35
|
|
|
33
36
|
|
|
@@ -58,6 +61,8 @@ class BuilderBase:
|
|
|
58
61
|
self._copy_model_files()
|
|
59
62
|
self._copy_docs()
|
|
60
63
|
self._copy_libs()
|
|
64
|
+
self._copy_input_data()
|
|
65
|
+
|
|
61
66
|
_ = self._write_dockerignore()
|
|
62
67
|
_ = self._copy_script()
|
|
63
68
|
|
|
@@ -88,7 +93,6 @@ class BuilderBase:
|
|
|
88
93
|
Returns:
|
|
89
94
|
PlatformBuildResults: build results
|
|
90
95
|
"""
|
|
91
|
-
self.print_build_info(platform_parameters)
|
|
92
96
|
builder = create_and_get_builder(Constants.LOCAL_BUILDX_BUILDER_NAME)
|
|
93
97
|
|
|
94
98
|
build_result = PlatformBuildResults(platform_parameters)
|
|
@@ -117,9 +121,15 @@ class BuilderBase:
|
|
|
117
121
|
"tags": [platform_parameters.tag],
|
|
118
122
|
}
|
|
119
123
|
|
|
124
|
+
if self._build_parameters.add_hosts:
|
|
125
|
+
builds["add_hosts"] = {}
|
|
126
|
+
for host in self._build_parameters.add_hosts:
|
|
127
|
+
host_name, host_ip = host.split(":")
|
|
128
|
+
builds["add_hosts"][host_name] = host_ip
|
|
129
|
+
|
|
120
130
|
export_to_tar_ball = False
|
|
121
131
|
if self._build_parameters.tarball_output is not None:
|
|
122
|
-
build_result.
|
|
132
|
+
build_result.tarball_filename = str(
|
|
123
133
|
self._build_parameters.tarball_output
|
|
124
134
|
/ f"{platform_parameters.tag}{Constants.TARBALL_FILE_EXTENSION}"
|
|
125
135
|
).replace(":", "-")
|
|
@@ -134,7 +144,7 @@ class BuilderBase:
|
|
|
134
144
|
builds["output"] = {
|
|
135
145
|
# type=oci cannot be loaded by docker: https://github.com/docker/buildx/issues/59
|
|
136
146
|
"type": "docker",
|
|
137
|
-
"dest": build_result.
|
|
147
|
+
"dest": build_result.tarball_filename,
|
|
138
148
|
}
|
|
139
149
|
else:
|
|
140
150
|
build_result.succeeded = False
|
|
@@ -155,21 +165,24 @@ class BuilderBase:
|
|
|
155
165
|
f"Building Holoscan Application Package: tag={platform_parameters.tag}"
|
|
156
166
|
)
|
|
157
167
|
|
|
168
|
+
self.print_build_info(platform_parameters)
|
|
169
|
+
|
|
158
170
|
try:
|
|
159
171
|
build_docker_image(**builds)
|
|
160
172
|
build_result.succeeded = True
|
|
161
173
|
if export_to_tar_ball:
|
|
162
174
|
try:
|
|
163
175
|
self._logger.info(
|
|
164
|
-
f"Saving {platform_parameters.tag} to {build_result.
|
|
176
|
+
f"Saving {platform_parameters.tag} to {build_result.tarball_filename}..."
|
|
165
177
|
)
|
|
166
178
|
docker_export_tarball(
|
|
167
|
-
build_result.
|
|
179
|
+
build_result.tarball_filename, platform_parameters.tag
|
|
168
180
|
)
|
|
169
181
|
except Exception as ex:
|
|
170
182
|
build_result.error = f"Error saving tarball: {ex}"
|
|
171
183
|
build_result.succeeded = False
|
|
172
|
-
except Exception:
|
|
184
|
+
except Exception as e:
|
|
185
|
+
print(e)
|
|
173
186
|
build_result.succeeded = False
|
|
174
187
|
build_result.error = (
|
|
175
188
|
"Error building image: see Docker output for additional details."
|
|
@@ -177,6 +190,13 @@ class BuilderBase:
|
|
|
177
190
|
|
|
178
191
|
return build_result
|
|
179
192
|
|
|
193
|
+
def _copy_input_data(self):
|
|
194
|
+
"""Copy input data to temporary location"""
|
|
195
|
+
if self._build_parameters.input_data is not None:
|
|
196
|
+
shutil.copytree(
|
|
197
|
+
self._build_parameters.input_data, os.path.join(self._temp_dir, "input")
|
|
198
|
+
)
|
|
199
|
+
|
|
180
200
|
def print_build_info(self, platform_parameters):
|
|
181
201
|
"""Print build information for the platform."""
|
|
182
202
|
self._logger.info(
|
|
@@ -186,6 +206,7 @@ Building image for: {platform_parameters.platform.value}
|
|
|
186
206
|
Architecture: {platform_parameters.platform_arch.value}
|
|
187
207
|
Base Image: {platform_parameters.base_image}
|
|
188
208
|
Build Image: {platform_parameters.build_image if platform_parameters.build_image is not None else "N/A"}
|
|
209
|
+
CUDA Version: {platform_parameters.cuda_version}
|
|
189
210
|
Cache: {'Disabled' if self._build_parameters.no_cache else 'Enabled'}
|
|
190
211
|
Configuration: {platform_parameters.platform_config.value}
|
|
191
212
|
Holoscan SDK Package: {platform_parameters.holoscan_sdk_file if platform_parameters.holoscan_sdk_file is not None else "N/A"}
|
|
@@ -337,7 +358,15 @@ Building image for: {platform_parameters.platform.value}
|
|
|
337
358
|
"""
|
|
338
359
|
)
|
|
339
360
|
|
|
340
|
-
|
|
361
|
+
if platform_parameters.cuda_version == 12:
|
|
362
|
+
jinja_template = jinja_env.get_template("Dockerfile-cu12.jinja2")
|
|
363
|
+
elif platform_parameters.cuda_version == 13:
|
|
364
|
+
jinja_template = jinja_env.get_template("Dockerfile.jinja2")
|
|
365
|
+
else:
|
|
366
|
+
raise IncompatiblePlatformConfigurationError(
|
|
367
|
+
f"Invalid CUDA version: {platform_parameters.cuda_version}"
|
|
368
|
+
)
|
|
369
|
+
|
|
341
370
|
return jinja_template.render(
|
|
342
371
|
{
|
|
343
372
|
**self._build_parameters.to_jinja,
|
|
@@ -377,18 +406,28 @@ class PythonAppBuilder(BuilderBase):
|
|
|
377
406
|
pip_folder = os.path.join(self._temp_dir, "pip")
|
|
378
407
|
os.makedirs(pip_folder, exist_ok=True)
|
|
379
408
|
pip_requirements_path = os.path.join(pip_folder, "requirements.txt")
|
|
409
|
+
|
|
410
|
+
# Build requirements content first
|
|
411
|
+
requirements_content = []
|
|
412
|
+
if self._build_parameters.requirements_file_path is not None:
|
|
413
|
+
with open(self._build_parameters.requirements_file_path) as lr:
|
|
414
|
+
requirements_content.extend(lr)
|
|
415
|
+
requirements_content.append("")
|
|
416
|
+
|
|
417
|
+
if self._build_parameters.pip_packages:
|
|
418
|
+
requirements_content.extend(self._build_parameters.pip_packages)
|
|
419
|
+
|
|
420
|
+
# Write all content at once
|
|
380
421
|
with open(pip_requirements_path, "w") as requirements_file:
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
"\n".join(self._build_parameters.pip_packages)
|
|
391
|
-
)
|
|
422
|
+
requirements_file.writelines(requirements_content)
|
|
423
|
+
self._logger.debug(
|
|
424
|
+
"================ Begin requirements.txt ================"
|
|
425
|
+
)
|
|
426
|
+
for req in requirements_content:
|
|
427
|
+
self._logger.debug(f" {req.strip()}")
|
|
428
|
+
self._logger.debug(
|
|
429
|
+
"================ End requirements.txt =================="
|
|
430
|
+
)
|
|
392
431
|
|
|
393
432
|
def _copy_sdk_file(self, sdk_file: Optional[Path]):
|
|
394
433
|
if sdk_file is not None and os.path.isfile(sdk_file):
|