1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 | # Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# pylint: disable=invalid-name, unused-import
"""Container recipe"""
from packaging.version import Version
import logging
import os
import sys
import traceback
import hpccm
import hpccm.config
from hpccm.common import container_type
from hpccm.Stage import Stage
from hpccm.building_blocks import *
from hpccm.primitives import *
def include(recipe_file, _globals=None, _locals=None, prepend_path=True,
raise_exceptions=False):
"""Include a recipe file
Args:
recipe_file: path to a recipe file (required)
_globals: a dictionary representing the global symbol table
_locals: a dictionary representing the local symbol table
prepend_path: If True, prepend the path of the main recipe to the
recipe_file. If the recipe_file is an absolute path, then the path
is not prepended regardless of the value of this parameter.
raise_exceptions: If False, do not print stack traces when an
exception is raised. The default value is False.
"""
if _locals is None:
# caller's locals
_locals = sys._getframe(1).f_locals
if _globals is None:
# caller's globals
_globals = sys._getframe(1).f_globals
# If a recipe file is included from another recipe file, some way
# is needed to find the included recipe if it specified using a
# relative path (relative to the including recipe file). Since
# recipe files are exec'ed, the value of __file__ is this file,
# not the recipe file. In order to make including recipes in other
# recipes using relative paths more intuitive, prepend the path of
# the base recipe file.
if (prepend_path and hasattr(include, 'prepend_path')
and not os.path.isabs(recipe_file)):
recipe_file = os.path.join(include.prepend_path, recipe_file)
try:
with open(recipe_file) as f:
# pylint: disable=exec-used
exec(compile(f.read(), recipe_file, 'exec'), _globals, _locals)
except Exception as e:
if raise_exceptions:
raise e
else:
traceback.print_exc()
exit(1)
def recipe(recipe_file, cpu_target=None, ctype=container_type.DOCKER,
raise_exceptions=False, single_stage=False,
singularity_version='2.6', userarg=None,
working_directory='/var/tmp',
singularity_tmp_fallback=True):
"""Recipe builder
Args:
recipe_file: path to a recipe file (required).
cpu_target: A CPU microarchitecture string recognized by archspec.
ctype: Enum representing the container specification format. The
default is `container_type.DOCKER`.
raise_exceptions: If False, do not print stack traces when an
exception is raised. The default value is False.
single_stage: If True, only print the first stage of a multi-stage
recipe. The default is False.
singularity_version: Version of the Singularity definition file
format to use. Multi-stage support was added in version 3.2, but
the changes are incompatible with earlier versions of Singularity.
The default is '2.6'.
userarg: A dictionary of key / value pairs provided to the recipe
as the `USERARG` dictionary.
working_directory: path to use as the working directory in the
container specification
singularity_tmp_fallback: If True (default), automatically handle
copy destinations under /tmp or /var/tmp using a %setup block when
targeting Singularity >= 3.6. If False, such copy operations are
rejected and will raise an error, requiring the user to modify the
recipe.
"""
# Make user arguments available
USERARG = {} # pylint: disable=unused-variable
if userarg:
USERARG = userarg # alias
# Consider just 2 stages for the time being
stages = [Stage(), Stage()]
Stage0 = stages[0] # alias # pylint: disable=unused-variable
Stage1 = stages[1] # alias # pylint: disable=unused-variable
# Set the CPU target
hpccm.config.set_cpu_target(cpu_target)
# Set the global container type
hpccm.config.g_ctype = ctype
# Set the global Singularity version
hpccm.config.g_singularity_version = Version(singularity_version)
# Set the global working directory
hpccm.config.g_wd = working_directory
# Set Singularity /tmp fallback behavior
hpccm.config.g_singularity_tmp_fallback = singularity_tmp_fallback
# Any included recipes that are specified using relative paths will
# need to prepend the path to the main recipe in order to be found.
# Save the path to the main recipe.
include.prepend_path = os.path.dirname(recipe_file)
# Load in the recipe file
include(recipe_file, _locals=locals(), _globals=globals(),
prepend_path=False, raise_exceptions=raise_exceptions)
# Only process the first stage of a recipe
if single_stage:
del stages[1:]
elif len(Stage1) > 0:
if (ctype == container_type.SINGULARITY and
hpccm.config.g_singularity_version < Version('3.2')):
# Singularity prior to version 3.2 did not support
# multi-stage builds. If the Singularity version is not
# sufficient to support multi-stage, provide advice to
# specify a sufficient Singularity version or disable
# multi-stage.
logging.warning('This looks like a multi-stage recipe. '
'Singularity 3.2 or later is required for '
'multi-stage builds. Use '
'--singularity-version=3.2 to enable this '
'feature or --single-stage to get rid of this '
'warning. Only processing the first stage...')
del stages[1:]
elif ctype == container_type.BASH:
logging.warning('This looks like a multi-stage recipe, but '
'bash does not support multi-stage builds. '
'Use --single-stage to get rid of this warning. '
'Only processing the first stage...')
del stages[1:]
r = []
for index, stage in enumerate(stages):
if index >= 1:
r.append('')
r.append(str(stage))
return '\n'.join(r)
|