|
|
@ -1,15 +1,19 @@ |
|
|
|
#include <array> |
|
|
|
#include <unistd.h> |
|
|
|
#include <cassert> |
|
|
|
|
|
|
|
|
|
|
|
#include <cstdlib> |
|
|
|
#include <fstream> |
|
|
|
#include <fstream> |
|
|
|
#include <map> |
|
|
|
#include <map> |
|
|
|
|
|
|
|
#include <string> |
|
|
|
|
|
|
|
|
|
|
|
#include "common/swaglog.h" |
|
|
|
#include <QDebug> |
|
|
|
#include "common/util.h" |
|
|
|
#include <QDir> |
|
|
|
#include "third_party/raylib/include/raylib.h" |
|
|
|
#include <QTimer> |
|
|
|
|
|
|
|
#include <QVBoxLayout> |
|
|
|
|
|
|
|
|
|
|
|
int freshClone(); |
|
|
|
#include "common/util.h" |
|
|
|
int cachedFetch(const std::string &cache); |
|
|
|
#include "selfdrive/ui/installer/installer.h" |
|
|
|
int executeGitCommand(const std::string &cmd); |
|
|
|
#include "selfdrive/ui/qt/util.h" |
|
|
|
|
|
|
|
#include "selfdrive/ui/qt/qt_window.h" |
|
|
|
|
|
|
|
|
|
|
|
std::string get_str(std::string const s) { |
|
|
|
std::string get_str(std::string const s) { |
|
|
|
std::string::size_type pos = s.find('?'); |
|
|
|
std::string::size_type pos = s.find('?'); |
|
|
@ -24,108 +28,136 @@ const std::string BRANCH_STR = get_str(BRANCH "? |
|
|
|
#define GIT_SSH_URL "git@github.com:commaai/openpilot.git" |
|
|
|
#define GIT_SSH_URL "git@github.com:commaai/openpilot.git" |
|
|
|
#define CONTINUE_PATH "/data/continue.sh" |
|
|
|
#define CONTINUE_PATH "/data/continue.sh" |
|
|
|
|
|
|
|
|
|
|
|
const std::string CACHE_PATH = "/data/openpilot.cache"; |
|
|
|
const QString CACHE_PATH = "/data/openpilot.cache"; |
|
|
|
|
|
|
|
|
|
|
|
#define INSTALL_PATH "/data/openpilot" |
|
|
|
#define INSTALL_PATH "/data/openpilot" |
|
|
|
#define TMP_INSTALL_PATH "/data/tmppilot" |
|
|
|
#define TMP_INSTALL_PATH "/data/tmppilot" |
|
|
|
|
|
|
|
|
|
|
|
extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start"); |
|
|
|
extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start"); |
|
|
|
extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end"); |
|
|
|
extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end"); |
|
|
|
extern const uint8_t inter_ttf[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_start"); |
|
|
|
|
|
|
|
extern const uint8_t inter_ttf_end[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_end"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Font font; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void run(const char* cmd) { |
|
|
|
void run(const char* cmd) { |
|
|
|
int err = std::system(cmd); |
|
|
|
int err = std::system(cmd); |
|
|
|
assert(err == 0); |
|
|
|
assert(err == 0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void renderProgress(int progress) { |
|
|
|
Installer::Installer(QWidget *parent) : QWidget(parent) { |
|
|
|
BeginDrawing(); |
|
|
|
QVBoxLayout *layout = new QVBoxLayout(this); |
|
|
|
ClearBackground(BLACK); |
|
|
|
layout->setContentsMargins(150, 290, 150, 150); |
|
|
|
DrawTextEx(font, "Installing...", (Vector2){150, 290}, 110, 0, WHITE); |
|
|
|
layout->setSpacing(0); |
|
|
|
Rectangle bar = {150, 570, (float)GetScreenWidth() - 300, 72}; |
|
|
|
|
|
|
|
DrawRectangleRec(bar, (Color){41, 41, 41, 255}); |
|
|
|
QLabel *title = new QLabel(tr("Installing...")); |
|
|
|
progress = std::clamp(progress, 0, 100); |
|
|
|
title->setStyleSheet("font-size: 90px; font-weight: 600;"); |
|
|
|
bar.width *= progress / 100.0f; |
|
|
|
layout->addWidget(title, 0, Qt::AlignTop); |
|
|
|
DrawRectangleRec(bar, (Color){70, 91, 234, 255}); |
|
|
|
|
|
|
|
DrawTextEx(font, (std::to_string(progress) + "%").c_str(), (Vector2){150, 670}, 85, 0, WHITE); |
|
|
|
layout->addSpacing(170); |
|
|
|
EndDrawing(); |
|
|
|
|
|
|
|
|
|
|
|
bar = new QProgressBar(); |
|
|
|
|
|
|
|
bar->setRange(0, 100); |
|
|
|
|
|
|
|
bar->setTextVisible(false); |
|
|
|
|
|
|
|
bar->setFixedHeight(72); |
|
|
|
|
|
|
|
layout->addWidget(bar, 0, Qt::AlignTop); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
layout->addSpacing(30); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val = new QLabel("0%"); |
|
|
|
|
|
|
|
val->setStyleSheet("font-size: 70px; font-weight: 300;"); |
|
|
|
|
|
|
|
layout->addWidget(val, 0, Qt::AlignTop); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
layout->addStretch(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QObject::connect(&proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &Installer::cloneFinished); |
|
|
|
|
|
|
|
QObject::connect(&proc, &QProcess::readyReadStandardError, this, &Installer::readProgress); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QTimer::singleShot(100, this, &Installer::doInstall); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setStyleSheet(R"( |
|
|
|
|
|
|
|
* { |
|
|
|
|
|
|
|
font-family: Inter; |
|
|
|
|
|
|
|
color: white; |
|
|
|
|
|
|
|
background-color: black; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
QProgressBar { |
|
|
|
|
|
|
|
border: none; |
|
|
|
|
|
|
|
background-color: #292929; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
QProgressBar::chunk { |
|
|
|
|
|
|
|
background-color: #364DEF; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
)"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int doInstall() { |
|
|
|
void Installer::updateProgress(int percent) { |
|
|
|
|
|
|
|
bar->setValue(percent); |
|
|
|
|
|
|
|
val->setText(QString("%1%").arg(percent)); |
|
|
|
|
|
|
|
update(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Installer::doInstall() { |
|
|
|
// wait for valid time
|
|
|
|
// wait for valid time
|
|
|
|
while (!util::system_time_valid()) { |
|
|
|
while (!util::system_time_valid()) { |
|
|
|
util::sleep_for(500); |
|
|
|
usleep(500 * 1000); |
|
|
|
LOGD("Waiting for valid time"); |
|
|
|
qDebug() << "Waiting for valid time"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// cleanup previous install attempts
|
|
|
|
// cleanup previous install attempts
|
|
|
|
run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH); |
|
|
|
run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH); |
|
|
|
|
|
|
|
|
|
|
|
// do the install
|
|
|
|
// do the install
|
|
|
|
if (util::file_exists(CACHE_PATH)) { |
|
|
|
if (QDir(CACHE_PATH).exists()) { |
|
|
|
return cachedFetch(CACHE_PATH); |
|
|
|
cachedFetch(CACHE_PATH); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return freshClone(); |
|
|
|
freshClone(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int freshClone() { |
|
|
|
void Installer::freshClone() { |
|
|
|
LOGD("Doing fresh clone"); |
|
|
|
qDebug() << "Doing fresh clone"; |
|
|
|
std::string cmd = util::string_format("git clone --progress %s -b %s --depth=1 --recurse-submodules %s 2>&1", |
|
|
|
proc.start("git", {"clone", "--progress", GIT_URL.c_str(), "-b", BRANCH_STR.c_str(), |
|
|
|
GIT_URL.c_str(), BRANCH_STR.c_str(), TMP_INSTALL_PATH); |
|
|
|
"--depth=1", "--recurse-submodules", TMP_INSTALL_PATH}); |
|
|
|
return executeGitCommand(cmd); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int cachedFetch(const std::string &cache) { |
|
|
|
void Installer::cachedFetch(const QString &cache) { |
|
|
|
LOGD("Fetching with cache: %s", cache.c_str()); |
|
|
|
qDebug() << "Fetching with cache: " << cache; |
|
|
|
|
|
|
|
|
|
|
|
run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str()); |
|
|
|
run(QString("cp -rp %1 %2").arg(cache, TMP_INSTALL_PATH).toStdString().c_str()); |
|
|
|
run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, BRANCH_STR.c_str()).c_str()); |
|
|
|
int err = chdir(TMP_INSTALL_PATH); |
|
|
|
|
|
|
|
assert(err == 0); |
|
|
|
|
|
|
|
run(("git remote set-branches --add origin " + BRANCH_STR).c_str()); |
|
|
|
|
|
|
|
|
|
|
|
renderProgress(10); |
|
|
|
updateProgress(10); |
|
|
|
|
|
|
|
|
|
|
|
return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, BRANCH_STR.c_str())); |
|
|
|
proc.setWorkingDirectory(TMP_INSTALL_PATH); |
|
|
|
|
|
|
|
proc.start("git", {"fetch", "--progress", "origin", BRANCH_STR.c_str()}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int executeGitCommand(const std::string &cmd) { |
|
|
|
void Installer::readProgress() { |
|
|
|
static const std::array stages = { |
|
|
|
const QVector<QPair<QString, int>> stages = { |
|
|
|
// prefix, weight in percentage
|
|
|
|
// prefix, weight in percentage
|
|
|
|
std::pair{"Receiving objects: ", 91}, |
|
|
|
{"Receiving objects: ", 91}, |
|
|
|
std::pair{"Resolving deltas: ", 2}, |
|
|
|
{"Resolving deltas: ", 2}, |
|
|
|
std::pair{"Updating files: ", 7}, |
|
|
|
{"Updating files: ", 7}, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
FILE *pipe = popen(cmd.c_str(), "r"); |
|
|
|
auto line = QString(proc.readAllStandardError()); |
|
|
|
if (!pipe) return -1; |
|
|
|
|
|
|
|
|
|
|
|
int base = 0; |
|
|
|
char buffer[512]; |
|
|
|
for (const QPair kv : stages) { |
|
|
|
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { |
|
|
|
if (line.startsWith(kv.first)) { |
|
|
|
std::string line(buffer); |
|
|
|
auto perc = line.split(kv.first)[1].split("%")[0]; |
|
|
|
int base = 0; |
|
|
|
int p = base + int(perc.toFloat() / 100. * kv.second); |
|
|
|
for (const auto &[text, weight] : stages) { |
|
|
|
updateProgress(p); |
|
|
|
if (line.find(text) != std::string::npos) { |
|
|
|
break; |
|
|
|
size_t percentPos = line.find("%"); |
|
|
|
|
|
|
|
if (percentPos != std::string::npos && percentPos >= 3) { |
|
|
|
|
|
|
|
int percent = std::stoi(line.substr(percentPos - 3, 3)); |
|
|
|
|
|
|
|
int progress = base + int(percent / 100. * weight); |
|
|
|
|
|
|
|
renderProgress(progress); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
base += weight; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
base += kv.second; |
|
|
|
} |
|
|
|
} |
|
|
|
return pclose(pipe); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void cloneFinished(int exitCode) { |
|
|
|
void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) { |
|
|
|
LOGD("git finished with %d", exitCode); |
|
|
|
qDebug() << "git finished with " << exitCode; |
|
|
|
assert(exitCode == 0); |
|
|
|
assert(exitCode == 0); |
|
|
|
|
|
|
|
|
|
|
|
renderProgress(100); |
|
|
|
updateProgress(100); |
|
|
|
|
|
|
|
|
|
|
|
// ensure correct branch is checked out
|
|
|
|
// ensure correct branch is checked out
|
|
|
|
int err = chdir(TMP_INSTALL_PATH); |
|
|
|
int err = chdir(TMP_INSTALL_PATH); |
|
|
@ -171,17 +203,13 @@ void cloneFinished(int exitCode) { |
|
|
|
run("mv /data/continue.sh.new " CONTINUE_PATH); |
|
|
|
run("mv /data/continue.sh.new " CONTINUE_PATH); |
|
|
|
|
|
|
|
|
|
|
|
// wait for the installed software's UI to take over
|
|
|
|
// wait for the installed software's UI to take over
|
|
|
|
util::sleep_for(60 * 1000); |
|
|
|
QTimer::singleShot(60 * 1000, &QCoreApplication::quit); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) { |
|
|
|
int main(int argc, char *argv[]) { |
|
|
|
InitWindow(2160, 1080, "Installer"); |
|
|
|
initApp(argc, argv); |
|
|
|
font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, 120, NULL, 0); |
|
|
|
QApplication a(argc, argv); |
|
|
|
SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); |
|
|
|
Installer installer; |
|
|
|
renderProgress(0); |
|
|
|
setMainWindow(&installer); |
|
|
|
int result = doInstall(); |
|
|
|
return a.exec(); |
|
|
|
cloneFinished(result); |
|
|
|
|
|
|
|
CloseWindow(); |
|
|
|
|
|
|
|
UnloadFont(font); |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|