-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhotmake.cpp
146 lines (140 loc) · 5.16 KB
/
hotmake.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <stdlib.h>
#include <unistd.h>
#include <array>
#include <cstdio>
#include <exception>
#include <future>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#include "ClangError.h"
#include "MakeRecord.h"
#include "inotify-cxx/inotify-cxx.h"
#define VERSION "1.0"
using std::async;
using std::cerr;
using std::cout;
using std::endl;
using std::exception;
using std::string;
using std::system;
using std::vector;
/**
* Prints the CLI usage to the terminal and returns
* error code of 1.
*/
int printUsage(char* executable) {
cerr << "Usage: " << executable << " [make rule]\n";
return 1;
}
/**
* Helps print interactive lines to the terminal which overwrite each other.
* Flushes cout and uses carrage returns.
*/
void printInteractive(string str) {
/* Keep track of last string printed to display so can be overwritten */
static string lastPrint = str;
cout << "\r";
/* Print over last interactive line */
for (size_t i = 0; i < lastPrint.size(); i++) cout << " ";
/* Print new line over existing one */
cout << "\r" << str;
std::flush(cout);
lastPrint = str;
}
/**
* POSIX execution wrapper to enable stdout capture.
* Originally Posted on StackOverflow:
* https://stackoverflow.com/questions/478898/how-do-i-execute-a-command-and-get-output-of-command-within-c-using-posix
*/
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) throw std::runtime_error("execution of target failed.");
while (fgets(buffer.data(), buffer.size(), pipe.get()) != NULL) {
result += buffer.data();
}
return result;
}
int main(int argc, char* argv[]) {
/* Primitive exception handling */
try {
MakeRecord makefile;
auto [target, deps] = (argc >= 2) ? makefile.getRule(argv[1]) : makefile.getRule();
/* Assemble executable command */
const char* executable = string("./" + target).c_str();
/* Watch all dependent files */
Inotify notify;
system("clear"); /* Clear terminal window */
cout << "\033[1;37mHotmake v" << VERSION << ":\033[0m \033[4m" << target << "\033[0m" << endl;
for (string dep : deps) {
cout << " » " << dep << endl;
dep = "./" + dep;
InotifyWatch watch(dep, IN_MODIFY);
notify.Add(watch);
}
auto interactiveBuild = [&]() {
string buildResult;
bool buildFailed = false;
bool buildComplete = false;
auto build = async(std::launch::async, [&] {
buildResult = exec("make --silent 2>&1"); /* Redirect stderr > stdout */
/* Primitive error checking - fix later */
if (buildResult.find("fail") != buildResult.npos) {
buildFailed = true;
}
buildComplete = true;
});
auto printProgress = async(std::launch::async, [&] {
string color_building = "\033[1;93m";
string color_complete = "\033[1;32m";
string color_failed = "\033[1;31m";
string base = "Building changes";
printInteractive(color_building + " " + base);
/* Spinner animation courtesy of cli-spinners on GitHub :) - thank you! */
vector<string> spinner = {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"};
unsigned frame = 0;
while (!buildComplete) {
cout << "\r" << color_building << spinner[frame] << " " << base;
std::flush(cout);
frame = (++frame) % spinner.size();
usleep(70000);
}
if (buildFailed) {
string failMessage = "✖ ";
/* Process stderr of make command */
ClangError result(buildResult);
/* Primitive display of errors using printInteractice - fix later */
for (ClangError::ERR err : result.getErrors()) {
failMessage += err.file + "[" + err.row + "]: " + err.type + ": " + err.details;
}
printInteractive(color_failed + failMessage);
} else {
printInteractive(color_complete + "✔ Build complete");
}
});
};
cout << endl;
interactiveBuild(); /* Run build on startup */
/* Watch for file updates and squash redundant triggers */
while (notify.WaitForEvents() && notify.WaitForEvents()) {
if (0 != notify.GetEventCount()) interactiveBuild();
}
} catch (const char* e) {
cerr << e << endl;
return 1;
} catch (InotifyException& e) {
cerr << "Inotify exception occurred: " << e.GetMessage() << endl;
return 1;
} catch (exception& e) {
cerr << "STL exception occurred: " << e.what() << endl;
return 1;
} catch (...) {
cerr << "unknown exception occurred" << endl;
return 1;
}
return 0;
}