/
ssukickstarter.cpp
336 lines (277 loc) · 10.4 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"
13
#include "libssu/sandbox_p.h"
14
15
#include "libssu/ssurepomanager.h"
#include "libssu/ssuvariables.h"
16
17
18
19
20
21
22
23
24
#include "../constants.h"
/* TODO:
* - commands from the command section should be verified
* - allow overriding brand key
*/
25
SsuKickstarter::SsuKickstarter() {
26
27
28
29
30
31
32
33
34
35
36
37
38
SsuDeviceInfo deviceInfo;
deviceModel = deviceInfo.deviceModel();
if ((ssu.deviceMode() & Ssu::RndMode) == Ssu::RndMode)
rndMode = true;
else
rndMode = false;
}
QStringList SsuKickstarter::commands(){
SsuDeviceInfo deviceInfo(deviceModel);
QStringList result;
39
40
41
QHash<QString, QString> h;
deviceInfo.variableSection("kickstart-commands", &h);
42
43
44
45
46
47
48
49
50
51
52
53
// read commands from variable, ...
QHash<QString, QString>::const_iterator it = h.constBegin();
while (it != h.constEnd()){
result.append(it.key() + " " + it.value());
it++;
}
return result;
}
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
QStringList SsuKickstarter::commandSection(const QString §ion, 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)));
if (dir.exists(replaceSpaces(deviceModel.toLower())))
commandFile = replaceSpaces(deviceModel.toLower());
else if (dir.exists(replaceSpaces(deviceInfo.deviceVariant(true).toLower())))
commandFile = replaceSpaces(deviceInfo.deviceVariant(true).toLower());
else if (dir.exists("default"))
commandFile = "default";
else {
if (description.isEmpty())
result.append("## No suitable configuration found in " + dir.path());
else
result.append("## No configuration for " + description + " found.");
return result;
}
QFile file(dir.path() + "/" + commandFile);
if (description.isEmpty())
result.append("### Commands from " + dir.path() + "/" + commandFile);
else
result.append("### " + description + " from " + commandFile);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)){
QTextStream in(&file);
while (!in.atEnd())
result.append(in.readLine());
}
return result;
}
QString SsuKickstarter::replaceSpaces(const QString &value){
QString retval = value;
return retval.replace(" ", "_");
}
99
100
101
102
QStringList SsuKickstarter::repos(){
QStringList result;
SsuDeviceInfo deviceInfo(deviceModel);
103
QStringList repos = deviceInfo.repos(rndMode, SsuRepoManager::BoardFilter);
104
105
106
foreach (const QString &repo, repos){
QString repoUrl = ssu.repoUrl(repo, rndMode, QHash<QString, QString>(), repoOverride);
107
108
109
// 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")) {
110
111
result.append(QString("repo --name=%1-%2-%3 --baseurl=%4")
.arg(repo)
112
.arg(replaceSpaces(deviceModel))
113
114
115
116
.arg((rndMode ? repoOverride.value("rndRelease")
: repoOverride.value("release")))
.arg(repoUrl)
);
117
}
118
119
120
121
122
123
124
else
result.append(QString("repo --name=%1-%2 --baseurl=%3")
.arg(repo)
.arg((rndMode ? repoOverride.value("rndRelease")
: repoOverride.value("release")))
.arg(repoUrl)
);
125
126
127
128
129
130
131
132
133
134
}
return result;
}
QStringList SsuKickstarter::packages(){
QStringList result;
// insert @vendor configuration device
QString configuration = QString("@%1 Configuration %2")
135
.arg(repoOverride.value("brand"))
136
137
138
139
140
141
142
143
144
145
146
.arg(deviceModel);
result.append(configuration);
result.sort();
result.removeDuplicates();
result.prepend("%packages");
result.append("%end");
return result;
}
// we intentionally don't support device-specific post scriptlets
147
QStringList SsuKickstarter::scriptletSection(QString name, int flags){
148
149
150
151
QStringList result;
QString path;
QDir dir;
152
153
if ((flags & NoChroot) == NoChroot)
path = Sandbox::map(QString("/%1/kickstart/%2_nochroot/")
154
.arg(SSU_DATA_DIR)
155
.arg(name));
156
else
157
path = Sandbox::map(QString("/%1/kickstart/%2/")
158
.arg(SSU_DATA_DIR)
159
.arg(name));
160
161
162
163
164
165
166
167
if ((flags & DeviceSpecific) == DeviceSpecific){
if (dir.exists(path + "/" + replaceSpaces(deviceModel.toLower())))
path = path + "/" + replaceSpaces(deviceModel.toLower());
else
path = path + "/default";
}
168
169
170
171
172
dir.setPath(path);
QStringList scriptlets = dir.entryList(QDir::AllEntries|QDir::NoDot|QDir::NoDotDot,
QDir::Name);
foreach (const QString &scriptlet, scriptlets){
173
QFile file(dir.filePath(scriptlet));
174
175
176
177
178
179
180
181
182
183
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);
}
if (!result.isEmpty()){
184
185
186
result.prepend(QString("export SSU_RELEASE_TYPE=%1")
.arg(rndMode ? "rnd" : "release"));
187
if ((flags & NoChroot) == NoChroot)
188
result.prepend("%" + name + " --nochroot");
189
190
191
else
result.prepend("%" + name);
192
193
194
195
196
197
198
199
200
201
202
203
204
result.append("%end");
}
return result;
}
void SsuKickstarter::setRepoParameters(QHash<QString, QString> parameters){
repoOverride = parameters;
if (repoOverride.contains("model"))
deviceModel = repoOverride.value("model");
}
205
bool SsuKickstarter::write(QString kickstart){
206
207
QFile ks;
QTextStream kout;
208
209
210
211
QTextStream qerr(stderr);
SsuDeviceInfo deviceInfo(deviceModel);
SsuRepoManager repoManager;
SsuVariables var;
212
213
214
215
216
217
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");
218
219
220
221
222
223
224
// 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;
225
226
}
227
228
229
230
231
232
233
QHash<QString, QString> defaults;
// get generic repo variables; domain and adaptation specific bits are not interesting
// in the kickstart
repoManager.repoVariables(&defaults, rndMode);
// overwrite with kickstart defaults
deviceInfo.variableSection("kickstart-defaults", &defaults);
234
235
236
237
238
if (deviceInfo.variable("kickstart-defaults", "commandSections")
.canConvert(QMetaType::QStringList)){
commandSections =
deviceInfo.variable("kickstart-defaults", "commandSections").toStringList();
}
239
240
241
242
243
244
245
246
QHash<QString, QString>::const_iterator it = defaults.constBegin();
while (it != defaults.constEnd()){
if (!repoOverride.contains(it.key()))
repoOverride.insert(it.key(), it.value());
it++;
}
247
248
249
250
251
// 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"));
252
253
254
255
256
257
258
259
// release mode variables should not contain flavourName
if (!rndMode && repoOverride.contains("flavourName"))
repoOverride.remove("flavourName");
//TODO: check for mandatory keys, brand, ..
if (!repoOverride.contains("deviceModel"))
repoOverride.insert("deviceModel", deviceInfo.deviceModel());
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
// 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;
}
277
278
279
280
281
if (!repoOverride.contains("brand")){
qerr << "No brand set. Check your configuration." << endl;
return false;
}
282
283
284
285
bool opened = false;
QString outputDir = repoOverride.value("outputdir");
if (!outputDir.isEmpty()) outputDir.append("/");
286
287
if (kickstart.isEmpty()){
if (repoOverride.contains("filename")){
288
QString fileName = QString("%1%2")
289
290
291
.arg(outputDir)
.arg(replaceSpaces(var.resolveString(repoOverride.value("filename"),
&repoOverride)));
292
293
ks.setFileName(fileName);
294
opened = ks.open(QIODevice::WriteOnly);
295
296
297
298
299
} else {
qerr << "No filename specified, and no default filename configured" << endl;
return false;
}
} else if (kickstart == "-")
300
opened = ks.open(stdout, QIODevice::WriteOnly);
301
else {
302
303
304
305
306
307
308
ks.setFileName(outputDir + kickstart);
opened = ks.open(QIODevice::WriteOnly);
}
if (!opened) {
qerr << "Unable to write output file " << ks.fileName() << ": " << ks.errorString() << endl;
return false;
309
310
} else if (!ks.fileName().isEmpty())
qerr << "Writing kickstart to " << ks.fileName() << endl;
311
312
313
314
315
316
317
318
319
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"));
320
kout.setDevice(&ks);
321
kout << displayName << endl << endl;
322
kout << commands().join("\n") << endl << endl;
323
324
325
foreach (const QString §ion, commandSections){
kout << commandSection(section).join("\n") << endl << endl;
}
326
327
kout << repos().join("\n") << endl << endl;
kout << packages().join("\n") << endl << endl;
328
329
330
331
332
// 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;
333
// POST, die-on-error
334
335
return true;
336
}