grbc/src/ext_cmake.cc
2024-10-13 10:22:50 -04:00

215 lines
6.4 KiB
C++

#include "grbc/ext_cmake.h"
#include "grbc/cJSON.h"
#include "grbc/ext.h"
#include "grbc/helpers.h"
#include "grbc/spec.h"
#include "sol/raii.hpp"
#include <cstdlib>
#include <filesystem>
std::filesystem::path cmake_eval_symlink(const std::filesystem::path &build_dir,
const std::filesystem::path &path) {
std::filesystem::path c_path = path;
while (std::filesystem::is_symlink(c_path)) {
c_path = std::filesystem::read_symlink(c_path);
// Make path absolute if its relative
if (c_path.is_relative())
c_path = path.parent_path() / c_path;
std::filesystem::path target_path = build_dir / c_path.filename();
if (std::filesystem::exists(target_path))
std::filesystem::remove(target_path);
std::filesystem::copy(c_path, target_path);
}
return c_path;
}
std::string cmake_util_read_file(FILE *file) {
// http://www.fundza.com/c4serious/fileIO_reading_all/index.html
char line[190];
std::string result;
while (fgets(line, 190, file))
result += line;
if (result.back() == '\n')
result.pop_back();
return result;
}
CMakeProject EXT_grbc_import_cmake(const std::string &cmake_path,
const CMakeConfig &cfg) {
CMakeProject project{};
log_msg(("configuring cmake project: " + cmake_path).c_str());
if (!std::filesystem::exists(cmake_path)) {
grbc_exception("CMake cannot configure in non-existant directory: " +
cmake_path);
}
if (!std::filesystem::exists(cmake_path + "/CMakeLists.txt")) {
grbc_exception("No CMakeLists.txt file in: " + cmake_path);
}
std::filesystem::path old_path = std::filesystem::current_path();
// Change directory into the cmake dir
std::filesystem::current_path(cmake_path);
std::filesystem::create_directory("grbc_configure");
std::filesystem::current_path("./grbc_configure");
project.build_dir = std::filesystem::current_path().string();
// Create configure arg string
std::string configure_arguments;
for (const std::string &argument : cfg.configure_arguments) {
configure_arguments += argument + " ";
}
// Run configure
int exit_code =
std::system(("cmake -S .. -B . -GNinja " + configure_arguments).c_str());
if (exit_code != EXIT_SUCCESS)
grbc_exception("Failed to configure cmake project in: " + cmake_path);
// Collect compilation database
FILE *compdb = popen("ninja -t compdb", "r");
std::string compdb_raw = cmake_util_read_file(compdb);
pclose(compdb);
project.compdb = cJSON_Parse(compdb_raw.c_str());
// Build the cmake subproject, we need this so we can copy artifacts later on in the build step
grbc_log("building cmake subproject...");
int build_exit_code = std::system("ninja");
if (build_exit_code != EXIT_SUCCESS)
grbc_exception("Failed to build cmake project in: " + project.build_dir);
std::filesystem::current_path(old_path);
return project;
}
Package EXT_grbc_get_cmake_library(const CMakeProject &self,
const std::string &library_name) {
// Loop over every output in the compdb
for (int i = 0; i < cJSON_GetArraySize(self.compdb); i++) {
cJSON *compdb_item = cJSON_GetArrayItem(self.compdb, i);
if (!cJSON_HasObjectItem(compdb_item, "output"))
grbc_exception("Ninja produced invalid compilation database!");
if (!cJSON_HasObjectItem(compdb_item, "command"))
grbc_exception("Compilation database item is missing 'command'");
if (!cJSON_HasObjectItem(compdb_item, "file"))
grbc_exception("Compilation database item is missing 'file'");
std::string command =
cJSON_GetStringValue(cJSON_GetObjectItem(compdb_item, "command"));
// Only handle objects with an empty command
// In the compilation databse objects with no command, but an output, and
// file give us the info we need Info needed: Target name, output name
if (!command.empty())
continue;
std::string output_name =
cJSON_GetStringValue(cJSON_GetObjectItem(compdb_item, "output"));
std::string library_path =
cJSON_GetStringValue(cJSON_GetObjectItem(compdb_item, "file"));
if (output_name != library_name)
continue;
std::filesystem::path full_lib_path = self.build_dir + "/" + library_path;
full_lib_path =
cmake_eval_symlink(grbc_get_config().build_dir, full_lib_path);
Package pkg{};
pkg.name = output_name;
pkg.linker_flags = grbc_get_config().build_dir + "/" + full_lib_path.filename().generic_string();
grbc_log("found cmake library at: " + full_lib_path.generic_string());
return pkg;
}
grbc_exception("Failed to find library with name: " + library_name);
return Package{};
}
std::string EXT_grbc_get_cmake_library_string(const CMakeProject &self) {
std::string library_string;
for (int i = 0; i < cJSON_GetArraySize(self.compdb); i++) {
cJSON *compdb_item = cJSON_GetArrayItem(self.compdb, i);
if (!cJSON_HasObjectItem(compdb_item, "output"))
grbc_exception("Ninja produced invalid compilation database!");
if (!cJSON_HasObjectItem(compdb_item, "command"))
grbc_exception("Compilation database item is missing 'command'");
if (!cJSON_HasObjectItem(compdb_item, "file"))
grbc_exception("Compilation database item is missing 'file'");
std::string cmd =
cJSON_GetStringValue(cJSON_GetObjectItem(compdb_item, "command"));
if (!cmd.empty())
continue;
std::string file =
cJSON_GetStringValue(cJSON_GetObjectItem(compdb_item, "file"));
std::string output =
cJSON_GetStringValue(cJSON_GetObjectItem(compdb_item, "output"));
library_string += "Target: " + output + "\n";
library_string += "\tOutputting to file: " + output + "\n\n";
}
return library_string;
}
void grbc_cmake_init(sol::state &lua) {
lua.new_usertype<CMakeConfig>(
"CMakeConfig", sol::constructors<CMakeConfig(sol::table)>(),
"configure_arguments", &CMakeConfig::configure_arguments);
lua.new_usertype<CMakeProject>("CMakeProject", "source_dir",
&CMakeProject::source_dir, "build_dir",
&CMakeProject::build_dir);
lua["CMakeProject"]["get_library"] = EXT_grbc_get_cmake_library;
lua["CMakeProject"]["get_library_string"] = EXT_grbc_get_cmake_library_string;
lua.set("grbc_import_cmake", EXT_grbc_import_cmake);
}
Extension grbc_cmake() {
Extension ext{};
ext.name = GRBC_EXT_cmake_NAME;
ext.hook_init = grbc_cmake_init;
return ext;
}