◆ accept_client()
static void accept_client |
( |
| ) |
|
|
static |
Accept a connection from a client, get the job it spawned from, get the targets, and mark them as dependencies of the job targets.
Definition at line 2622 of file remake.cpp.
2630 if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0))
2633 std::cerr <<
"Unexpected failure while setting connection with client" << std::endl;
2639 if (ioctlsocket(fd, FIONBIO, &nbio))
goto error2;
2640 #elif defined(LINUX)
2641 int fd = accept4(
socket_fd, NULL, NULL, SOCK_CLOEXEC);
2646 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
return;
2649 client_list::iterator proc =
clients.begin();
2655 std::cerr <<
"Received an ill-formed client message" << std::endl;
2666 std::vector<char> buf;
2668 while (len <
sizeof(
int) + 2 || buf[len - 1] || buf[len - 2])
2670 buf.resize(len + 1024);
2671 ssize_t l = recv(fd, &buf[0] + len, 1024, 0);
2672 if (l <= 0)
goto error;
2678 memcpy(&job_id, &buf[0],
sizeof(
int));
2680 proc->job_id = job_id;
2681 job_map::const_iterator i =
jobs.find(job_id);
2682 if (i ==
jobs.end())
goto error;
2683 DEBUG <<
"receiving request from job " << job_id << std::endl;
2690 char const *p = &buf[0] +
sizeof(int);
2703 if (len == 1)
goto error;
2704 std::string target(p + 1, p + len);
2705 DEBUG <<
"adding dependency " << target <<
" to job\n";
2706 proc->pending.push_back(target);
2707 dep.
deps.insert(target);
2712 if (len == 1)
goto error;
2713 std::string var(p + 1, p + len);
2714 DEBUG <<
"adding variable " << var <<
" to job\n";
2715 last_var = &proc->vars[var];
2721 if (!last_var)
goto error;
2722 last_var->push_back(std::string(p + 1, p + len));
2733 std::cerr <<
"Assignments are ignored unless 'variable-propagation' is enabled" << std::endl;
Referenced by server_loop().
◆ complete_job()
static void complete_job |
( |
int |
job_id, |
|
|
bool |
success, |
|
|
bool |
started = true |
|
) |
| |
|
static |
Handle job completion.
Definition at line 2066 of file remake.cpp.
2068 DEBUG <<
"Completing job " << job_id <<
'\n';
2069 job_map::iterator i =
jobs.find(job_id);
2070 assert(i !=
jobs.end());
2071 string_list const &targets = i->second.rule.targets;
2075 if (show) std::cout <<
"Finished";
2076 for (string_list::const_iterator j = targets.begin(),
2077 j_end = targets.end(); j != j_end; ++j)
2080 if (show) std::cout <<
' ' << *j;
2082 if (show) std::cout << std::endl;
2086 std::cerr <<
"Failed to build";
2087 for (string_list::const_iterator j = targets.begin(),
2088 j_end = targets.end(); j != j_end; ++j)
2090 std::cerr <<
' ' << *j;
2095 DEBUG <<
"Removing " << *j <<
'\n';
2100 std::cerr << std::endl;
Referenced by complete_request(), finalize_job(), and run_script().
◆ complete_request()
static void complete_request |
( |
client_t & |
client, |
|
|
bool |
success |
|
) |
| |
|
static |
Send a reply to a client then remove it. If the client was a dependency client, start the actual script.
Definition at line 2370 of file remake.cpp.
2372 DEBUG_open <<
"Completing request from client of job " << client.
job_id <<
"... ";
2378 job_map::const_iterator i =
jobs.find(client.
job_id);
2379 assert(i !=
jobs.end());
2388 char res = success ? 1 : 0;
2389 send(client.
socket, &res, 1, MSG_NOSIGNAL);
2391 closesocket(client.
socket);
Referenced by handle_clients().
◆ create_server()
static void create_server |
( |
| ) |
|
|
static |
Create a named unix socket that listens for build requests. Also set the REMAKE_SOCKET environment variable that will be inherited by all the job scripts.
Definition at line 2542 of file remake.cpp.
2547 perror(
"Failed to create server");
2557 struct sockaddr_in socket_addr;
2558 socket_addr.sin_family = AF_INET;
2559 socket_addr.sin_addr.s_addr = inet_addr(
"127.0.0.1");
2560 socket_addr.sin_port = 0;
2563 socket_fd = socket(AF_INET, SOCK_STREAM, 0);
2565 if (!SetHandleInformation((HANDLE)
socket_fd, HANDLE_FLAG_INHERIT, 0))
2567 if (bind(
socket_fd, (
struct sockaddr *)&socket_addr,
sizeof(sockaddr_in)))
2569 int len =
sizeof(sockaddr_in);
2570 if (getsockname(
socket_fd, (
struct sockaddr *)&socket_addr, &len))
2572 std::ostringstream buf;
2573 buf << socket_addr.sin_port;
2574 if (!SetEnvironmentVariable(
"REMAKE_SOCKET", buf.str().c_str()))
2576 if (listen(
socket_fd, 1000))
goto error;
2581 sigemptyset(&sigmask);
2582 sigaddset(&sigmask, SIGCHLD);
2583 if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == -1)
goto error;
2584 struct sigaction sa;
2586 sigemptyset(&sa.sa_mask);
2588 if (sigaction(SIGCHLD, &sa, NULL) == -1)
goto error;
2590 if (sigaction(SIGINT, &sa, NULL) == -1)
goto error;
2595 struct sockaddr_un socket_addr;
2597 if (len >=
sizeof(socket_addr.sun_path) - 1)
goto error2;
2598 socket_addr.sun_family = AF_UNIX;
2600 len +=
sizeof(socket_addr.sun_family);
2601 if (setenv(
"REMAKE_SOCKET",
socket_name, 1))
goto error;
2605 socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
2608 socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
2610 if (fcntl(
socket_fd, F_SETFD, FD_CLOEXEC) < 0)
goto error;
2612 if (bind(
socket_fd, (
struct sockaddr *)&socket_addr, len))
2614 if (listen(
socket_fd, 1000))
goto error;
Referenced by server_mode().
◆ finalize_job()
static void finalize_job |
( |
pid_t |
pid, |
|
|
bool |
res |
|
) |
| |
|
static |
Handle child process exit status.
Definition at line 2741 of file remake.cpp.
2743 pid_job_map::iterator i =
job_pids.find(pid);
2745 int job_id = i->second;
Referenced by server_loop().
◆ handle_clients()
static bool handle_clients |
( |
| ) |
|
|
static |
Handle client requests:
- check for running targets that have finished,
- start as many pending targets as allowed,
- complete the request if there are neither running nor pending targets left or if any of them failed.
- Returns
- true if some child processes are still running.
- Postcondition
- If there are pending requests, at least one child process is running.
- Invariant
- New free slots cannot appear during a run, since the only way to decrease running_jobs is finalize_job and the only way to increase waiting_jobs is accept_client. None of these functions are called during a run. So breaking out as soon as there no free slots left is fine.
Definition at line 2427 of file remake.cpp.
2429 DEBUG_open <<
"Handling client requests... ";
2431 bool need_restart =
false;
2433 for (client_list::iterator i =
clients.begin(), i_next = i,
2437 DEBUG_open <<
"Handling client from job " << i->job_id <<
"... ";
2440 for (string_set::iterator j = i->running.begin(), j_next = j,
2441 j_end = i->running.end(); j != j_end; j = j_next)
2444 status_map::const_iterator k =
status.find(*j);
2445 assert(k !=
status.end());
2446 switch (k->second.status)
2457 i->running.erase(j);
2466 while (!i->pending.empty())
2468 std::string target = i->pending.front();
2469 i->pending.pop_front();
2474 i->running.insert(target);
2486 client_list::iterator j = i;
2487 switch (
start(target, i))
2490 goto pending_failed;
2493 j->running.insert(target);
2498 j->running.insert(target);
2503 need_restart =
true;
2513 if (i->running.empty() || i->failed)
2517 DEBUG_close << (i->failed ?
"failed\n" :
"finished\n");
2519 need_restart =
true;
2525 if (need_restart)
goto restart;
2530 std::cerr <<
"Circular dependency detected" << std::endl;
2531 client_list::iterator i =
clients.begin();
Referenced by server_loop().
◆ has_free_slots()
static bool has_free_slots |
( |
| ) |
|
|
static |
◆ prepare_script()
static std::string prepare_script |
( |
job_t const & |
job | ) |
|
|
static |
Return the script obtained by substituting variables.
Definition at line 2108 of file remake.cpp.
2110 std::string
const &s = job.rule.script;
2111 std::istringstream in(s);
2112 std::ostringstream out;
2113 size_t len = s.size();
2117 size_t pos = in.tellg(), p = s.find(
'$', pos);
2118 if (p == std::string::npos || p == len - 1) p = len;
2119 out.write(&s[pos], p - pos);
2120 if (p == len)
break;
2129 if (!job.rule.deps.empty())
2130 out << job.rule.deps.front();
2136 for (string_list::const_iterator i = job.rule.deps.begin(),
2137 i_end = job.rule.deps.end(); i != i_end; ++i)
2139 if (first) first =
false;
2147 assert(!job.rule.targets.empty());
2148 out << job.rule.targets.front();
2169 if (s ==
Eof)
break;
2170 if (first) first =
false;
Referenced by run_script().
◆ run_script()
Execute the script from rule.
Definition at line 2191 of file remake.cpp.
2194 dep->
targets = job.rule.targets;
2195 dep->
deps.insert(job.rule.deps.begin(), job.rule.deps.end());
2197 for (string_list::const_iterator i = job.rule.targets.begin(),
2198 i_end = job.rule.targets.end(); i != i_end; ++i)
2207 std::ostringstream job_id_buf;
2208 job_id_buf << job_id;
2209 std::string job_id_ = job_id_buf.str();
2211 DEBUG_open <<
"Starting script for job " << job_id <<
"... ";
2232 CloseHandle(pfd[0]);
2233 CloseHandle(pfd[1]);
2236 if (!CreatePipe(&pfd[0], &pfd[1], NULL, 0))
2238 if (!SetHandleInformation(pfd[0], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
2241 ZeroMemory(&si,
sizeof(STARTUPINFO));
2242 si.cb =
sizeof(STARTUPINFO);
2243 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
2244 si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
2245 si.hStdInput = pfd[0];
2246 si.dwFlags |= STARTF_USESTDHANDLES;
2247 PROCESS_INFORMATION pi;
2248 ZeroMemory(&pi,
sizeof(PROCESS_INFORMATION));
2249 if (!SetEnvironmentVariable(
"REMAKE_JOB_ID", job_id_.c_str()))
2251 char const *argv =
echo_scripts ?
"SH.EXE -e -s -v" :
"SH.EXE -e -s";
2252 if (!CreateProcess(NULL, (
char *)argv, NULL, NULL,
2253 true, 0, NULL, NULL, &si, &pi))
2257 CloseHandle(pi.hThread);
2258 DWORD len = script.length(), wlen;
2259 if (!WriteFile(pfd[1], script.c_str(), len, &wlen, NULL) || wlen < len)
2260 std::cerr <<
"Unexpected failure while sending script to shell" << std::endl;
2261 CloseHandle(pfd[0]);
2262 CloseHandle(pfd[1]);
2275 if (pipe(pfd) == -1)
2277 if (setenv(
"REMAKE_JOB_ID", job_id_.c_str(), 1))
2279 if (pid_t pid = vfork())
2281 if (pid == -1)
goto error2;
2282 ssize_t len = script.length();
2283 if (write(pfd[1], script.c_str(), len) < len)
2284 std::cerr <<
"Unexpected failure while sending script to shell" << std::endl;
2292 char const *argv[5] = {
"sh",
"-e",
"-s", NULL, NULL };
2300 execve(
"/bin/sh", (
char **)argv,
environ);
2301 _exit(EXIT_FAILURE);
Referenced by complete_request(), and start().
◆ server_loop()
static void server_loop |
( |
| ) |
|
|
static |
Loop until all the jobs have finished.
- Postcondition
- There are no client requests left, not even virtual ones.
Definition at line 2756 of file remake.cpp.
2765 for (pid_job_map::const_iterator i =
job_pids.begin(),
2766 i_end =
job_pids.end(); i != i_end; ++i, ++num)
2770 WSAEVENT aev = WSACreateEvent();
2772 WSAEventSelect(
socket_fd, aev, FD_ACCEPT);
2773 DWORD w = WaitForMultipleObjects(len, h,
false, INFINITE);
2785 bool res = GetExitCodeProcess(pid, &s) && s == 0;
2790 sigemptyset(&emptymask);
2794 int ret = pselect(
socket_fd + 1, &fdset, NULL, NULL, NULL, &emptymask);
2800 while ((pid = waitpid(-1, &
status, WNOHANG)) > 0)
2802 bool res = WIFEXITED(
status) && WEXITSTATUS(
status) == 0;
Referenced by server_mode().
◆ server_mode()
static void server_mode |
( |
std::string const & |
remakefile, |
|
|
string_list const & |
targets |
|
) |
| |
|
static |
Load dependencies and rules, listen to client requests, and loop until all the requests have completed. If Remakefile is obsolete, perform a first run with it only, then reload the rules, and perform a second with the original clients.
Definition at line 2817 of file remake.cpp.
2825 clients.back().pending.push_back(remakefile);
2835 if (!targets.empty())
clients.back().pending = targets;
2848 std::cout <<
"remake: Leaving directory `" <<
prefix_dir <<
'\'' << std::endl;
Referenced by main().
◆ start()
static status_e start |
( |
std::string const & |
target, |
|
|
client_list::iterator & |
current |
|
) |
| |
|
static |
Create a job for target according to the loaded rules. Mark all the targets from the rule as running and reset their dependencies. Inherit variables from current, if enabled. If the rule has dependencies, create a new client to build them just before current, and change current so that it points to it.
Definition at line 2312 of file remake.cpp.
2315 DEBUG_open <<
"Starting job " << job_id <<
" for " << target <<
"... ";
2322 std::cerr <<
"No rule for building " << target << std::endl;
2329 for (string_list::const_iterator i = job.
rule.
targets.begin(),
2335 for (assign_map::const_iterator i = job.
rule.
assigns.begin(),
2338 std::pair<variable_map::iterator, bool> k =
2341 if (i->second.append)
2345 variable_map::const_iterator j =
variables.find(i->first);
2346 if (j !=
variables.end()) v = j->second;
2349 else if (!k.second) v.clear();
2350 v.insert(v.end(), i->second.value.begin(), i->second.value.end());
2355 current->job_id = job_id;
2357 current->pending.insert(current->pending.end(),
2360 current->delayed =
true;
Referenced by handle_clients().
static std::string first_target
static client_list clients
socket_t socket
Socket used to reply to the client (invalid for pseudo clients).
static void save_dependencies()
std::list< std::string > string_list
@ Recheck
Target has an obsolete dependency.
static bool propagate_vars
variable_map vars
Values of local variables.
@ Uptodate
Target is up-to-date.
static bool build_failure
static pid_job_map job_pids
static dependency_map dependencies
string_list targets
Files produced by this rule.
static const status_t & get_status(std::string const &target)
@ Remade
Target was successfully rebuilt.
static void load_rules(std::string const &remakefile)
static void find_rule(job_t &job, std::string const &target)
static int max_active_jobs
static bool changed_prefix_dir
static bool handle_clients()
static status_e run_script(int job_id, job_t const &job)
static socket_t socket_fd
static void accept_client()
@ Todo
Target is missing or obsolete.
static volatile sig_atomic_t got_SIGCHLD
static void finalize_job(pid_t pid, bool res)
@ Failed
Build failed for target.
static rule_list generic_rules
bool delayed
Whether it is a dependency client and a script has to be started on request completion.
static void update_status(std::string const &target)
static rule_map specific_rules
int job_id
Job for which the built script called remake and spawned the client (negative for original clients).
static void complete_job(int job_id, bool success, bool started=true)
static std::string prefix_dir
@ Running
Target is being rebuilt.
static std::string prepare_script(job_t const &job)
static void create_server()
static bool still_need_rebuild(std::string const &target)
assign_map assigns
Assignment of variables.
static variable_map variables
static void sigint_handler(int)
static status_e start(std::string const &target, client_list::iterator ¤t)
string_list deps
Dependencies used for an implicit call to remake at the start of the script.
static void sigchld_handler(int)
static void load_dependencies(std::istream &in)
@ RunningRecheck
Static prerequisites are being rebuilt.
string_list wdeps
Like deps, except that they are not registered as dependencies.
static void complete_request(client_t &client, bool success)
static bool has_free_slots()
rule_t rule
Original rule.
static char * socket_name
static void server_loop()