◆ 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 2691 of file remake.cpp.
2699 if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0))
2702 std::cerr <<
"Unexpected failure while setting connection with client" << std::endl;
2708 if (ioctlsocket(fd, FIONBIO, &nbio))
goto error2;
2709 #elif defined(LINUX)
2710 int fd = accept4(
socket_fd, NULL, NULL, SOCK_CLOEXEC);
2715 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
return;
2718 client_list::iterator proc =
clients.begin();
2724 std::cerr <<
"Received an ill-formed client message" << std::endl;
2735 std::vector<char> buf;
2737 while (len <
sizeof(
int) + 2 || buf[len - 1] || buf[len - 2])
2739 buf.resize(len + 1024);
2740 ssize_t l = recv(fd, &buf[0] + len, 1024, 0);
2741 if (l <= 0)
goto error;
2747 memcpy(&job_id, &buf[0],
sizeof(
int));
2749 proc->job_id = job_id;
2750 job_map::const_iterator i =
jobs.find(job_id);
2751 if (i ==
jobs.end())
goto error;
2752 DEBUG <<
"receiving request from job " << job_id << std::endl;
2759 char const *p = &buf[0] +
sizeof(int);
2772 if (len == 1)
goto error;
2773 std::string target(p + 1, p + len);
2774 DEBUG <<
"adding dependency " << target <<
" to job\n";
2775 proc->pending.push_back(target);
2776 dep.
deps.insert(target);
2781 if (len == 1)
goto error;
2782 std::string var(p + 1, p + len);
2783 DEBUG <<
"adding variable " << var <<
" to job\n";
2784 last_var = &proc->vars[var];
2790 if (!last_var)
goto error;
2791 last_var->push_back(std::string(p + 1, p + len));
2802 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 2133 of file remake.cpp.
2135 DEBUG <<
"Completing job " << job_id <<
'\n';
2136 job_map::iterator i =
jobs.find(job_id);
2137 assert(i !=
jobs.end());
2138 string_list const &targets = i->second.rule.targets;
2142 if (show) std::cout <<
"Finished";
2143 for (string_list::const_iterator j = targets.begin(),
2144 j_end = targets.end(); j != j_end; ++j)
2147 if (show) std::cout <<
' ' << *j;
2149 if (show) std::cout << std::endl;
2153 std::cerr <<
"Failed to build";
2154 for (string_list::const_iterator j = targets.begin(),
2155 j_end = targets.end(); j != j_end; ++j)
2157 std::cerr <<
' ' << *j;
2162 DEBUG <<
"Removing " << *j <<
'\n';
2167 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 2438 of file remake.cpp.
2440 DEBUG_open <<
"Completing request from client of job " << client.
job_id <<
"... ";
2446 job_map::const_iterator i =
jobs.find(client.
job_id);
2447 assert(i !=
jobs.end());
2456 char res = success ? 1 : 0;
2457 send(client.
socket, &res, 1, MSG_NOSIGNAL);
2459 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 2611 of file remake.cpp.
2616 perror(
"Failed to create server");
2626 struct sockaddr_in socket_addr;
2627 socket_addr.sin_family = AF_INET;
2628 socket_addr.sin_addr.s_addr = inet_addr(
"127.0.0.1");
2629 socket_addr.sin_port = 0;
2632 socket_fd = socket(AF_INET, SOCK_STREAM, 0);
2634 if (!SetHandleInformation((HANDLE)
socket_fd, HANDLE_FLAG_INHERIT, 0))
2636 if (bind(
socket_fd, (
struct sockaddr *)&socket_addr,
sizeof(sockaddr_in)))
2638 int len =
sizeof(sockaddr_in);
2639 if (getsockname(
socket_fd, (
struct sockaddr *)&socket_addr, &len))
2641 std::ostringstream buf;
2642 buf << socket_addr.sin_port;
2643 if (!SetEnvironmentVariable(
"REMAKE_SOCKET", buf.str().c_str()))
2645 if (listen(
socket_fd, 1000))
goto error;
2650 sigemptyset(&sigmask);
2651 sigaddset(&sigmask, SIGCHLD);
2652 if (sigprocmask(SIG_BLOCK, &sigmask, &
old_sigmask) == -1)
goto error;
2653 struct sigaction sa;
2655 sigemptyset(&sa.sa_mask);
2657 if (sigaction(SIGCHLD, &sa, NULL) == -1)
goto error;
2659 if (sigaction(SIGINT, &sa, NULL) == -1)
goto error;
2664 struct sockaddr_un socket_addr;
2666 if (len >=
sizeof(socket_addr.sun_path) - 1)
goto error2;
2667 socket_addr.sun_family = AF_UNIX;
2669 len +=
sizeof(socket_addr.sun_family);
2670 if (setenv(
"REMAKE_SOCKET",
socket_name, 1))
goto error;
2674 socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
2677 socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
2679 if (fcntl(
socket_fd, F_SETFD, FD_CLOEXEC) < 0)
goto error;
2681 if (bind(
socket_fd, (
struct sockaddr *)&socket_addr, len))
2683 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 2810 of file remake.cpp.
2812 pid_job_map::iterator i =
job_pids.find(pid);
2814 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 are no free slots left is fine.
Definition at line 2495 of file remake.cpp.
2497 DEBUG_open <<
"Handling client requests... ";
2499 bool need_restart =
false;
2501 for (client_list::iterator i =
clients.begin(), i_next = i,
2502 i_end =
clients.end(); i != i_end; i = i_next)
2506 DEBUG_open <<
"Handling client from job " << i->job_id <<
"... ";
2509 for (string_set::iterator j = i->running.begin(), j_next = j,
2510 j_end = i->running.end(); j != j_end; j = j_next)
2513 status_map::const_iterator k =
status.find(*j);
2514 assert(k !=
status.end());
2515 switch (k->second.status)
2526 i->running.erase(j);
2535 while (!i->pending.empty())
2537 std::string target = i->pending.front();
2538 i->pending.pop_front();
2543 i->running.insert(target);
2555 client_list::iterator j = i;
2556 switch (
start(target, i))
2559 goto pending_failed;
2562 j->running.insert(target);
2567 j->running.insert(target);
2572 need_restart =
true;
2582 if (i->running.empty() || i->failed)
2586 DEBUG_close << (i->failed ?
"failed\n" :
"finished\n");
2588 need_restart =
true;
2594 if (need_restart)
goto restart;
2599 std::cerr <<
"Circular dependency detected" << std::endl;
2600 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 2175 of file remake.cpp.
2177 std::string
const &s = job.rule.script;
2178 std::istringstream in(s);
2179 std::ostringstream out;
2180 size_t len = s.size();
2184 size_t pos = in.tellg(), p = s.find(
'$', pos);
2185 if (p == std::string::npos || p == len - 1) p = len;
2186 out.write(&s[pos], p - pos);
2187 if (p == len)
break;
2196 if (!job.rule.deps.empty())
2197 out << job.rule.deps.front();
2203 for (string_list::const_iterator i = job.rule.deps.begin(),
2204 i_end = job.rule.deps.end(); i != i_end; ++i)
2206 if (first) first =
false;
2214 assert(!job.rule.targets.empty());
2215 out << job.rule.targets.front();
2219 out << job.rule.stem;
2236 if (s ==
Eof)
break;
2237 if (first) first =
false;
Referenced by run_script().
◆ run_script()
Execute the script from rule.
Definition at line 2258 of file remake.cpp.
2261 dep->
targets = job.rule.targets;
2262 dep->
deps.insert(job.rule.deps.begin(), job.rule.deps.end());
2264 for (string_list::const_iterator i = job.rule.targets.begin(),
2265 i_end = job.rule.targets.end(); i != i_end; ++i)
2274 std::ostringstream job_id_buf;
2275 job_id_buf << job_id;
2276 std::string job_id_ = job_id_buf.str();
2278 DEBUG_open <<
"Starting script for job " << job_id <<
"... ";
2299 CloseHandle(pfd[0]);
2300 CloseHandle(pfd[1]);
2303 if (!CreatePipe(&pfd[0], &pfd[1], NULL, 0))
2305 if (!SetHandleInformation(pfd[0], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
2308 ZeroMemory(&si,
sizeof(STARTUPINFO));
2309 si.cb =
sizeof(STARTUPINFO);
2310 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
2311 si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
2312 si.hStdInput = pfd[0];
2313 si.dwFlags |= STARTF_USESTDHANDLES;
2314 PROCESS_INFORMATION pi;
2315 ZeroMemory(&pi,
sizeof(PROCESS_INFORMATION));
2316 if (!SetEnvironmentVariable(
"REMAKE_JOB_ID", job_id_.c_str()))
2318 char const *argv =
echo_scripts ?
"SH.EXE -e -s -v" :
"SH.EXE -e -s";
2319 if (!CreateProcess(NULL, (
char *)argv, NULL, NULL,
2320 true, 0, NULL, NULL, &si, &pi))
2324 CloseHandle(pi.hThread);
2325 DWORD len = script.length(), wlen;
2326 if (!WriteFile(pfd[1], script.c_str(), len, &wlen, NULL) || wlen < len)
2327 std::cerr <<
"Unexpected failure while sending script to shell" << std::endl;
2328 CloseHandle(pfd[0]);
2329 CloseHandle(pfd[1]);
2342 if (pipe(pfd) == -1)
2344 if (setenv(
"REMAKE_JOB_ID", job_id_.c_str(), 1))
2346 if (pid_t pid = vfork())
2348 if (pid == -1)
goto error2;
2349 ssize_t len = script.length();
2350 if (write(pfd[1], script.c_str(), len) < len)
2351 std::cerr <<
"Unexpected failure while sending script to shell" << std::endl;
2359 char const *argv[5] = {
"sh",
"-e",
"-s", NULL, NULL };
2368 execve(
"/bin/sh", (
char **)argv,
environ);
2369 _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 2825 of file remake.cpp.
2834 for (pid_job_map::const_iterator i =
job_pids.begin(),
2835 i_end =
job_pids.end(); i != i_end; ++i, ++num)
2839 WSAEVENT aev = WSACreateEvent();
2841 WSAEventSelect(
socket_fd, aev, FD_ACCEPT);
2842 DWORD w = WaitForMultipleObjects(len, h,
false, INFINITE);
2854 bool res = GetExitCodeProcess(pid, &s) && s == 0;
2859 sigemptyset(&emptymask);
2863 int ret = pselect(
socket_fd + 1, &fdset, NULL, NULL, NULL, &emptymask);
2869 while ((pid = waitpid(-1, &
status, WNOHANG)) > 0)
2871 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 2886 of file remake.cpp.
2894 clients.back().pending.push_back(remakefile);
2904 if (!targets.empty())
clients.back().pending = targets;
2917 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 2380 of file remake.cpp.
2383 DEBUG_open <<
"Starting job " << job_id <<
" for " << target <<
"... ";
2390 std::cerr <<
"No rule for building " << target << std::endl;
2397 for (string_list::const_iterator i = job.
rule.
targets.begin(),
2403 for (assign_map::const_iterator i = job.
rule.
assigns.begin(),
2406 std::pair<variable_map::iterator, bool> k =
2409 if (i->second.append)
2413 variable_map::const_iterator j =
variables.find(i->first);
2414 if (j !=
variables.end()) v = j->second;
2417 else if (!k.second) v.clear();
2418 v.insert(v.end(), i->second.value.begin(), i->second.value.end());
2423 current->job_id = job_id;
2425 current->pending.insert(current->pending.end(),
2428 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 sigset_t old_sigmask
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()