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(),
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(),
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(),
1593 dep->deps.insert(
d->deps.begin(),
d->deps.end());
1603 if (!rule.
script.empty())
1612 targets.swap(
r.targets);
1614 targets.swap(
r.targets);
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(),
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;
1693 for (string_list::const_iterator
i = rule.
targets.begin(),
1696 if (
i->find(
'%') == std::string::npos)
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(),
1761 for (string_list::const_iterator
i = targets.begin(),
1781 std::cerr <<
"Failed to load rules: syntax error" << std::endl;
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;
1817 else dest.splice(
dest.end(), value);
1826 for (string_list::const_iterator
i =
options.begin(),
1832 std::cerr <<
"Failed to load rules: unrecognized option" << std::endl;
1848 dest.deps.insert(
dest.deps.end(),
src.deps.begin(),
src.deps.end());
1849 dest.wdeps.insert(
dest.wdeps.end(),
src.wdeps.begin(),
src.wdeps.end());
1850 for (assign_map::const_iterator
i =
src.assigns.begin(),
1853 if (!
i->second.append)
1856 dest.assigns[
i->first] =
i->second;
1859 assign_map::iterator
j =
dest.assigns.find(
i->first);
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(),
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));
1888 for (string_list::const_iterator
j =
src.targets.begin(),
1891 size_t len =
j->length();
1894 size_t pos =
j->find(
'%');
1895 if (
pos == std::string::npos)
continue;
1935 if (
i !=
i_end && !
i->second->script.empty())
1937 job.rule = *
i->second;
1942 if (
job.rule.targets.empty())
1946 job.rule = *
i->second;
1951 if (
job.rule.targets.size() == 1)
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;
2007 ts.last =
s.st_mtime;
2020 for (string_list::const_iterator
k =
dep.targets.begin(),
2024 if (
stat(
k->c_str(), &
s) != 0)
2034 for (string_set::const_iterator
k =
dep.deps.begin(),
2046 DEBUG <<
"obsolete dependency " << *
k << std::endl;
2052 for (string_list::const_iterator
k =
dep.targets.begin(),
2082 else if (
s.st_mtime !=
ts.last)
2085 ts.last =
s.st_mtime;
2106 for (string_set::const_iterator
k =
dep.deps.begin(),
2111 for (string_list::const_iterator
k =
dep.targets.begin(),
2133 DEBUG <<
"Completing job " << job_id <<
'\n';
2134 job_map::iterator
i =
jobs.find(job_id);
2140 if (
show) std::cout <<
"Finished";
2141 for (string_list::const_iterator
j = targets.begin(),
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(),
2155 std::cerr <<
' ' << *
j;
2160 DEBUG <<
"Removing " << *
j <<
'\n';
2165 std::cerr << std::endl;
2175 std::string
const &
s =
job.rule.script;
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;
2185 if (
p ==
len)
break;
2194 if (!
job.rule.deps.empty())
2195 out <<
job.rule.deps.front();
2201 for (string_list::const_iterator
i =
job.rule.deps.begin(),
2213 out <<
job.rule.targets.front();
2234 if (
s ==
Eof)
break;
2259 dep->targets =
job.rule.targets;
2260 dep->deps.insert(
job.rule.deps.begin(),
job.rule.deps.end());
2262 for (string_list::const_iterator
i =
job.rule.targets.begin(),
2276 DEBUG_open <<
"Starting script for job " << job_id <<
"... ";
2310 si.hStdInput =
pfd[0];
2325 std::cerr <<
"Unexpected failure while sending script to shell" << std::endl;
2349 std::cerr <<
"Unexpected failure while sending script to shell" << std::endl;
2357 char const *
argv[5] = {
"sh",
"-e",
"-s",
NULL,
NULL };
2384 if (
job.rule.targets.empty())
2388 std::cerr <<
"No rule for building " <<
target << std::endl;
2391 bool has_deps = !
job.rule.deps.empty() || !
job.rule.wdeps.empty();
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);
2415 else if (!
k.second)
v.clear();
2416 v.insert(
v.end(),
i->second.value.begin(),
i->second.value.end());
2424 job.rule.wdeps.begin(),
job.rule.wdeps.end());
2438 DEBUG_open <<
"Completing request from client of job " << client.
job_id <<
"... ";
2444 job_map::const_iterator
i =
jobs.find(client.
job_id);
2495 DEBUG_open <<
"Handling client requests... ";
2504 DEBUG_open <<
"Handling client from job " <<
i->job_id <<
"... ";
2507 for (string_set::iterator
j =
i->running.begin(),
j_next =
j,
2511 status_map::const_iterator
k =
status.find(*
j);
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();
2553 client_list::iterator
j =
i;
2580 if (
i->running.empty() ||
i->failed)
2584 DEBUG_close << (
i->failed ?
"failed\n" :
"finished\n");
2597 std::cerr <<
"Circular dependency detected" << std::endl;
2598 client_list::iterator
i =
clients.begin();
2614 perror(
"Failed to create server");
2639 std::ostringstream
buf;
2700 std::cerr <<
"Unexpected failure while setting connection with client" << std::endl;
2722 std::cerr <<
"Received an ill-formed client message" << std::endl;
2733 std::vector<char>
buf;
2747 proc->job_id = job_id;
2748 job_map::const_iterator
i =
jobs.find(job_id);
2750 DEBUG <<
"receiving request from job " << job_id << std::endl;
2757 char const *
p = &
buf[0] +
sizeof(
int);
2772 DEBUG <<
"adding dependency " <<
target <<
" to job\n";
2781 DEBUG <<
"adding variable " <<
var <<
" to job\n";
2800 std::cerr <<
"Assignments are ignored unless 'variable-propagation' is enabled" << std::endl;
2812 int job_id =
i->second;
2832 for (pid_job_map::const_iterator
i =
job_pids.begin(),
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");
2972 char *
id =
getenv(
"REMAKE_JOB_ID");
2973 int job_id =
id ?
atoi(
id) : -1;
2978 for (string_list::const_iterator
i = targets.begin(),
2982 std::string
s =
'T' + *
i;
2989 for (variable_map::const_iterator
i =
variables.begin(),
2992 DEBUG_open <<
"Sending variable " <<
i->first <<
"... ";
2993 std::string
s =
'V' +
i->first;
2997 for (string_list::const_iterator
j =
i->second.begin(),
3000 std::string
s =
'W' + *
j;
3001 len =
s.length() + 1;
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";
3060 for (
int i = 1;
i <
argc; ++
i)
3069 else if (
arg ==
"-k" ||
arg ==
"--keep-going")
3071 else if (
arg ==
"-s" ||
arg ==
"--silent" ||
arg ==
"--quiet")
3073 else if (
arg ==
"-r")
3075 else if (
arg ==
"-B" ||
arg ==
"--always-make")
3077 else if (
arg ==
"-f")
3082 else if (
arg ==
"--")
3084 else if (
arg.compare(0, 2,
"-j") == 0)
3086 else if (
arg.compare(0, 7,
"--jobs=") == 0)
3091 if (
arg.find(
'=') != std::string::npos)
3093 std::istringstream in(
arg);
3100 targets.push_back(
arg);
3101 DEBUG <<
"New target: " <<
arg <<
'\n';
3117 for (string_list::const_iterator
i =
l.begin(),
3123 for (string_set::const_iterator
k =
dep.deps.begin(),
3136 std::cerr <<
"Unexpected failure while initializing Windows Socket" << std::endl;
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