10#include <QDirIterator>
24SsuKickstarter::SsuKickstarter()
26 SsuDeviceInfo deviceInfo;
35QStringList SsuKickstarter::commands()
37 SsuDeviceInfo deviceInfo(deviceModel);
40 QHash<QString, QString> h;
46 QHash<QString, QString>::const_iterator it = h.constBegin();
47 while (it != h.constEnd()) {
48 result.append(it.key() +
" " + it.value());
55QStringList SsuKickstarter::commandSection(
const QString §ion,
const QString &description)
58 SsuDeviceInfo deviceInfo(deviceModel);
62 QDir dir(Sandbox::map(QString(
"/%1/kickstart/%2/")
66 if (dir.exists(replaceSpaces(deviceModel.toLower()))) {
67 commandFile = replaceSpaces(deviceModel.toLower());
68 }
else if (dir.exists(replaceSpaces(deviceInfo.
deviceVariant(
true).toLower()))) {
69 commandFile = replaceSpaces(deviceInfo.
deviceVariant(
true).toLower());
70 }
else if (dir.exists(
"default")) {
71 commandFile =
"default";
73 if (description.isEmpty())
74 result.append(
"## No suitable configuration found in " + dir.path());
76 result.append(
"## No configuration for " + description +
" found.");
80 QFile file(dir.path() +
"/" + commandFile);
82 if (description.isEmpty())
83 result.append(
"### Commands from " + dir.path() +
"/" + commandFile);
85 result.append(
"### " + description +
" from " + commandFile);
87 if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
88 QTextStream in(&file);
90 result.append(in.readLine());
96QString SsuKickstarter::replaceSpaces(
const QString &value)
98 QString retval = value;
99 return retval.replace(
" ",
"_");
102QStringList SsuKickstarter::repos()
105 SsuDeviceInfo deviceInfo(deviceModel);
106 SsuRepoManager repoManager;
107 QTextStream qerr(stderr);
111 foreach (
const QString &repo, repos) {
112 QString repoUrl = ssu.repoUrl(repo, rndMode, QHash<QString, QString>(), repoOverride);
114 if (repoUrl.isEmpty()) {
115 qerr <<
"Repository " << repo <<
" does not have an URL, ignoring" << endl;
121 if (repo.startsWith(
"adaptation")) {
122 result.append(QString(
"repo --name=%1-%2-%3%4 --baseurl=%5")
124 .arg(replaceSpaces(deviceModel))
125 .arg(repoOverride.value(
"release"))
126 .arg((rndMode ?
"-" + repoOverride.value(
"flavour")
131 result.append(QString(
"repo --name=%1-%2%3 --baseurl=%4")
133 .arg(repoOverride.value(
"release"))
134 .arg((rndMode ?
"-" + repoOverride.value(
"flavour")
144QStringList SsuKickstarter::packagesSection(
const QString &name)
148 if (name ==
"packages") {
150 QString dashedDeviceModel = deviceModel;
151 dashedDeviceModel.replace(
' ',
'-');
152 QString configuration = QString(
"patterns-sailfish-device-configuration-%1")
153 .arg(dashedDeviceModel);
154 result.append(configuration);
157 result.removeDuplicates();
159 result = commandSection(name);
162 result.prepend(
"%" + name);
163 result.append(
"%end");
168QStringList SsuKickstarter::scriptletSection(
const QString &name,
int flags)
174 if ((flags & NoChroot) == NoChroot)
175 path = Sandbox::map(QString(
"/%1/kickstart/%2_nochroot/")
179 path = Sandbox::map(QString(
"/%1/kickstart/%2/")
183 if ((flags & DeviceSpecific) == DeviceSpecific) {
184 if (dir.exists(path +
"/" + replaceSpaces(deviceModel.toLower())))
185 path = path +
"/" + replaceSpaces(deviceModel.toLower());
187 path = path +
"/default";
191 QStringList scriptlets = dir.entryList(QDir::AllEntries | QDir::NoDot | QDir::NoDotDot,
194 foreach (
const QString &scriptlet, scriptlets) {
195 QFile file(dir.filePath(scriptlet));
196 result.append(
"### begin " + scriptlet);
197 if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
198 QTextStream in(&file);
200 result.append(in.readLine());
202 result.append(
"### end " + scriptlet);
205 if (!result.isEmpty()) {
206 result.prepend(QString(
"export SSU_RELEASE_TYPE=%1")
207 .arg(rndMode ?
"rnd" :
"release"));
209 if ((flags & NoChroot) == NoChroot)
210 result.prepend(
"%" + name +
" --nochroot --erroronfail");
212 result.prepend(
"%" + name +
" --erroronfail");
214 result.append(
"%end");
220void SsuKickstarter::setRepoParameters(QHash<QString, QString> parameters)
222 repoOverride = parameters;
224 if (repoOverride.contains(
"model"))
225 deviceModel = repoOverride.value(
"model");
228bool SsuKickstarter::write(
const QString &kickstart)
230 QTextStream qerr(stderr);
231 QStringList commandSections;
236 commandSections.append(
"part");
239 if (repoOverride.contains(
"rnd")) {
240 if (repoOverride.value(
"rnd") ==
"true")
242 else if (repoOverride.value(
"rnd") ==
"false")
246 if (repoOverride.contains(
"domain")) {
247 ssu.setDomain(repoOverride.value(
"domain"));
250 QHash<QString, QString> defaults;
252 SsuRepoManager repoManager;
256 SsuDeviceInfo deviceInfo(deviceModel);
258 if (deviceInfo.
variable(
"kickstart-defaults",
"commandSections")
259 .canConvert(QMetaType::QStringList)) {
261 deviceInfo.
variable(
"kickstart-defaults",
"commandSections").toStringList();
264 QHash<QString, QString>::const_iterator it = defaults.constBegin();
265 while (it != defaults.constEnd()) {
266 if (!repoOverride.contains(it.key()))
267 repoOverride.insert(it.key(), it.value());
273 if (repoOverride.contains(
"flavour")) {
274 repoOverride.insert(
"flavourName", repoOverride.value(
"flavour"));
275 }
else if (repoOverride.contains(
"flavourName")) {
276 qerr <<
"Warning: Should use flavour instead of flavourName" << endl;
277 repoOverride.insert(
"flavour", repoOverride.value(
"flavourName"));
279 qerr <<
"Warning: RND mode but flavour is not set" << endl;
283 if (repoOverride.remove(
"flavour")) {
284 qerr <<
"Warning: Unset flavour in release mode." << endl;
286 if (repoOverride.remove(
"flavourName")) {
287 qerr <<
"Warning: Unset flavourName in release mode." << endl;
292 if (!repoOverride.contains(
"deviceModel"))
293 repoOverride.insert(
"deviceModel", deviceInfo.
deviceModel());
296 if (deviceInfo.
contains() ==
false) {
297 qerr <<
"Device model '" << deviceInfo.
deviceModel() <<
"' does not exist" << endl;
299 if (repoOverride.value(
"force") !=
"true")
303 QRegExp regex(
" {2,}", Qt::CaseSensitive, QRegExp::RegExp2);
304 if (regex.indexIn(deviceInfo.
deviceModel(), 0) != -1) {
305 qerr <<
"Device model '" << deviceInfo.
deviceModel()
306 <<
"' contains multiple consecutive spaces." << endl;
308 qerr <<
"Since the model exists it looks like your configuration is broken." << endl;
312 if (!repoOverride.contains(
"brand")) {
313 qerr <<
"Brand is missing in your configuration." << endl;
318 QString outputDir = repoOverride.value(
"outputdir");
319 if (!outputDir.isEmpty()) outputDir.append(
"/");
323 if (kickstart.isEmpty()) {
326 if (repoOverride.contains(
"filename")) {
327 QString fileName = QString(
"%1%2")
329 .arg(replaceSpaces(var.
resolveString(repoOverride.value(
"filename"),
332 ks.setFileName(fileName);
333 opened = ks.open(QIODevice::WriteOnly);
335 qerr <<
"No filename specified, and no default filename configured" << endl;
338 }
else if (kickstart ==
"-") {
339 opened = ks.open(stdout, QIODevice::WriteOnly);
341 ks.setFileName(outputDir + kickstart);
342 opened = ks.open(QIODevice::WriteOnly);
346 qerr <<
"Unable to write output file " << ks.fileName() <<
": " << ks.errorString() << endl;
348 }
else if (!ks.fileName().isEmpty()) {
349 qerr <<
"Writing kickstart to " << ks.fileName() << endl;
352 QString displayName = QString(
"# DisplayName: %1 %2/%3 (%4) %5")
353 .arg(repoOverride.value(
"brand"))
355 .arg(repoOverride.value(
"arch"))
356 .arg((rndMode ?
"rnd"
358 .arg(repoOverride.value(
"version"));
361 QStringList featuresList = deviceInfo.
value(
"img-features").toStringList();
364 if (rndMode && !featuresList.contains(
"-developer-mode"))
365 featuresList <<
"developer-mode";
367 featuresList = featuresList.filter(QRegExp(
"^[^-]"));
369 QString suggestedFeatures;
372 if (featuresList.count() == 1)
373 suggestedFeatures = QString(
"# SuggestedFeatures: %1,")
374 .arg(featuresList.join(
", "));
375 else if (featuresList.count() > 1)
376 suggestedFeatures = QString(
"# SuggestedFeatures: %1")
377 .arg(featuresList.join(
", "));
379 QString imageType =
"fs";
380 if (!deviceInfo.
value(
"img-type").toString().isEmpty())
381 imageType = deviceInfo.
value(
"img-type").toString();
383 QString imageArchitecture =
"armv7hl";
384 if (!deviceInfo.
value(
"img-arch").toString().isEmpty())
385 imageArchitecture = deviceInfo.
value(
"img-arch").toString();
387 QString kickstartType = QString(
"# KickstartType: %1")
388 .arg((rndMode ?
"rnd" :
"release"));
390 const QString deviceModel = deviceInfo.
deviceModel();
394 kout << displayName << endl;
395 kout << kickstartType << endl;
396 kout <<
"# DeviceModel: " << deviceModel << endl;
397 kout <<
"# DeviceVariant: " << deviceInfo.
deviceVariant(
true) << endl;
398 kout <<
"# Brand: " << repoOverride.value(
"brand") << endl;
401 for (
auto const &repo : deviceInfo.
value(
"repository-specific-variables").toStringList()) {
402 if (!repo.startsWith(deviceModel +
'-'))
405 QHash<QString, QString> section;
407 for (
auto var = section.cbegin(); var != section.cend(); ++var)
408 kout <<
"# Var@" << repo.mid(deviceModel.size() + 1) <<
'@' << var.key() <<
": " << var.value() << endl;
411 if (!suggestedFeatures.isEmpty())
412 kout << suggestedFeatures << endl;
413 kout <<
"# SuggestedImageType: " << imageType << endl;
414 kout <<
"# SuggestedArchitecture: " << imageArchitecture << endl << endl;
415 kout << commands().join(
"\n") << endl << endl;
416 foreach (
const QString §ion, commandSections) {
417 kout << commandSection(section).join(
"\n") << endl << endl;
419 QString repoSection = repos().join(
"\n");
420 kout << repoSection << endl << endl;
421 kout << packagesSection(
"packages").join(
"\n") << endl << endl;
422 kout << packagesSection(
"attachment").join(
"\n") << endl << endl;
424 kout << scriptletSection(
"pre",
Chroot).join(
"\n") << endl << endl;
425 kout << scriptletSection(
"post",
Chroot).join(
"\n") << endl << endl;
426 kout << scriptletSection(
"post", NoChroot).join(
"\n") << endl << endl;
427 kout << scriptletSection(
"pack", DeviceSpecific).join(
"\n") << endl << endl;
void variableSection(const QString §ion, QHash< QString, QString > *storageHash)
QVariant value(const QString &key, const QVariant &value=QVariant())
QVariant variable(const QString §ion, const QString &key)
bool contains(const QString &model=QString())
Q_INVOKABLE QString deviceVariant(bool fallback=false)
Q_INVOKABLE QString deviceModel()
@ Chroot
Chroot is not useful, but helps in making the code more readable.
QStringList repos(int filter=Ssu::NoFilter|Ssu::UserBlacklist)
QStringList repoVariables(QHash< QString, QString > *storageHash, bool rnd=false)
static QString resolveString(const QString &pattern, QHash< QString, QString > *variables, int recursionDepth=0)
@ RndMode
Enable RnD mode for device.
@ BoardFilter
Only global repositories.
#define SSU_DATA_DIR
Directory where all the non-user modifiable data sits.