-
Notifications
You must be signed in to change notification settings - Fork 0
/
builders.py
400 lines (336 loc) · 14.8 KB
/
builders.py
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
"""
builders.py
Builders definitions.
"""
import os
import tools
import config
import printer
import components
import xmlparser
class BuilderGenerator(object):
"""
BuilderGenerator class.
It generates the required Builder class type depending on
values obtained from the recipe file.
"""
def __init__(self, builder_name):
# Default values
self.builder_data_dict = {
"name": builder_name,
"env_PATH_value": "${UNSET_VARIABLE}",
"chapters_list": [],
"excludes": [],
"components_to_build": [],
"setenv_directory": config.BASE_DIRECTORY,
"setenv_filename": "setenv.sh",
"setenv_template": "setenv.tpl",
"book": "lfs",
"runscript_cmd": "env -i /bin/bash",
"base_module": "builders",
"base_builder": "ComponentsBuilder",
"sources_directory": os.path.join(config.BASE_DIRECTORY,
"sources"),
"tools_directory": os.path.join(config.BASE_DIRECTORY, "tools"),
"lfsbuilder_src_directory": os.path.dirname(
os.path.realpath(__file__)
),
"lfsbuilder_tmp_directory": os.path.join(
os.path.dirname(os.path.realpath(__file__)),
"tmp"),
"lfsbuilder_templates_directory": os.path.join(
os.path.dirname(os.path.realpath(__file__)),
"templates")
}
# Read the builder recipe and return a reference to the object type
self.builder_recipe_data = tools.read_recipe_file(
self.builder_data_dict["name"],
directory="builders")
# Add 'xml_commands_filename' to 'self.builder_data_dict'
# from 'config.py' file. default=None
getattr_data_value = self.builder_data_dict["name"].upper()
tools.add_to_dictionary(self.builder_data_dict,
"xml_commands_filename",
getattr(config,
"{b}_XML_FILENAME".format(
b=getattr_data_value),
None),
concat=False)
# Join dicts. 'self.builder_recipe_data' values will have preference
# over those currently in 'self.builder_data_dict' (defaults)
self.builder_data_dict = tools.join_dicts(self.builder_data_dict,
self.builder_recipe_data)
# Include '-x' parameter to the 'runscript_cmd'
# if 'config.DEBUG_SCRIPTS' is 'True'
if config.DEBUG_SCRIPTS is True:
value = "{c} -x".format(c=self.builder_data_dict["runscript_cmd"])
tools.add_to_dictionary(
self.builder_data_dict,
"runscript_cmd",
value,
concat=False
)
# Instantiate a ComponentsBuilder by default
self.class_fullname = "{m}.{t}".format(
m=self.builder_data_dict["base_module"],
t=self.builder_data_dict["base_builder"]
)
# Instantiate a 'InfrastructureComponentsBuilder' if required
if self.builder_data_dict["base_builder"].lower() == "componentsbuilder":
self.class_fullname = "{m}.{t}".format(
m=self.builder_data_dict["base_module"],
t="ComponentsBuilder"
)
elif self.builder_data_dict["base_builder"].lower() == "infrastructurecomponentsbuilder":
self.class_fullname = "{m}.{t}".format(
m=self.builder_data_dict["base_module"],
t="InfrastructureComponentsBuilder"
)
else:
text = "Unknown 'base_builder': '{b}'"
text = text.format(b=self.builder_data_dict["base_builder"])
printer.error(text)
# Create object
self.obj = tools.get_class(self.class_fullname)
def get_builder_reference(self):
"""
Return memory reference for the generate builder object.
"""
return self.obj(self.builder_data_dict)
class BaseBuilder(object):
"""
BaseBuilder class.
"""
def __init__(self, builder_data_dict):
self.builder_data_dict = tools.join_dicts({}, builder_data_dict)
# Current books names and available builders.
self.book_names = ["lfs", "blfs"]
self.lfs_book_builders = ["toolchain", "system", "configuration"]
self.disable_continue_at_builders = ["provider", "collector"]
# Get 'functions.py' if any
self.extra_functions = tools.read_functions_file(self.builder_data_dict["name"],
directory="builders")
# Generate data files if necessary
if config.GENERATE_DATA_FILES is True:
self.generate_data_files()
# Get 'components_to_build_list' if necessary
if hasattr(self.extra_functions, "get_components_to_build_list"):
self.extra_functions.get_components_to_build_list(self.builder_data_dict,
self.get_components_to_build_list)
else:
self.get_components_to_build_list()
def generate_data_files(self):
"""
Generate 'builder_data.xml' and 'builder_components_to_file.txt' files.
"""
if self.builder_data_dict["book"] in self.book_names:
# We have to parse book xmlfiles
xmlp = xmlparser.LFSXmlParser(self.builder_data_dict)
xmlp.generate_commands_xmlfile()
del xmlp
else:
pass
def get_components_to_build_list(self):
"""
Get 'components_to_build' list from book if necessary.
"""
if config.CUSTOM_COMPONENTS_TO_BUILD is False and \
self.builder_data_dict["name"] in self.lfs_book_builders:
# Get 'components_to_build' from book
self.index_filename = "{n}_components_to_build.txt"
self.index_filename = self.index_filename.format(n=self.builder_data_dict["name"])
self.index_filename_path = os.path.join(
self.builder_data_dict["lfsbuilder_tmp_directory"],
self.index_filename
)
# Update dictionary entry
tools.add_to_dictionary(
self.builder_data_dict,
"components_to_build",
tools.list_from_file(self.index_filename_path),
concat=False
)
# Set 'components_to_buil' to empty list if 'None'
if self.builder_data_dict["components_to_build"] is None:
tools.add_to_dictionary(
self.builder_data_dict,
"components_to_build",
[],
concat=False
)
# .- continue-at
if config.CONTINUE_AT is not None and \
self.builder_data_dict["name"] not in self.disable_continue_at_builders:
# .- Try to start from the 'continue-at' component
self.continue_at()
def continue_at(self):
"""
Start from the 'config.CONTINUE_AT' component of the first provided builder.
Fails in case this component do not exist.
"""
# .- is component present
if tools.is_element_present(self.builder_data_dict["components_to_build"],
config.CONTINUE_AT) is True:
# get component index and trim 'components_to_build' list
index = tools.get_element_index(self.builder_data_dict["components_to_build"],
config.CONTINUE_AT)
# trim list and update 'self.builder_data_dict' value
aux_list = self.builder_data_dict["components_to_build"][index:]
tools.add_to_dictionary(self.builder_data_dict,
"components_to_build",
aux_list,
concat=False)
# .- set 'config.CONTINUE_AT' to 'None' so we do not get into this method
# any more on the current execution
setattr(config, "CONTINUE_AT", None)
else:
text = """'continue-at' component '{c}' do not exists on the \
'components_to_build' list for the '{b}' builder""".format(c=config.CONTINUE_AT,
b=self.builder_data_dict["name"])
printer.error(text)
def do_nothing(self):
"""
Dummy function to maintain code structure. Does nothing.
"""
pass
def set_attributes(self):
"""
Set attributes after running the '__init__' method. Can be overwritted in the
'functions.py' file of the builder recipe directory to customize builder attributes.
If so, arguments are:
- 'builder_data_dict': current builder data, which can be accessed and modified.
- 'parent_function': parent method reference.
"""
if hasattr(self.extra_functions, "set_attributes"):
self.extra_functions.set_attributes(self.builder_data_dict,
self.do_nothing)
def build(self):
"""
Steps to build current 'builder'.
"""
pass
def create_setenv_script(self):
"""
Create the 'setenv.sh' file from the 'templates/setenv.tpl' template under the
lfsbuilder source directory.
"""
setenv_filename = os.path.join(self.builder_data_dict["setenv_directory"],
self.builder_data_dict["setenv_filename"])
template = os.path.join(self.builder_data_dict["lfsbuilder_templates_directory"],
self.builder_data_dict["setenv_template"])
substitution_list = ["@@LFS_BUILDER_NAME@@", self.builder_data_dict["name"],
"@@LFS_ENV_PATH_VALUE@@", self.builder_data_dict["env_PATH_value"],
"@@LFS_SOURCES_DIRECTORY@@",
self.builder_data_dict["sources_directory"],
"@@LFS_TOOLS_DIRECTROY@@", self.builder_data_dict["tools_directory"],
"@@LFSBUILDER_SRC_DIRECTORY@@",
self.builder_data_dict["lfsbuilder_src_directory"],
"@@LFS_BASE_DIRECTORY@@", config.BASE_DIRECTORY,
"@@LFS_MAKEFLAGS@@", config.MAKEFLAGS]
# Copy template
tools.copy_file(template, setenv_filename)
# Substitute parameters
tools.substitute_multiple_in_file(setenv_filename, substitution_list)
class BaseComponentsBuilder(BaseBuilder):
"""
BaseComponentsBuilder.
"""
def __init__(self, builder_data_dict):
BaseBuilder.__init__(self, builder_data_dict)
self.xml_components_data_dict = {}
self.component_data_dict = {}
def generate_xml_components_data_dict(self):
"""
Generates data files (commands, and components_to_build) for the current builder.
"""
self.xml_components_data_dict = {}
if self.builder_data_dict["book"] == "lfs" or \
self.builder_data_dict["book"] == "blfs":
lfsxml = xmlparser.LFSXmlParser(self.builder_data_dict)
self.xml_components_data_dict = lfsxml.generate_dict_from_xmlfile(
self.builder_data_dict["xml_commands_filename"])
del lfsxml
else:
pass
def build_components(self):
"""
Stepts to build components of the 'components_to_build' list attribute.
"""
# Create setenv.sh file
self.create_setenv_script()
# Generate components_data_dict from XML file
self.generate_xml_components_data_dict()
# Build componentsList
i = 1
for component in self.builder_data_dict["components_to_build"]:
msg = "[{i}/{n}] Building component '{c}'"
msg = msg.format(
i=i,
n=len(self.builder_data_dict["components_to_build"]),
c=component
)
printer.info(msg)
os.chdir(self.builder_data_dict["lfsbuilder_src_directory"])
# Get component to build reference
cg = components.ComponentGenerator(component,
self.builder_data_dict,
self.xml_components_data_dict)
o = cg.get_component_reference()
# Remove reference to the 'ComponentGenerator'. It is useless
del cg
# Build the real component
o.set_attributes()
os.chdir(self.builder_data_dict["sources_directory"])
o.build()
o.clean_workspace()
del o
# Update index
i += 1
def build(self):
"""
Steps to build current 'builder'.
"""
if hasattr(self.extra_functions, "build"):
self.extra_functions.build(self.builder_data_dict,
self.build_components)
else:
# Call parent function directly
self.build_components()
def clean_workspace(self):
"""
Clean used workspace to build current builder.
"""
# Remove 'setenv.sh' file
filepath = (
os.path.join(
self.builder_data_dict["setenv_directory"],
self.builder_data_dict["setenv_filename"]
)
)
if os.path.exists(filepath):
os.remove(filepath)
class InfrastructureComponentsBuilder(BaseComponentsBuilder):
"""
InfrastructureComponentsBuilder class.
Build components that configure the building environment for the rest of the components,
e.g. create the image file, mount the 'sources' directories...
"""
def __init__(self, builder_data_dict):
BaseComponentsBuilder.__init__(self, builder_data_dict)
# Build components from the 'lfs_src_directory/tmp' directory
tools.add_to_dictionary(self.builder_data_dict,
"setenv_directory",
builder_data_dict["lfsbuilder_tmp_directory"],
concat=False)
tools.add_to_dictionary(self.builder_data_dict,
"sources_directory",
builder_data_dict["lfsbuilder_tmp_directory"],
concat=False)
class ComponentsBuilder(BaseComponentsBuilder):
"""
ComponentsBuilder class.
Build components into the final system according to the values of both the 'component' recipe
and book commands if necessary.
"""
def __init__(self, builder_data_dict):
BaseComponentsBuilder.__init__(self, builder_data_dict)