comparison installer.cpp @ 97:b5230bda1f1f

The RDF versions are too unreliable, nobody ever remembers to update them. Pull the bundled versions from the actual bundled libraries instead
author Chris Cannam
date Fri, 28 Feb 2020 10:41:54 +0000
parents 24d64a983fc8
children fb4ca43863b5
comparison
equal deleted inserted replaced
96:e7badb8450d8 97:b5230bda1f1f
143 QString title; 143 QString title;
144 QString maker; 144 QString maker;
145 QString description; 145 QString description;
146 QString page; 146 QString page;
147 QStringList pluginTitles; 147 QStringList pluginTitles;
148 map<QString, int> pluginVersions; // id -> version
149 QString licence; 148 QString licence;
150 }; 149 };
151 150
152 QString 151 QString
153 identifyLicence(QString libraryBasename) 152 identifyLicence(QString libraryBasename)
282 store.expand("dc:title"), 281 store.expand("dc:title"),
283 Node())); 282 Node()));
284 if (ptitle.type == Node::Literal) { 283 if (ptitle.type == Node::Literal) {
285 info.pluginTitles.push_back(ptitle.value); 284 info.pluginTitles.push_back(ptitle.value);
286 } 285 }
287
288 Node pident = store.complete(Triple(p.object(),
289 store.expand("vamp:identifier"),
290 Node()));
291 Node pversion = store.complete(Triple(p.object(),
292 store.expand("owl:versionInfo"),
293 Node()));
294 if (pident.type == Node::Literal &&
295 pversion.type == Node::Literal) {
296 bool ok = false;
297 int version = pversion.value.toInt(&ok);
298 if (ok) {
299 info.pluginVersions[pident.value] = version;
300 }
301 }
302 } 286 }
303 287
304 info.licence = identifyLicence(libId.value); 288 info.licence = identifyLicence(libId.value);
305 SVCERR << "licence = " << info.licence << endl; 289 SVCERR << "licence = " << info.licence << endl;
306 290
322 QFile(tempFile).remove(); 306 QFile(tempFile).remove();
323 } 307 }
324 } 308 }
325 QString tempFile; 309 QString tempFile;
326 }; 310 };
311
312 bool
313 unbundleFile(QString filePath, QString targetPath, bool isExecutable)
314 {
315 SVCERR << "Copying " << filePath.toStdString() << " to "
316 << targetPath.toStdString() << "..." << endl;
317
318 // This has to be able to work even if the destination exists, and
319 // to do so without deleting it first - e.g. when copying to a
320 // temporary file. So we open the file and copy to it ourselves
321 // rather than use QFile::copy
322
323 QFile source(filePath);
324 if (!source.open(QFile::ReadOnly)) {
325 SVCERR << "ERROR: Failed to read bundled file " << filePath << endl;
326 return {};
327 }
328 QByteArray content = source.readAll();
329 source.close();
330
331 QFile target(targetPath);
332 if (!target.open(QFile::WriteOnly)) {
333 SVCERR << "ERROR: Failed to read target file " << targetPath << endl;
334 return {};
335 }
336 if (target.write(content) != content.size()) {
337 SVCERR << "ERROR: Incomplete write to target file" << endl;
338 return {};
339 }
340 target.close();
341
342 auto permissions =
343 QFile::ReadOwner | QFile::WriteOwner |
344 QFile::ReadGroup |
345 QFile::ReadOther;
346
347 if (isExecutable) {
348 permissions |=
349 QFile::ExeOwner |
350 QFile::ExeGroup |
351 QFile::ExeOther;
352 };
353
354 if (!QFile::setPermissions(targetPath, permissions)) {
355 SVCERR << "Failed to set permissions on "
356 << targetPath.toStdString() << endl;
357 return false;
358 }
359
360 return true;
361 }
327 362
328 map<QString, int> 363 map<QString, int>
329 getLibraryPluginVersions(QString libraryFilePath) 364 getLibraryPluginVersions(QString libraryFilePath)
330 { 365 {
331 static QMutex mutex; 366 static QMutex mutex;
354 389
355 #ifdef Q_OS_WIN32 390 #ifdef Q_OS_WIN32
356 QString helperPath = ":out/get-version.exe"; 391 QString helperPath = ":out/get-version.exe";
357 #else 392 #else
358 QString helperPath = ":out/get-version"; 393 QString helperPath = ":out/get-version";
359 #endif 394 #endif
360 QFile helper(helperPath); 395
361 if (!helper.open(QFile::ReadOnly)) { 396 tempFile.close();
362 SVCERR << "ERROR: Failed to read helper code" << endl; 397 if (!unbundleFile(helperPath, tempFileName, true)) {
398 SVCERR << "ERROR: Failed to unbundle helper code" << endl;
363 return {}; 399 return {};
364 } 400 }
365 QByteArray content = helper.readAll(); 401
366 helper.close();
367
368 if (tempFile.write(content) != content.size()) {
369 SVCERR << "ERROR: Incomplete write to temporary file" << endl;
370 return {};
371 }
372 tempFile.close();
373
374 if (!QFile::setPermissions
375 (tempFileName,
376 QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner)) {
377 SVCERR << "ERROR: Failed to set execute permission on helper "
378 << tempFileName << endl;
379 return {};
380 }
381
382 initSucceeded = true; 402 initSucceeded = true;
383 } 403 }
384 404
385 if (!initSucceeded) { 405 if (!initSucceeded) {
386 return {}; 406 return {};
431 } 451 }
432 versions[parts[0]] = version; 452 versions[parts[0]] = version;
433 } 453 }
434 454
435 return versions; 455 return versions;
456 }
457
458 map<QString, int>
459 getBundledLibraryPluginVersions(QString libraryFileName)
460 {
461 QString tempFileName;
462 TempFileDeleter deleter;
463
464 {
465 QTemporaryFile tempFile;
466 tempFile.setAutoRemove(false);
467 if (!tempFile.open()) {
468 SVCERR << "ERROR: Failed to open a temporary file" << endl;
469 return {};
470 }
471
472 // We can't use QTemporaryFile's auto-remove, as it will hold
473 // the file open and that prevents us from executing it. Hence
474 // the separate deleter.
475
476 tempFileName = tempFile.fileName();
477 deleter.tempFile = tempFileName;
478 tempFile.close();
479 }
480
481 if (!unbundleFile(":out/" + libraryFileName, tempFileName, true)) {
482 return {};
483 }
484
485 return getLibraryPluginVersions(tempFileName);
436 } 486 }
437 487
438 bool isLibraryNewer(map<QString, int> a, map<QString, int> b) 488 bool isLibraryNewer(map<QString, int> a, map<QString, int> b)
439 { 489 {
440 // a and b are maps from plugin id to plugin version for libraries 490 // a and b are maps from plugin id to plugin version for libraries
520 RelativeStatus 570 RelativeStatus
521 getRelativeStatus(LibraryInfo info, QString targetDir) 571 getRelativeStatus(LibraryInfo info, QString targetDir)
522 { 572 {
523 QString destination = targetDir + "/" + info.fileName; 573 QString destination = targetDir + "/" + info.fileName;
524 574
525 RelativeStatus status = RelativeStatus::New;
526
527 SVCERR << "\ngetRelativeStatus: " << info.fileName << ":\n"; 575 SVCERR << "\ngetRelativeStatus: " << info.fileName << ":\n";
528 576
529 if (QFileInfo(destination).exists()) { 577 if (!QFileInfo(destination).exists()) {
530 578 SVCERR << " - relative status: " << relativeStatusLabel(RelativeStatus::New) << endl;
531 auto installed = getLibraryPluginVersions(destination); 579 return RelativeStatus::New;
532 580 }
533 SVCERR << " * installed: " << versionsString(installed) 581
534 << "\n * packaged: " << versionsString(info.pluginVersions) 582 RelativeStatus status = RelativeStatus::Same;
535 << endl; 583
536 584 auto packaged = getBundledLibraryPluginVersions(info.fileName);
537 status = RelativeStatus::Same; 585 auto installed = getLibraryPluginVersions(destination);
538 586
539 if (installed.empty()) { 587 SVCERR << " * installed: " << versionsString(installed)
540 status = RelativeStatus::TargetNotLoadable; 588 << "\n * packaged: " << versionsString(packaged)
541 } 589 << endl;
542 590
543 if (isLibraryNewer(installed, info.pluginVersions)) { 591 if (installed.empty()) {
544 status = RelativeStatus::Downgrade; 592 status = RelativeStatus::TargetNotLoadable;
545 } 593 }
546 594
547 if (isLibraryNewer(info.pluginVersions, installed)) { 595 if (isLibraryNewer(installed, packaged)) {
548 status = RelativeStatus::Upgrade; 596 status = RelativeStatus::Downgrade;
549 } 597 }
598
599 if (isLibraryNewer(packaged, installed)) {
600 status = RelativeStatus::Upgrade;
550 } 601 }
551 602
552 SVCERR << " - relative status: " << relativeStatusLabel(status) << endl; 603 SVCERR << " - relative status: " << relativeStatusLabel(status) << endl;
553 604
554 return status; 605 return status;
582 QString 633 QString
583 installLibrary(LibraryInfo info, QString targetDir) 634 installLibrary(LibraryInfo info, QString targetDir)
584 { 635 {
585 QString library = info.fileName; 636 QString library = info.fileName;
586 QString source = ":out"; 637 QString source = ":out";
587 QFile f(source + "/" + library);
588 QString destination = targetDir + "/" + library; 638 QString destination = targetDir + "/" + library;
589 639
590 static QString backupDirName; 640 static QString backupDirName;
591 if (backupDirName == "") { 641 if (backupDirName == "") {
592 // Static so as to be created once - don't go creating a 642 // Static so as to be created once - don't go creating a
603 } 653 }
604 654
605 if (!backup(destination, backupDir)) { 655 if (!backup(destination, backupDir)) {
606 return QObject::tr("Failed to move aside existing library"); 656 return QObject::tr("Failed to move aside existing library");
607 } 657 }
608 658
609 SVCERR << "Copying " << library.toStdString() << " to " 659 if (!unbundleFile(source + "/" + library, destination, true)) {
610 << destination.toStdString() << "..." << endl; 660 return QObject::tr("Failed to copy library file to target directory");
611 if (!f.copy(destination)) { 661 }
612 SVCERR << "Failed to copy " << library.toStdString() 662
613 << " to target " << destination.toStdString() << endl;
614 return QObject::tr("Failed to copy library to destination directory");
615 }
616 if (!QFile::setPermissions
617 (destination,
618 QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner |
619 QFile::ReadGroup | QFile::ExeGroup |
620 QFile::ReadOther | QFile::ExeOther)) {
621 SVCERR << "Failed to set permissions on "
622 << library.toStdString() << endl;
623 return QObject::tr("Failed to set correct permissions on installed library");
624 }
625
626 QString base = QFileInfo(library).baseName(); 663 QString base = QFileInfo(library).baseName();
627 QDir dir(source); 664 QDir dir(source);
628 auto entries = dir.entryList({ base + "*" }); 665 auto entries = dir.entryList({ base + "*" });
629 for (auto e: entries) { 666 for (auto e: entries) {
630 if (e == library) continue; 667 if (e == library) continue;
631 QString destination = targetDir + "/" + e; 668 QString destination = targetDir + "/" + e;
632 if (!backup(destination, backupDir)) { 669 if (!backup(destination, backupDir)) {
633 continue; 670 continue;
634 } 671 }
635 SVCERR << "Copying " << e.toStdString() << " to " 672 if (!unbundleFile(source + "/" + e, destination, false)) {
636 << destination.toStdString() << "..." << endl;
637 if (!QFile(source + "/" + e).copy(destination)) {
638 SVCERR << "Failed to copy " << e.toStdString()
639 << " to target " << destination.toStdString()
640 << " (ignoring)" << endl;
641 continue;
642 }
643 if (!QFile::setPermissions
644 (destination,
645 QFile::ReadOwner | QFile::WriteOwner |
646 QFile::ReadGroup |
647 QFile::ReadOther)) {
648 SVCERR << "Failed to set permissions on "
649 << destination.toStdString()
650 << " (ignoring)" << endl;
651 continue; 673 continue;
652 } 674 }
653 } 675 }
654 676
655 return {}; 677 return {};