419#define WIN32_LEAN_AND_MEAN
440#include <sys/types.h>
457#include <sys/socket.h>
465#if (defined(WINDOWS) || defined(MACOSX)) && !defined(MSG_NOSIGNAL)
466enum { MSG_NOSIGNAL = 0 };
495 if (
ptr == p.
ptr)
return *
this;
570typedef std::map<std::string, ref_ptr<rule_t> >
rule_map;
729static time_t
now = time(NULL);
783 if (
open) std::cerr << std::endl;
785 std::cerr << std::string(
depth * 2,
' ');
791 if (o &&
open) std::cerr << std::endl;
794 if (o || !
open) std::cerr << std::string(
depth * 2,
' ');
815#define DEBUG if (debug.active) debug()
816#define DEBUG_open log_auto_close auto_close; if (debug.active) debug(true)
817#define DEBUG_close if ((auto_close.still_open = false), debug.active) debug(false)
836 std::string
const &s = se.
input;
837 char const *quoted_char =
",: '";
838 char const *escaped_char =
"\"\\$!";
839 bool need_quotes =
false;
841 size_t len = s.length(), last = 0, j = 0;
842 for (
size_t i = 0; i < len; ++i)
844 if (strchr(escaped_char, s[i]))
847 if (!buf) buf =
new char[len * 2];
848 memcpy(&buf[j], &s[last], i - last);
854 if (!need_quotes && strchr(quoted_char, s[i]))
857 if (!need_quotes)
return out << s;
859 if (!buf)
return out << s <<
'"';
861 out.write(&s[last], len - last);
878 char *res = getcwd(buf,
sizeof(buf));
881 perror(
"Failed to get working directory");
886 for (
size_t i = 0, l =
working_dir.size(); i != l; ++i)
902 if (stat((
prefix_dir +
"/Remakefile").c_str(), &s) == 0)
907 perror(
"Failed to change working directory");
912 std::cout <<
"remake: Entering directory `" <<
prefix_dir <<
'\'' << std::endl;
917 if (pos == std::string::npos)
919 std::cerr <<
"Failed to locate Remakefile in the current directory or one of its parents" << std::endl;
933 size_t l = p.length();
934 if (s.compare(0, l, p))
return s;
935 size_t ll = s.length();
936 if (ll == l)
return ".";
939 size_t pos = s.rfind(
'/', l);
940 assert(pos != std::string::npos);
941 return s.substr(pos + 1);
943 if (ll == l + 1)
return ".";
944 return s.substr(l + 1);
953static std::string
normalize(std::string
const &s, std::string
const &w, std::string
const &p)
956 char const *delim =
"/\\";
960 size_t pos = s.find_first_of(delim);
961 if (pos == std::string::npos && w == p)
return s;
962 bool absolute = pos == 0;
963 if (!absolute && w != p && !w.empty())
965 size_t prev = 0, len = s.length();
971 std::string n = s.substr(prev, pos - prev);
974 if (!l.empty()) l.pop_back();
975 else if (!absolute && !w.empty())
982 if (pos >= len)
break;
984 pos = s.find_first_of(delim, prev);
985 if (pos == std::string::npos) pos = len;
987 string_list::const_iterator i = l.begin(), i_end = l.end();
988 if (i == i_end)
return absolute ?
"/" :
".";
990 if (absolute) n.push_back(
'/');
992 for (++i; i != i_end; ++i)
1006 for (string_list::iterator i = l.begin(),
1007 i_end = l.end(); i != i_end; ++i)
1027 while (strchr(
" \t", (c = in.get()))) {}
1028 if (in.good()) in.putback(c);
1037 while (strchr(
"\r\n", (c = in.get()))) {}
1038 if (in.good()) in.putback(c);
1045static bool skip_eol(std::istream &in,
bool multi =
false)
1048 if (c ==
'\r') c = in.get();
1049 if (c !=
'\n' && in.good()) in.putback(c);
1050 if (c !=
'\n' && !in.eof())
return false;
1086 case ':': tok =
Colon;
break;
1087 case ',': tok =
Comma;
break;
1088 case '=': tok =
Equal;
break;
1090 case '|': tok =
Pipe;
break;
1120static std::string
read_word(std::istream &in,
bool detect_equal =
true)
1124 if (!in.good())
return res;
1125 char const *separators =
" \t\r\n$(),:";
1126 bool quoted = c ==
'"';
1127 if (quoted) in.ignore(1);
1132 if (!in.good())
return res;
1144 if (detect_equal && c ==
'=')
1146 if (plus) in.putback(
'+');
1154 if (strchr(separators, c))
return res;
1156 if (detect_equal && c ==
'+') plus =
true;
1202 if (local_variables)
1204 variable_map::const_iterator i = local_variables->find(
name);
1205 if (i != local_variables->end())
1207 vcur = i->second.begin();
1208 vend = i->second.end();
1214 vcur = i->second.begin();
1215 vend = i->second.end();
1290 res.push_back(std::string());
1319 : gen(top.in, top.local_variables)
1375 : gen(top.in, top.local_variables)
1421 if (!g || ok)
return g;
1442 std::cerr <<
"Failed to load database" << std::endl;
1450 if (in.eof())
return;
1451 if (targets.empty())
goto error;
1452 DEBUG <<
"reading dependencies of target " << targets.front() << std::endl;
1453 if (in.get() !=
':')
goto error;
1455 dep->targets = targets;
1458 dep->deps.insert(deps.begin(), deps.end());
1459 for (string_list::const_iterator i = targets.begin(),
1460 i_end = targets.end(); i != i_end; ++i)
1474 std::ifstream in(
".remake");
1490 std::ofstream db(
".remake");
1494 for (string_list::const_iterator i = dep->targets.begin(),
1495 i_end = dep->targets.end(); i != i_end; ++i)
1501 for (string_set::const_iterator i = dep->deps.begin(),
1502 i_end = dep->deps.end(); i != i_end; ++i)
1531 assert(rule.
script.empty());
1532 for (string_list::const_iterator i = targets.begin(),
1533 i_end = targets.end(); i != i_end; ++i)
1535 std::pair<rule_map::iterator, bool> j =
1544 if (!r->script.empty())
1546 std::cerr <<
"Failed to load rules: " << *i
1547 <<
" cannot be the target of several rules" << std::endl;
1550 assert(r->targets.size() == 1 && r->targets.front() == *i);
1554 for (string_list::const_iterator i = targets.begin(),
1555 i_end = targets.end(); i != i_end; ++i)
1558 if (dep->targets.empty()) dep->targets.push_back(*i);
1559 dep->deps.insert(rule.
deps.begin(), rule.
deps.end());
1575 for (string_list::const_iterator i = rule.
targets.begin(),
1576 i_end = rule.
targets.end(); i != i_end; ++i)
1578 std::pair<rule_map::iterator, bool> j =
1580 if (j.second)
continue;
1581 std::cerr <<
"Failed to load rules: " << *i
1582 <<
" cannot be the target of several rules" << std::endl;
1588 dep->deps.insert(rule.
deps.begin(), rule.
deps.end());
1589 for (string_list::const_iterator i = rule.
targets.begin(),
1590 i_end = rule.
targets.end(); i != i_end; ++i)
1593 dep->deps.insert(d->deps.begin(), d->deps.end());
1603 if (!rule.
script.empty())
1626static void load_rule(std::istream &in, std::string
const &first)
1628 DEBUG_open <<
"Reading rule for target " << first <<
"... ";
1633 std::cerr <<
"Failed to load rules: syntax error" << std::endl;
1640 if (!first.empty()) targets.push_front(first);
1641 else if (targets.empty())
goto error;
1642 else DEBUG <<
"actual target: " << targets.front() << std::endl;
1643 bool generic =
false;
1645 for (string_list::const_iterator i = targets.begin(),
1646 i_end = targets.end(); i != i_end; ++i)
1648 if (i->empty())
goto error;
1649 if ((i->find(
'%') != std::string::npos) != generic)
1651 if (i == targets.begin())
generic =
true;
1656 if (in.get() !=
':')
goto error;
1658 bool assignment =
false, static_pattern =
false;
1692 if (rule.
targets.empty())
goto error;
1693 for (string_list::const_iterator i = rule.
targets.begin(),
1694 i_end = rule.
targets.end(); i != i_end; ++i)
1696 if (i->find(
'%') == std::string::npos)
goto error;
1699 static_pattern =
true;
1712 if (!
skip_eol(in,
true))
goto error;
1715 std::ostringstream buf;
1719 if (!in.good())
break;
1720 if (c ==
'\t' || c ==
' ')
1722 in.get(*buf.rdbuf());
1723 if (in.fail() && !in.eof()) in.clear();
1725 else if (c ==
'\r' || c ==
'\n')
1736 if (rule.
targets.front() ==
".PHONY")
1738 for (string_list::const_iterator i = rule.
deps.begin(),
1739 i_end = rule.
deps.end(); i != i_end; ++i)
1749 if (assignment)
goto error;
1754 if (!static_pattern)
1756 if (!rule.
script.empty() && assignment)
goto error;
1761 for (string_list::const_iterator i = targets.begin(),
1762 i_end = targets.end(); i != i_end; ++i)
1781 std::cerr <<
"Failed to load rules: syntax error" << std::endl;
1784 std::ifstream in(remakefile.c_str());
1787 std::cerr <<
"Failed to load rules: no Remakefile found" << std::endl;
1800 while (in.get() !=
'\n') {}
1804 if (c ==
' ' || c ==
'\t')
goto error;
1808 if (name.empty())
goto error;
1811 DEBUG <<
"Assignment to variable " << name << std::endl;
1815 *(name ==
".OPTIONS" ? &options : &
variables[name]);
1816 if (tok ==
Equal) dest.swap(value);
1817 else dest.splice(dest.end(), value);
1818 if (!
skip_eol(in,
true))
goto error;
1826 for (string_list::const_iterator i = options.begin(),
1827 i_end = options.end(); i != i_end; ++i)
1832 std::cerr <<
"Failed to load rules: unrecognized option" << std::endl;
1850 for (assign_map::const_iterator i = src.
assigns.begin(),
1851 i_end = src.
assigns.end(); i != i_end; ++i)
1853 if (!i->second.append)
1856 dest.
assigns[i->first] = i->second;
1859 assign_map::iterator j = dest.
assigns.find(i->first);
1860 if (j == dest.
assigns.end())
goto new_assign;
1861 j->second.value.insert(j->second.value.end(),
1862 i->second.value.begin(), i->second.value.end());
1871 for (string_list::const_iterator i = src.begin(),
1872 i_end = src.end(); i != i_end; ++i)
1874 size_t pos = i->find(
'%');
1875 if (pos == std::string::npos) dst.push_back(*i);
1876 else dst.push_back(i->substr(0, pos) + pat + i->substr(pos + 1));
1887 size_t tlen = target.length(), plen = dst.
stem.length();
1888 for (string_list::const_iterator j = src.
targets.begin(),
1889 j_end = src.
targets.end(); j != j_end; ++j)
1891 size_t len = j->length();
1892 if (tlen < len)
continue;
1893 if (plen && plen <= tlen - (len - 1))
continue;
1894 size_t pos = j->find(
'%');
1895 if (pos == std::string::npos)
continue;
1896 size_t len2 = len - (pos + 1);
1897 if (j->compare(0, pos, target, 0, pos) ||
1898 j->compare(pos + 1, len2, target, tlen - len2, len2))
1900 plen = tlen - (len - 1);
1902 dst.stem = target.substr(pos, plen);
1935 if (i != i_end && !i->second->script.empty())
1937 job.
rule = *i->second;
1946 job.
rule = *i->second;
1953 if (i == i_end)
return;
1959 for (string_list::const_iterator j = job.
rule.
targets.begin(),
1963 if (i == i_end)
continue;
1964 if (!i->second->script.empty())
return;
1989 std::pair<status_map::iterator,bool> i =
1992 if (!i.second)
return ts;
1993 DEBUG_open <<
"Checking status of " << target <<
"... ";
1994 dependency_map::const_iterator j =
dependencies.find(target);
1998 if (stat(target.c_str(), &s) != 0)
2007 ts.
last = s.st_mtime;
2020 for (string_list::const_iterator k = dep.
targets.begin(),
2021 k_end = dep.
targets.end(); k != k_end; ++k)
2024 if (stat(k->c_str(), &s) != 0)
2030 status[*k].last = s.st_mtime;
2031 if (s.st_mtime > latest) latest = s.st_mtime;
2034 for (string_set::const_iterator k = dep.
deps.begin(),
2035 k_end = dep.
deps.end(); k != k_end; ++k)
2038 if (latest < ts_.
last)
2046 DEBUG <<
"obsolete dependency " << *k << std::endl;
2052 for (string_list::const_iterator k = dep.
targets.begin(),
2053 k_end = dep.
targets.end(); k != k_end; ++k)
2066 DEBUG_open <<
"Rechecking status of " << target <<
"... ";
2067 status_map::iterator i =
status.find(target);
2068 assert(i !=
status.end());
2077 if (stat(target.c_str(), &s) != 0)
2082 else if (s.st_mtime != ts.
last)
2085 ts.
last = s.st_mtime;
2099 status_map::const_iterator i =
status.find(target);
2100 assert(i !=
status.end());
2102 DEBUG_open <<
"Rechecking obsoleteness of " << target <<
"... ";
2103 dependency_map::const_iterator j =
dependencies.find(target);
2106 for (string_set::const_iterator k = dep.
deps.begin(),
2107 k_end = dep.
deps.end(); k != k_end; ++k)
2111 for (string_list::const_iterator k = dep.
targets.begin(),
2112 k_end = dep.
targets.end(); k != k_end; ++k)
2133 DEBUG <<
"Completing job " << job_id <<
'\n';
2134 job_map::iterator i =
jobs.find(job_id);
2135 assert(i !=
jobs.end());
2136 string_list const &targets = i->second.rule.targets;
2140 if (show) std::cout <<
"Finished";
2141 for (string_list::const_iterator j = targets.begin(),
2142 j_end = targets.end(); j != j_end; ++j)
2145 if (show) std::cout <<
' ' << *j;
2147 if (show) std::cout << std::endl;
2151 std::cerr <<
"Failed to build";
2152 for (string_list::const_iterator j = targets.begin(),
2153 j_end = targets.end(); j != j_end; ++j)
2155 std::cerr <<
' ' << *j;
2160 DEBUG <<
"Removing " << *j <<
'\n';
2165 std::cerr << std::endl;
2176 std::istringstream in(s);
2177 std::ostringstream out;
2178 size_t len = s.size();
2182 size_t pos = in.tellg(), p = s.find(
'$', pos);
2183 if (p == std::string::npos || p == len - 1) p = len;
2184 out.write(&s[pos], p - pos);
2185 if (p == len)
break;
2201 for (string_list::const_iterator i = job.
rule.
deps.begin(),
2202 i_end = job.
rule.
deps.end(); i != i_end; ++i)
2204 if (first) first =
false;
2234 if (s ==
Eof)
break;
2235 if (first) first =
false;
2262 for (string_list::const_iterator i = job.
rule.
targets.begin(),
2272 std::ostringstream job_id_buf;
2273 job_id_buf << job_id;
2274 std::string job_id_ = job_id_buf.str();
2276 DEBUG_open <<
"Starting script for job " << job_id <<
"... ";
2297 CloseHandle(pfd[0]);
2298 CloseHandle(pfd[1]);
2301 if (!CreatePipe(&pfd[0], &pfd[1], NULL, 0))
2303 if (!SetHandleInformation(pfd[0], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
2306 ZeroMemory(&si,
sizeof(STARTUPINFO));
2307 si.cb =
sizeof(STARTUPINFO);
2308 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
2309 si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
2310 si.hStdInput = pfd[0];
2311 si.dwFlags |= STARTF_USESTDHANDLES;
2312 PROCESS_INFORMATION pi;
2313 ZeroMemory(&pi,
sizeof(PROCESS_INFORMATION));
2314 if (!SetEnvironmentVariable(
"REMAKE_JOB_ID", job_id_.c_str()))
2316 char const *argv =
echo_scripts ?
"SH.EXE -e -s -v" :
"SH.EXE -e -s";
2317 if (!CreateProcess(NULL, (
char *)argv, NULL, NULL,
2318 true, 0, NULL, NULL, &si, &pi))
2322 CloseHandle(pi.hThread);
2323 DWORD len = script.length(), wlen;
2324 if (!WriteFile(pfd[1], script.c_str(), len, &wlen, NULL) || wlen < len)
2325 std::cerr <<
"Unexpected failure while sending script to shell" << std::endl;
2326 CloseHandle(pfd[0]);
2327 CloseHandle(pfd[1]);
2340 if (pipe(pfd) == -1)
2342 if (setenv(
"REMAKE_JOB_ID", job_id_.c_str(), 1))
2344 if (pid_t pid = vfork())
2346 if (pid == -1)
goto error2;
2347 ssize_t len = script.length();
2348 if (write(pfd[1], script.c_str(), len) < len)
2349 std::cerr <<
"Unexpected failure while sending script to shell" << std::endl;
2357 char const *argv[5] = {
"sh",
"-e",
"-s", NULL, NULL };
2366 execve(
"/bin/sh", (
char **)argv,
environ);
2367 _exit(EXIT_FAILURE);
2381 DEBUG_open <<
"Starting job " << job_id <<
" for " << target <<
"... ";
2388 std::cerr <<
"No rule for building " << target << std::endl;
2395 for (string_list::const_iterator i = job.
rule.
targets.begin(),
2401 for (assign_map::const_iterator i = job.
rule.
assigns.begin(),
2404 std::pair<variable_map::iterator, bool> k =
2407 if (i->second.append)
2411 variable_map::const_iterator j =
variables.find(i->first);
2412 if (j !=
variables.end()) v = j->second;
2415 else if (!k.second) v.clear();
2416 v.insert(v.end(), i->second.value.begin(), i->second.value.end());
2421 current->job_id = job_id;
2423 current->pending.insert(current->pending.end(),
2426 current->delayed =
true;
2438 DEBUG_open <<
"Completing request from client of job " << client.
job_id <<
"... ";
2444 job_map::const_iterator i =
jobs.find(client.
job_id);
2445 assert(i !=
jobs.end());
2454 char res = success ? 1 : 0;
2455 send(client.
socket, &res, 1, MSG_NOSIGNAL);
2457 closesocket(client.
socket);
2495 DEBUG_open <<
"Handling client requests... ";
2497 bool need_restart =
false;
2499 for (client_list::iterator i =
clients.begin(), i_next = i,
2500 i_end =
clients.end(); i != i_end; i = i_next)
2504 DEBUG_open <<
"Handling client from job " << i->job_id <<
"... ";
2507 for (string_set::iterator j = i->running.begin(), j_next = j,
2508 j_end = i->running.end(); j != j_end; j = j_next)
2511 status_map::const_iterator k =
status.find(*j);
2512 assert(k !=
status.end());
2513 switch (k->second.status)
2524 i->running.erase(j);
2533 while (!i->pending.empty())
2535 std::string target = i->pending.front();
2536 i->pending.pop_front();
2541 i->running.insert(target);
2553 client_list::iterator j = i;
2554 switch (
start(target, i))
2557 goto pending_failed;
2560 j->running.insert(target);
2565 j->running.insert(target);
2570 need_restart =
true;
2580 if (i->running.empty() || i->failed)
2584 DEBUG_close << (i->failed ?
"failed\n" :
"finished\n");
2586 need_restart =
true;
2592 if (need_restart)
goto restart;
2597 std::cerr <<
"Circular dependency detected" << std::endl;
2598 client_list::iterator i =
clients.begin();
2614 perror(
"Failed to create server");
2624 struct sockaddr_in socket_addr;
2625 socket_addr.sin_family = AF_INET;
2626 socket_addr.sin_addr.s_addr = inet_addr(
"127.0.0.1");
2627 socket_addr.sin_port = 0;
2630 socket_fd = socket(AF_INET, SOCK_STREAM, 0);
2632 if (!SetHandleInformation((HANDLE)
socket_fd, HANDLE_FLAG_INHERIT, 0))
2634 if (bind(
socket_fd, (
struct sockaddr *)&socket_addr,
sizeof(sockaddr_in)))
2636 int len =
sizeof(sockaddr_in);
2637 if (getsockname(
socket_fd, (
struct sockaddr *)&socket_addr, &len))
2639 std::ostringstream buf;
2640 buf << socket_addr.sin_port;
2641 if (!SetEnvironmentVariable(
"REMAKE_SOCKET", buf.str().c_str()))
2643 if (listen(
socket_fd, 1000))
goto error;
2648 sigemptyset(&sigmask);
2649 sigaddset(&sigmask, SIGCHLD);
2650 if (sigprocmask(SIG_BLOCK, &sigmask, &
old_sigmask) == -1)
goto error;
2651 struct sigaction sa;
2653 sigemptyset(&sa.sa_mask);
2655 if (sigaction(SIGCHLD, &sa, NULL) == -1)
goto error;
2657 if (sigaction(SIGINT, &sa, NULL) == -1)
goto error;
2662 struct sockaddr_un socket_addr;
2664 if (len >=
sizeof(socket_addr.sun_path) - 1)
goto error2;
2665 socket_addr.sun_family = AF_UNIX;
2667 len +=
sizeof(socket_addr.sun_family);
2668 if (setenv(
"REMAKE_SOCKET",
socket_name, 1))
goto error;
2672 socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
2675 socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
2677 if (fcntl(
socket_fd, F_SETFD, FD_CLOEXEC) < 0)
goto error;
2679 if (bind(
socket_fd, (
struct sockaddr *)&socket_addr, len))
2681 if (listen(
socket_fd, 1000))
goto error;
2697 if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0))
2700 std::cerr <<
"Unexpected failure while setting connection with client" << std::endl;
2706 if (ioctlsocket(fd, FIONBIO, &nbio))
goto error2;
2708 int fd = accept4(
socket_fd, NULL, NULL, SOCK_CLOEXEC);
2713 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
return;
2716 client_list::iterator proc =
clients.begin();
2722 std::cerr <<
"Received an ill-formed client message" << std::endl;
2733 std::vector<char> buf;
2735 while (len <
sizeof(
int) + 2 || buf[len - 1] || buf[len - 2])
2737 buf.resize(len + 1024);
2738 ssize_t l = recv(fd, &buf[0] + len, 1024, 0);
2739 if (l <= 0)
goto error;
2745 memcpy(&job_id, &buf[0],
sizeof(
int));
2747 proc->job_id = job_id;
2748 job_map::const_iterator i =
jobs.find(job_id);
2749 if (i ==
jobs.end())
goto error;
2750 DEBUG <<
"receiving request from job " << job_id << std::endl;
2757 char const *p = &buf[0] +
sizeof(int);
2770 if (len == 1)
goto error;
2771 std::string target(p + 1, p + len);
2772 DEBUG <<
"adding dependency " << target <<
" to job\n";
2773 proc->pending.push_back(target);
2774 dep.
deps.insert(target);
2779 if (len == 1)
goto error;
2780 std::string var(p + 1, p + len);
2781 DEBUG <<
"adding variable " << var <<
" to job\n";
2782 last_var = &proc->vars[var];
2788 if (!last_var)
goto error;
2789 last_var->push_back(std::string(p + 1, p + len));
2800 std::cerr <<
"Assignments are ignored unless 'variable-propagation' is enabled" << std::endl;
2810 pid_job_map::iterator i =
job_pids.find(pid);
2812 int job_id = i->second;
2832 for (pid_job_map::const_iterator i =
job_pids.begin(),
2833 i_end =
job_pids.end(); i != i_end; ++i, ++num)
2837 WSAEVENT aev = WSACreateEvent();
2839 WSAEventSelect(
socket_fd, aev, FD_ACCEPT);
2840 DWORD w = WaitForMultipleObjects(len, h,
false, INFINITE);
2852 bool res = GetExitCodeProcess(pid, &s) && s == 0;
2857 sigemptyset(&emptymask);
2861 int ret = pselect(
socket_fd + 1, &fdset, NULL, NULL, NULL, &emptymask);
2867 while ((pid = waitpid(-1, &
status, WNOHANG)) > 0)
2869 bool res = WIFEXITED(
status) && WEXITSTATUS(
status) == 0;
2893 clients.back().pending.push_back(remakefile);
2903 if (!targets.empty())
clients.back().pending = targets;
2916 std::cout <<
"remake: Leaving directory `" <<
prefix_dir <<
'\'' << std::endl;
2938 perror(
"Failed to send targets to server");
2941 if (targets.empty()) exit(EXIT_SUCCESS);
2946 struct sockaddr_in socket_addr;
2947 socket_fd = socket(AF_INET, SOCK_STREAM, 0);
2949 socket_addr.sin_family = AF_INET;
2950 socket_addr.sin_addr.s_addr = inet_addr(
"127.0.0.1");
2952 if (connect(
socket_fd, (
struct sockaddr *)&socket_addr,
sizeof(sockaddr_in)))
2955 struct sockaddr_un socket_addr;
2957 if (len >=
sizeof(socket_addr.sun_path) - 1) exit(EXIT_FAILURE);
2958 socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
2960 socket_addr.sun_family = AF_UNIX;
2962 if (connect(
socket_fd, (
struct sockaddr *)&socket_addr,
sizeof(socket_addr.sun_family) + len))
2966 if (setsockopt(
socket_fd, SOL_SOCKET, SO_NOSIGPIPE, &set_option,
sizeof(set_option)))
2972 char *
id = getenv(
"REMAKE_JOB_ID");
2973 int job_id =
id ? atoi(
id) : -1;
2974 if (send(
socket_fd, (
char *)&job_id,
sizeof(job_id), MSG_NOSIGNAL) !=
sizeof(job_id))
2978 for (string_list::const_iterator i = targets.begin(),
2979 i_end = targets.end(); i != i_end; ++i)
2981 DEBUG_open <<
"Sending target " << *i <<
"... ";
2982 std::string s =
'T' + *i;
2983 ssize_t len = s.length() + 1;
2984 if (send(
socket_fd, s.c_str(), len, MSG_NOSIGNAL) != len)
2989 for (variable_map::const_iterator i =
variables.begin(),
2990 i_end =
variables.end(); i != i_end; ++i)
2992 DEBUG_open <<
"Sending variable " << i->first <<
"... ";
2993 std::string s =
'V' + i->first;
2994 ssize_t len = s.length() + 1;
2995 if (send(
socket_fd, s.c_str(), len, MSG_NOSIGNAL) != len)
2997 for (string_list::const_iterator j = i->second.begin(),
2998 j_end = i->second.end(); j != j_end; ++j)
3000 std::string s =
'W' + *j;
3001 len = s.length() + 1;
3002 if (send(
socket_fd, s.c_str(), len, MSG_NOSIGNAL) != len)
3009 if (send(
socket_fd, &result, 1, MSG_NOSIGNAL) != 1)
goto error;
3010 if (recv(
socket_fd, &result, 1, 0) != 1) exit(EXIT_FAILURE);
3011 exit(result ? EXIT_SUCCESS : EXIT_FAILURE);
3027 std::cerr <<
"Usage: remake [options] [target] ...\n"
3029 " -B, --always-make Unconditionally make all targets.\n"
3030 " -d Echo script commands.\n"
3031 " -d -d Print lots of debugging information.\n"
3032 " -f FILE Read FILE as Remakefile.\n"
3033 " -h, --help Print this message and exit.\n"
3034 " -j[N], --jobs=[N] Allow N jobs at once; infinite jobs with no arg.\n"
3035 " -k, --keep-going Keep going when some targets cannot be made.\n"
3036 " -r Look up targets from the dependencies on stdin.\n"
3037 " -s, --silent, --quiet Do not echo targets.\n";
3054 std::string remakefile;
3056 bool literal_targets =
false;
3057 bool indirect_targets =
false;
3060 for (
int i = 1; i < argc; ++i)
3062 std::string arg = argv[i];
3063 if (arg.empty())
usage(EXIT_FAILURE);
3064 if (literal_targets)
goto new_target;
3065 if (arg ==
"-h" || arg ==
"--help")
usage(EXIT_SUCCESS);
3069 else if (arg ==
"-k" || arg ==
"--keep-going")
3071 else if (arg ==
"-s" || arg ==
"--silent" || arg ==
"--quiet")
3073 else if (arg ==
"-r")
3074 indirect_targets =
true;
3075 else if (arg ==
"-B" || arg ==
"--always-make")
3077 else if (arg ==
"-f")
3079 if (++i == argc)
usage(EXIT_FAILURE);
3080 remakefile = argv[i];
3082 else if (arg ==
"--")
3083 literal_targets =
true;
3084 else if (arg.compare(0, 2,
"-j") == 0)
3086 else if (arg.compare(0, 7,
"--jobs=") == 0)
3090 if (arg[0] ==
'-')
usage(EXIT_FAILURE);
3091 if (arg.find(
'=') != std::string::npos)
3093 std::istringstream in(arg);
3100 targets.push_back(arg);
3101 DEBUG <<
"New target: " << arg <<
'\n';
3108 if (indirect_targets)
3115 l.push_back(
dependencies.begin()->second->targets.front());
3117 for (string_list::const_iterator i = l.begin(),
3118 i_end = l.end(); i != i_end; ++i)
3120 dependency_map::const_iterator j =
dependencies.find(*i);
3123 for (string_set::const_iterator k = dep.
deps.begin(),
3124 k_end = dep.
deps.end(); k != k_end; ++k)
3134 if (WSAStartup(MAKEWORD(2,2), &wsaData))
3136 std::cerr <<
"Unexpected failure while initializing Windows Socket" << std::endl;
3142 if (
char *sn = getenv(
"REMAKE_SOCKET"))
client_mode(sn, targets);
3145 if (remakefile.empty())
3147 remakefile =
"Remakefile";
static void client_mode(char *socket_name, string_list const &targets)
static void save_dependencies()
static void load_dependencies()
static bool skip_eol(std::istream &in, bool multi=false)
static int expect_token(std::istream &in, int mask)
static void skip_empty(std::istream &in)
static std::string read_word(std::istream &in, bool detect_equal=true)
static void skip_spaces(std::istream &in)
static void load_rules(std::string const &remakefile)
static void register_transparent_rule(rule_t const &rule, string_list const &targets)
static void register_scripted_rule(rule_t const &rule)
static void register_rule(rule_t const &rule)
static void load_rule(std::istream &in, std::string const &first)
static std::string normalize_abs(std::string const &s, std::string const &p)
static std::string normalize(std::string const &s, std::string const &w, std::string const &p)
static void init_working_dir()
static void init_prefix_dir()
static void normalize_list(string_list &l, std::string const &w, std::string const &p)
static void substitute_pattern(std::string const &pat, string_list const &src, string_list &dst)
static void instantiate_rule(std::string const &target, rule_t const &src, rule_t &dst)
static void find_generic_rule(job_t &job, std::string const &target)
static void merge_rule(rule_t &dest, rule_t const &src)
static void find_rule(job_t &job, std::string const &target)
static void complete_request(client_t &client, bool success)
static void accept_client()
static bool handle_clients()
static void create_server()
static void finalize_job(pid_t pid, bool res)
static std::string prepare_script(job_t const &job)
static status_e start(std::string const &target, client_list::iterator ¤t)
static status_e run_script(int job_id, job_t const &job)
static bool has_free_slots()
static void complete_job(int job_id, bool success, bool started=true)
static void server_loop()
static void server_mode(std::string const &remakefile, string_list const &targets)
static bool still_need_rebuild(std::string const &target)
static void update_status(std::string const &target)
static status_t const & get_status(std::string const &target)
input_status next(std::string &)
addprefix_generator(input_generator const &, bool &)
variable_generator(std::string const &, variable_map const *)
static bool read_words(input_generator &in, string_list &res)
input_status next(std::string &)
input_status next(std::string &)
addsuffix_generator(input_generator const &, bool &)
static generator * get_function(input_generator const &, std::string const &)
input_status next(std::string &)
int main(int argc, char *argv[])
static void usage(int exit_status)
static int max_active_jobs
static bool build_failure
static void sigchld_handler(int)
std::map< int, job_t > job_map
std::map< std::string, status_t > status_map
std::map< std::string, ref_ptr< rule_t > > rule_map
static client_list clients
static std::string first_target
static rule_list generic_rules
std::list< std::string > string_list
static std::string working_dir
std::list< rule_t > rule_list
@ Failed
Build failed for target.
@ Todo
Target is missing or obsolete.
@ Running
Target is being rebuilt.
@ Recheck
Target has an obsolete dependency.
@ Remade
Target was successfully rebuilt.
@ Uptodate
Target is up-to-date.
@ RunningRecheck
Static prerequisites are being rebuilt.
static dependency_map dependencies
static std::ostream & operator<<(std::ostream &out, escape_string const &se)
static variable_map variables
std::set< std::string > string_set
static bool obsolete_targets
static bool changed_prefix_dir
std::map< pid_t, int > pid_job_map
static char * socket_name
static pid_job_map job_pids
std::map< std::string, ref_ptr< dependency_t > > dependency_map
std::map< std::string, assign_t > assign_map
static sigset_t old_sigmask
std::map< std::string, string_list > variable_map
static rule_map specific_rules
static socket_t socket_fd
static std::string prefix_dir
static bool propagate_vars
static void sigint_handler(int)
static volatile sig_atomic_t got_SIGCHLD
std::list< client_t > client_list
string_list::const_iterator prei
string_list::const_iterator sufi
string_list pending
Targets not yet started.
socket_t socket
Socket used to reply to the client (invalid for pseudo clients).
bool delayed
Whether it is a dependency client and a script has to be started on request completion.
bool failed
Whether some targets failed in mode -k.
int job_id
Job for which the built script called remake and spawned the client (negative for original clients).
string_set running
Targets being built.
variable_map vars
Variables set on request.
escape_string(std::string const &s)
std::string const & input
virtual input_status next(std::string &)=0
variable_map vars
Values of local variables.
rule_t rule
Original rule.
std::ostream & operator()(bool o)
std::ostream & operator()()
ref_ptr(ref_ptr const &p)
ref_ptr & operator=(ref_ptr const &p)
assign_map assigns
Assignment of variables.
string_list wdeps
Like deps, except that they are not registered as dependencies.
std::string script
Shell script for building the targets.
string_list targets
Files produced by this rule.
std::string stem
Stem used to instantiate the rule, if any.
string_list deps
Dependencies used for an implicit call to remake at the start of the script.
status_e status
Actual status.
time_t last
Last-modified date.
string_list::const_iterator vend
string_list::const_iterator vcur