reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

install.cpp (3933B)


      1 /* ReaPack: Package manager for REAPER
      2  * Copyright (C) 2015-2025  Christian Fillion
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU Lesser General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  * GNU Lesser General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Lesser General Public License
     15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16  */
     17 
     18 #include "task.hpp"
     19 
     20 #include "archive.hpp"
     21 #include "config.hpp"
     22 #include "download.hpp"
     23 #include "filesystem.hpp"
     24 #include "index.hpp"
     25 #include "reapack.hpp"
     26 #include "transaction.hpp"
     27 
     28 InstallTask::InstallTask(const Version *ver, const int flags,
     29     const Registry::Entry &re, const ArchiveReaderPtr &reader, Transaction *tx)
     30   : Task(tx), m_version(ver), m_flags(flags), m_oldEntry(std::move(re)), m_reader(reader),
     31     m_fail(false), m_index(ver->package()->category()->index()->shared_from_this())
     32 {
     33 }
     34 
     35 bool InstallTask::start()
     36 {
     37   // get current files before overwriting the entry
     38   m_oldFiles = tx()->registry()->getFiles(m_oldEntry);
     39 
     40   // prevent file conflicts (don't worry, the registry push is reverted)
     41   try {
     42     std::vector<Path> conflicts;
     43     tx()->registry()->push(m_version, m_flags, &conflicts);
     44 
     45     if(!conflicts.empty()) {
     46       for(const Path &path : conflicts) {
     47         tx()->receipt()->addError({"Conflict: " + path.join() +
     48           " is already owned by another package", m_version->fullName()});
     49       }
     50 
     51       return false;
     52     }
     53   }
     54   catch(const reapack_error &e) {
     55     tx()->receipt()->addError({e.what(), m_version->fullName()});
     56     return false;
     57   }
     58 
     59   for(const Source *src : m_version->sources()) {
     60     const Path &targetPath = src->targetPath();
     61 
     62     const auto &old = find_if(m_oldFiles.begin(), m_oldFiles.end(),
     63       [&](const Registry::File &f) { return f.path == targetPath; });
     64 
     65     if(old != m_oldFiles.end())
     66       m_oldFiles.erase(old);
     67 
     68     if(m_reader) {
     69       FileExtractor *ex = new FileExtractor(targetPath, m_reader);
     70       push(ex, ex->path());
     71     }
     72     else {
     73       const NetworkOpts &opts = g_reapack->config()->network;
     74       FileDownload *dl = new FileDownload(targetPath, src->url(), opts);
     75       dl->setExpectedChecksum(src->checksum());
     76       push(dl, dl->path());
     77     }
     78   }
     79 
     80   return true;
     81 }
     82 
     83 void InstallTask::push(ThreadTask *job, const TempPath &path)
     84 {
     85   job->onStartAsync >> [=] { m_newFiles.push_back(path); };
     86   job->onFinishAsync >> [=] {
     87     m_waiting.erase(job);
     88 
     89     if(job->state() != ThreadTask::Success)
     90       rollback();
     91   };
     92 
     93   m_waiting.insert(job);
     94   tx()->threadPool()->push(job);
     95 }
     96 
     97 void InstallTask::commit()
     98 {
     99   if(m_fail)
    100     return;
    101 
    102   for(const TempPath &paths : m_newFiles) {
    103     if(!FS::rename(paths)) {
    104       tx()->receipt()->addError({
    105         String::format("Cannot rename to target: %s", FS::lastError()),
    106         paths.target().join()});
    107 
    108       // it's a bit late to rollback here as some files might already have been
    109       // overwritten. at least we can delete the temporary files
    110       rollback();
    111       return;
    112     }
    113   }
    114 
    115   for(const Registry::File &file : m_oldFiles) {
    116     if(FS::remove(file.path))
    117       tx()->receipt()->addRemoval(file.path);
    118 
    119     tx()->registerFile({false, m_oldEntry, file});
    120   }
    121 
    122   tx()->receipt()->addInstall(m_version, m_oldEntry);
    123 
    124   const Registry::Entry &newEntry = tx()->registry()->push(m_version, m_flags);
    125   tx()->registerAll(true, newEntry);
    126 }
    127 
    128 void InstallTask::rollback()
    129 {
    130   for(const TempPath &paths : m_newFiles)
    131     FS::removeRecursive(paths.temp());
    132 
    133   for(ThreadTask *job : m_waiting)
    134     job->abort();
    135 
    136   m_fail = true;
    137 }