Initial commit for CCManager
ADD
src/ccmanager.cpp
+#include "ccmanager.h"
+
+#include "processorhandler.h"
+#include "ripessettings.h"
+
+#include <QProcess>
+
+namespace Ripes {
+
+const static std::vector<QString> s_validAutodetectedCCs = {"riscv64-unknown-elf-gcc"};
+const static QString s_testprogram = "int main() { return 0; }";
+const static QString s_baseCC = "%1 -march=%2 -mabi=%3 -s %4 -o %5";
+
+CCManager::CCManager() {
+ if (RipesSettings::value(RIPES_SETTING_CCPATH) == "") {
+ // No previous compiler path has been set. Try to autodetect a valid compiler within the current path
+ const auto CCPath = tryAutodetectCC();
+ if (!CCPath.isEmpty()) {
+ RipesSettings::setValue(RIPES_SETTING_CCPATH, CCPath);
+ }
+ }
+
+ if (RipesSettings::value(RIPES_SETTING_CCPATH) != "") {
+ verifyCC(RipesSettings::value(RIPES_SETTING_CCPATH).toString());
+ }
+}
+
+QString CCManager::tryAutodetectCC() {
+ for (const auto& path : s_validAutodetectedCCs) {
+ QProcess process;
+
+ process.start(path);
+ process.waitForFinished();
+
+ if (process.error() != QProcess::FailedToStart) {
+ // We have detected that a valid compiler exists in path
+ return path;
+ }
+ }
+
+ return QString();
+}
+
+void CCManager::verifyCC(const QString& CCPath) {
+ // Write test program to temporary file with a .c extension
+
+ QTemporaryFile testSrcFile(QDir::tempPath() + QDir::separator() + QCoreApplication::applicationName() +
+ ".XXXXXX.c");
+ if (testSrcFile.open()) {
+ QTextStream stream(&testSrcFile);
+ stream << s_testprogram;
+ }
+ Q_ASSERT(!testSrcFile.fileName().isEmpty());
+ const QString& testSrcFileOut = testSrcFile.fileName() + ".out";
+
+ const auto& currentISA = ProcessorHandler::get()->currentISA();
+
+ // Generate compile command
+ QString s_cc = s_baseCC;
+
+ // Substitute compiler path
+ s_cc = s_cc.arg(CCPath);
+
+ // Substitute machine architecture
+ s_cc = s_cc.arg(currentISA->CCmarch());
+
+ // Substitute machine ABI
+ s_cc = s_cc.arg(currentISA->CCmabi());
+
+ // Substitute in and out files
+ s_cc = s_cc.arg(testSrcFile.fileName()).arg(testSrcFileOut);
+
+ // Run compiler
+ QProcess process;
+ process.start(s_cc);
+ process.waitForFinished();
+
+ if (!QFile::exists(testSrcFileOut)) {
+#ifdef QT_DEBUG
+ qDebug() << "Failed to compile test program";
+ qDebug() << "CC output: ";
+ qDebug() << "Standard output: " << process.readAllStandardOutput();
+ qDebug() << "Standard error: " << process.readAllStandardError();
+#endif
+ } else {
+ }
+
+ // Cleanup
+ QFile::remove(testSrcFile.fileName());
+ QFile::remove(testSrcFileOut);
+}
+
+} // namespace Ripes

ADD
src/ccmanager.h
+#pragma once
+
+#include <QString>
+
+namespace Ripes {
+
+/**
+ * @brief The CCManager class
+ * Manages the detection, verification and execution of a valid C/C++ compiler suitable for the ISAs targetted by the
+ * various processor models of Ripes.
+ */
+class CCManager {
+public:
+ static CCManager& get() {
+ static CCManager manager;
+ return manager;
+ }
+
+private:
+ /**
+ * @brief tryAutodetectCC
+ * Will attempt to scan the current PATH to locate a valid compiler
+ */
+ QString tryAutodetectCC();
+
+ /**
+ * @brief verifyCC
+ * Attempts to compile a simple test program using the provided compiler path @param CC
+ */
+ void verifyCC(const QString& CC);
+
+ CCManager();
+};
+
+} // namespace Ripes

MODIFY
src/ripessettings.h
src/ripessettings.h
@@ -6,9 +6,11 @@ namespace Ripes {
// Definitions of the name of all settings within Ripes
#define RIPES_SETTING_REWINDSTACKSIZE ("simulator_rewindstacksize")
+#define RIPES_SETTING_CCPATH ("compiler_path")
// Definitions of all default settings within Ripes
-const static std::map<QString, QVariant> s_defaultSettings = {{RIPES_SETTING_REWINDSTACKSIZE, 100}};
+const static std::map<QString, QVariant> s_defaultSettings = {{RIPES_SETTING_REWINDSTACKSIZE, 100},
+ {RIPES_SETTING_CCPATH, ""}};
/**
* @brief The SettingObserver class

MODIFY
src/settingsdialog.cpp
src/settingsdialog.cpp
#include "settingsdialog.h"
#include "ui_settingsdialog.h"
+#include "ccmanager.h"
#include "ripessettings.h"
+#include <QGroupBox>
#include <QLabel>
#include <QLayout>
#include <QLineEdit>
+#include <QPushButton>
#include <QSpacerItem>
#include <QSpinBox>
#include <QStackedWidget>
@@ -39,7 +42,8 @@ std::pair<QLabel*, T_Widget*> createSettingsWidgets(const QString& settingName,
widget->connect(widget, QOverload<int>::of(&QSpinBox::valueChanged), settingObserver,
&SettingObserver::setValue);
} else if constexpr (std::is_same<T_Widget, QLineEdit>()) {
- connect(widget, &QLineEdit::textChanged, settingObserver, &SettingObserver::setValue);
+ widget->connect(widget, &QLineEdit::textChanged, settingObserver, &SettingObserver::setValue);
+ widget->setText(settingObserver->value().toString());
}
return {label, widget};
@@ -52,8 +56,8 @@ SettingsDialog::SettingsDialog(QWidget* parent) : QDialog(parent), m_ui(new Ui::
setWindowTitle("Settings");
// Create settings pages
- addPage("Simulator", createSimulatorPage());
addPage("Editor", createEditorPage());
+ addPage("Simulator", createSimulatorPage());
m_ui->settingsList->setCurrentRow(0);
}
@@ -69,6 +73,29 @@ void SettingsDialog::accept() {
QWidget* SettingsDialog::createEditorPage() {
auto [pageWidget, pageLayout] = constructPage();
+ // Setting: RIPES_SETTING_CCPATH
+ CCManager::get();
+
+ auto* CCGroupBox = new QGroupBox("RISC-V C/C++ Compiler");
+ auto* CCLayout = new QVBoxLayout();
+ CCGroupBox->setLayout(CCLayout);
+ auto* CCDesc = new QLabel(
+ "Providing a valid C/C++ compiler enables direct editing, compilation "
+ "and execution of C-language source files within Ripes.\n\n"
+ "A compatible compiler may be autodetected if availabe in PATH.");
+ CCDesc->setWordWrap(true);
+ CCLayout->addWidget(CCDesc);
+
+ auto* CCHLayout = new QHBoxLayout();
+ auto [cclabel, ccpath] = createSettingsWidgets<QLineEdit>(RIPES_SETTING_CCPATH, "C/C++ compiler path:");
+ CCLayout->addLayout(CCHLayout);
+ CCHLayout->addWidget(cclabel);
+ CCHLayout->addWidget(ccpath);
+ auto* pathBrowseButton = new QPushButton("Browse");
+ CCHLayout->addWidget(pathBrowseButton);
+
+ pageLayout->addWidget(CCGroupBox);
+
return pageWidget;
}

MODIFY
src/settingsdialog.ui
src/settingsdialog.ui
<rect>
<x>0</x>
<y>0</y>
- <width>400</width>
- <height>300</height>
+ <width>851</width>
+ <height>407</height>
</rect>
</property>
<property name="windowTitle">

Initial commit for CCManager