Skip to content

Latest commit

 

History

History
423 lines (347 loc) · 14.5 KB

ssukickstarter.cpp

File metadata and controls

423 lines (347 loc) · 14.5 KB
 
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @file ssukickstarter.cpp
* @copyright 2013 Jolla Ltd.
* @author Bernd Wachter <bwachter@lart.info>
* @date 2013
*/
#include <QStringList>
#include <QRegExp>
#include <QDirIterator>
#include "ssukickstarter.h"
May 24, 2013
May 24, 2013
13
#include "libssu/sandbox_p.h"
Apr 5, 2013
Apr 5, 2013
14
#include "libssu/ssurepomanager.h"
Aug 21, 2015
Aug 21, 2015
15
#include "libssu/ssuvariables_p.h"
16
17
18
19
20
21
22
23
#include "../constants.h"
/* TODO:
* - commands from the command section should be verified
*/
Oct 10, 2016
Oct 10, 2016
24
25
26
27
28
29
30
31
32
SsuKickstarter::SsuKickstarter()
{
SsuDeviceInfo deviceInfo;
deviceModel = deviceInfo.deviceModel();
if ((ssu.deviceMode() & Ssu::RndMode) == Ssu::RndMode)
rndMode = true;
else
rndMode = false;
33
34
}
Oct 10, 2016
Oct 10, 2016
35
36
37
38
QStringList SsuKickstarter::commands()
{
SsuDeviceInfo deviceInfo(deviceModel);
QStringList result;
Oct 10, 2016
Oct 10, 2016
40
QHash<QString, QString> h;
Apr 5, 2013
Apr 5, 2013
41
Oct 10, 2016
Oct 10, 2016
42
deviceInfo.variableSection("kickstart-commands", &h);
Oct 10, 2016
Oct 10, 2016
44
// read commands from variable, ...
Oct 10, 2016
Oct 10, 2016
46
47
48
49
50
QHash<QString, QString>::const_iterator it = h.constBegin();
while (it != h.constEnd()) {
result.append(it.key() + " " + it.value());
it++;
}
Oct 10, 2016
Oct 10, 2016
52
return result;
53
54
}
Oct 10, 2016
Oct 10, 2016
55
56
57
58
59
60
61
62
63
64
65
QStringList SsuKickstarter::commandSection(const QString &section, const QString &description)
{
QStringList result;
SsuDeviceInfo deviceInfo(deviceModel);
QString commandFile;
QFile part;
QDir dir(Sandbox::map(QString("/%1/kickstart/%2/")
.arg(SSU_DATA_DIR)
.arg(section)));
May 24, 2017
May 24, 2017
66
if (dir.exists(replaceSpaces(deviceModel.toLower()))) {
Oct 10, 2016
Oct 10, 2016
67
commandFile = replaceSpaces(deviceModel.toLower());
May 24, 2017
May 24, 2017
68
} else if (dir.exists(replaceSpaces(deviceInfo.deviceVariant(true).toLower()))) {
Oct 10, 2016
Oct 10, 2016
69
commandFile = replaceSpaces(deviceInfo.deviceVariant(true).toLower());
May 24, 2017
May 24, 2017
70
} else if (dir.exists("default")) {
Oct 10, 2016
Oct 10, 2016
71
commandFile = "default";
May 24, 2017
May 24, 2017
72
} else {
Oct 10, 2016
Oct 10, 2016
73
74
75
76
77
78
if (description.isEmpty())
result.append("## No suitable configuration found in " + dir.path());
else
result.append("## No configuration for " + description + " found.");
return result;
}
Jun 13, 2013
Jun 13, 2013
79
Oct 10, 2016
Oct 10, 2016
80
QFile file(dir.path() + "/" + commandFile);
Jun 13, 2013
Jun 13, 2013
81
Oct 10, 2016
Oct 10, 2016
82
83
84
85
if (description.isEmpty())
result.append("### Commands from " + dir.path() + "/" + commandFile);
else
result.append("### " + description + " from " + commandFile);
Jun 13, 2013
Jun 13, 2013
86
Oct 10, 2016
Oct 10, 2016
87
88
89
90
91
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
while (!in.atEnd())
result.append(in.readLine());
}
Jun 13, 2013
Jun 13, 2013
92
Oct 10, 2016
Oct 10, 2016
93
return result;
Jun 13, 2013
Jun 13, 2013
94
95
}
Oct 10, 2016
Oct 10, 2016
96
97
98
99
QString SsuKickstarter::replaceSpaces(const QString &value)
{
QString retval = value;
return retval.replace(" ", "_");
Jun 13, 2013
Jun 13, 2013
100
101
}
Oct 10, 2016
Oct 10, 2016
102
103
104
105
106
107
108
109
110
111
112
113
QStringList SsuKickstarter::repos()
{
QStringList result;
SsuDeviceInfo deviceInfo(deviceModel);
SsuRepoManager repoManager;
QTextStream qerr(stderr);
QStringList repos = repoManager.repos(rndMode, deviceInfo, Ssu::BoardFilter);
foreach (const QString &repo, repos) {
QString repoUrl = ssu.repoUrl(repo, rndMode, QHash<QString, QString>(), repoOverride);
May 24, 2017
May 24, 2017
114
if (repoUrl.isEmpty()) {
Oct 10, 2016
Oct 10, 2016
115
116
117
118
119
120
121
122
123
124
125
qerr << "Repository " << repo << " does not have an URL, ignoring" << endl;
continue;
}
// Adaptation repos need to have separate naming so that when images are done
// the repository caches will not be mixed with each other.
if (repo.startsWith("adaptation")) {
result.append(QString("repo --name=%1-%2-%3%4 --baseurl=%5")
.arg(repo)
.arg(replaceSpaces(deviceModel))
.arg((rndMode ? repoOverride.value("rndRelease")
May 24, 2017
May 24, 2017
126
: repoOverride.value("release")))
Oct 10, 2016
Oct 10, 2016
127
.arg((rndMode ? "-" + repoOverride.value("flavourName")
May 24, 2017
May 24, 2017
128
: QString()))
Oct 10, 2016
Oct 10, 2016
129
130
.arg(repoUrl)
);
May 24, 2017
May 24, 2017
131
} else {
Oct 10, 2016
Oct 10, 2016
132
133
134
result.append(QString("repo --name=%1-%2%3 --baseurl=%4")
.arg(repo)
.arg((rndMode ? repoOverride.value("rndRelease")
May 24, 2017
May 24, 2017
135
: repoOverride.value("release")))
Oct 10, 2016
Oct 10, 2016
136
.arg((rndMode ? "-" + repoOverride.value("flavourName")
May 24, 2017
May 24, 2017
137
: QString()))
Oct 10, 2016
Oct 10, 2016
138
139
.arg(repoUrl)
);
May 24, 2017
May 24, 2017
140
}
Oct 10, 2016
Oct 10, 2016
141
}
142
Oct 10, 2016
Oct 10, 2016
143
144
return result;
}
145
May 24, 2017
May 24, 2017
146
QStringList SsuKickstarter::packagesSection(const QString &name)
Oct 10, 2016
Oct 10, 2016
147
148
{
QStringList result;
Oct 6, 2013
Oct 6, 2013
149
Oct 10, 2016
Oct 10, 2016
150
151
152
153
154
155
if (name == "packages") {
// insert @vendor configuration device
QString configuration = QString("@%1 Configuration %2")
.arg(repoOverride.value("brand"))
.arg(deviceModel);
result.append(configuration);
Oct 6, 2013
Oct 6, 2013
156
Oct 10, 2016
Oct 10, 2016
157
158
159
160
result.sort();
result.removeDuplicates();
} else {
result = commandSection(name);
Jun 12, 2013
Jun 12, 2013
161
}
162
Oct 10, 2016
Oct 10, 2016
163
164
165
result.prepend("%" + name);
result.append("%end");
return result;
166
167
168
}
// we intentionally don't support device-specific post scriptlets
May 24, 2017
May 24, 2017
169
QStringList SsuKickstarter::scriptletSection(const QString &name, int flags)
Oct 10, 2016
Oct 10, 2016
170
171
172
173
174
175
176
177
178
{
QStringList result;
QString path;
QDir dir;
if ((flags & NoChroot) == NoChroot)
path = Sandbox::map(QString("/%1/kickstart/%2_nochroot/")
.arg(SSU_DATA_DIR)
.arg(name));
Aug 30, 2013
Aug 30, 2013
179
else
Oct 10, 2016
Oct 10, 2016
180
181
182
183
184
185
186
187
188
path = Sandbox::map(QString("/%1/kickstart/%2/")
.arg(SSU_DATA_DIR)
.arg(name));
if ((flags & DeviceSpecific) == DeviceSpecific) {
if (dir.exists(path + "/" + replaceSpaces(deviceModel.toLower())))
path = path + "/" + replaceSpaces(deviceModel.toLower());
else
path = path + "/default";
189
190
}
Oct 10, 2016
Oct 10, 2016
191
192
193
194
195
196
197
198
199
200
201
202
203
204
dir.setPath(path);
QStringList scriptlets = dir.entryList(QDir::AllEntries | QDir::NoDot | QDir::NoDotDot,
QDir::Name);
foreach (const QString &scriptlet, scriptlets) {
QFile file(dir.filePath(scriptlet));
result.append("### begin " + scriptlet);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
while (!in.atEnd())
result.append(in.readLine());
}
result.append("### end " + scriptlet);
}
Apr 2, 2013
Apr 2, 2013
205
Oct 10, 2016
Oct 10, 2016
206
207
208
if (!result.isEmpty()) {
result.prepend(QString("export SSU_RELEASE_TYPE=%1")
.arg(rndMode ? "rnd" : "release"));
Aug 30, 2013
Aug 30, 2013
209
Oct 10, 2016
Oct 10, 2016
210
211
212
213
214
215
216
if ((flags & NoChroot) == NoChroot)
result.prepend("%" + name + " --nochroot");
else
result.prepend("%" + name);
result.append("%end");
}
217
Oct 10, 2016
Oct 10, 2016
218
return result;
219
220
}
Oct 10, 2016
Oct 10, 2016
221
222
223
void SsuKickstarter::setRepoParameters(QHash<QString, QString> parameters)
{
repoOverride = parameters;
224
Oct 10, 2016
Oct 10, 2016
225
226
if (repoOverride.contains("model"))
deviceModel = repoOverride.value("model");
227
228
}
May 24, 2017
May 24, 2017
229
bool SsuKickstarter::write(const QString &kickstart)
Oct 10, 2016
Oct 10, 2016
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
{
QTextStream qerr(stderr);
QStringList commandSections;
// initialize with default 'part' for compatibility, as partitions
// used to work without configuration. It'll get replaced with
// configuration values, if found
commandSections.append("part");
// rnd mode should not come from the defaults
if (repoOverride.contains("rnd")) {
if (repoOverride.value("rnd") == "true")
rndMode = true;
else if (repoOverride.value("rnd") == "false")
rndMode = false;
Apr 5, 2013
Apr 5, 2013
245
}
Oct 10, 2016
Oct 10, 2016
246
247
248
249
QHash<QString, QString> defaults;
// get generic repo variables; domain and adaptation specific bits are not interesting
// in the kickstart
May 24, 2017
May 24, 2017
250
SsuRepoManager repoManager;
Oct 10, 2016
Oct 10, 2016
251
252
253
repoManager.repoVariables(&defaults, rndMode);
// overwrite with kickstart defaults
May 24, 2017
May 24, 2017
254
SsuDeviceInfo deviceInfo(deviceModel);
Oct 10, 2016
Oct 10, 2016
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
deviceInfo.variableSection("kickstart-defaults", &defaults);
if (deviceInfo.variable("kickstart-defaults", "commandSections")
.canConvert(QMetaType::QStringList)) {
commandSections =
deviceInfo.variable("kickstart-defaults", "commandSections").toStringList();
}
QHash<QString, QString>::const_iterator it = defaults.constBegin();
while (it != defaults.constEnd()) {
if (!repoOverride.contains(it.key()))
repoOverride.insert(it.key(), it.value());
it++;
}
// in rnd mode both rndRelease an release should be the same,
// as the variable name used is %(release)
if (rndMode && repoOverride.contains("rndRelease"))
repoOverride.insert("release", repoOverride.value("rndRelease"));
// release mode variables should not contain flavourName
if (!rndMode && repoOverride.contains("flavourName"))
repoOverride.remove("flavourName");
Apr 18, 2019
Apr 18, 2019
278
//TODO: check for mandatory keys, ..
Oct 10, 2016
Oct 10, 2016
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
if (!repoOverride.contains("deviceModel"))
repoOverride.insert("deviceModel", deviceInfo.deviceModel());
// do sanity checking on the model
if (deviceInfo.contains() == false) {
qerr << "Device model '" << deviceInfo.deviceModel() << "' does not exist" << endl;
if (repoOverride.value("force") != "true")
return false;
}
QRegExp regex(" {2,}", Qt::CaseSensitive, QRegExp::RegExp2);
if (regex.indexIn(deviceInfo.deviceModel(), 0) != -1) {
qerr << "Device model '" << deviceInfo.deviceModel()
<< "' contains multiple consecutive spaces." << endl;
if (deviceInfo.contains())
qerr << "Since the model exists it looks like your configuration is broken." << endl;
return false;
Jan 1, 2015
Jan 1, 2015
297
}
Oct 10, 2016
Oct 10, 2016
298
299
if (!repoOverride.contains("brand")) {
Apr 18, 2019
Apr 18, 2019
300
qerr << "Brand is missing in your configuration." << endl;
Oct 10, 2016
Oct 10, 2016
301
302
303
304
305
306
307
return false;
}
bool opened = false;
QString outputDir = repoOverride.value("outputdir");
if (!outputDir.isEmpty()) outputDir.append("/");
May 24, 2017
May 24, 2017
308
309
QFile ks;
Oct 10, 2016
Oct 10, 2016
310
if (kickstart.isEmpty()) {
May 24, 2017
May 24, 2017
311
312
SsuVariables var;
Oct 10, 2016
Oct 10, 2016
313
314
315
316
317
318
319
320
321
322
323
324
if (repoOverride.contains("filename")) {
QString fileName = QString("%1%2")
.arg(outputDir)
.arg(replaceSpaces(var.resolveString(repoOverride.value("filename"),
&repoOverride)));
ks.setFileName(fileName);
opened = ks.open(QIODevice::WriteOnly);
} else {
qerr << "No filename specified, and no default filename configured" << endl;
return false;
}
May 24, 2017
May 24, 2017
325
} else if (kickstart == "-") {
Oct 10, 2016
Oct 10, 2016
326
opened = ks.open(stdout, QIODevice::WriteOnly);
May 24, 2017
May 24, 2017
327
} else {
Oct 10, 2016
Oct 10, 2016
328
329
330
331
332
333
334
ks.setFileName(outputDir + kickstart);
opened = ks.open(QIODevice::WriteOnly);
}
if (!opened) {
qerr << "Unable to write output file " << ks.fileName() << ": " << ks.errorString() << endl;
return false;
May 24, 2017
May 24, 2017
335
} else if (!ks.fileName().isEmpty()) {
Oct 10, 2016
Oct 10, 2016
336
qerr << "Writing kickstart to " << ks.fileName() << endl;
May 24, 2017
May 24, 2017
337
}
Oct 10, 2016
Oct 10, 2016
338
339
340
341
342
343
344
345
346
QString displayName = QString("# DisplayName: %1 %2/%3 (%4) %5")
.arg(repoOverride.value("brand"))
.arg(deviceInfo.deviceModel())
.arg(repoOverride.value("arch"))
.arg((rndMode ? "rnd"
: "release"))
.arg(repoOverride.value("version"));
Mar 15, 2018
Mar 15, 2018
347
// Feature names can be prefixed with '-' to inhibit implicit suggestion
Oct 10, 2016
Oct 10, 2016
348
349
QStringList featuresList = deviceInfo.value("img-features").toStringList();
Sep 30, 2017
Sep 30, 2017
350
// Add developer-mode feature to rnd images by default
Mar 15, 2018
Mar 15, 2018
351
if (rndMode && !featuresList.contains("-developer-mode"))
Sep 30, 2017
Sep 30, 2017
352
353
featuresList << "developer-mode";
Mar 15, 2018
Mar 15, 2018
354
355
featuresList = featuresList.filter(QRegExp("^[^-]"));
Oct 10, 2016
Oct 10, 2016
356
357
358
359
360
QString suggestedFeatures;
// work around some idiotic JS list parsing on our side by terminating one-element list by comma
if (featuresList.count() == 1)
suggestedFeatures = QString("# SuggestedFeatures: %1,")
Sep 30, 2017
Sep 30, 2017
361
.arg(featuresList.join(", "));
Oct 10, 2016
Oct 10, 2016
362
363
else if (featuresList.count() > 1)
suggestedFeatures = QString("# SuggestedFeatures: %1")
Sep 30, 2017
Sep 30, 2017
364
.arg(featuresList.join(", "));
Oct 10, 2016
Oct 10, 2016
365
366
367
368
369
370
371
372
373
374
375
376
QString imageType = "fs";
if (!deviceInfo.value("img-type").toString().isEmpty())
imageType = deviceInfo.value("img-type").toString();
QString imageArchitecture = "armv7hl";
if (!deviceInfo.value("img-arch").toString().isEmpty())
imageArchitecture = deviceInfo.value("img-arch").toString();
QString kickstartType = QString("# KickstartType: %1")
.arg((rndMode ? "rnd" : "release"));
May 24, 2017
May 24, 2017
377
QTextStream kout;
Oct 10, 2016
Oct 10, 2016
378
379
380
kout.setDevice(&ks);
kout << displayName << endl;
kout << kickstartType << endl;
Sep 6, 2017
Sep 6, 2017
381
kout << "# DeviceModel: " << deviceInfo.deviceModel() << endl;
Sep 7, 2017
Sep 7, 2017
382
kout << "# DeviceVariant: " << deviceInfo.deviceVariant(true) << endl;
Apr 18, 2019
Apr 18, 2019
383
kout << "# Brand: " << repoOverride.value("brand") << endl;
Oct 10, 2016
Oct 10, 2016
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
if (!suggestedFeatures.isEmpty())
kout << suggestedFeatures << endl;
kout << "# SuggestedImageType: " << imageType << endl;
kout << "# SuggestedArchitecture: " << imageArchitecture << endl << endl;
kout << commands().join("\n") << endl << endl;
foreach (const QString &section, commandSections) {
kout << commandSection(section).join("\n") << endl << endl;
}
// this allows simple search and replace postprocessing of the repos section
// to overcome shortcomings of the "keep image creation simple token based"
// approach
// TODO: QHash looks messy in the config, provide tool to edit it
QString repoSection = repos().join("\n");
if (deviceInfo.variable("kickstart-defaults", "urlPostprocess")
.canConvert(QMetaType::QVariantHash)) {
QHash<QString, QVariant> postproc =
deviceInfo.variable("kickstart-defaults", "urlPostprocess").toHash();
QHash<QString, QVariant>::const_iterator it = postproc.constBegin();
while (it != postproc.constEnd()) {
QRegExp regex(it.key(), Qt::CaseSensitive, QRegExp::RegExp2);
repoSection.replace(regex, it.value().toString());
it++;
}
}
kout << repoSection << endl << endl;
kout << packagesSection("packages").join("\n") << endl << endl;
kout << packagesSection("attachment").join("\n") << endl << endl;
// TODO: now that extending scriptlet section is might make sense to make it configurable
kout << scriptletSection("pre", Chroot).join("\n") << endl << endl;
kout << scriptletSection("post", Chroot).join("\n") << endl << endl;
kout << scriptletSection("post", NoChroot).join("\n") << endl << endl;
kout << scriptletSection("pack", DeviceSpecific).join("\n") << endl << endl;
// POST, die-on-error
return true;
423
}