-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbake.cpp
More file actions
233 lines (206 loc) · 7.87 KB
/
Copy pathbake.cpp
File metadata and controls
233 lines (206 loc) · 7.87 KB
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#include "bakelib.hpp"
#include "bake_utilities.hpp"
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <functional>
#include <ext/stdio_filebuf.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
using std::cerr;
using std::cout;
using std::cin;
using std::endl;
using std::function;
using std::getenv;
using std::ifstream;
//using std::setenv;
using std::strcmp;
using std::strlen;
using __gnu_cxx::stdio_filebuf;
//Usage: bake, bake -sub dir, bake target
int main(int argc, char** argv)
{
//Command line parameters
string target = "";
string subdir = "";
string filename = "Bakefile";
//Parse our command line
int i=1;
try
{
while(i<argc)
{
if(strcmp(argv[i],"-f")==0)
{
if(i+1==argc || filename!="Bakefile") throw i;
i++;
filename=argv[i];
}
else if(strcmp(argv[i],"-sub")==0 || subdir!="")
{
if(i+1==argc) throw i;
i++;
subdir=argv[i];
}
else
{
if(target!="") throw i;
target=argv[i];
}
i++;
}
}
catch(int x)
{
cerr << argv[0] << ": Invalid invocation at parameter " << x << endl;
return 1;
}
try {
/*Okay, we've parsed our command line.
Now, let's get cracking.*/
//Create our DepSystem
DepSystem dep_tree;
//If we were called with -sub, do initial sub processing.
if(subdir!="")
{
//Check that the subdir actually exists and is a directory.
struct stat subdir_info;
if(stat(subdir.c_str(),&subdir_info)==-1)
{
cerr << argv[0] << ": Error accessing directory " << subdir << endl;
return 1;
}
//Check that the file we got is actually a directory.
if(!S_ISDIR(subdir_info.st_mode))
{
cerr << argv[0] << ": " << subdir << ": Not a directory.\n";
return 1;
}
//Change our working directory to subdir
/*Note that any use of setenv causes a memory leak, but it's okay
since we only change our directory once. Use putenv if you
want to avoid memory leaks.*/
chdir((string(getenv("PWD"))+"/"+subdir).c_str());
//Construct initial depsystem by reading dep_tree from standard in
//Mutate dep_tree by prefacing symbols with "../"
bakelib::construct_depsystem(dep_tree,[](string symname) noexcept { return string("../")+symname; });
}
//Open our Bakefile
ifstream fin(filename);
//Iteratively augment dep_tree by executing commands in Bakefile
while(fin.good())
{
string next_command = bake_utilities::get_command(fin);
if(next_command=="\n" || next_command[0]=='#')
continue;
pair<int,pid_t> cmd_result = bake_utilities::bakery_execute(next_command,dep_tree);
//Read our pipe
stdio_filebuf<char> child_in_(cmd_result.first,std::ios::in);
istream child_in(&child_in_);
bake_utilities::augment_depsystem(child_in,dep_tree);
//Ensure command completed normally by waiting on child.
siginfo_t child_status;
waitid(P_PID,cmd_result.second,&child_status,WEXITED);
if(child_status.si_code!=CLD_EXITED)
{
cerr << next_command << ": terminated by signal " << child_status.si_status << endl;
return 1;
}
else if(child_status.si_status!=0)
{
cerr << next_command << ": exited with abnormal status " << child_status.si_status << endl;
return 1;
}
}
if(subdir=="")
{
//Start valid, then set to other status later if required.
for(const string& symname : dep_tree.get_symbols())
dep_tree.set_state(symname,DepSystem::VALID);
//Go through and stat every target, setting dep_tree symbol statuses accordingly.
for(const string& symname : dep_tree.get_symbols())
{
struct stat statbuf;
int retval = stat(symname.c_str(),&statbuf);
if(retval!=0)
{
dep_tree.set_state(symname,DepSystem::NONBUILT);
dep_tree.invalidate_dependents(symname);
continue;
}
//See if any of our dependencies was modified after us.
time_t sym_mtime = statbuf.st_mtime;
for(const string& depname : dep_tree.get_dependency_edges(symname))
{
retval = stat(depname.c_str(),&statbuf);
if(retval==0 && sym_mtime < statbuf.st_mtime)
{
dep_tree.set_state(symname,DepSystem::STALE);
dep_tree.invalidate_dependents(symname);
break;
}
}
}
//Actually execute build plan
vector<string> symbols_remaining;
if(target!="")
symbols_remaining = dep_tree.get_build_plan(target);
else
symbols_remaining = dep_tree.get_symbols();
while(symbols_remaining.size())
{
DepSystem temp_deptree = dep_tree;
for(auto i = symbols_remaining.begin(); i!=symbols_remaining.end();)
{
vector<string> build_plan = temp_deptree.get_build_plan(*i);
if(build_plan.size()==1)
dep_tree.build_symbol(*i);
if(build_plan.size()==0)
i = symbols_remaining.erase(i);
else
++i;
}
while(bake_utilities::wait_queue.size())
{
tuple<string,pid_t,time_t> build_result = bake_utilities::wait_queue.front();
string& symname = std::get<0>(build_result);
pid_t& child_pid = std::get<1>(build_result);
time_t& before_build = std::get<2>(build_result);
bake_utilities::wait_queue.pop();
siginfo_t child_status;
waitid(P_PID,child_pid,&child_status,WEXITED);
if(child_status.si_code!=CLD_EXITED || child_status.si_status!=0)
throw StringFunctions::permanent_c_str(symname+": build failure.");
//Okay, build exited normally. Check if file modified.
/*Note: If this behavior is found to sometimes be undesirable, perhaps a global option could disable it.
Then again, if this behavior is found by someone to be undesirable, perhaps that person is doing it wrong.*/
struct stat status;
stat(symname.c_str(),&status);
if(status.st_mtime < before_build)
throw StringFunctions::permanent_c_str(symname+": build appeared to complete successfully but did not modify file.");
}
}
}
else //We were invoked with -sub, so output dep_tree to handler
{
//TODO: need to parrot back anything from standard in unmodified, right?
auto output_mutator = [&subdir](string symname) noexcept
{
if(symname.find("../")==0)
return symname.substr(strlen("../"));
else
return subdir+"/"+symname;
};
bakelib::output_depsystem(cout,dep_tree,output_mutator);
}
}
catch(const char* e)
{
cerr << e << endl;
return 1;
}
return 0;
}