421#define WIN32_LEAN_AND_MEAN
442#include <sys/types.h>
459#include <sys/socket.h>
467#if defined(WINDOWS) || defined(MACOSX)
468enum { MSG_NOSIGNAL = 0 };
497 if (
ptr == p.
ptr)
return *
this;
572typedef std::map<std::string, ref_ptr<rule_t> >
rule_map;
731static time_t
now = time(NULL);
785 if (
open) std::cerr << std::endl;
787 std::cerr << std::string(
depth * 2,
' ');
793 if (o &&
open) std::cerr << std::endl;
796 if (o || !
open) std::cerr << std::string(
depth * 2,
' ');
817#define DEBUG if (debug.active) debug()
818#define DEBUG_open log_auto_close auto_close; if (debug.active) debug(true)
819#define DEBUG_close if ((auto_close.still_open = false), debug.active) debug(false)
838 std::string
const &s = se.
input;
839 char const *quoted_char =
",: '";
840 char const *escaped_char =
"\"\\$!";
841 bool need_quotes =
false;
843 size_t len = s.length(), last = 0, j = 0;
844 for (
size_t i = 0; i < len; ++i)
846 if (strchr(escaped_char, s[i]))
849 if (!buf) buf =
new char[len * 2];
850 memcpy(&buf[j], &s[last], i - last);
856 if (!need_quotes && strchr(quoted_char, s[i]))
859 if (!need_quotes)
return out << s;
861 if (!buf)
return out << s <<
'"';
863 out.write(&s[last], len - last);
880 char *res = getcwd(buf,
sizeof(buf));
883 perror(
"Failed to get working directory");
888 for (
size_t i = 0, l =
working_dir.size(); i != l; ++i)
904 if (stat((
prefix_dir +
"/Remakefile").c_str(), &s) == 0)
909 perror(
"Failed to change working directory");
914 std::cout <<
"remake: Entering directory `" <<
prefix_dir <<
'\'' << std::endl;
919 if (pos == std::string::npos)
921 std::cerr <<
"Failed to locate Remakefile in the current directory or one of its parents" << std::endl;
935 size_t l = p.length();
936 if (s.compare(0, l, p))
return s;
937 size_t ll = s.length();
938 if (ll == l)
return ".";
941 size_t pos = s.rfind(
'/', l);
942 assert(pos != std::string::npos);
943 return s.substr(pos + 1);
945 if (ll == l + 1)
return ".";
946 return s.substr(l + 1);
955static std::string
normalize(std::string
const &s, std::string
const &w, std::string
const &p)
958 char const *delim =
"/\\";
962 size_t pos = s.find_first_of(delim);
963 if (pos == std::string::npos && w == p)
return s;
964 bool absolute = pos == 0;
965 if (!absolute && w != p && !w.empty())
967 size_t prev = 0, len = s.length();
973 std::string n = s.substr(prev, pos - prev);
976 if (!l.empty()) l.pop_back();
977 else if (!absolute && !w.empty())
984 if (pos >= len)
break;
986 pos = s.find_first_of(delim, prev);
987 if (pos == std::string::npos) pos = len;
989 string_list::const_iterator i = l.begin(), i_end = l.end();
990 if (i == i_end)
return absolute ?
"/" :
".";
992 if (absolute) n.push_back(
'/');
994 for (++i; i != i_end; ++i)
1008 for (string_list::iterator i = l.begin(),
1009 i_end = l.end(); i != i_end; ++i)
1029 while (strchr(
" \t", (c = in.get()))) {}
1030 if (in.good()) in.putback(c);
1039 while (strchr(
"\r\n", (c = in.get()))) {}
1040 if (in.good()) in.putback(c);
1047static bool skip_eol(std::istream &in,
bool multi =
false)
1050 if (c ==
'\r') c = in.get();
1051 if (c !=
'\n' && in.good()) in.putback(c);
1052 if (c !=
'\n' && !in.eof())
return false;
1088 case ':': tok =
Colon;
break;
1089 case ',': tok =
Comma;
break;
1090 case '=': tok =
Equal;
break;
1092 case '|': tok =
Pipe;
break;
1122static std::string
read_word(std::istream &in,
bool detect_equal =
true)
1126 if (!in.good())
return res;
1127 char const *separators =
" \t\r\n$(),:";
1128 bool quoted = c ==
'"';
1129 if (quoted) in.ignore(1);
1134 if (!in.good())
return res;
1146 if (detect_equal && c ==
'=')
1148 if (plus) in.putback(
'+');
1156 if (strchr(separators, c))
return res;
1158 if (detect_equal && c ==
'+') plus =
true;
1204 if (local_variables)
1206 variable_map::const_iterator i = local_variables->find(
name);
1207 if (i != local_variables->end())
1209 vcur = i->second.begin();
1210 vend = i->second.end();
1216 vcur = i->second.begin();
1217 vend = i->second.end();
1292 res.push_back(std::string());
1321 : gen(top.in, top.local_variables)
1377 : gen(top.in, top.local_variables)
1423 if (!g || ok)
return g;
1444 std::cerr <<
"Failed to load database" << std::endl;
1452 if (in.eof())
return;
1453 if (targets.empty())
goto error;
1454 DEBUG <<
"reading dependencies of target " << targets.front() << std::endl;
1455 if (in.get() !=
':')
goto error;
1457 dep->targets = targets;
1460 dep->deps.insert(deps.begin(), deps.end());
1461 for (string_list::const_iterator i = targets.begin(),
1462 i_end = targets.end(); i != i_end; ++i)
1476 std::ifstream in(
".remake");
1492 std::ofstream db(
".remake");
1496 for (string_list::const_iterator i = dep->targets.begin(),
1497 i_end = dep->targets.end(); i != i_end; ++i)
1503 for (string_set::const_iterator i = dep->deps.begin(),
1504 i_end = dep->deps.end(); i != i_end; ++i)
1533 assert(rule.
script.empty());
1534 for (string_list::const_iterator i = targets.begin(),
1535 i_end = targets.end(); i != i_end; ++i)
1537 std::pair<rule_map::iterator, bool> j =
1546 if (!
r->script.empty())
1548 std::cerr <<
"Failed to load rules: " << *
i
1549 <<
" cannot be the target of several rules" << std::endl;
1552 assert(
r->targets.size() == 1 &&
r->targets.front() == *
i);
1556 for (string_list::const_iterator
i = targets.begin(),
1560 if (
dep->targets.empty())
dep->targets.push_back(*
i);
1561 dep->deps.insert(rule.
deps.begin(), rule.
deps.end());
1577 for (string_list::const_iterator
i = rule.
targets.begin(),
1580 std::pair<rule_map::iterator, bool>
j =
1582 if (
j.second)
continue;
1583 std::cerr <<
"Failed to load rules: " << *
i
1584 <<
" cannot be the target of several rules" << std::endl;
1590 dep->deps.insert(rule.
deps.begin(), rule.
deps.end());
1591 for (string_list::const_iterator
i = rule.
targets.begin(),
1595 dep->deps.insert(
d->deps.begin(),
d->deps.end());
1605 if (!rule.
script.empty())
1614 targets.swap(
r.targets);
1616 targets.swap(
r.targets);
1635 std::cerr <<
"Failed to load rules: syntax error" << std::endl;
1642 if (!
first.empty()) targets.push_front(
first);
1643 else if (targets.empty())
goto error;
1644 else DEBUG <<
"actual target: " << targets.front() << std::endl;
1645 bool generic =
false;
1647 for (string_list::const_iterator
i = targets.begin(),
1650 if (
i->empty())
goto error;
1651 if ((
i->find(
'%') != std::string::npos) !=
generic)
1653 if (
i == targets.begin())
generic =
true;
1658 if (in.get() !=
':')
goto error;
1695 for (string_list::const_iterator
i = rule.
targets.begin(),
1698 if (
i->find(
'%') == std::string::npos)
goto error;
1717 std::ostringstream
buf;
1721 if (!in.good())
break;
1722 if (
c ==
'\t' ||
c ==
' ')
1724 in.get(*
buf.rdbuf());
1725 if (in.fail() && !in.eof()) in.clear();
1727 else if (
c ==
'\r' ||
c ==
'\n')
1738 if (rule.
targets.front() ==
".PHONY")
1740 for (string_list::const_iterator
i = rule.
deps.begin(),
1763 for (string_list::const_iterator
i = targets.begin(),
1783 std::cerr <<
"Failed to load rules: syntax error" << std::endl;
1789 std::cerr <<
"Failed to load rules: no Remakefile found" << std::endl;
1802 while (in.get() !=
'\n') {}
1806 if (
c ==
' ' ||
c ==
'\t')
goto error;
1810 if (name.empty())
goto error;
1813 DEBUG <<
"Assignment to variable " << name << std::endl;
1819 else dest.splice(
dest.end(), value);
1828 for (string_list::const_iterator
i =
options.begin(),
1834 std::cerr <<
"Failed to load rules: unrecognized option" << std::endl;
1850 dest.deps.insert(
dest.deps.end(),
src.deps.begin(),
src.deps.end());
1851 dest.wdeps.insert(
dest.wdeps.end(),
src.wdeps.begin(),
src.wdeps.end());
1852 for (assign_map::const_iterator
i =
src.assigns.begin(),
1855 if (!
i->second.append)
1858 dest.assigns[
i->first] =
i->second;
1861 assign_map::iterator
j =
dest.assigns.find(
i->first);
1863 j->second.value.insert(
j->second.value.end(),
1864 i->second.value.begin(),
i->second.value.end());
1873 for (string_list::const_iterator
i =
src.begin(),
1876 size_t pos =
i->find(
'%');
1877 if (
pos == std::string::npos)
dst.push_back(*
i);
1878 else dst.push_back(
i->substr(0,
pos) +
pat +
i->substr(
pos + 1));
1890 for (string_list::const_iterator
j =
src.targets.begin(),
1893 size_t len =
j->length();
1896 size_t pos =
j->find(
'%');
1897 if (
pos == std::string::npos)
continue;
1937 if (
i !=
i_end && !
i->second->script.empty())
1939 job.rule = *
i->second;
1944 if (
job.rule.targets.empty())
1948 job.rule = *
i->second;
1953 if (
job.rule.targets.size() == 1)
1961 for (string_list::const_iterator
j =
job.rule.targets.begin(),
1965 if (
i ==
i_end)
continue;
1966 if (!
i->second->script.empty())
return;
1991 std::pair<status_map::iterator,bool>
i =
1994 if (!
i.second)
return ts;
2009 ts.last =
s.st_mtime;
2022 for (string_list::const_iterator
k =
dep.targets.begin(),
2026 if (
stat(
k->c_str(), &
s) != 0)
2036 for (string_set::const_iterator
k =
dep.deps.begin(),
2048 DEBUG <<
"obsolete dependency " << *
k << std::endl;
2054 for (string_list::const_iterator
k =
dep.targets.begin(),
2084 else if (
s.st_mtime !=
ts.last)
2087 ts.last =
s.st_mtime;
2108 for (string_set::const_iterator
k =
dep.deps.begin(),
2113 for (string_list::const_iterator
k =
dep.targets.begin(),
2135 DEBUG <<
"Completing job " << job_id <<
'\n';
2136 job_map::iterator
i =
jobs.find(job_id);
2142 if (
show) std::cout <<
"Finished";
2143 for (string_list::const_iterator
j = targets.begin(),
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(),
2157 std::cerr <<
' ' << *
j;
2162 DEBUG <<
"Removing " << *
j <<
'\n';
2167 std::cerr << std::endl;
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;
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(),
2215 out <<
job.rule.targets.front();
2236 if (
s ==
Eof)
break;
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(),
2278 DEBUG_open <<
"Starting script for job " << job_id <<
"... ";
2312 si.hStdInput =
pfd[0];
2327 std::cerr <<
"Unexpected failure while sending script to shell" << std::endl;
2351 std::cerr <<
"Unexpected failure while sending script to shell" << std::endl;
2359 char const *
argv[5] = {
"sh",
"-e",
"-s",
NULL,
NULL };
2386 if (
job.rule.targets.empty())
2390 std::cerr <<
"No rule for building " <<
target << std::endl;
2393 bool has_deps = !
job.rule.deps.empty() || !
job.rule.wdeps.empty();
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);
2417 else if (!
k.second)
v.clear();
2418 v.insert(
v.end(),
i->second.value.begin(),
i->second.value.end());
2426 job.rule.wdeps.begin(),
job.rule.wdeps.end());
2440 DEBUG_open <<
"Completing request from client of job " << client.
job_id <<
"... ";
2446 job_map::const_iterator
i =
jobs.find(client.
job_id);
2497 DEBUG_open <<
"Handling client requests... ";
2506 DEBUG_open <<
"Handling client from job " <<
i->job_id <<
"... ";
2509 for (string_set::iterator
j =
i->running.begin(),
j_next =
j,
2513 status_map::const_iterator
k =
status.find(*
j);
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();
2555 client_list::iterator
j =
i;
2582 if (
i->running.empty() ||
i->failed)
2586 DEBUG_close << (
i->failed ?
"failed\n" :
"finished\n");
2599 std::cerr <<
"Circular dependency detected" << std::endl;
2600 client_list::iterator
i =
clients.begin();
2616 perror(
"Failed to create server");
2641 std::ostringstream
buf;
2702 std::cerr <<
"Unexpected failure while setting connection with client" << std::endl;
2724 std::cerr <<
"Received an ill-formed client message" << std::endl;
2735 std::vector<char>
buf;
2749 proc->job_id = job_id;
2750 job_map::const_iterator
i =
jobs.find(job_id);
2752 DEBUG <<
"receiving request from job " << job_id << std::endl;
2759 char const *
p = &
buf[0] +
sizeof(
int);
2774 DEBUG <<
"adding dependency " <<
target <<
" to job\n";
2783 DEBUG <<
"adding variable " <<
var <<
" to job\n";
2802 std::cerr <<
"Assignments are ignored unless 'variable-propagation' is enabled" << std::endl;
2814 int job_id =
i->second;
2834 for (pid_job_map::const_iterator
i =
job_pids.begin(),
2904 if (!targets.empty())
clients.back().pending = targets;
2917 std::cout <<
"remake: Leaving directory `" <<
prefix_dir <<
'\'' << std::endl;
2939 perror(
"Failed to send targets to server");
2973 char *
id =
getenv(
"REMAKE_JOB_ID");
2974 int job_id =
id ?
atoi(
id) : -1;
2979 for (string_list::const_iterator
i = targets.begin(),
2983 std::string
s =
'T' + *
i;
2990 for (variable_map::const_iterator
i =
variables.begin(),
2993 DEBUG_open <<
"Sending variable " <<
i->first <<
"... ";
2994 std::string
s =
'V' +
i->first;
2998 for (string_list::const_iterator
j =
i->second.begin(),
3001 std::string
s =
'W' + *
j;
3002 len =
s.length() + 1;
3028 std::cerr <<
"Usage: remake [options] [target] ...\n"
3030 " -B, --always-make Unconditionally make all targets.\n"
3031 " -d Echo script commands.\n"
3032 " -d -d Print lots of debugging information.\n"
3033 " -f FILE Read FILE as Remakefile.\n"
3034 " -h, --help Print this message and exit.\n"
3035 " -j[N], --jobs=[N] Allow N jobs at once; infinite jobs with no arg.\n"
3036 " -k, --keep-going Keep going when some targets cannot be made.\n"
3037 " -r Look up targets from the dependencies on stdin.\n"
3038 " -s, --silent, --quiet Do not echo targets.\n";
3061 for (
int i = 1;
i <
argc; ++
i)
3070 else if (
arg ==
"-k" ||
arg ==
"--keep-going")
3072 else if (
arg ==
"-s" ||
arg ==
"--silent" ||
arg ==
"--quiet")
3074 else if (
arg ==
"-r")
3076 else if (
arg ==
"-B" ||
arg ==
"--always-make")
3078 else if (
arg ==
"-f")
3083 else if (
arg ==
"--")
3085 else if (
arg.compare(0, 2,
"-j") == 0)
3087 else if (
arg.compare(0, 7,
"--jobs=") == 0)
3092 if (
arg.find(
'=') != std::string::npos)
3094 std::istringstream in(
arg);
3101 targets.push_back(
arg);
3102 DEBUG <<
"New target: " <<
arg <<
'\n';
3118 for (string_list::const_iterator
i =
l.begin(),
3124 for (string_set::const_iterator
k =
dep.deps.begin(),
3137 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