Add build.cxx/sh

This commit is contained in:
Esteban I. RM 2017-10-02 01:36:14 -03:00
parent 4d10173f00
commit c934cf8eb9
3 changed files with 329 additions and 16 deletions

1
.gitignore vendored
View File

@ -36,3 +36,4 @@ font.h
*.swp
*.kate-swp
build.run

327
build.cxx Normal file
View File

@ -0,0 +1,327 @@
// exec
#include <cstdio>
#include <cstddef>
#include <fstream>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>
#include <cstring>
#include <unistd.h>
#include <signal.h>
#include <linux/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dirent.h>
// dependency stuff
#include <string>
#include <vector>
#include <set>
#include <map>
#define READ_END 0
#define WRITE_END 1
namespace SH {
bool process_running(int pid) {
return (kill(pid,0) == 0);
}
std::string exec(std::vector<std::string> cmd, const std::string& input) {
std::string result;
char buffer[2048];
pid_t pid = 0;
int pipefd[2][2];
if (pipe2(pipefd[0], O_CLOEXEC) == -1) {
throw std::runtime_error("exec/pipe (1) failed");
}
if (pipe2(pipefd[1], O_CLOEXEC) == -1) {
throw std::runtime_error("exec/pipe (2) failed");
}
pid = fork();
if (pid == 0) {
const char* arr[cmd.size()+1];
for (unsigned int i=0; i<cmd.size(); i++) {
arr[i] = cmd[i].c_str();
}
arr[cmd.size()] = NULL;
// Child
dup2(pipefd[0][ READ_END], STDIN_FILENO);
dup2(pipefd[1][WRITE_END], STDOUT_FILENO);
dup2(pipefd[1][WRITE_END], STDERR_FILENO);
// fucking awful code, I hate C
execvp(arr[0], (char * const*) arr);
std::cerr << "execl failed " << errno << std::endl;
// Nothing below this line should be executed by child process. If so,
// it means that the execl function wasn't successfull, so lets exit:
_exit(1);
}
close(pipefd[0][READ_END]);
if (input.size() > 0) {
if (write(pipefd[0][WRITE_END], input.c_str(), input.size()) == -1) {
std::cout << "(exec warning, write failed)" << std::endl;
}
}
close(pipefd[0][WRITE_END]);
close(pipefd[1][WRITE_END]);
try {
int i;
while ((i = read(pipefd[1][READ_END], buffer, 2048)) > 0) {
buffer[i] = 0;
result += buffer;
}
} catch(...) {
// zombie
throw;
}
return result;
}
void exec_i(std::string cmd) {
int status = system(cmd.c_str());
if (status != 0) throw std::runtime_error("exec_i/system failed");
}
void mkdir(std::string str) {
DIR* dir = ::opendir(str.c_str());
if (!dir) {
if (ENOENT == errno) {
int status = ::mkdir(str.c_str(), S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP | S_IXUSR | S_IXGRP | S_IROTH);
if (status != 0) throw std::runtime_error("mkdir/mkdir failed");
} else {
throw std::runtime_error("mkdir/opendir failed");
}
}
}
void rm(std::string str) {
remove(str.c_str()); // ignore errors, we don't really care if it didn't exist, or so.
}
};
class Task {
public:
virtual std::string getName() = 0;
virtual std::vector<std::string> dependencies() = 0;
virtual bool task() = 0;
};
class TaskBuild : public Task {
private:
std::string binary;
std::vector<std::string> sources;
std::vector<std::string> optimization;
std::vector<std::string> static_bin;
public:
TaskBuild(std::string binary, std::vector<std::string> sources, int opt, bool static_bin) : binary(binary) {
this->sources = sources;
switch (opt) {
case 1:
optimization = {"-O1"};
break;
case 2:
optimization = {"-O2"};
break;
case 3:
optimization = {"-O3"};
break;
default:
if (opt != 0) {
std::cout << "Invalid optimization mode; assuming 0/debug" << std::endl;
}
optimization = {"-O0","-g"};
break;
}
this->static_bin = static_bin ? std::vector<std::string>{"-static"} : std::vector<std::string>{};
}
std::string getName() { return "build"; }
std::vector<std::string> dependencies() {
return { "fonts" };
}
bool task() {
try {
SH::mkdir("build/");
std::vector<std::string> args = {"g++","-std=c++11","-lm","-o","build/" + binary,"-Iinclude/","-Ifont/"};
args.insert(args.end(), sources.begin(), sources.end());
args.insert(args.end(), optimization.begin(), optimization.end());
args.insert(args.end(), static_bin.begin(), static_bin.end());
SH::exec(args, "");
} catch (std::runtime_error& err) {
return false;
}
return true;
}
};
class TaskFonts : public Task {
private:
std::vector<char> chars;
public:
void add(char start, char end) {
for (char i = start; i <= end; i++) {
chars.push_back(i);
}
}
TaskFonts() {
add('0', '9');
add('A', 'Z');
add('a', 'z');
}
std::string getName() { return "fonts"; }
std::vector<std::string> dependencies() {
return {};
}
bool task() {
SH::mkdir("font");
SH::rm("font/font.h");
std::ofstream f("font/font.h");
for (auto c : chars) {
std::string s(1, c);
std::string xbm = SH::exec({"convert","-font","./letvezi.ttf","-resize","16x32!", "-pointsize","14","label:"+s, "xbm:-"}, "");
std::string tail = SH::exec({"tail", "--lines=7"}, xbm);
std::string sed = SH::exec({"sed","s/^static char \\(.*\\)_bits\\[\\]/bits['" + s +"']/"}, tail);
f << sed << std::endl;
}
return true;
}
};
class TaskManager {
private:
std::map<std::string, std::shared_ptr<Task>> deps;
bool sanity_internal(const std::string& start, std::map<std::string,std::string> visited, const std::string& requester) {
bool failed = false;
if (deps[start] == NULL) {
/* dependency not found */
std::cout << "Task not found: " << start << " (required by " << requester << ")" << std::endl;
return true;
}
visited[start] = requester;
for (auto dep : deps[start]->dependencies()) {
if (visited[dep] != "") {
std::cout << "Loop found" << std::endl;
std::cout << "Task " << dep << " requested by " << requester << std::endl;
std::string curr = dep;
while (visited[dep] != "") {
std::cout << "Task " << visited[dep] << " requested by " << dep << std::endl;
}
std::cout << "---------" << std::endl;
failed = true;
} else {
failed |= sanity_internal(dep, visited, start);
// if we are here
visited[dep] = "";
}
}
return failed;
}
bool sanity(const std::string& start) {
std::map<std::string,std::string> visited;
return !sanity_internal(start, visited, "<<user>>");
}
/* run_internal assumes there are no loops, and all tasks exist */
bool run_internal(std::string task, bool ignore_deps, std::set<std::string> finished) {
if (finished.find(task) != finished.end()) return true; // task has been completed already
bool deps_ok = true;
if (!ignore_deps) {
std::vector<std::string> deps_v = deps[task]->dependencies();
for (auto dep : deps_v) {
std::cout << "[NEEDED] " << task << " requires " << dep << std::endl;
deps_ok &= run_internal(dep, false, finished);
}
} else {
std::cout << "[WARNING] Assuming dependencies have been met already" << std::endl;
}
if (!deps_ok) {
std::cout << "Some dependencies have failed, cannot continue" << std::endl;
return false;
}
std::cout << "[RES] Task '" << task << "' | ";
bool task_ret = deps[task]->task();
if (task_ret) {
std::cout << "OK :)" << std::endl;
} else {
std::cout << "failed :(" << std::endl;
}
return task_ret;
}
public:
TaskManager() {
std::cout.setf(std::ios::unitbuf);
};
void list_tasks() {
std::cout << "Available tasks: " << std::endl;
for (auto task : deps) {
std::cout << "\t" << task.second->getName() << std::endl;
}
}
void add(std::shared_ptr<Task> task) {
if (task == NULL) {
std::cout << "NULL Task added (BROKEN CODE)" << std::endl;
return;
}
if (deps[task->getName()] != NULL) {
std::cout << "A dependency with the name " << task->getName() << " already exists" << std::endl;
return;
}
deps[task->getName()] = task;
}
void run(std::string str, bool ignore_deps) {
if (!sanity(str)) {
return;
}
std::set<std::string> finished;
run_internal(str, ignore_deps,finished);
}
};
int main(int argc, char** argv) {
TaskManager man;
std::vector<std::string> sources = {
"main.cxx",
"src/Color.cxx",
"src/DoubleBuffering.cxx",
"src/FontManager.cxx",
"src/Pos.cxx",
"src/RawFB.cxx",
"src/Toolkit.cxx"
};
man.add(std::shared_ptr<Task>(new TaskFonts()));
man.add(std::shared_ptr<Task>(new TaskBuild("fb", sources, 0, false)));
std::string task = "build";
bool ignore = false;
if (argc > 1) {
if(strcmp(argv[1],"ignore_deps") == 0) {
ignore = true;
if (argc > 2) task = argv[2];
} else {
task = argv[1];
}
}
man.run(task, ignore);
}

View File

@ -1,18 +1,3 @@
#!/bin/bash
mkdir -p build/
mkdir -p font/
if [[ "$1" != "ignore_fonts" ]]; then
rm -f font/font.h
for f in {a..z} {A..Z} {0..9}; do
convert -resize 16x32\! -font ./letvezi.ttf -pointsize 14 label:"$f" "$f".xbm
cat "$f".xbm | tail --lines=7 | sed 's/^static char \(.*\)_bits\[\]/bits['"'\1'"']/' >> font/font.h
rm "$f".xbm
done
fi
g++ -o build/fb -Iinclude/ -Ifont/ main.cxx src/* -O0 -g -lm -static -std=c++11
g++ -std=c++11 -o build.run build.cxx -O1 && ./build.run $@