You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
stmdspgui/source/code.cpp

190 lines
5.0 KiB
C++

/**
* @file code.cpp
* @brief Contains code for algorithm-code-related UI elements and logic.
*
* Copyright (C) 2021 Clyne Sullivan
*
* Distributed under the GNU GPL v3 or later. You should have received a copy of
* the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
#include "imgui.h"
#include "backends/imgui_impl_sdl.h"
#include "backends/imgui_impl_opengl2.h"
#include "TextEditor.h"
#include "config.h"
#include "stmdsp.hpp"
#include "stmdsp_code.hpp"
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
extern std::shared_ptr<stmdsp::device> m_device;
extern void log(const std::string& str);
TextEditor editor; // file.cpp
std::string tempFileName; // device.cpp
static std::string editorCompiled;
static std::string newTempFileName();
static bool codeExecuteCommand(const std::string& command, const std::string& file);
static void stringReplaceAll(std::string& str, const std::string& what, const std::string& with);
static void compileEditorCode();
static void disassembleCode();
void codeEditorInit()
{
editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
editor.SetPalette(TextEditor::GetLightPalette());
}
void codeRenderMenu()
{
if (ImGui::BeginMenu("Code")) {
if (ImGui::MenuItem("Compile code"))
compileEditorCode();
if (ImGui::MenuItem("Show disassembly"))
disassembleCode();
ImGui::EndMenu();
}
}
void codeRenderToolbar()
{
if (ImGui::Button("Compile"))
compileEditorCode();
}
void codeRenderWidgets()
{
editor.Render("code", {WINDOW_WIDTH - 15, 450}, true);
}
std::string newTempFileName()
{
const auto path = std::filesystem::temp_directory_path() / "stmdspgui_build";
return path.string();
}
bool codeExecuteCommand(const std::string& command, const std::string& file)
{
if (system(command.c_str()) == 0) {
if (std::ifstream output (file); output.good()) {
std::ostringstream sstr;
sstr << output.rdbuf();
log(sstr.str().c_str());
} else {
log("Could not read command output!");
}
std::filesystem::remove(file);
return true;
} else {
return false;
}
}
void stringReplaceAll(std::string& str, const std::string& what, const std::string& with)
{
std::size_t i;
while ((i = str.find(what)) != std::string::npos) {
str.replace(i, what.size(), with);
i += what.size();
}
};
void compileEditorCode()
{
log("Compiling...");
// Scrap cached build if there are changes
if (editor.GetText().compare(editorCompiled) != 0) {
std::filesystem::remove(tempFileName + ".o");
std::filesystem::remove(tempFileName + ".orig.o");
}
const auto platform = m_device ? m_device->get_platform()
: stmdsp::platform::L4;
if (tempFileName.empty())
tempFileName = newTempFileName();
{
std::ofstream file (tempFileName, std::ios::trunc | std::ios::binary);
auto file_text =
platform == stmdsp::platform::L4 ? stmdsp::file_header_l4
: stmdsp::file_header_h7;
const auto buffer_size = m_device ? m_device->get_buffer_size()
: stmdsp::SAMPLES_MAX;
stringReplaceAll(file_text, "$0", std::to_string(buffer_size));
file << file_text << '\n' << editor.GetText();
}
const auto scriptFile = tempFileName +
#ifndef STMDSP_WIN32
".sh";
#else
".bat";
#endif
{
std::ofstream makefile (scriptFile, std::ios::binary);
auto make_text =
platform == stmdsp::platform::L4 ? stmdsp::makefile_text_l4
: stmdsp::makefile_text_h7;
stringReplaceAll(make_text, "$0", tempFileName);
stringReplaceAll(make_text, "$1",
std::filesystem::current_path().string());
makefile << make_text;
}
#ifndef STMDSP_WIN32
system((std::string("chmod +x ") + scriptFile).c_str());
#endif
const auto makeOutput = scriptFile + ".log";
const auto makeCommand = scriptFile + " > " + makeOutput + " 2>&1";
if (codeExecuteCommand(makeCommand, makeOutput)) {
editorCompiled = editor.GetText();
log("Compilation succeeded.");
} else {
log("Compilation failed.");
}
std::filesystem::remove(tempFileName);
std::filesystem::remove(scriptFile);
}
void disassembleCode()
{
log("Disassembling...");
if (tempFileName.size() == 0 || editor.GetText().compare(editorCompiled) != 0) {
compileEditorCode();
}
const auto output = tempFileName + ".asm.log";
const auto command =
std::string("arm-none-eabi-objdump -d --no-show-raw-insn ") +
tempFileName + ".orig.o > " + output + " 2>&1";
if (codeExecuteCommand(command, output)) {
log("Ready.");
} else {
log("Failed to load disassembly.");
}
}