Remake
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Classes | Macros | Typedefs | Enumerations | Functions | Variables
remake.cpp File Reference
#include <fstream>
#include <iostream>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <ctime>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>

Go to the source code of this file.

Classes

struct  ref_ptr< T >
 
struct  ref_ptr< T >::content
 
struct  dependency_t
 
struct  status_t
 
struct  assign_t
 
struct  rule_t
 
struct  client_t
 
struct  log
 
struct  log_auto_close
 
struct  escape_string
 

Macros

#define DEBUG   if (debug.active) debug()
 
#define DEBUG_open   log_auto_close auto_close; if (debug.active) debug(true)
 
#define DEBUG_close   if ((auto_close.still_open = false), debug.active) debug(false)
 

Typedefs

typedef int socket_t
 
typedef std::list< std::string > string_list
 
typedef std::set< std::string > string_set
 
typedef std::map< std::string,
ref_ptr< dependency_t > > 
dependency_map
 
typedef std::map< std::string,
string_list
variable_map
 
typedef std::map< std::string,
status_t
status_map
 
typedef std::list< assign_tassign_list
 
typedef std::list< rule_trule_list
 
typedef std::map< std::string,
ref_ptr< rule_t > > 
rule_map
 
typedef std::map< int,
string_list
job_targets_map
 
typedef std::map< pid_t, int > pid_job_map
 
typedef std::list< client_tclient_list
 

Enumerations

enum  { INVALID_SOCKET = -1 }
 
enum  status_e {
  Uptodate, Todo, Recheck, Running,
  Remade, Failed
}
 
enum  {
  Unexpected = 0, Word = 1 << 1, Colon = 1 << 2, Equal = 1 << 3,
  Dollarpar = 1 << 4, Rightpar = 1 << 5, Comma = 1 << 6, Plusequal = 1 << 7
}
 

Functions

static void sigchld_handler (int)
 
static void sigint_handler (int)
 
static std::ostream & operator<< (std::ostream &out, escape_string const &se)
 
void init_working_dir ()
 
static std::string normalize_abs (std::string const &s)
 
static std::string normalize (std::string const &s)
 
static void normalize_list (string_list &l)
 
static void skip_spaces (std::istream &in)
 
static void skip_empty (std::istream &in)
 
static bool skip_eol (std::istream &in, bool multi=false)
 
static int expect_token (std::istream &in, int mask)
 
static std::string read_word (std::istream &in)
 
static string_list read_words (std::istream &in)
 
static void execute_function (std::istream &in, std::string const &name, string_list &dest)
 
static void load_dependencies (std::istream &in)
 
static void load_dependencies ()
 
static void register_transparent_rule (rule_t const &rule)
 
static void register_scripted_rule (rule_t const &rule)
 
static void load_rule (std::istream &in, std::string const &first)
 
static void save_dependencies ()
 
static void load_rules ()
 
static void substitute_pattern (std::string const &pat, string_list const &src, string_list &dst)
 
static rule_t find_generic_rule (std::string const &target)
 
static rule_t find_rule (std::string const &target)
 
static status_t const & get_status (std::string const &target)
 
static void update_status (std::string const &target)
 
static bool still_need_rebuild (std::string const &target)
 
static void complete_job (int job_id, bool success)
 
static bool run_script (int job_id, rule_t const &rule)
 
static bool start (std::string const &target, client_list::iterator &current)
 
static void complete_request (client_t &client, bool success)
 
static bool has_free_slots ()
 
static bool handle_clients ()
 
static void create_server ()
 
void accept_client ()
 
void finalize_job (pid_t pid, bool res)
 
void server_loop ()
 
void server_mode (string_list const &targets)
 
void client_mode (char *socket_name, string_list const &targets)
 
void usage (int exit_status)
 
int main (int argc, char *argv[])
 

Variables

static variable_map variables
 
static dependency_map dependencies
 
static status_map status
 
static rule_list generic_rules
 
static rule_map specific_rules
 
static job_targets_map job_targets
 
static pid_job_map job_pids
 
static client_list clients
 
static int max_active_jobs = 1
 
static bool keep_going = false
 
static int running_jobs = 0
 
static int waiting_jobs = 0
 
static int job_counter = 0
 
static socket_t socket_fd
 
static bool build_failure
 
static char * socket_name
 
static std::string first_target
 
static bool show_targets = true
 
static bool echo_scripts = false
 
static time_t now = time(NULL)
 
static std::string working_dir
 
static volatile sig_atomic_t got_SIGCHLD = 0
 
log debug
 

Macro Definition Documentation

#define DEBUG   if (debug.active) debug()

Definition at line 693 of file remake.cpp.

Referenced by accept_client(), get_status(), load_dependencies(), load_rule(), load_rules(), and main().

#define DEBUG_close   if ((auto_close.still_open = false), debug.active) debug(false)
#define DEBUG_open   log_auto_close auto_close; if (debug.active) debug(true)

Typedef Documentation

typedef std::list<assign_t> assign_list

Definition at line 473 of file remake.cpp.

typedef std::list<client_t> client_list

Definition at line 518 of file remake.cpp.

typedef std::map<std::string, ref_ptr<dependency_t> > dependency_map

Definition at line 435 of file remake.cpp.

typedef std::map<int, string_list> job_targets_map

Definition at line 490 of file remake.cpp.

typedef std::map<pid_t, int> pid_job_map

Definition at line 492 of file remake.cpp.

typedef std::list<rule_t> rule_list

Definition at line 486 of file remake.cpp.

typedef std::map<std::string, ref_ptr<rule_t> > rule_map

Definition at line 488 of file remake.cpp.

typedef int socket_t

Definition at line 381 of file remake.cpp.

typedef std::map<std::string, status_t> status_map

Definition at line 461 of file remake.cpp.

typedef std::list<std::string> string_list

Definition at line 389 of file remake.cpp.

typedef std::set<std::string> string_set

Definition at line 391 of file remake.cpp.

typedef std::map<std::string, string_list> variable_map

Definition at line 437 of file remake.cpp.

Enumeration Type Documentation

anonymous enum
Enumerator
INVALID_SOCKET 

Definition at line 382 of file remake.cpp.

382 { INVALID_SOCKET = -1 };
anonymous enum
Enumerator
Unexpected 
Word 
Colon 
Equal 
Dollarpar 
Rightpar 
Comma 
Plusequal 

Definition at line 874 of file remake.cpp.

875 {
876  Unexpected = 0,
877  Word = 1 << 1,
878  Colon = 1 << 2,
879  Equal = 1 << 3,
880  Dollarpar = 1 << 4,
881  Rightpar = 1 << 5,
882  Comma = 1 << 6,
883  Plusequal = 1 << 7,
884 };
enum status_e

Build status of a target.

Enumerator
Uptodate 

Target is up-to-date.

Todo 

Target is missing or obsolete.

Recheck 

Target has an obsolete dependency.

Running 

Target is being rebuilt.

Remade 

Target was successfully rebuilt.

Failed 

Build failed for target.

Definition at line 442 of file remake.cpp.

443 {
444  Uptodate, ///< Target is up-to-date.
445  Todo, ///< Target is missing or obsolete.
446  Recheck, ///< Target has an obsolete dependency.
447  Running, ///< Target is being rebuilt.
448  Remade, ///< Target was successfully rebuilt.
449  Failed ///< Build failed for target.
450 };

Function Documentation

void accept_client ( )

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 2080 of file remake.cpp.

Referenced by server_loop().

2081 {
2082  DEBUG_open << "Handling client request... ";
2083 
2084  // Accept connection.
2085 #ifdef WINDOWS
2086  socket_t fd = accept(socket_fd, NULL, NULL);
2087  if (fd == INVALID_SOCKET) return;
2088  if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0))
2089  {
2090  error2:
2091  std::cerr << "Unexpected failure while setting connection with client" << std::endl;
2092  closesocket(fd);
2093  return;
2094  }
2095  // WSAEventSelect puts sockets into nonblocking mode, so disable it here.
2096  u_long nbio = 0;
2097  if (ioctlsocket(fd, FIONBIO, &nbio)) goto error2;
2098 #elif defined(LINUX)
2099  int fd = accept4(socket_fd, NULL, NULL, SOCK_CLOEXEC);
2100  if (fd < 0) return;
2101 #else
2102  int fd = accept(socket_fd, NULL, NULL);
2103  if (fd < 0) return;
2104  if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) return;
2105 #endif
2106  clients.push_front(client_t());
2107  client_list::iterator proc = clients.begin();
2108 
2109  if (false)
2110  {
2111  error:
2112  DEBUG_close << "failed\n";
2113  std::cerr << "Received an ill-formed client message" << std::endl;
2114  #ifdef WINDOWS
2115  closesocket(fd);
2116  #else
2117  close(fd);
2118  #endif
2119  clients.erase(proc);
2120  return;
2121  }
2122 
2123  // Receive message. Stop when encountering two nuls in a row.
2124  std::vector<char> buf;
2125  size_t len = 0;
2126  while (len < sizeof(int) + 2 || buf[len - 1] || buf[len - 2])
2127  {
2128  buf.resize(len + 1024);
2129  ssize_t l = recv(fd, &buf[0] + len, 1024, 0);
2130  if (l <= 0) goto error;
2131  len += l;
2132  }
2133 
2134  // Parse job that spawned the client.
2135  int job_id;
2136  memcpy(&job_id, &buf[0], sizeof(int));
2137  proc->socket = fd;
2138  proc->job_id = job_id;
2139  job_targets_map::const_iterator i = job_targets.find(job_id);
2140  if (i == job_targets.end()) goto error;
2141  DEBUG << "receiving request from job " << job_id << std::endl;
2142 
2143  // Parse the targets and mark them as dependencies from the job targets.
2144  dependency_t &dep = *dependencies[job_targets[job_id].front()];
2145  char const *p = &buf[0] + sizeof(int);
2146  while (true)
2147  {
2148  len = strlen(p);
2149  if (len == 0)
2150  {
2151  ++waiting_jobs;
2152  return;
2153  }
2154  std::string target(p, p + len);
2155  DEBUG << "adding dependency " << target << " to job\n";
2156  proc->pending.push_back(target);
2157  dep.deps.insert(target);
2158  p += len + 1;
2159  }
2160 }
void client_mode ( char *  socket_name,
string_list const &  targets 
)

Connect to the server socket_name, send a build request for targets, and exit with the status returned by the server.

Definition at line 2277 of file remake.cpp.

Referenced by main().

2278 {
2279  if (false)
2280  {
2281  error:
2282  perror("Failed to send targets to server");
2283  exit(EXIT_FAILURE);
2284  }
2285  if (targets.empty()) exit(EXIT_SUCCESS);
2286  DEBUG_open << "Connecting to server... ";
2287 
2288  // Connect to server.
2289 #ifdef WINDOWS
2290  struct sockaddr_in socket_addr;
2291  socket_fd = socket(AF_INET, SOCK_STREAM, 0);
2292  if (socket_fd < 0) goto error;
2293  socket_addr.sin_family = AF_INET;
2294  socket_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
2295  socket_addr.sin_port = atoi(socket_name);
2296  if (connect(socket_fd, (struct sockaddr *)&socket_addr, sizeof(sockaddr_in)))
2297  goto error;
2298 #else
2299  struct sockaddr_un socket_addr;
2300  size_t len = strlen(socket_name);
2301  if (len >= sizeof(socket_addr.sun_path) - 1) exit(EXIT_FAILURE);
2302  socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
2303  if (socket_fd < 0) goto error;
2304  socket_addr.sun_family = AF_UNIX;
2305  strcpy(socket_addr.sun_path, socket_name);
2306  if (connect(socket_fd, (struct sockaddr *)&socket_addr, sizeof(socket_addr.sun_family) + len))
2307  goto error;
2308 #ifdef MACOSX
2309  int set_option = 1;
2310  if (setsockopt(socket_fd, SOL_SOCKET, SO_NOSIGPIPE, &set_option, sizeof(set_option)))
2311  goto error;
2312 #endif
2313 #endif
2314 
2315  // Send current job id.
2316  char *id = getenv("REMAKE_JOB_ID");
2317  int job_id = id ? atoi(id) : -1;
2318  if (send(socket_fd, (char *)&job_id, sizeof(job_id), MSG_NOSIGNAL) != sizeof(job_id))
2319  goto error;
2320 
2321  // Send tagets.
2322  for (string_list::const_iterator i = targets.begin(),
2323  i_end = targets.end(); i != i_end; ++i)
2324  {
2325  DEBUG_open << "Sending " << *i << "... ";
2326  ssize_t len = i->length() + 1;
2327  if (send(socket_fd, i->c_str(), len, MSG_NOSIGNAL) != len)
2328  goto error;
2329  }
2330 
2331  // Send terminating nul and wait for reply.
2332  char result = 0;
2333  if (send(socket_fd, &result, 1, MSG_NOSIGNAL) != 1) goto error;
2334  if (recv(socket_fd, &result, 1, 0) != 1) exit(EXIT_FAILURE);
2335  exit(result ? EXIT_SUCCESS : EXIT_FAILURE);
2336 }
static void complete_job ( int  job_id,
bool  success 
)
static

Handle job completion.

Definition at line 1617 of file remake.cpp.

Referenced by complete_request(), finalize_job(), and run_script().

1618 {
1619  DEBUG_open << "Completing job " << job_id << "... ";
1620  job_targets_map::iterator i = job_targets.find(job_id);
1621  assert(i != job_targets.end());
1622  string_list const &targets = i->second;
1623  if (success)
1624  {
1625  for (string_list::const_iterator j = targets.begin(),
1626  j_end = targets.end(); j != j_end; ++j)
1627  {
1628  update_status(*j);
1629  }
1630  }
1631  else
1632  {
1633  DEBUG_close << "failed\n";
1634  std::cerr << "Failed to build";
1635  for (string_list::const_iterator j = targets.begin(),
1636  j_end = targets.end(); j != j_end; ++j)
1637  {
1638  status[*j].status = Failed;
1639  std::cerr << ' ' << *j;
1640  remove(j->c_str());
1641  }
1642  std::cerr << std::endl;
1643  }
1644  job_targets.erase(i);
1645 }
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 1846 of file remake.cpp.

Referenced by handle_clients().

1847 {
1848  DEBUG_open << "Completing request from client of job " << client.job_id << "... ";
1849  if (client.delayed)
1850  {
1851  assert(client.socket == INVALID_SOCKET);
1852  if (success)
1853  {
1854  if (still_need_rebuild(client.delayed->targets.front()))
1855  run_script(client.job_id, *client.delayed);
1856  else complete_job(client.job_id, true);
1857  }
1858  else complete_job(client.job_id, false);
1859  delete client.delayed;
1860  }
1861  else if (client.socket != INVALID_SOCKET)
1862  {
1863  char res = success ? 1 : 0;
1864  send(client.socket, &res, 1, 0);
1865  #ifdef WINDOWS
1866  closesocket(client.socket);
1867  #else
1868  close(client.socket);
1869  #endif
1870  --waiting_jobs;
1871  }
1872 
1873  if (client.job_id < 0 && !success) build_failure = true;
1874 }
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 2000 of file remake.cpp.

Referenced by server_mode().

2001 {
2002  if (false)
2003  {
2004  error:
2005  perror("Failed to create server");
2006 #ifndef WINDOWS
2007  error2:
2008 #endif
2009  exit(EXIT_FAILURE);
2010  }
2011  DEBUG_open << "Creating server... ";
2012 
2013 #ifdef WINDOWS
2014  // Prepare a windows socket.
2015  struct sockaddr_in socket_addr;
2016  socket_addr.sin_family = AF_INET;
2017  socket_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
2018  socket_addr.sin_port = 0;
2019 
2020  // Create and listen to the socket.
2021  socket_fd = socket(AF_INET, SOCK_STREAM, 0);
2022  if (socket_fd < 0) goto error;
2023  if (!SetHandleInformation((HANDLE)socket_fd, HANDLE_FLAG_INHERIT, 0))
2024  goto error;
2025  if (bind(socket_fd, (struct sockaddr *)&socket_addr, sizeof(sockaddr_in)))
2026  goto error;
2027  int len = sizeof(sockaddr_in);
2028  if (getsockname(socket_fd, (struct sockaddr *)&socket_addr, &len))
2029  goto error;
2030  std::ostringstream buf;
2031  buf << socket_addr.sin_port;
2032  if (!SetEnvironmentVariable("REMAKE_SOCKET", buf.str().c_str()))
2033  goto error;
2034  if (listen(socket_fd, 1000)) goto error;
2035 #else
2036  // Set signal handlers for SIGCHLD and SIGINT.
2037  // Block SIGCHLD (unblocked during select).
2038  sigset_t sigmask;
2039  sigemptyset(&sigmask);
2040  sigaddset(&sigmask, SIGCHLD);
2041  if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == -1) goto error;
2042  struct sigaction sa;
2043  sa.sa_flags = 0;
2044  sigemptyset(&sa.sa_mask);
2045  sa.sa_handler = &sigchld_handler;
2046  if (sigaction(SIGCHLD, &sa, NULL) == -1) goto error;
2047  sa.sa_handler = &sigint_handler;
2048  if (sigaction(SIGINT, &sa, NULL) == -1) goto error;
2049 
2050  // Prepare a named unix socket in temporary directory.
2051  socket_name = tempnam(NULL, "rmk-");
2052  if (!socket_name) goto error2;
2053  struct sockaddr_un socket_addr;
2054  size_t len = strlen(socket_name);
2055  if (len >= sizeof(socket_addr.sun_path) - 1) goto error2;
2056  socket_addr.sun_family = AF_UNIX;
2057  strcpy(socket_addr.sun_path, socket_name);
2058  len += sizeof(socket_addr.sun_family);
2059  if (setenv("REMAKE_SOCKET", socket_name, 1)) goto error;
2060 
2061  // Create and listen to the socket.
2062 #ifdef LINUX
2063  socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
2064  if (socket_fd < 0) goto error;
2065 #else
2066  socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
2067  if (socket_fd < 0) goto error;
2068  if (fcntl(socket_fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
2069 #endif
2070  if (bind(socket_fd, (struct sockaddr *)&socket_addr, len))
2071  goto error;
2072  if (listen(socket_fd, 1000)) goto error;
2073 #endif
2074 }
static void execute_function ( std::istream &  in,
std::string const &  name,
string_list dest 
)
static

Execute a built-in function name and append its result to dest.

Definition at line 983 of file remake.cpp.

Referenced by read_words().

984 {
985  if (false)
986  {
987  error:
988  std::cerr << "Failed to load rules: syntax error" << std::endl;
989  exit(EXIT_FAILURE);
990  }
991  skip_spaces(in);
992  string_list fix = read_words(in);
993  if (!expect_token(in, Comma)) goto error;
994  string_list names = read_words(in);
995  if (!expect_token(in, Rightpar)) goto error;
996  size_t fixl = fix.size();
997  if (name == "addprefix")
998  {
999  for (string_list::const_iterator i = names.begin(),
1000  i_end = names.end(); i != i_end; ++i)
1001  {
1002  if (!fixl)
1003  {
1004  dest.push_back(*i);
1005  continue;
1006  }
1007  string_list::const_iterator k = fix.begin();
1008  for (size_t j = 1; j != fixl; ++j)
1009  {
1010  dest.push_back(*k++);
1011  }
1012  dest.push_back(*k++ + *i);
1013  }
1014  }
1015  else if (name == "addsuffix")
1016  {
1017  for (string_list::const_iterator i = names.begin(),
1018  i_end = names.end(); i != i_end; ++i)
1019  {
1020  if (!fixl)
1021  {
1022  dest.push_back(*i);
1023  continue;
1024  }
1025  string_list::const_iterator k = fix.begin();
1026  dest.push_back(*i + *k++);
1027  for (size_t j = 1; j != fixl; ++j)
1028  {
1029  dest.push_back(*k++);
1030  }
1031  }
1032  }
1033  else goto error;
1034 }
static int expect_token ( std::istream &  in,
int  mask 
)
static

Skip spaces and peek at the next token. If it is one of mask, skip it (if it is not Word) and return it.

Note
For composite tokens allowed by mask, input characters might have been eaten even for an Unexpected result.

Definition at line 892 of file remake.cpp.

Referenced by execute_function(), load_rule(), load_rules(), and read_words().

893 {
894  while (true)
895  {
896  skip_spaces(in);
897  char c = in.peek();
898  if (!in.good()) return Unexpected;
899  int tok;
900  switch (c)
901  {
902  case '\r':
903  case '\n': return Unexpected;
904  case ':': tok = Colon; break;
905  case ',': tok = Comma; break;
906  case '=': tok = Equal; break;
907  case ')': tok = Rightpar; break;
908  case '$':
909  if (!(mask & Dollarpar)) return Unexpected;
910  in.ignore(1);
911  tok = Dollarpar;
912  if (in.peek() != '(') return Unexpected;
913  break;
914  case '+':
915  if (!(mask & Plusequal)) return Unexpected;
916  in.ignore(1);
917  tok = Plusequal;
918  if (in.peek() != '=') return Unexpected;
919  break;
920  case '\\':
921  in.ignore(1);
922  if (skip_eol(in)) continue;
923  in.putback('\\');
924  return mask & Word ? Word : Unexpected;
925  default:
926  return mask & Word ? Word : Unexpected;
927  }
928  if (!(tok & mask)) return Unexpected;
929  in.ignore(1);
930  return tok;
931  }
932 }
void finalize_job ( pid_t  pid,
bool  res 
)

Handle child process exit status.

Definition at line 2165 of file remake.cpp.

Referenced by server_loop().

2166 {
2167  pid_job_map::iterator i = job_pids.find(pid);
2168  assert(i != job_pids.end());
2169  int job_id = i->second;
2170  job_pids.erase(i);
2171  --running_jobs;
2172  complete_job(job_id, res);
2173 }
static rule_t find_generic_rule ( std::string const &  target)
static

Find a generic rule matching target:

  • the one leading to shorter matches has priority,
  • among equivalent rules, the earliest one has priority.

Definition at line 1404 of file remake.cpp.

Referenced by find_rule().

1405 {
1406  size_t tlen = target.length(), plen = tlen + 1;
1407  rule_t rule;
1408  for (rule_list::const_iterator i = generic_rules.begin(),
1409  i_end = generic_rules.end(); i != i_end; ++i)
1410  {
1411  for (string_list::const_iterator j = i->targets.begin(),
1412  j_end = i->targets.end(); j != j_end; ++j)
1413  {
1414  size_t len = j->length();
1415  if (tlen < len) continue;
1416  if (plen <= tlen - (len - 1)) continue;
1417  size_t pos = j->find('%');
1418  if (pos == std::string::npos) continue;
1419  size_t len2 = len - (pos + 1);
1420  if (j->compare(0, pos, target, 0, pos) ||
1421  j->compare(pos + 1, len2, target, tlen - len2, len2))
1422  continue;
1423  plen = tlen - (len - 1);
1424  std::string pat = target.substr(pos, plen);
1425  rule = rule_t();
1426  rule.script = i->script;
1427  substitute_pattern(pat, i->targets, rule.targets);
1428  substitute_pattern(pat, i->deps, rule.deps);
1429  break;
1430  }
1431  }
1432  return rule;
1433 }
static rule_t find_rule ( std::string const &  target)
static

Find a specific rule matching target. Return a generic one otherwise. If there is both a specific rule with an empty script and a generic rule, the generic one is returned after adding the dependencies of the specific one.

Definition at line 1440 of file remake.cpp.

Referenced by start().

1441 {
1442  rule_map::const_iterator i = specific_rules.find(target),
1443  i_end = specific_rules.end();
1444  // If there is a specific rule with a script, return it.
1445  if (i != i_end && !i->second->script.empty()) return *i->second;
1446  rule_t grule = find_generic_rule(target);
1447  // If there is no generic rule, return the specific rule (no script), if any.
1448  if (grule.targets.empty())
1449  {
1450  if (i != i_end) return *i->second;
1451  return grule;
1452  }
1453  // Optimize the lookup when there is only one target (already looked up).
1454  if (grule.targets.size() == 1)
1455  {
1456  if (i == i_end) return grule;
1457  grule.deps.insert(grule.deps.end(),
1458  i->second->deps.begin(), i->second->deps.end());
1459  grule.vars.insert(grule.vars.end(),
1460  i->second->vars.begin(), i->second->vars.end());
1461  return grule;
1462  }
1463  // Add the dependencies of the specific rules of every target to the
1464  // generic rule. If any of those rules has a nonempty script, error out.
1465  for (string_list::const_iterator j = grule.targets.begin(),
1466  j_end = grule.targets.end(); j != j_end; ++j)
1467  {
1468  i = specific_rules.find(*j);
1469  if (i == i_end) continue;
1470  if (!i->second->script.empty()) return rule_t();
1471  grule.deps.insert(grule.deps.end(),
1472  i->second->deps.begin(), i->second->deps.end());
1473  grule.vars.insert(grule.vars.end(),
1474  i->second->vars.begin(), i->second->vars.end());
1475  }
1476  return grule;
1477 }
static status_t const& get_status ( std::string const &  target)
static

Compute and memoize the status of target:

  • if the file does not exist, the target is obsolete,
  • if any dependency is obsolete or younger than the file, it is obsolete,
  • otherwise it is up-to-date.
Note
For rules with multiple targets, all the targets share the same status. (If one is obsolete, they all are.) The second rule above is modified in that case: the latest target is chosen, not the oldest!

Definition at line 1489 of file remake.cpp.

Referenced by handle_clients(), and server_mode().

1490 {
1491  std::pair<status_map::iterator,bool> i =
1492  status.insert(std::make_pair(target, status_t()));
1493  status_t &ts = i.first->second;
1494  if (!i.second) return ts;
1495  DEBUG_open << "Checking status of " << target << "... ";
1496  dependency_map::const_iterator j = dependencies.find(target);
1497  if (j == dependencies.end())
1498  {
1499  struct stat s;
1500  if (stat(target.c_str(), &s) != 0)
1501  {
1502  DEBUG_close << "missing\n";
1503  ts.status = Todo;
1504  ts.last = 0;
1505  return ts;
1506  }
1507  DEBUG_close << "up-to-date\n";
1508  ts.status = Uptodate;
1509  ts.last = s.st_mtime;
1510  return ts;
1511  }
1512  dependency_t const &dep = *j->second;
1513  status_e st = Uptodate;
1514  time_t latest = 0;
1515  for (string_list::const_iterator k = dep.targets.begin(),
1516  k_end = dep.targets.end(); k != k_end; ++k)
1517  {
1518  struct stat s;
1519  if (stat(k->c_str(), &s) != 0)
1520  {
1521  if (st == Uptodate) DEBUG_close << *k << " missing\n";
1522  s.st_mtime = 0;
1523  st = Todo;
1524  }
1525  status[*k].last = s.st_mtime;
1526  if (s.st_mtime > latest) latest = s.st_mtime;
1527  }
1528  if (st == Todo) goto update;
1529  for (string_set::const_iterator k = dep.deps.begin(),
1530  k_end = dep.deps.end(); k != k_end; ++k)
1531  {
1532  status_t const &ts_ = get_status(*k);
1533  if (latest < ts_.last)
1534  {
1535  DEBUG_close << "older than " << *k << std::endl;
1536  st = Todo;
1537  goto update;
1538  }
1539  if (ts_.status == Uptodate) continue;
1540  if (st == Uptodate)
1541  DEBUG << "obsolete dependency " << *k << std::endl;
1542  st = Recheck;
1543  }
1544  if (st == Uptodate) DEBUG_close << "all siblings up-to-date\n";
1545  update:
1546  for (string_list::const_iterator k = dep.targets.begin(),
1547  k_end = dep.targets.end(); k != k_end; ++k)
1548  {
1549  status[*k].status = st;
1550  }
1551  return ts;
1552 }
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.

Definition at line 1896 of file remake.cpp.

Referenced by server_loop().

1897 {
1898  DEBUG_open << "Handling client requests... ";
1899  restart:
1900 
1901  for (client_list::iterator i = clients.begin(), i_next = i,
1902  i_end = clients.end(); i != i_end && has_free_slots(); i = i_next)
1903  {
1904  ++i_next;
1905  DEBUG_open << "Handling client from job " << i->job_id << "... ";
1906  if (false)
1907  {
1908  failed:
1909  complete_request(*i, false);
1910  clients.erase(i);
1911  DEBUG_close << "failed\n";
1912  continue;
1913  }
1914 
1915  // Remove running targets that have finished.
1916  for (string_set::iterator j = i->running.begin(), j_next = j,
1917  j_end = i->running.end(); j != j_end; j = j_next)
1918  {
1919  ++j_next;
1920  status_map::const_iterator k = status.find(*j);
1921  assert(k != status.end());
1922  switch (k->second.status)
1923  {
1924  case Running:
1925  break;
1926  case Failed:
1927  if (!keep_going) goto failed;
1928  i->failed = true;
1929  // no break
1930  case Uptodate:
1931  case Remade:
1932  i->running.erase(j);
1933  break;
1934  case Recheck:
1935  case Todo:
1936  assert(false);
1937  }
1938  }
1939 
1940  // Start pending targets.
1941  while (!i->pending.empty())
1942  {
1943  std::string target = i->pending.front();
1944  i->pending.pop_front();
1945  switch (get_status(target).status)
1946  {
1947  case Running:
1948  i->running.insert(target);
1949  break;
1950  case Failed:
1951  pending_failed:
1952  if (!keep_going) goto failed;
1953  i->failed = true;
1954  // no break
1955  case Uptodate:
1956  case Remade:
1957  break;
1958  case Recheck:
1959  case Todo:
1960  client_list::iterator j = i;
1961  if (!start(target, i)) goto pending_failed;
1962  j->running.insert(target);
1963  if (!has_free_slots()) return true;
1964  // Job start might insert a dependency client.
1965  i_next = i;
1966  ++i_next;
1967  break;
1968  }
1969  }
1970 
1971  // Try to complete the request.
1972  // (This might start a new job if it was a dependency client.)
1973  if (i->running.empty())
1974  {
1975  if (i->failed) goto failed;
1976  complete_request(*i, true);
1977  clients.erase(i);
1978  DEBUG_close << "finished\n";
1979  }
1980  }
1981 
1982  if (running_jobs != waiting_jobs) return true;
1983  if (running_jobs == 0 && clients.empty()) return false;
1984 
1985  // There is a circular dependency.
1986  // Try to break it by completing one of the requests.
1987  assert(!clients.empty());
1988  std::cerr << "Circular dependency detected" << std::endl;
1989  client_list::iterator i = clients.begin();
1990  complete_request(*i, false);
1991  clients.erase(i);
1992  goto restart;
1993 }
static bool has_free_slots ( )
static

Return whether there are slots for starting new jobs.

Definition at line 1879 of file remake.cpp.

Referenced by handle_clients().

1880 {
1881  if (max_active_jobs <= 0) return true;
1883 }
void init_working_dir ( )

Initialize working_dir.

Definition at line 747 of file remake.cpp.

Referenced by main().

748 {
749  char buf[1024];
750  char *res = getcwd(buf, sizeof(buf));
751  if (!res)
752  {
753  perror("Failed to get working directory");
754  exit(EXIT_FAILURE);
755  }
756  working_dir = buf;
757 }
static void load_dependencies ( std::istream &  in)
static

Load dependencies from in.

Definition at line 1077 of file remake.cpp.

Referenced by load_dependencies(), main(), and server_mode().

1078 {
1079  while (!in.eof())
1080  {
1081  string_list targets = read_words(in);
1082  if (targets.empty()) return;
1083  DEBUG << "reading dependencies of target " << targets.front() << std::endl;
1084  if (in.get() != ':')
1085  {
1086  std::cerr << "Failed to load database" << std::endl;
1087  exit(EXIT_FAILURE);
1088  }
1090  dep->targets = targets;
1091  string_list d = read_words(in);
1092  dep->deps.insert(d.begin(), d.end());
1093  for (string_list::const_iterator i = targets.begin(),
1094  i_end = targets.end(); i != i_end; ++i)
1095  {
1096  dependencies[*i] = dep;
1097  }
1098  skip_empty(in);
1099  }
1100 }
static void load_dependencies ( )
static

Load known dependencies from file .remake.

Definition at line 1105 of file remake.cpp.

1106 {
1107  DEBUG_open << "Loading database... ";
1108  std::ifstream in(".remake");
1109  if (!in.good())
1110  {
1111  DEBUG_close << "not found\n";
1112  return;
1113  }
1114  load_dependencies(in);
1115 }
static void load_rule ( std::istream &  in,
std::string const &  first 
)
static

Read a rule starting with target first, if nonempty. Store into generic_rules or specific_rules depending on its genericity.

Definition at line 1199 of file remake.cpp.

Referenced by load_rules().

1200 {
1201  DEBUG_open << "Reading rule for target " << first << "... ";
1202  if (false)
1203  {
1204  error:
1205  DEBUG_close << "failed\n";
1206  std::cerr << "Failed to load rules: syntax error" << std::endl;
1207  exit(EXIT_FAILURE);
1208  }
1209  rule_t rule;
1210 
1211  // Read targets and check genericity.
1212  string_list targets = read_words(in);
1213  if (!first.empty()) targets.push_front(first);
1214  else if (targets.empty()) goto error;
1215  else DEBUG << "actual target: " << targets.front() << std::endl;
1216  bool generic = false;
1217  normalize_list(targets);
1218  for (string_list::const_iterator i = targets.begin(),
1219  i_end = targets.end(); i != i_end; ++i)
1220  {
1221  if (i->empty()) goto error;
1222  if ((i->find('%') != std::string::npos) != generic)
1223  {
1224  if (i == targets.begin()) generic = true;
1225  else goto error;
1226  }
1227  }
1228  std::swap(rule.targets, targets);
1229  skip_spaces(in);
1230  if (in.get() != ':') goto error;
1231 
1232  bool assignment = false;
1233 
1234  // Read dependencies.
1235  if (expect_token(in, Word))
1236  {
1237  std::string d = read_word(in);
1238  if (int tok = expect_token(in, Equal | Plusequal))
1239  {
1240  rule.vars.push_back(assign_t());
1241  string_list v = read_words(in);
1242  assign_t &a = rule.vars.back();
1243  a.name = d;
1244  a.append = tok == Plusequal;
1245  a.value.swap(v);
1246  assignment = true;
1247  }
1248  else
1249  {
1250  string_list v = read_words(in);
1251  v.push_front(d);
1252  normalize_list(v);
1253  rule.deps.swap(v);
1254  }
1255  }
1256  else
1257  {
1258  string_list v = read_words(in);
1259  normalize_list(v);
1260  rule.deps.swap(v);
1261  }
1262  skip_spaces(in);
1263  if (!skip_eol(in, true)) goto error;
1264 
1265  // Read script.
1266  std::ostringstream buf;
1267  while (true)
1268  {
1269  char c = in.get();
1270  if (!in.good()) break;
1271  if (c == '\t' || c == ' ')
1272  {
1273  in.get(*buf.rdbuf());
1274  if (in.fail() && !in.eof()) in.clear();
1275  }
1276  else if (c == '\r' || c == '\n')
1277  buf << c;
1278  else
1279  {
1280  in.putback(c);
1281  break;
1282  }
1283  }
1284  rule.script = buf.str();
1285 
1286  // Add generic rules to the correct set.
1287  if (generic)
1288  {
1289  if (assignment) goto error;
1290  generic_rules.push_back(rule);
1291  return;
1292  }
1293 
1294  if (!rule.script.empty())
1295  {
1296  if (assignment) goto error;
1297  register_scripted_rule(rule);
1298  }
1299  else
1301 
1302  // If there is no default target yet, mark it as such.
1303  if (first_target.empty())
1304  first_target = rule.targets.front();
1305 }
static void load_rules ( )
static

Load rules. If some rules have dependencies and non-generic targets, add these dependencies to the targets.

Definition at line 1338 of file remake.cpp.

Referenced by server_mode().

1339 {
1340  DEBUG_open << "Loading rules... ";
1341  if (false)
1342  {
1343  error:
1344  std::cerr << "Failed to load rules: syntax error" << std::endl;
1345  exit(EXIT_FAILURE);
1346  }
1347  std::ifstream in("Remakefile");
1348  if (!in.good())
1349  {
1350  std::cerr << "Failed to load rules: no Remakefile found" << std::endl;
1351  exit(EXIT_FAILURE);
1352  }
1353  skip_empty(in);
1354 
1355  // Read rules
1356  while (in.good())
1357  {
1358  char c = in.peek();
1359  if (c == '#')
1360  {
1361  while (in.get() != '\n') {}
1362  skip_empty(in);
1363  continue;
1364  }
1365  if (c == ' ' || c == '\t') goto error;
1366  if (expect_token(in, Word))
1367  {
1368  std::string name = read_word(in);
1369  if (name.empty()) goto error;
1370  if (int tok = expect_token(in, Equal | Plusequal))
1371  {
1372  DEBUG << "Assignment to variable " << name << std::endl;
1373  string_list value = read_words(in);
1374  string_list &dest = variables[name];
1375  if (tok == Equal) dest.swap(value);
1376  else dest.splice(dest.end(), value);
1377  if (!skip_eol(in, true)) goto error;
1378  }
1379  else load_rule(in, name);
1380  }
1381  else load_rule(in, std::string());
1382  }
1383 }
int main ( int  argc,
char *  argv[] 
)

This program behaves in two different ways.

  • If the environment contains the REMAKE_SOCKET variable, the client connects to this socket and sends to the server its build targets. It exits once it receives the server reply.
  • Otherwise, it creates a server that waits for build requests. It also creates a pseudo-client that requests the targets passed on the command line.

Definition at line 2366 of file remake.cpp.

2367 {
2368  init_working_dir();
2369 
2370  string_list targets;
2371  bool indirect_targets = false;
2372 
2373  // Parse command-line arguments.
2374  for (int i = 1; i < argc; ++i)
2375  {
2376  std::string arg = argv[i];
2377  if (arg.empty()) usage(EXIT_FAILURE);
2378  if (arg == "-h" || arg == "--help") usage(EXIT_SUCCESS);
2379  if (arg == "-d")
2380  if (echo_scripts) debug.active = true;
2381  else echo_scripts = true;
2382  else if (arg == "-k" || arg =="--keep-going")
2383  keep_going = true;
2384  else if (arg == "-s" || arg == "--silent" || arg == "--quiet")
2385  show_targets = false;
2386  else if (arg == "-r")
2387  indirect_targets = true;
2388  else if (arg.compare(0, 2, "-j") == 0)
2389  max_active_jobs = atoi(arg.c_str() + 2);
2390  else if (arg.compare(0, 7, "--jobs=") == 0)
2391  max_active_jobs = atoi(arg.c_str() + 7);
2392  else
2393  {
2394  if (arg[0] == '-') usage(1);
2395  targets.push_back(normalize(arg));
2396  DEBUG << "New target: " << arg << '\n';
2397  }
2398  }
2399 
2400  if (indirect_targets)
2401  {
2402  load_dependencies(std::cin);
2403  string_list l;
2404  targets.swap(l);
2405  if (l.empty() && !dependencies.empty())
2406  {
2407  l.push_back(dependencies.begin()->second->targets.front());
2408  }
2409  for (string_list::const_iterator i = l.begin(),
2410  i_end = l.end(); i != i_end; ++i)
2411  {
2412  dependency_map::const_iterator j = dependencies.find(*i);
2413  if (j == dependencies.end()) continue;
2414  dependency_t const &dep = *j->second;
2415  for (string_set::const_iterator k = dep.deps.begin(),
2416  k_end = dep.deps.end(); k != k_end; ++k)
2417  {
2418  targets.push_back(normalize(*k));
2419  }
2420  }
2421  dependencies.clear();
2422  }
2423 
2424 #ifdef WINDOWS
2425  WSADATA wsaData;
2426  if (WSAStartup(MAKEWORD(2,2), &wsaData))
2427  {
2428  std::cerr << "Unexpected failure while initializing Windows Socket" << std::endl;
2429  return 1;
2430  }
2431 #endif
2432 
2433  // Run as client if REMAKE_SOCKET is present in the environment.
2434  if (char *sn = getenv("REMAKE_SOCKET")) client_mode(sn, targets);
2435 
2436  // Otherwise run as server.
2437  server_mode(targets);
2438 }
static std::string normalize ( std::string const &  s)
static

Normalize a target name.

Definition at line 782 of file remake.cpp.

Referenced by main(), and normalize_list().

783 {
784 #ifdef WINDOWS
785  char const *delim = "/\\";
786 #else
787  char delim = '/';
788 #endif
789  size_t prev = 0, len = s.length();
790  size_t pos = s.find_first_of(delim);
791  if (pos == std::string::npos) return s;
792  bool absolute = pos == 0;
793  string_list l;
794  for (;;)
795  {
796  if (pos != prev)
797  {
798  std::string n = s.substr(prev, pos - prev);
799  if (n == "..")
800  {
801  if (!l.empty()) l.pop_back();
802  else if (!absolute)
803  return normalize(working_dir + '/' + s);
804  }
805  else if (n != ".")
806  l.push_back(n);
807  }
808  ++pos;
809  if (pos >= len) break;
810  prev = pos;
811  pos = s.find_first_of(delim, prev);
812  if (pos == std::string::npos) pos = len;
813  }
814  string_list::const_iterator i = l.begin(), i_end = l.end();
815  if (i == i_end) return absolute ? "/" : ".";
816  std::string n;
817  if (absolute) n.push_back('/');
818  n.append(*i);
819  for (++i; i != i_end; ++i)
820  {
821  n.push_back('/');
822  n.append(*i);
823  }
824  if (absolute) return normalize_abs(n);
825  return n;
826 }
static std::string normalize_abs ( std::string const &  s)
static

Normalize an absolute path with respect to the working directory. Paths outside the working subtree are left unchanged.

Definition at line 763 of file remake.cpp.

Referenced by normalize().

764 {
765  size_t l = working_dir.length();
766  if (s.compare(0, l, working_dir)) return s;
767  size_t ll = s.length();
768  if (ll == l) return ".";
769  if (s[l] != '/')
770  {
771  size_t pos = s.rfind('/', l);
772  assert(pos != std::string::npos);
773  return s.substr(pos + 1);
774  }
775  if (ll == l + 1) return ".";
776  return s.substr(l + 1);
777 }
static void normalize_list ( string_list l)
static

Normalize the content of a list of targets.

Definition at line 831 of file remake.cpp.

Referenced by load_rule().

832 {
833  for (string_list::iterator i = l.begin(),
834  i_end = l.end(); i != i_end; ++i)
835  {
836  *i = normalize(*i);
837  }
838 }
static std::ostream& operator<< ( std::ostream &  out,
escape_string const &  se 
)
static

Write the string in se to out if it does not contain any special characters, a quoted and escaped string otherwise.

Definition at line 712 of file remake.cpp.

713 {
714  std::string const &s = se.input;
715  char const *quoted_char = ",: '";
716  char const *escaped_char = "\"\\$!";
717  bool need_quotes = false;
718  char *buf = NULL;
719  size_t len = s.length(), last = 0, j = 0;
720  for (size_t i = 0; i < len; ++i)
721  {
722  if (strchr(escaped_char, s[i]))
723  {
724  need_quotes = true;
725  if (!buf) buf = new char[len * 2];
726  memcpy(&buf[j], &s[last], i - last);
727  j += i - last;
728  buf[j++] = '\\';
729  buf[j++] = s[i];
730  last = i + 1;
731  }
732  if (!need_quotes && strchr(quoted_char, s[i]))
733  need_quotes = true;
734  }
735  if (!need_quotes) return out << s;
736  out << '"';
737  if (!buf) return out << s << '"';
738  out.write(buf, j);
739  out.write(&s[last], len - last);
740  delete[] buf;
741  return out << '"';
742 }
static std::string read_word ( std::istream &  in)
static

Read a (possibly quoted) word.

Definition at line 937 of file remake.cpp.

Referenced by load_rule(), load_rules(), and read_words().

938 {
939  int c = in.get();
940  std::string res;
941  if (!in.good()) return res;
942  char const *separators = " \t\r\n:$(),=+\"";
943  bool quoted = c == '"';
944  if (!quoted)
945  {
946  if (strchr(separators, c))
947  {
948  in.putback(c);
949  return res;
950  }
951  res += c;
952  }
953  while (true)
954  {
955  c = in.get();
956  if (!in.good()) return res;
957  if (quoted)
958  {
959  if (c == '\\')
960  res += in.get();
961  else if (c == '"')
962  return res;
963  else
964  res += c;
965  }
966  else
967  {
968  if (strchr(separators, c))
969  {
970  in.putback(c);
971  return res;
972  }
973  res += c;
974  }
975  }
976 }
static string_list read_words ( std::istream &  in)
static

Read a list of words, possibly executing functions.

Definition at line 1039 of file remake.cpp.

Referenced by execute_function(), load_dependencies(), load_rule(), and load_rules().

1040 {
1041  if (false)
1042  {
1043  error:
1044  std::cerr << "Failed to load rules: syntax error" << std::endl;
1045  exit(EXIT_FAILURE);
1046  }
1047  string_list res;
1048  while (true)
1049  {
1050  switch (expect_token(in, Word | Dollarpar))
1051  {
1052  case Word:
1053  res.push_back(read_word(in));
1054  break;
1055  case Dollarpar:
1056  {
1057  std::string name = read_word(in);
1058  if (name.empty()) goto error;
1059  if (expect_token(in, Rightpar))
1060  {
1061  variable_map::const_iterator i = variables.find(name);
1062  if (i != variables.end())
1063  res.insert(res.end(), i->second.begin(), i->second.end());
1064  }
1065  else execute_function(in, name, res);
1066  break;
1067  }
1068  default:
1069  return res;
1070  }
1071  }
1072 }
static void register_scripted_rule ( rule_t const &  rule)
static

Register a specific rule with a nonempty script:

  • Check that none of the targets already has an associated rule.
  • Create a single shared rule and associate it to all the targets.
  • Merge the prerequisites of all the targets into a single set and add the prerequisites of the rule to it. (The preexisting prerequisites, if any, come from a previous run.)

Definition at line 1169 of file remake.cpp.

Referenced by load_rule().

1170 {
1171  ref_ptr<rule_t> r(rule);
1172  for (string_list::const_iterator i = rule.targets.begin(),
1173  i_end = rule.targets.end(); i != i_end; ++i)
1174  {
1175  std::pair<rule_map::iterator, bool> j =
1176  specific_rules.insert(std::make_pair(*i, r));
1177  if (j.second) continue;
1178  std::cerr << "Failed to load rules: " << *i
1179  << " cannot be the target of several rules" << std::endl;
1180  exit(EXIT_FAILURE);
1181  }
1182 
1184  dep->targets = rule.targets;
1185  dep->deps.insert(rule.deps.begin(), rule.deps.end());
1186  for (string_list::const_iterator i = rule.targets.begin(),
1187  i_end = rule.targets.end(); i != i_end; ++i)
1188  {
1190  dep->deps.insert(d->deps.begin(), d->deps.end());
1191  d = dep;
1192  }
1193 }
static void register_transparent_rule ( rule_t const &  rule)
static

Register a specific rule with an empty script:

  • Check that none of the targets already has an associated rule with a nonempty script.
  • Create a new rule with a single target for each target, if needed.
  • Add the prerequisites of rule to all these associated rules.

Definition at line 1125 of file remake.cpp.

Referenced by load_rule().

1126 {
1127  assert(rule.script.empty());
1128  for (string_list::const_iterator i = rule.targets.begin(),
1129  i_end = rule.targets.end(); i != i_end; ++i)
1130  {
1131  std::pair<rule_map::iterator, bool> j =
1132  specific_rules.insert(std::make_pair(*i, ref_ptr<rule_t>()));
1133  ref_ptr<rule_t> &r = j.first->second;
1134  if (j.second)
1135  {
1136  r = ref_ptr<rule_t>(rule);
1137  r->targets = string_list(1, *i);
1138  continue;
1139  }
1140  if (!r->script.empty())
1141  {
1142  std::cerr << "Failed to load rules: " << *i
1143  << " cannot be the target of several rules" << std::endl;
1144  exit(EXIT_FAILURE);
1145  }
1146  assert(r->targets.size() == 1 && r->targets.front() == *i);
1147  r->deps.insert(r->deps.end(), rule.deps.begin(), rule.deps.end());
1148  r->vars.insert(r->vars.end(), rule.vars.begin(), rule.vars.end());
1149  }
1150 
1151  for (string_list::const_iterator i = rule.targets.begin(),
1152  i_end = rule.targets.end(); i != i_end; ++i)
1153  {
1155  if (dep->targets.empty()) dep->targets.push_back(*i);
1156  dep->deps.insert(rule.deps.begin(), rule.deps.end());
1157  }
1158 }
static bool run_script ( int  job_id,
rule_t const &  rule 
)
static

Execute the script from rule.

Definition at line 1650 of file remake.cpp.

Referenced by complete_request(), and start().

1651 {
1652  if (show_targets)
1653  {
1654  std::cout << "Building";
1655  for (string_list::const_iterator i = rule.targets.begin(),
1656  i_end = rule.targets.end(); i != i_end; ++i)
1657  {
1658  std::cout << ' ' << *i;
1659  }
1660  std::cout << std::endl;
1661  }
1662 
1664  dep->targets = rule.targets;
1665  dep->deps.insert(rule.deps.begin(), rule.deps.end());
1666  for (string_list::const_iterator i = rule.targets.begin(),
1667  i_end = rule.targets.end(); i != i_end; ++i)
1668  {
1669  dependencies[*i] = dep;
1670  }
1671 
1672  // Update the content of variables.
1673  variable_map vars = variables;
1674  for (assign_list::const_iterator i = rule.vars.begin(),
1675  i_end = rule.vars.end(); i != i_end; ++i)
1676  {
1677  string_list &val = vars[i->name];
1678  if (!i->append) val.clear();
1679  val.insert(val.end(), i->value.begin(), i->value.end());
1680  }
1681 
1682  // Output them at the start of the script.
1683  std::ostringstream script_buf;
1684  for (variable_map::const_iterator i = vars.begin(),
1685  i_end = vars.end(); i != i_end; ++i)
1686  {
1687  std::ostringstream var;
1688  bool first = true;
1689  for (string_list::const_iterator j = i->second.begin(),
1690  j_end = i->second.end(); j != j_end; ++j)
1691  {
1692  if (first) first = false;
1693  else var << ' ';
1694  var << *j;
1695  }
1696  script_buf << i->first << '=' << escape_string(var.str()) << std::endl;
1697  }
1698  script_buf << rule.script;
1699  std::string const &script = script_buf.str();
1700 
1701  DEBUG_open << "Starting script for job " << job_id << "... ";
1702 #ifdef WINDOWS
1703  HANDLE pfd[2];
1704  if (false)
1705  {
1706  error2:
1707  CloseHandle(pfd[0]);
1708  CloseHandle(pfd[1]);
1709  error:
1710  DEBUG_close << "failed\n";
1711  complete_job(job_id, false);
1712  return false;
1713  }
1714  if (!CreatePipe(&pfd[0], &pfd[1], NULL, 0))
1715  goto error;
1716  if (!SetHandleInformation(pfd[0], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
1717  goto error2;
1718  STARTUPINFO si;
1719  ZeroMemory(&si, sizeof(STARTUPINFO));
1720  si.cb = sizeof(STARTUPINFO);
1721  si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1722  si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1723  si.hStdInput = pfd[0];
1724  si.dwFlags |= STARTF_USESTDHANDLES;
1725  PROCESS_INFORMATION pi;
1726  ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
1727  std::ostringstream buf;
1728  buf << job_id;
1729  if (!SetEnvironmentVariable("REMAKE_JOB_ID", buf.str().c_str()))
1730  goto error2;
1731  std::ostringstream argv;
1732  argv << "SH.EXE -e -s";
1733  if (echo_scripts) argv << " -v";
1734  for (string_list::const_iterator i = rule.targets.begin(),
1735  i_end = rule.targets.end(); i != i_end; ++i)
1736  {
1737  argv << " \"" << escape_string(*i) << '"';
1738  }
1739  if (!CreateProcess(NULL, (char *)argv.str().c_str(), NULL, NULL,
1740  true, 0, NULL, NULL, &si, &pi))
1741  {
1742  goto error2;
1743  }
1744  CloseHandle(pi.hThread);
1745  DWORD len = script.length(), wlen;
1746  if (!WriteFile(pfd[1], script.c_str(), len, &wlen, NULL) || wlen < len)
1747  std::cerr << "Unexpected failure while sending script to shell" << std::endl;
1748  CloseHandle(pfd[0]);
1749  CloseHandle(pfd[1]);
1750  ++running_jobs;
1751  job_pids[pi.hProcess] = job_id;
1752  return true;
1753 #else
1754  int pfd[2];
1755  if (false)
1756  {
1757  error2:
1758  close(pfd[0]);
1759  close(pfd[1]);
1760  error:
1761  DEBUG_close << "failed\n";
1762  complete_job(job_id, false);
1763  return false;
1764  }
1765  if (pipe(pfd) == -1)
1766  goto error;
1767  if (pid_t pid = fork())
1768  {
1769  if (pid == -1) goto error2;
1770  ssize_t len = script.length();
1771  if (write(pfd[1], script.c_str(), len) < len)
1772  std::cerr << "Unexpected failure while sending script to shell" << std::endl;
1773  close(pfd[0]);
1774  close(pfd[1]);
1775  ++running_jobs;
1776  job_pids[pid] = job_id;
1777  return true;
1778  }
1779  // Child process starts here.
1780  std::ostringstream buf;
1781  buf << job_id;
1782  if (setenv("REMAKE_JOB_ID", buf.str().c_str(), 1))
1783  _exit(EXIT_FAILURE);
1784  int num = echo_scripts ? 4 : 3;
1785  char const **argv = new char const *[num + rule.targets.size() + 1];
1786  argv[0] = "sh";
1787  argv[1] = "-e";
1788  argv[2] = "-s";
1789  if (echo_scripts) argv[3] = "-v";
1790  for (string_list::const_iterator i = rule.targets.begin(),
1791  i_end = rule.targets.end(); i != i_end; ++i, ++num)
1792  {
1793  argv[num] = i->c_str();
1794  }
1795  argv[num] = NULL;
1796  if (pfd[0] != 0)
1797  {
1798  dup2(pfd[0], 0);
1799  close(pfd[0]);
1800  }
1801  close(pfd[1]);
1802  execv("/bin/sh", (char **)argv);
1803  _exit(EXIT_FAILURE);
1804 #endif
1805 }
static void save_dependencies ( )
static

Save all the dependencies in file .remake.

Definition at line 1310 of file remake.cpp.

Referenced by server_mode().

1311 {
1312  DEBUG_open << "Saving database... ";
1313  std::ofstream db(".remake");
1314  while (!dependencies.empty())
1315  {
1316  ref_ptr<dependency_t> dep = dependencies.begin()->second;
1317  for (string_list::const_iterator i = dep->targets.begin(),
1318  i_end = dep->targets.end(); i != i_end; ++i)
1319  {
1320  db << escape_string(*i) << ' ';
1321  dependencies.erase(*i);
1322  }
1323  db << ':';
1324  for (string_set::const_iterator i = dep->deps.begin(),
1325  i_end = dep->deps.end(); i != i_end; ++i)
1326  {
1327  db << ' ' << escape_string(*i);
1328  }
1329  db << std::endl;
1330  }
1331 }
void server_loop ( )

Loop until all the jobs have finished.

Postcondition
There are no client requests left, not even virtual ones.

Definition at line 2180 of file remake.cpp.

Referenced by server_mode().

2181 {
2182  while (handle_clients())
2183  {
2184  DEBUG_open << "Handling events... ";
2185  #ifdef WINDOWS
2186  size_t len = job_pids.size() + 1;
2187  HANDLE h[len];
2188  int num = 0;
2189  for (pid_job_map::const_iterator i = job_pids.begin(),
2190  i_end = job_pids.end(); i != i_end; ++i, ++num)
2191  {
2192  h[num] = i->first;
2193  }
2194  WSAEVENT aev = WSACreateEvent();
2195  h[num] = aev;
2196  WSAEventSelect(socket_fd, aev, FD_ACCEPT);
2197  DWORD w = WaitForMultipleObjects(len, h, false, INFINITE);
2198  WSAEventSelect(socket_fd, aev, 0);
2199  WSACloseEvent(aev);
2200  if (w < WAIT_OBJECT_0 || WAIT_OBJECT_0 + len <= w)
2201  continue;
2202  if (w == WAIT_OBJECT_0 + len - 1)
2203  {
2204  accept_client();
2205  continue;
2206  }
2207  pid_t pid = h[w - WAIT_OBJECT_0];
2208  DWORD s = 0;
2209  bool res = GetExitCodeProcess(pid, &s) && s == 0;
2210  CloseHandle(pid);
2211  finalize_job(pid, res);
2212  #else
2213  sigset_t emptymask;
2214  sigemptyset(&emptymask);
2215  fd_set fdset;
2216  FD_ZERO(&fdset);
2217  FD_SET(socket_fd, &fdset);
2218  int ret = pselect(socket_fd + 1, &fdset, NULL, NULL, NULL, &emptymask);
2219  if (ret > 0 /* && FD_ISSET(socket_fd, &fdset)*/) accept_client();
2220  if (!got_SIGCHLD) continue;
2221  got_SIGCHLD = 0;
2222  pid_t pid;
2223  int status;
2224  while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
2225  {
2226  bool res = WIFEXITED(status) && WEXITSTATUS(status) == 0;
2227  finalize_job(pid, res);
2228  }
2229  #endif
2230  }
2231 
2232  assert(clients.empty());
2233 }
void server_mode ( string_list const &  targets)

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 2241 of file remake.cpp.

Referenced by main().

2242 {
2244  load_rules();
2245  create_server();
2246  if (get_status("Remakefile").status != Uptodate)
2247  {
2248  clients.push_back(client_t());
2249  clients.back().pending.push_back("Remakefile");
2250  server_loop();
2251  if (build_failure) goto early_exit;
2252  variables.clear();
2253  specific_rules.clear();
2254  generic_rules.clear();
2255  first_target.clear();
2256  load_rules();
2257  }
2258  clients.push_back(client_t());
2259  if (!targets.empty()) clients.back().pending = targets;
2260  else if (!first_target.empty())
2261  clients.back().pending.push_back(first_target);
2262  server_loop();
2263  early_exit:
2264  close(socket_fd);
2265 #ifndef WINDOWS
2266  remove(socket_name);
2267  free(socket_name);
2268 #endif
2270  exit(build_failure ? EXIT_FAILURE : EXIT_SUCCESS);
2271 }
static void sigchld_handler ( int  )
static

Definition at line 639 of file remake.cpp.

Referenced by create_server().

640 {
641  got_SIGCHLD = 1;
642 }
static void sigint_handler ( int  )
static

Definition at line 644 of file remake.cpp.

Referenced by create_server().

645 {
646  // Child processes will receive the signal too, so just prevent
647  // new jobs from starting and wait for the running jobs to fail.
648  keep_going = false;
649 }
static void skip_empty ( std::istream &  in)
static

Skip empty lines.

Definition at line 853 of file remake.cpp.

Referenced by load_dependencies(), load_rules(), and skip_eol().

854 {
855  char c;
856  while (strchr("\r\n", (c = in.get()))) {}
857  if (in.good()) in.putback(c);
858 }
static bool skip_eol ( std::istream &  in,
bool  multi = false 
)
static

Skip end of line. If multi is true, skip the following empty lines too.

Returns
true if there was a line to end.

Definition at line 864 of file remake.cpp.

Referenced by expect_token(), load_rule(), and load_rules().

865 {
866  char c = in.get();
867  if (c == '\r') c = in.get();
868  if (c != '\n' && in.good()) in.putback(c);
869  if (c != '\n' && !in.eof()) return false;
870  if (multi) skip_empty(in);
871  return true;
872 }
static void skip_spaces ( std::istream &  in)
static

Skip spaces.

Definition at line 843 of file remake.cpp.

Referenced by execute_function(), expect_token(), and load_rule().

844 {
845  char c;
846  while (strchr(" \t", (c = in.get()))) {}
847  if (in.good()) in.putback(c);
848 }
static bool 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. 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 1813 of file remake.cpp.

Referenced by handle_clients().

1814 {
1815  DEBUG_open << "Starting job " << job_counter << " for " << target << "... ";
1816  rule_t rule = find_rule(target);
1817  if (rule.targets.empty())
1818  {
1819  status[target].status = Failed;
1820  DEBUG_close << "failed\n";
1821  std::cerr << "No rule for building " << target << std::endl;
1822  return false;
1823  }
1824  for (string_list::const_iterator i = rule.targets.begin(),
1825  i_end = rule.targets.end(); i != i_end; ++i)
1826  {
1827  status[*i].status = Running;
1828  }
1829  int job_id = job_counter++;
1830  job_targets[job_id] = rule.targets;
1831  if (!rule.deps.empty())
1832  {
1833  current = clients.insert(current, client_t());
1834  current->job_id = job_id;
1835  current->pending = rule.deps;
1836  current->delayed = new rule_t(rule);
1837  return true;
1838  }
1839  return run_script(job_id, rule);
1840 }
static bool still_need_rebuild ( std::string const &  target)
static

Check if all the prerequisites of target ended being up-to-date.

Definition at line 1591 of file remake.cpp.

Referenced by complete_request().

1592 {
1593  DEBUG_open << "Rechecking obsoleteness of " << target << "... ";
1594  status_map::const_iterator i = status.find(target);
1595  assert(i != status.end());
1596  if (i->second.status != Recheck) return true;
1597  dependency_map::const_iterator j = dependencies.find(target);
1598  assert(j != dependencies.end());
1599  dependency_t const &dep = *j->second;
1600  for (string_set::const_iterator k = dep.deps.begin(),
1601  k_end = dep.deps.end(); k != k_end; ++k)
1602  {
1603  if (status[*k].status != Uptodate) return true;
1604  }
1605  for (string_list::const_iterator k = dep.targets.begin(),
1606  k_end = dep.targets.end(); k != k_end; ++k)
1607  {
1608  status[*k].status = Uptodate;
1609  }
1610  DEBUG_close << "no longer obsolete\n";
1611  return false;
1612 }
static void substitute_pattern ( std::string const &  pat,
string_list const &  src,
string_list dst 
)
static

Substitute a pattern into a list of strings.

Definition at line 1388 of file remake.cpp.

Referenced by find_generic_rule().

1389 {
1390  for (string_list::const_iterator i = src.begin(),
1391  i_end = src.end(); i != i_end; ++i)
1392  {
1393  size_t pos = i->find('%');
1394  if (pos == std::string::npos)dst.push_back(*i);
1395  else dst.push_back(i->substr(0, pos) + pat + i->substr(pos + 1));
1396  }
1397 }
static void update_status ( std::string const &  target)
static

Change the status of target to Remade or Uptodate depending on whether its modification time changed.

Definition at line 1558 of file remake.cpp.

Referenced by complete_job().

1559 {
1560  DEBUG_open << "Rechecking status of " << target << "... ";
1561  status_map::iterator i = status.find(target);
1562  assert(i != status.end());
1563  status_t &ts = i->second;
1564  ts.status = Remade;
1565  if (ts.last >= now)
1566  {
1567  DEBUG_close << "possibly remade\n";
1568  return;
1569  }
1570  struct stat s;
1571  if (stat(target.c_str(), &s) != 0)
1572  {
1573  DEBUG_close << "missing\n";
1574  ts.last = 0;
1575  }
1576  else if (s.st_mtime != ts.last)
1577  {
1578  DEBUG_close << "remade\n";
1579  ts.last = s.st_mtime;
1580  }
1581  else
1582  {
1583  DEBUG_close << "unchanged\n";
1584  ts.status = Uptodate;
1585  }
1586 }
void usage ( int  exit_status)

Display usage and exit with exit_status.

Definition at line 2341 of file remake.cpp.

Referenced by main().

2342 {
2343  std::cerr << "Usage: remake [options] [target] ...\n"
2344  "Options\n"
2345  " -d Echo script commands.\n"
2346  " -d -d Print lots of debugging information.\n"
2347  " -h, --help Print this message and exit.\n"
2348  " -j[N], --jobs=[N] Allow N jobs at once; infinite jobs with no arg.\n"
2349  " -k Keep going when some targets cannot be made.\n"
2350  " -r Look up targets from the dependencies on standard input.\n"
2351  " -s, --silent, --quiet Do not echo targets.\n";
2352  exit(exit_status);
2353 }

Variable Documentation

bool build_failure
static

Whether the request of an original client failed.

Definition at line 608 of file remake.cpp.

Referenced by complete_request(), and server_mode().

client_list clients
static

List of clients waiting for a request to complete. New clients are put to front, so that the build process is depth-first.

Definition at line 559 of file remake.cpp.

Referenced by accept_client(), handle_clients(), server_loop(), server_mode(), and start().

log debug

Definition at line 679 of file remake.cpp.

Referenced by log_auto_close::~log_auto_close().

dependency_map dependencies
static

Map from targets to their known dependencies.

Definition at line 528 of file remake.cpp.

Referenced by accept_client(), get_status(), load_dependencies(), main(), register_scripted_rule(), register_transparent_rule(), run_script(), save_dependencies(), and still_need_rebuild().

bool echo_scripts = false
static

Whether script commands are echoed.

Definition at line 630 of file remake.cpp.

Referenced by main(), and run_script().

std::string first_target
static

Name of the first target of the first specific rule, used for default run.

Definition at line 620 of file remake.cpp.

Referenced by load_rule(), and server_mode().

rule_list generic_rules
static

Set of generic rules loaded from Remakefile.

Definition at line 538 of file remake.cpp.

Referenced by find_generic_rule(), load_rule(), and server_mode().

volatile sig_atomic_t got_SIGCHLD = 0
static

Definition at line 637 of file remake.cpp.

Referenced by server_loop(), and sigchld_handler().

int job_counter = 0
static

Global counter used to produce increasing job numbers.

See Also
job_targets

Definition at line 598 of file remake.cpp.

Referenced by start().

pid_job_map job_pids
static

Map from jobs to shell pids.

Definition at line 553 of file remake.cpp.

Referenced by finalize_job(), run_script(), and server_loop().

job_targets_map job_targets
static

Map from jobs to targets being built.

Definition at line 548 of file remake.cpp.

Referenced by accept_client(), complete_job(), and start().

bool keep_going = false
static

Whether to keep building targets in case of failure. Can be modified by the -k option.

Definition at line 571 of file remake.cpp.

Referenced by handle_clients(), main(), and sigint_handler().

int max_active_jobs = 1
static

Maximum number of parallel jobs (non-positive if unbounded). Can be modified by the -j option.

Definition at line 565 of file remake.cpp.

Referenced by has_free_slots(), and main().

time_t now = time(NULL)
static

Definition at line 632 of file remake.cpp.

Referenced by update_status().

int running_jobs = 0
static

Number of jobs currently running:

  • it increases when a process is created in run_script,
  • it decreases when a completion message is received in finalize_job.
Note
There might be some jobs running while clients is empty. Indeed, if a client requested two targets to be rebuilt, if they are running concurrently, if one of them fails, the client will get a failure notice and might terminate before the other target finishes.

Definition at line 584 of file remake.cpp.

Referenced by finalize_job(), handle_clients(), has_free_slots(), and run_script().

bool show_targets = true
static

Whether a short message should be displayed for each target.

Definition at line 625 of file remake.cpp.

Referenced by main(), and run_script().

socket_t socket_fd
static

Socket on which the server listens for client request.

Definition at line 603 of file remake.cpp.

Referenced by accept_client(), client_mode(), create_server(), server_loop(), and server_mode().

char* socket_name
static

Name of the server socket in the file system.

Definition at line 614 of file remake.cpp.

Referenced by create_server(), and server_mode().

rule_map specific_rules
static

Map from targets to specific rules loaded from Remakefile.

Definition at line 543 of file remake.cpp.

Referenced by find_rule(), register_scripted_rule(), register_transparent_rule(), and server_mode().

status_map status
static

Map from targets to their build status.

Definition at line 533 of file remake.cpp.

Referenced by complete_job(), get_status(), handle_clients(), server_loop(), server_mode(), start(), still_need_rebuild(), and update_status().

variable_map variables
static

Map from variable names to their content.

Definition at line 523 of file remake.cpp.

Referenced by load_rules(), read_words(), run_script(), and server_mode().

int waiting_jobs = 0
static

Number of jobs currently waiting for a build request to finish:

  • it increases when a build request is received in accept_client (since the client is presumably waiting for the reply),
  • it decreases when a reply is sent in complete_request.

Definition at line 592 of file remake.cpp.

Referenced by accept_client(), complete_request(), handle_clients(), and has_free_slots().

std::string working_dir
static

Definition at line 634 of file remake.cpp.

Referenced by init_working_dir(), normalize(), and normalize_abs().