next advance
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
/.vscode/
|
||||||
|
/bin/
|
||||||
|
/out/
|
||||||
|
makevars.mk
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
// Friendly reminder to alter the includepath
|
||||||
|
// in YOUR OWN .vscode/settings.json
|
||||||
|
"C_Cpp.default.includePath": [
|
||||||
|
"./src"
|
||||||
|
],
|
||||||
|
"terminal.integrated.defaultProfile.windows": "MSYS2 UCRT",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
name: ''
|
||||||
|
author: '' # author and authors are added together
|
||||||
|
authors: ['']
|
||||||
|
descr:
|
||||||
|
- ''
|
||||||
|
# Amount of text to output
|
||||||
|
verbosity: normal
|
||||||
|
build:
|
||||||
|
# Set build folders
|
||||||
|
src:
|
||||||
|
path: './src'
|
||||||
|
targets: ['c','cpp'] # Set targets for compilation
|
||||||
|
out:
|
||||||
|
path: './out'
|
||||||
|
target: 'exe'
|
||||||
|
bin:
|
||||||
|
path: './bin'
|
||||||
|
target: 'o'
|
||||||
|
deps:
|
||||||
|
path: './bin' # Reuse binaries folder (default)
|
||||||
|
target: 'd'
|
||||||
|
rev_target: 'rd' # Extension for reverse dependencies
|
||||||
|
style: gcc # Dependency style of generated output
|
||||||
|
|
||||||
|
# Set number of threads to use
|
||||||
|
# for compilation of multiple files
|
||||||
|
# 0 To turn off multithreading.
|
||||||
|
# Default: 0
|
||||||
|
# Recommended: 4
|
||||||
|
threads: 4
|
||||||
|
vars:
|
||||||
|
distros:
|
||||||
|
distro_name:
|
||||||
|
# Distro Vars (overrides global)
|
||||||
|
vars:
|
||||||
|
# Commands executed at the start
|
||||||
|
start:
|
||||||
|
- ''
|
||||||
|
# Commands to generate dependencies
|
||||||
|
deps:
|
||||||
|
- ''
|
||||||
|
# Commands to compile one file
|
||||||
|
# Files may be compiled on multiple threads
|
||||||
|
compile:
|
||||||
|
- ''
|
||||||
|
# Commands to link one file
|
||||||
|
link:
|
||||||
|
- ''
|
||||||
|
# Commands executed after linking program
|
||||||
|
end:
|
||||||
|
- ''
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# Include variables from user defined
|
||||||
|
# makevars.mak for paths & other stuff.
|
||||||
|
include makevars.mk
|
||||||
|
|
||||||
|
#Compiler and Linker
|
||||||
|
CC := g++
|
||||||
|
|
||||||
|
#The Target Binary Program
|
||||||
|
TARGET := out.exe
|
||||||
|
|
||||||
|
#The Directories, Source, Includes, Objects, Binary and Resources
|
||||||
|
SRCDIR := src
|
||||||
|
BUILDDIR := bin
|
||||||
|
TARGETDIR := out
|
||||||
|
SRCEXT := cpp
|
||||||
|
DEPEXT := d
|
||||||
|
OBJEXT := o
|
||||||
|
|
||||||
|
#Flags, Libraries and Includes
|
||||||
|
ROOT := ./
|
||||||
|
CFLAGS := -std=c++20 -O2 \
|
||||||
|
-Wall -Werror -Wextra \
|
||||||
|
-Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align \
|
||||||
|
-Wunused -Woverloaded-virtual -Wconversion \
|
||||||
|
-Wsign-conversion -Wnull-dereference -Wdouble-promotion \
|
||||||
|
-Wformat=2 -Wimplicit-fallthrough -Wsuggest-override \
|
||||||
|
-Wextra-semi -Wduplicated-cond -Wduplicated-branches \
|
||||||
|
-Wlogical-op -Wuseless-cast -Wno-unused-parameter
|
||||||
|
LFLAGS := -std=c++20 -static-libstdc++ -static-libgcc \
|
||||||
|
-Wl,--fatal-warnings -Wl,--warn-common
|
||||||
|
LIBDIRS := $(EXT_LIBDIRS)
|
||||||
|
LIB := $(EXT_LIB)
|
||||||
|
INC := -I./src/ $(EXT_INC)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# DO NOT EDIT BELOW THIS LINE
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
|
||||||
|
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
|
||||||
|
|
||||||
|
#Defauilt Make
|
||||||
|
all: directories $(TARGET)
|
||||||
|
|
||||||
|
#Remake
|
||||||
|
remake: cleaner all
|
||||||
|
|
||||||
|
#Make the Directories
|
||||||
|
directories:
|
||||||
|
@mkdir -p $(TARGETDIR)
|
||||||
|
@mkdir -p $(BUILDDIR)
|
||||||
|
|
||||||
|
#Clean only Objecst
|
||||||
|
clean:
|
||||||
|
@$(RM) -rf $(BUILDDIR)
|
||||||
|
|
||||||
|
#Full Clean, Objects and Binaries
|
||||||
|
cleaner: clean
|
||||||
|
@$(RM) -rf $(TARGETDIR)
|
||||||
|
|
||||||
|
#Pull in dependency info for *existing* .o files
|
||||||
|
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
|
||||||
|
|
||||||
|
#Link
|
||||||
|
$(TARGET): $(OBJECTS)
|
||||||
|
$(CC) $(LFLAGS) -o $(TARGETDIR)/$(TARGET) $^ $(LIBDIRS) $(LIB)
|
||||||
|
|
||||||
|
#Compile
|
||||||
|
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CC) $(CFLAGS) $(INC) -c -o $@ $<
|
||||||
|
@$(CC) $(CFLAGS) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT)
|
||||||
|
@cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp
|
||||||
|
@sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT)
|
||||||
|
@sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT)
|
||||||
|
@rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp
|
||||||
|
|
||||||
|
#Non-File Targets
|
||||||
|
.PHONY: all remake clean cleaner resources
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
BOLD="\033[1m"
|
||||||
|
RESET="\033[0m"
|
||||||
|
|
||||||
|
SUCCESS="\033[38;2;80;220;100m"
|
||||||
|
FAIL="\033[38;2;255;90;90m"
|
||||||
|
INFO="\033[38;2;100;200;255m"
|
||||||
|
DIM="\033[2m"
|
||||||
|
|
||||||
|
LOG="./out/out.log"
|
||||||
|
|
||||||
|
# Start
|
||||||
|
printf "${DIM}────────────────────────────────────────${RESET}\n"
|
||||||
|
printf "${INFO}${BOLD}> Running...${RESET}\n"
|
||||||
|
printf "${DIM}────────────────────────────────────────${RESET}\n\n"
|
||||||
|
|
||||||
|
# Send both stdout and stderr to the console and the log file.
|
||||||
|
start=$(date +%s.%N)
|
||||||
|
./out/out "$@" 2>&1 | tee "$LOG"
|
||||||
|
status=${PIPESTATUS[0]}
|
||||||
|
end=$(date +%s.%N)
|
||||||
|
elapsed=$(awk "BEGIN { printf \"%.3f\", $end - $start }")
|
||||||
|
|
||||||
|
# Status!
|
||||||
|
printf "\n${DIM}────────────────────────────────────────${RESET}\n"
|
||||||
|
if (( status == 0 )); then
|
||||||
|
printf "${SUCCESS}${BOLD}✓ Success${RESET} (exit %d)\n" "$status"
|
||||||
|
else
|
||||||
|
printf "${FAIL}${BOLD}✗ Failed${RESET} (exit %d)\n" "$status"
|
||||||
|
fi
|
||||||
|
printf "${INFO}${BOLD}> Time${RESET} ${elapsed}s\n"
|
||||||
|
printf "${INFO}${BOLD}> Log${RESET} %s\n" "$LOG"
|
||||||
|
printf "${DIM}────────────────────────────────────────${RESET}"
|
||||||
|
exit "$status"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#include "kmake.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "hello" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ckitty/memory/primitives.hpp>
|
||||||
|
|
||||||
|
#include <desktoplib/terminal/Terminal.hpp>
|
||||||
|
#include <desktoplib/terminal/ui/Box.hpp>
|
||||||
|
#include <desktoplib/terminal/ui/MenuMap.hpp>
|
||||||
|
#include <desktoplib/terminal/ui/Content.hpp>
|
||||||
|
#include <desktoplib/terminal/ui/InnerScroll.hpp>
|
||||||
|
|
||||||
|
namespace kmake {
|
||||||
|
|
||||||
|
using namespace ckitty;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#include "Distro.hpp"
|
||||||
|
|
||||||
|
namespace kmake {
|
||||||
|
|
||||||
|
Distro::Distro() {}
|
||||||
|
|
||||||
|
Distro::~Distro() {}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <kmake/kmake.hpp>
|
||||||
|
|
||||||
|
namespace kmake {
|
||||||
|
|
||||||
|
class Distro {
|
||||||
|
public:
|
||||||
|
|
||||||
|
map<string, string> vars;
|
||||||
|
vector<string> start;
|
||||||
|
vector<string> deps;
|
||||||
|
vector<string> compile;
|
||||||
|
vector<string> link;
|
||||||
|
vector<string> end;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Distro();
|
||||||
|
|
||||||
|
~Distro();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
#include "Project.hpp"
|
||||||
|
|
||||||
|
#include <ckitty/strings/utils.hpp>
|
||||||
|
|
||||||
|
#include <kmake/project/Distro.hpp>
|
||||||
|
|
||||||
|
#include <c4/yml/tree.hpp>
|
||||||
|
#include <c4/yml/parse.hpp>
|
||||||
|
#include <c4/yml/std/std.hpp>
|
||||||
|
#include <c4/yml/std/string.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace ryml {
|
||||||
|
using namespace c4::yml;
|
||||||
|
using namespace c4;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace kmake {
|
||||||
|
|
||||||
|
using namespace ryml;
|
||||||
|
|
||||||
|
// Safely converts c4::csubstr view to string
|
||||||
|
static string to_string(csubstr s) {
|
||||||
|
if (s.str == nullptr || s.len == 0) return "";
|
||||||
|
return string(s.str, s.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extracts a string list and logs unexpected nodes/types
|
||||||
|
static void parse_string_list(ryml::ConstNodeRef node, vector<string>& dest, const string& path, vector<string>& log) {
|
||||||
|
if (!node.valid()) {
|
||||||
|
log.push_back("[WARN] Section missing at path: '" + path + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node.is_seq()) {
|
||||||
|
log.push_back("[WARN] Expected a list/sequence at '" + path + "', but found a scalar or map.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
for (ryml::ConstNodeRef child : node.children()) {
|
||||||
|
string item_path = path + "[" + std::to_string(index++) + "]";
|
||||||
|
|
||||||
|
// Unexpected: List item is a map or nested list instead of a string value
|
||||||
|
if (!child.has_val()) {
|
||||||
|
log.push_back("[WARN] Expected scalar string at '" + item_path + "', but found a nested container.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string val;
|
||||||
|
child >> val;
|
||||||
|
|
||||||
|
if (val.empty()) {
|
||||||
|
log.push_back("[WARN] Empty string element ignored at '" + item_path + "'");
|
||||||
|
} else {
|
||||||
|
dest.push_back(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extracts a key-value string map and logs unexpected entries/types
|
||||||
|
static void parse_string_map(ryml::ConstNodeRef node, map<string, string>& dest, const string& path, vector<string>& log) {
|
||||||
|
if (!node.valid()) {
|
||||||
|
log.push_back("[WARN] Section missing at path: '" + path + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node.is_map()) {
|
||||||
|
log.push_back("[WARN] Expected key-value map at '" + path + "', but found a scalar or list.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ryml::ConstNodeRef child : node.children()) {
|
||||||
|
if (!child.has_key()) {
|
||||||
|
log.push_back("[WARN] Entry without a key found inside map at '" + path + "'");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string k = to_string(child.key());
|
||||||
|
string key_path = path + "." + k;
|
||||||
|
|
||||||
|
// Unexpected: Value is a sub-map or sub-list instead of a string scalar
|
||||||
|
if (!child.has_val()) {
|
||||||
|
log.push_back("[WARN] Expected string scalar for key '" + key_path + "', but found a nested object/list.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string v;
|
||||||
|
child >> v;
|
||||||
|
|
||||||
|
if (v.empty()) {
|
||||||
|
log.push_back("[WARN] Value for key '" + key_path + "' is empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Distro parse_distro(c4::yml::ConstNodeRef node, const std::string& path, std::vector<std::string>& log) {
|
||||||
|
Distro d;
|
||||||
|
if (node.has_child("vars")) parse_string_map(node["vars"], d.vars, path + ".vars", log);
|
||||||
|
if (node.has_child("start")) parse_string_list(node["start"], d.start, path + ".start", log);
|
||||||
|
if (node.has_child("deps")) parse_string_list(node["deps"], d.deps, path + ".deps", log);
|
||||||
|
if (node.has_child("compile")) parse_string_list(node["compile"], d.compile, path + ".compile", log);
|
||||||
|
if (node.has_child("link")) parse_string_list(node["link"], d.link, path + ".link", log);
|
||||||
|
if (node.has_child("end")) parse_string_list(node["end"], d.end, path + ".end", log);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
Project::Project()
|
||||||
|
: verbosity(Verbosity::NORMAL) {
|
||||||
|
build.threads = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Project::~Project() {}
|
||||||
|
|
||||||
|
bool Project::load(const string& filename) {
|
||||||
|
auto logs = vector<string>();
|
||||||
|
|
||||||
|
// 1. Read file into buffer
|
||||||
|
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
logs.push_back("Failed to open file: " + filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize size = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
vector<char> buffer(size);
|
||||||
|
if (!file.read(buffer.data(), size)) {
|
||||||
|
logs.push_back("Failed to read file content: " + filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Parse in-place
|
||||||
|
ryml::Tree tree = ryml::parse_in_place(ryml::to_substr(buffer));
|
||||||
|
ryml::ConstNodeRef root = tree.rootref();
|
||||||
|
|
||||||
|
if (!root.is_map()) {
|
||||||
|
logs.push_back("[ERROR] Root YAML document must be a map.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse 'name'
|
||||||
|
if (root.has_child("name") && root["name"].has_val()) {
|
||||||
|
root["name"] >> name;
|
||||||
|
} else {
|
||||||
|
logs.push_back("[WARN] Key 'name' is missing or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse combined 'author' and 'authors'
|
||||||
|
if (root.has_child("author") && root["author"].has_val()) {
|
||||||
|
string single_author;
|
||||||
|
root["author"] >> single_author;
|
||||||
|
if (!single_author.empty()) {
|
||||||
|
authors.push_back(single_author);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (root.has_child("authors")) {
|
||||||
|
parse_string_list(root["authors"], authors, "authors", logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse 'descr'
|
||||||
|
if (root.has_child("descr")) {
|
||||||
|
parse_string_list(root["descr"], descr, "descr", logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse 'verbosity'
|
||||||
|
if (root.has_child("verbosity") && root["verbosity"].has_val()) {
|
||||||
|
string v_str;
|
||||||
|
root["verbosity"] >> v_str;
|
||||||
|
v_str = strings::to_lower(v_str);
|
||||||
|
if (v_str == "debug") {
|
||||||
|
verbosity = Verbosity::DEBUG;
|
||||||
|
} else if (v_str == "high") {
|
||||||
|
verbosity = Verbosity::HIGH;
|
||||||
|
} else if (v_str == "low") {
|
||||||
|
verbosity = Verbosity::LOW;
|
||||||
|
} else if (v_str == "normal") {
|
||||||
|
verbosity = Verbosity::NORMAL;
|
||||||
|
} else {
|
||||||
|
logs.push_back("[WARN] Unknown verbosity level '" + v_str + "'. Defaulting to 'normal'.");
|
||||||
|
verbosity = Verbosity::NORMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse 'build'
|
||||||
|
if (root.has_child("build")) {
|
||||||
|
ryml::ConstNodeRef b_node = root["build"];
|
||||||
|
if (b_node.has_child("src")) {
|
||||||
|
ryml::ConstNodeRef src_node = b_node["src"];
|
||||||
|
if (src_node.has_child("path") && src_node["path"].has_val()) {
|
||||||
|
src_node["path"] >> build.src.path;
|
||||||
|
}
|
||||||
|
if (src_node.has_child("targets")) {
|
||||||
|
parse_string_list(src_node["targets"], build.src.targets, "build.src.targets", logs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b_node.has_child("threads") && b_node["threads"].has_val()) {
|
||||||
|
b_node["threads"] >> build.threads;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse 'vars'
|
||||||
|
if (root.has_child("vars")) {
|
||||||
|
parse_string_map(root["vars"], vars, "vars", logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse 'distros'
|
||||||
|
if (root.has_child("distros")) {
|
||||||
|
ryml::ConstNodeRef d_node = root["distros"];
|
||||||
|
if (d_node.is_map()) {
|
||||||
|
for (ryml::ConstNodeRef child : d_node.children()) {
|
||||||
|
if (child.has_key()) {
|
||||||
|
string distro_name = to_string(child.key());
|
||||||
|
distros[distro_name] = parse_distro(child, "distros." + distro_name, logs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logs.push_back("[WARN] Key 'distros' must be a map of distro configs.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <kmake/kmake.hpp>
|
||||||
|
|
||||||
|
namespace kmake {
|
||||||
|
|
||||||
|
class Distro;
|
||||||
|
|
||||||
|
enum class Verbosity : int {
|
||||||
|
DEBUG = 3, HIGH = 2, NORMAL = 1, LOW = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
class Project {
|
||||||
|
public:
|
||||||
|
|
||||||
|
string name;
|
||||||
|
vector<string> authors;
|
||||||
|
vector<string> descr;
|
||||||
|
Verbosity verbosity;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
string path;
|
||||||
|
vector<string> targets;
|
||||||
|
} src;
|
||||||
|
int threads;
|
||||||
|
} build;
|
||||||
|
|
||||||
|
map<string, string> vars;
|
||||||
|
map<string, Distro> distros;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Project();
|
||||||
|
|
||||||
|
~Project();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool load(const string& filename);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user