Remake
Functions
Server

Functions

static void complete_job (int job_id, bool success, bool started=true)
 
static std::string prepare_script (job_t const &job)
 
static status_e run_script (int job_id, job_t const &job)
 
static status_e 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 ()
 
static void accept_client ()
 
static void finalize_job (pid_t pid, bool res)
 
static void server_loop ()
 
static void server_mode (std::string const &remakefile, string_list const &targets)
 

Detailed Description

Function Documentation

static void accept_client ( )
static

Accept a connection from a client, get the job it spawned from, get the targets, and mark them as dependencies of the job targets.

Definition at line 2622 of file remake.cpp.

Referenced by server_loop().

2623 {
2624  DEBUG_open << "Handling client request... ";
2625 
2626  // Accept connection.
2627 #ifdef WINDOWS
2628  socket_t fd = accept(socket_fd, NULL, NULL);
2629  if (fd == INVALID_SOCKET) return;
2630  if (!SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0))
2631  {
2632  error2:
2633  std::cerr << "Unexpected failure while setting connection with client" << std::endl;
2634  closesocket(fd);
2635  return;
2636  }
2637  // WSAEventSelect puts sockets into nonblocking mode, so disable it here.
2638  u_long nbio = 0;
2639  if (ioctlsocket(fd, FIONBIO, &nbio)) goto error2;
2640 #elif defined(LINUX)
2641  int fd = accept4(socket_fd, NULL, NULL, SOCK_CLOEXEC);
2642  if (fd < 0) return;
2643 #else
2644  int fd = accept(socket_fd, NULL, NULL);
2645  if (fd < 0) return;
2646  if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) return;
2647 #endif
2648  clients.push_front(client_t());
2649  client_list::iterator proc = clients.begin();
2650 
2651  if (false)
2652  {
2653  error:
2654  DEBUG_close << "failed\n";
2655  std::cerr << "Received an ill-formed client message" << std::endl;
2656  #ifdef WINDOWS
2657  closesocket(fd);
2658  #else
2659  close(fd);
2660  #endif
2661  clients.erase(proc);
2662  return;
2663  }
2664 
2665  // Receive message. Stop when encountering two nuls in a row.
2666  std::vector<char> buf;
2667  size_t len = 0;
2668  while (len < sizeof(int) + 2 || buf[len - 1] || buf[len - 2])
2669  {
2670  buf.resize(len + 1024);
2671  ssize_t l = recv(fd, &buf[0] + len, 1024, 0);
2672  if (l <= 0) goto error;
2673  len += l;
2674  }
2675 
2676  // Parse job that spawned the client.
2677  int job_id;
2678  memcpy(&job_id, &buf[0], sizeof(int));
2679  proc->socket = fd;
2680  proc->job_id = job_id;
2681  job_map::const_iterator i = jobs.find(job_id);
2682  if (i == jobs.end()) goto error;
2683  DEBUG << "receiving request from job " << job_id << std::endl;
2684  if (propagate_vars) proc->vars = i->second.vars;
2685 
2686  // Parse the targets and the variable assignments.
2687  // Mark the targets as dependencies of the job targets.
2688  dependency_t &dep = *dependencies[i->second.rule.targets.front()];
2689  string_list *last_var = NULL;
2690  char const *p = &buf[0] + sizeof(int);
2691  while (true)
2692  {
2693  len = strlen(p);
2694  if (len == 0)
2695  {
2696  ++waiting_jobs;
2697  break;
2698  }
2699  switch (*p)
2700  {
2701  case 'T':
2702  {
2703  if (len == 1) goto error;
2704  std::string target(p + 1, p + len);
2705  DEBUG << "adding dependency " << target << " to job\n";
2706  proc->pending.push_back(target);
2707  dep.deps.insert(target);
2708  break;
2709  }
2710  case 'V':
2711  {
2712  if (len == 1) goto error;
2713  std::string var(p + 1, p + len);
2714  DEBUG << "adding variable " << var << " to job\n";
2715  last_var = &proc->vars[var];
2716  last_var->clear();
2717  break;
2718  }
2719  case 'W':
2720  {
2721  if (!last_var) goto error;
2722  last_var->push_back(std::string(p + 1, p + len));
2723  break;
2724  }
2725  default:
2726  goto error;
2727  }
2728  p += len + 1;
2729  }
2730 
2731  if (!propagate_vars && !proc->vars.empty())
2732  {
2733  std::cerr << "Assignments are ignored unless 'variable-propagation' is enabled" << std::endl;
2734  proc->vars.clear();
2735  }
2736 }
#define DEBUG_open
Definition: remake.cpp:801
static job_map jobs
Definition: remake.cpp:629
static bool propagate_vars
Definition: remake.cpp:736
std::list< std::string > string_list
Definition: remake.cpp:456
static int waiting_jobs
Definition: remake.cpp:673
static client_list clients
Definition: remake.cpp:640
string_set deps
Definition: remake.cpp:499
int socket_t
Definition: remake.cpp:447
#define DEBUG
Definition: remake.cpp:800
#define DEBUG_close
Definition: remake.cpp:802
static socket_t socket_fd
Definition: remake.cpp:684
static dependency_map dependencies
Definition: remake.cpp:609
static void complete_job ( int  job_id,
bool  success,
bool  started = true 
)
static

Handle job completion.

Definition at line 2066 of file remake.cpp.

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

2067 {
2068  DEBUG << "Completing job " << job_id << '\n';
2069  job_map::iterator i = jobs.find(job_id);
2070  assert(i != jobs.end());
2071  string_list const &targets = i->second.rule.targets;
2072  if (success)
2073  {
2074  bool show = show_targets && started;
2075  if (show) std::cout << "Finished";
2076  for (string_list::const_iterator j = targets.begin(),
2077  j_end = targets.end(); j != j_end; ++j)
2078  {
2079  update_status(*j);
2080  if (show) std::cout << ' ' << *j;
2081  }
2082  if (show) std::cout << std::endl;
2083  }
2084  else
2085  {
2086  std::cerr << "Failed to build";
2087  for (string_list::const_iterator j = targets.begin(),
2088  j_end = targets.end(); j != j_end; ++j)
2089  {
2090  std::cerr << ' ' << *j;
2091  update_status(*j);
2092  status_e &s = status[*j].status;
2093  if (s != Uptodate)
2094  {
2095  DEBUG << "Removing " << *j << '\n';
2096  remove(j->c_str());
2097  }
2098  s = Failed;
2099  }
2100  std::cerr << std::endl;
2101  }
2102  jobs.erase(i);
2103 }
status_e
Definition: remake.cpp:509
static job_map jobs
Definition: remake.cpp:629
std::list< std::string > string_list
Definition: remake.cpp:456
Target is up-to-date.
Definition: remake.cpp:511
static status_map status
Definition: remake.cpp:614
static void update_status(std::string const &target)
Definition: remake.cpp:1999
static bool show_targets
Definition: remake.cpp:706
Build failed for target.
Definition: remake.cpp:517
#define DEBUG
Definition: remake.cpp:800
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 2370 of file remake.cpp.

Referenced by handle_clients().

2371 {
2372  DEBUG_open << "Completing request from client of job " << client.job_id << "... ";
2373  if (client.delayed)
2374  {
2375  assert(client.socket == INVALID_SOCKET);
2376  if (success)
2377  {
2378  job_map::const_iterator i = jobs.find(client.job_id);
2379  assert(i != jobs.end());
2380  if (still_need_rebuild(i->second.rule.targets.front()))
2381  run_script(client.job_id, i->second);
2382  else complete_job(client.job_id, true, false);
2383  }
2384  else complete_job(client.job_id, false);
2385  }
2386  else if (client.socket != INVALID_SOCKET)
2387  {
2388  char res = success ? 1 : 0;
2389  send(client.socket, &res, 1, MSG_NOSIGNAL);
2390  #ifdef WINDOWS
2391  closesocket(client.socket);
2392  #else
2393  close(client.socket);
2394  #endif
2395  --waiting_jobs;
2396  }
2397 
2398  if (client.job_id < 0 && !success) build_failure = true;
2399 }
static bool still_need_rebuild(std::string const &target)
Definition: remake.cpp:2032
int job_id
Job for which the built script called remake and spawned the client (negative for original clients)...
Definition: remake.cpp:589
static status_e run_script(int job_id, job_t const &job)
Definition: remake.cpp:2191
#define DEBUG_open
Definition: remake.cpp:801
static job_map jobs
Definition: remake.cpp:629
bool delayed
Whether it is a dependency client and a script has to be started on request completion.
Definition: remake.cpp:594
static int waiting_jobs
Definition: remake.cpp:673
static bool build_failure
Definition: remake.cpp:689
socket_t socket
Socket used to reply to the client (invalid for pseudo clients).
Definition: remake.cpp:588
static void complete_job(int job_id, bool success, bool started=true)
Definition: remake.cpp:2066
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 2542 of file remake.cpp.

Referenced by server_mode().

2543 {
2544  if (false)
2545  {
2546  error:
2547  perror("Failed to create server");
2548 #ifndef WINDOWS
2549  error2:
2550 #endif
2551  exit(EXIT_FAILURE);
2552  }
2553  DEBUG_open << "Creating server... ";
2554 
2555 #ifdef WINDOWS
2556  // Prepare a windows socket.
2557  struct sockaddr_in socket_addr;
2558  socket_addr.sin_family = AF_INET;
2559  socket_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
2560  socket_addr.sin_port = 0;
2561 
2562  // Create and listen to the socket.
2563  socket_fd = socket(AF_INET, SOCK_STREAM, 0);
2564  if (socket_fd == INVALID_SOCKET) goto error;
2565  if (!SetHandleInformation((HANDLE)socket_fd, HANDLE_FLAG_INHERIT, 0))
2566  goto error;
2567  if (bind(socket_fd, (struct sockaddr *)&socket_addr, sizeof(sockaddr_in)))
2568  goto error;
2569  int len = sizeof(sockaddr_in);
2570  if (getsockname(socket_fd, (struct sockaddr *)&socket_addr, &len))
2571  goto error;
2572  std::ostringstream buf;
2573  buf << socket_addr.sin_port;
2574  if (!SetEnvironmentVariable("REMAKE_SOCKET", buf.str().c_str()))
2575  goto error;
2576  if (listen(socket_fd, 1000)) goto error;
2577 #else
2578  // Set signal handlers for SIGCHLD and SIGINT.
2579  // Block SIGCHLD (unblocked during select).
2580  sigset_t sigmask;
2581  sigemptyset(&sigmask);
2582  sigaddset(&sigmask, SIGCHLD);
2583  if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == -1) goto error;
2584  struct sigaction sa;
2585  sa.sa_flags = 0;
2586  sigemptyset(&sa.sa_mask);
2587  sa.sa_handler = &sigchld_handler;
2588  if (sigaction(SIGCHLD, &sa, NULL) == -1) goto error;
2589  sa.sa_handler = &sigint_handler;
2590  if (sigaction(SIGINT, &sa, NULL) == -1) goto error;
2591 
2592  // Prepare a named unix socket in temporary directory.
2593  socket_name = tempnam(NULL, "rmk-");
2594  if (!socket_name) goto error2;
2595  struct sockaddr_un socket_addr;
2596  size_t len = strlen(socket_name);
2597  if (len >= sizeof(socket_addr.sun_path) - 1) goto error2;
2598  socket_addr.sun_family = AF_UNIX;
2599  strcpy(socket_addr.sun_path, socket_name);
2600  len += sizeof(socket_addr.sun_family);
2601  if (setenv("REMAKE_SOCKET", socket_name, 1)) goto error;
2602 
2603  // Create and listen to the socket.
2604 #ifdef LINUX
2605  socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
2606  if (socket_fd == INVALID_SOCKET) goto error;
2607 #else
2608  socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
2609  if (socket_fd == INVALID_SOCKET) goto error;
2610  if (fcntl(socket_fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
2611 #endif
2612  if (bind(socket_fd, (struct sockaddr *)&socket_addr, len))
2613  goto error;
2614  if (listen(socket_fd, 1000)) goto error;
2615 #endif
2616 }
#define DEBUG_open
Definition: remake.cpp:801
static char * socket_name
Definition: remake.cpp:695
static void sigint_handler(int)
Definition: remake.cpp:751
static socket_t socket_fd
Definition: remake.cpp:684
static void sigchld_handler(int)
Definition: remake.cpp:746
static void finalize_job ( pid_t  pid,
bool  res 
)
static

Handle child process exit status.

Definition at line 2741 of file remake.cpp.

Referenced by server_loop().

2742 {
2743  pid_job_map::iterator i = job_pids.find(pid);
2744  assert(i != job_pids.end());
2745  int job_id = i->second;
2746  job_pids.erase(i);
2747  --running_jobs;
2748  complete_job(job_id, res);
2749 }
static pid_job_map job_pids
Definition: remake.cpp:634
static int running_jobs
Definition: remake.cpp:665
static void complete_job(int job_id, bool success, bool started=true)
Definition: remake.cpp:2066
static bool handle_clients ( )
static

Handle client requests:

  • check for running targets that have finished,
  • start as many pending targets as allowed,
  • complete the request if there are neither running nor pending targets left or if any of them failed.
Returns
true if some child processes are still running.
Postcondition
If there are pending requests, at least one child process is running.
Invariant
New free slots cannot appear during a run, since the only way to decrease running_jobs is finalize_job and the only way to increase waiting_jobs is accept_client. None of these functions are called during a run. So breaking out as soon as there no free slots left is fine.

Definition at line 2427 of file remake.cpp.

Referenced by server_loop().

2428 {
2429  DEBUG_open << "Handling client requests... ";
2430  restart:
2431  bool need_restart = false;
2432 
2433  for (client_list::iterator i = clients.begin(), i_next = i,
2434  i_end = clients.end(); i != i_end && has_free_slots(); i = i_next)
2435  {
2436  ++i_next;
2437  DEBUG_open << "Handling client from job " << i->job_id << "... ";
2438 
2439  // Remove running targets that have finished.
2440  for (string_set::iterator j = i->running.begin(), j_next = j,
2441  j_end = i->running.end(); j != j_end; j = j_next)
2442  {
2443  ++j_next;
2444  status_map::const_iterator k = status.find(*j);
2445  assert(k != status.end());
2446  switch (k->second.status)
2447  {
2448  case Running:
2449  case RunningRecheck:
2450  break;
2451  case Failed:
2452  i->failed = true;
2453  if (!keep_going) goto complete;
2454  // no break
2455  case Uptodate:
2456  case Remade:
2457  i->running.erase(j);
2458  break;
2459  case Recheck:
2460  case Todo:
2461  assert(false);
2462  }
2463  }
2464 
2465  // Start pending targets.
2466  while (!i->pending.empty())
2467  {
2468  std::string target = i->pending.front();
2469  i->pending.pop_front();
2470  switch (get_status(target).status)
2471  {
2472  case Running:
2473  case RunningRecheck:
2474  i->running.insert(target);
2475  break;
2476  case Failed:
2477  pending_failed:
2478  i->failed = true;
2479  if (!keep_going) goto complete;
2480  // no break
2481  case Uptodate:
2482  case Remade:
2483  break;
2484  case Recheck:
2485  case Todo:
2486  client_list::iterator j = i;
2487  switch (start(target, i))
2488  {
2489  case Failed:
2490  goto pending_failed;
2491  case Running:
2492  // A shell was started, check for free slots.
2493  j->running.insert(target);
2494  if (!has_free_slots()) return true;
2495  break;
2496  case RunningRecheck:
2497  // Switch to the dependency client that was inserted.
2498  j->running.insert(target);
2499  i_next = j;
2500  break;
2501  case Remade:
2502  // Nothing to run.
2503  need_restart = true;
2504  break;
2505  default:
2506  assert(false);
2507  }
2508  }
2509  }
2510 
2511  // Try to complete the request.
2512  // (This might start a new job if it was a dependency client.)
2513  if (i->running.empty() || i->failed)
2514  {
2515  complete:
2516  complete_request(*i, !i->failed);
2517  DEBUG_close << (i->failed ? "failed\n" : "finished\n");
2518  clients.erase(i);
2519  need_restart = true;
2520  }
2521  }
2522 
2523  if (running_jobs != waiting_jobs) return true;
2524  if (running_jobs == 0 && clients.empty()) return false;
2525  if (need_restart) goto restart;
2526 
2527  // There is a circular dependency.
2528  // Try to break it by completing one of the requests.
2529  assert(!clients.empty());
2530  std::cerr << "Circular dependency detected" << std::endl;
2531  client_list::iterator i = clients.begin();
2532  complete_request(*i, false);
2533  clients.erase(i);
2534  goto restart;
2535 }
static void complete_request(client_t &client, bool success)
Definition: remake.cpp:2370
#define DEBUG_open
Definition: remake.cpp:801
Target is being rebuilt.
Definition: remake.cpp:514
static int waiting_jobs
Definition: remake.cpp:673
Target is up-to-date.
Definition: remake.cpp:511
static status_t const & get_status(std::string const &target)
Definition: remake.cpp:1922
static client_list clients
Definition: remake.cpp:640
static bool keep_going
Definition: remake.cpp:652
static status_map status
Definition: remake.cpp:614
static int running_jobs
Definition: remake.cpp:665
Target is missing or obsolete.
Definition: remake.cpp:512
Build failed for target.
Definition: remake.cpp:517
static bool has_free_slots()
Definition: remake.cpp:2404
Target has an obsolete dependency.
Definition: remake.cpp:513
Target was successfully rebuilt.
Definition: remake.cpp:516
#define DEBUG_close
Definition: remake.cpp:802
static status_e start(std::string const &target, client_list::iterator &current)
Definition: remake.cpp:2312
Static prerequisites are being rebuilt.
Definition: remake.cpp:515
static bool has_free_slots ( )
static

Return whether there are slots for starting new jobs.

Definition at line 2404 of file remake.cpp.

Referenced by handle_clients().

2405 {
2406  if (max_active_jobs <= 0) return true;
2408 }
static int max_active_jobs
Definition: remake.cpp:646
static int waiting_jobs
Definition: remake.cpp:673
static int running_jobs
Definition: remake.cpp:665
static std::string prepare_script ( job_t const &  job)
static

Return the script obtained by substituting variables.

Definition at line 2108 of file remake.cpp.

Referenced by run_script().

2109 {
2110  std::string const &s = job.rule.script;
2111  std::istringstream in(s);
2112  std::ostringstream out;
2113  size_t len = s.size();
2114 
2115  while (!in.eof())
2116  {
2117  size_t pos = in.tellg(), p = s.find('$', pos);
2118  if (p == std::string::npos || p == len - 1) p = len;
2119  out.write(&s[pos], p - pos);
2120  if (p == len) break;
2121  ++p;
2122  switch (s[p])
2123  {
2124  case '$':
2125  out << '$';
2126  in.seekg(p + 1);
2127  break;
2128  case '<':
2129  if (!job.rule.deps.empty())
2130  out << job.rule.deps.front();
2131  in.seekg(p + 1);
2132  break;
2133  case '^':
2134  {
2135  bool first = true;
2136  for (string_list::const_iterator i = job.rule.deps.begin(),
2137  i_end = job.rule.deps.end(); i != i_end; ++i)
2138  {
2139  if (first) first = false;
2140  else out << ' ';
2141  out << *i;
2142  }
2143  in.seekg(p + 1);
2144  break;
2145  }
2146  case '@':
2147  assert(!job.rule.targets.empty());
2148  out << job.rule.targets.front();
2149  in.seekg(p + 1);
2150  break;
2151  case '*':
2152  out << job.stem;
2153  in.seekg(p + 1);
2154  break;
2155  case '(':
2156  {
2157  in.seekg(p - 1);
2158  bool first = true;
2159  input_generator gen(in, &job.vars, true);
2160  while (true)
2161  {
2162  std::string w;
2163  input_status s = gen.next(w);
2164  if (s == SyntaxError)
2165  {
2166  // TODO
2167  return "false";
2168  }
2169  if (s == Eof) break;
2170  if (first) first = false;
2171  else out << ' ';
2172  out << w;
2173  }
2174  break;
2175  }
2176  default:
2177  // Let dollars followed by an unrecognized character
2178  // go through. This differs from Make, which would
2179  // use a one-letter variable.
2180  out << '$';
2181  in.seekg(p);
2182  }
2183  }
2184 
2185  return out.str();
2186 }
Definition: remake.cpp:1161
input_status
Definition: remake.cpp:1157
static status_e run_script ( int  job_id,
job_t const &  job 
)
static

Execute the script from rule.

Definition at line 2191 of file remake.cpp.

Referenced by complete_request(), and start().

2192 {
2194  dep->targets = job.rule.targets;
2195  dep->deps.insert(job.rule.deps.begin(), job.rule.deps.end());
2196  if (show_targets) std::cout << "Building";
2197  for (string_list::const_iterator i = job.rule.targets.begin(),
2198  i_end = job.rule.targets.end(); i != i_end; ++i)
2199  {
2200  dependencies[*i] = dep;
2201  if (show_targets) std::cout << ' ' << *i;
2202  }
2203  if (show_targets) std::cout << std::endl;
2204 
2205  std::string script = prepare_script(job);
2206 
2207  std::ostringstream job_id_buf;
2208  job_id_buf << job_id;
2209  std::string job_id_ = job_id_buf.str();
2210 
2211  DEBUG_open << "Starting script for job " << job_id << "... ";
2212  if (script.empty())
2213  {
2214  DEBUG_close << "no script\n";
2215  complete_job(job_id, true);
2216  return Remade;
2217  }
2218 
2219  if (false)
2220  {
2221  error:
2222  DEBUG_close << "failed\n";
2223  complete_job(job_id, false);
2224  return Failed;
2225  }
2226 
2227 #ifdef WINDOWS
2228  HANDLE pfd[2];
2229  if (false)
2230  {
2231  error2:
2232  CloseHandle(pfd[0]);
2233  CloseHandle(pfd[1]);
2234  goto error;
2235  }
2236  if (!CreatePipe(&pfd[0], &pfd[1], NULL, 0))
2237  goto error;
2238  if (!SetHandleInformation(pfd[0], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
2239  goto error2;
2240  STARTUPINFO si;
2241  ZeroMemory(&si, sizeof(STARTUPINFO));
2242  si.cb = sizeof(STARTUPINFO);
2243  si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
2244  si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
2245  si.hStdInput = pfd[0];
2246  si.dwFlags |= STARTF_USESTDHANDLES;
2247  PROCESS_INFORMATION pi;
2248  ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
2249  if (!SetEnvironmentVariable("REMAKE_JOB_ID", job_id_.c_str()))
2250  goto error2;
2251  char const *argv = echo_scripts ? "SH.EXE -e -s -v" : "SH.EXE -e -s";
2252  if (!CreateProcess(NULL, (char *)argv, NULL, NULL,
2253  true, 0, NULL, NULL, &si, &pi))
2254  {
2255  goto error2;
2256  }
2257  CloseHandle(pi.hThread);
2258  DWORD len = script.length(), wlen;
2259  if (!WriteFile(pfd[1], script.c_str(), len, &wlen, NULL) || wlen < len)
2260  std::cerr << "Unexpected failure while sending script to shell" << std::endl;
2261  CloseHandle(pfd[0]);
2262  CloseHandle(pfd[1]);
2263  ++running_jobs;
2264  job_pids[pi.hProcess] = job_id;
2265  return Running;
2266 #else
2267  int pfd[2];
2268  if (false)
2269  {
2270  error2:
2271  close(pfd[0]);
2272  close(pfd[1]);
2273  goto error;
2274  }
2275  if (pipe(pfd) == -1)
2276  goto error;
2277  if (setenv("REMAKE_JOB_ID", job_id_.c_str(), 1))
2278  goto error2;
2279  if (pid_t pid = vfork())
2280  {
2281  if (pid == -1) goto error2;
2282  ssize_t len = script.length();
2283  if (write(pfd[1], script.c_str(), len) < len)
2284  std::cerr << "Unexpected failure while sending script to shell" << std::endl;
2285  close(pfd[0]);
2286  close(pfd[1]);
2287  ++running_jobs;
2288  job_pids[pid] = job_id;
2289  return Running;
2290  }
2291  // Child process starts here. Notice the use of vfork above.
2292  char const *argv[5] = { "sh", "-e", "-s", NULL, NULL };
2293  if (echo_scripts) argv[3] = "-v";
2294  close(pfd[1]);
2295  if (pfd[0] != 0)
2296  {
2297  dup2(pfd[0], 0);
2298  close(pfd[0]);
2299  }
2300  execve("/bin/sh", (char **)argv, environ);
2301  _exit(EXIT_FAILURE);
2302 #endif
2303 }
#define DEBUG_open
Definition: remake.cpp:801
string_list targets
Definition: remake.cpp:498
Target is being rebuilt.
Definition: remake.cpp:514
string_set deps
Definition: remake.cpp:499
static pid_job_map job_pids
Definition: remake.cpp:634
char ** environ
static bool echo_scripts
Definition: remake.cpp:711
static int running_jobs
Definition: remake.cpp:665
static bool show_targets
Definition: remake.cpp:706
Build failed for target.
Definition: remake.cpp:517
Target was successfully rebuilt.
Definition: remake.cpp:516
#define DEBUG_close
Definition: remake.cpp:802
static void complete_job(int job_id, bool success, bool started=true)
Definition: remake.cpp:2066
static dependency_map dependencies
Definition: remake.cpp:609
static std::string prepare_script(job_t const &job)
Definition: remake.cpp:2108
static void server_loop ( )
static

Loop until all the jobs have finished.

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

Definition at line 2756 of file remake.cpp.

Referenced by server_mode().

2757 {
2758  while (handle_clients())
2759  {
2760  DEBUG_open << "Handling events... ";
2761  #ifdef WINDOWS
2762  size_t len = job_pids.size() + 1;
2763  HANDLE h[len];
2764  int num = 0;
2765  for (pid_job_map::const_iterator i = job_pids.begin(),
2766  i_end = job_pids.end(); i != i_end; ++i, ++num)
2767  {
2768  h[num] = i->first;
2769  }
2770  WSAEVENT aev = WSACreateEvent();
2771  h[num] = aev;
2772  WSAEventSelect(socket_fd, aev, FD_ACCEPT);
2773  DWORD w = WaitForMultipleObjects(len, h, false, INFINITE);
2774  WSAEventSelect(socket_fd, aev, 0);
2775  WSACloseEvent(aev);
2776  if (len <= w)
2777  continue;
2778  if (w == len - 1)
2779  {
2780  accept_client();
2781  continue;
2782  }
2783  pid_t pid = h[w];
2784  DWORD s = 0;
2785  bool res = GetExitCodeProcess(pid, &s) && s == 0;
2786  CloseHandle(pid);
2787  finalize_job(pid, res);
2788  #else
2789  sigset_t emptymask;
2790  sigemptyset(&emptymask);
2791  fd_set fdset;
2792  FD_ZERO(&fdset);
2793  FD_SET(socket_fd, &fdset);
2794  int ret = pselect(socket_fd + 1, &fdset, NULL, NULL, NULL, &emptymask);
2795  if (ret > 0 /* && FD_ISSET(socket_fd, &fdset)*/) accept_client();
2796  if (!got_SIGCHLD) continue;
2797  got_SIGCHLD = 0;
2798  pid_t pid;
2799  int status;
2800  while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
2801  {
2802  bool res = WIFEXITED(status) && WEXITSTATUS(status) == 0;
2803  finalize_job(pid, res);
2804  }
2805  #endif
2806  }
2807 
2808  assert(clients.empty());
2809 }
#define DEBUG_open
Definition: remake.cpp:801
static client_list clients
Definition: remake.cpp:640
static pid_job_map job_pids
Definition: remake.cpp:634
static status_map status
Definition: remake.cpp:614
static volatile sig_atomic_t got_SIGCHLD
Definition: remake.cpp:744
static bool handle_clients()
Definition: remake.cpp:2427
static void accept_client()
Definition: remake.cpp:2622
static socket_t socket_fd
Definition: remake.cpp:684
static void finalize_job(pid_t pid, bool res)
Definition: remake.cpp:2741
static void server_mode ( std::string const &  remakefile,
string_list const &  targets 
)
static

Load dependencies and rules, listen to client requests, and loop until all the requests have completed. If Remakefile is obsolete, perform a first run with it only, then reload the rules, and perform a second with the original clients.

Definition at line 2817 of file remake.cpp.

Referenced by main().

2818 {
2820  load_rules(remakefile);
2821  create_server();
2822  if (get_status(remakefile).status != Uptodate)
2823  {
2824  clients.push_back(client_t());
2825  clients.back().pending.push_back(remakefile);
2826  server_loop();
2827  if (build_failure) goto early_exit;
2828  variables.clear();
2829  specific_rules.clear();
2830  generic_rules.clear();
2831  first_target.clear();
2832  load_rules(remakefile);
2833  }
2834  clients.push_back(client_t());
2835  if (!targets.empty()) clients.back().pending = targets;
2836  else if (!first_target.empty())
2837  clients.back().pending.push_back(first_target);
2838  server_loop();
2839  early_exit:
2840  close(socket_fd);
2841 #ifndef WINDOWS
2842  remove(socket_name);
2843  free(socket_name);
2844 #endif
2847  {
2848  std::cout << "remake: Leaving directory `" << prefix_dir << '\'' << std::endl;
2849  }
2850  exit(build_failure ? EXIT_FAILURE : EXIT_SUCCESS);
2851 }
static void create_server()
Definition: remake.cpp:2542
static void save_dependencies()
Definition: remake.cpp:1472
static char * socket_name
Definition: remake.cpp:695
static variable_map variables
Definition: remake.cpp:604
static rule_map specific_rules
Definition: remake.cpp:624
Target is up-to-date.
Definition: remake.cpp:511
static status_t const & get_status(std::string const &target)
Definition: remake.cpp:1922
static std::string first_target
Definition: remake.cpp:701
static void server_loop()
Definition: remake.cpp:2756
static bool build_failure
Definition: remake.cpp:689
static client_list clients
Definition: remake.cpp:640
static status_map status
Definition: remake.cpp:614
static std::string prefix_dir
Definition: remake.cpp:726
static bool show_targets
Definition: remake.cpp:706
static bool changed_prefix_dir
Definition: remake.cpp:731
static socket_t socket_fd
Definition: remake.cpp:684
static void load_dependencies(std::istream &in)
Definition: remake.cpp:1422
static void load_rules(std::string const &remakefile)
Definition: remake.cpp:1720
static rule_list generic_rules
Definition: remake.cpp:619
static status_e start ( std::string const &  target,
client_list::iterator &  current 
)
static

Create a job for target according to the loaded rules. Mark all the targets from the rule as running and reset their dependencies. Inherit variables from current, if enabled. If the rule has dependencies, create a new client to build them just before current, and change current so that it points to it.

Definition at line 2312 of file remake.cpp.

Referenced by handle_clients().

2313 {
2314  int job_id = job_counter++;
2315  DEBUG_open << "Starting job " << job_id << " for " << target << "... ";
2316  job_t &job = jobs[job_id];
2317  find_rule(job, target);
2318  if (job.rule.targets.empty())
2319  {
2320  status[target].status = Failed;
2321  DEBUG_close << "failed\n";
2322  std::cerr << "No rule for building " << target << std::endl;
2323  return Failed;
2324  }
2325  bool has_deps = !job.rule.deps.empty() || !job.rule.wdeps.empty();
2326  status_e st = Running;
2327  if (has_deps && status[target].status == Recheck)
2328  st = RunningRecheck;
2329  for (string_list::const_iterator i = job.rule.targets.begin(),
2330  i_end = job.rule.targets.end(); i != i_end; ++i)
2331  {
2332  status[*i].status = st;
2333  }
2334  if (propagate_vars) job.vars = current->vars;
2335  for (assign_map::const_iterator i = job.rule.assigns.begin(),
2336  i_end = job.rule.assigns.end(); i != i_end; ++i)
2337  {
2338  std::pair<variable_map::iterator, bool> k =
2339  job.vars.insert(std::make_pair(i->first, string_list()));
2340  string_list &v = k.first->second;
2341  if (i->second.append)
2342  {
2343  if (k.second)
2344  {
2345  variable_map::const_iterator j = variables.find(i->first);
2346  if (j != variables.end()) v = j->second;
2347  }
2348  }
2349  else if (!k.second) v.clear();
2350  v.insert(v.end(), i->second.value.begin(), i->second.value.end());
2351  }
2352  if (has_deps)
2353  {
2354  current = clients.insert(current, client_t());
2355  current->job_id = job_id;
2356  current->pending = job.rule.deps;
2357  current->pending.insert(current->pending.end(),
2358  job.rule.wdeps.begin(), job.rule.wdeps.end());
2359  if (propagate_vars) current->vars = job.vars;
2360  current->delayed = true;
2361  return RunningRecheck;
2362  }
2363  return run_script(job_id, job);
2364 }
static status_e run_script(int job_id, job_t const &job)
Definition: remake.cpp:2191
status_e
Definition: remake.cpp:509
static void find_rule(job_t &job, std::string const &target)
Definition: remake.cpp:1865
#define DEBUG_open
Definition: remake.cpp:801
static job_map jobs
Definition: remake.cpp:629
static bool propagate_vars
Definition: remake.cpp:736
static variable_map variables
Definition: remake.cpp:604
std::list< std::string > string_list
Definition: remake.cpp:456
Target is being rebuilt.
Definition: remake.cpp:514
string_list wdeps
Like deps, except that they are not registered as dependencies.
Definition: remake.cpp:549
static client_list clients
Definition: remake.cpp:640
string_list deps
Dependencies used for an implicit call to remake at the start of the script.
Definition: remake.cpp:548
assign_map assigns
Assignment of variables.
Definition: remake.cpp:550
static status_map status
Definition: remake.cpp:614
variable_map vars
Values of local variables.
Definition: remake.cpp:566
Build failed for target.
Definition: remake.cpp:517
string_list targets
Files produced by this rule.
Definition: remake.cpp:547
Target has an obsolete dependency.
Definition: remake.cpp:513
#define DEBUG_close
Definition: remake.cpp:802
rule_t rule
Original rule.
Definition: remake.cpp:564
static int job_counter
Definition: remake.cpp:679
Static prerequisites are being rebuilt.
Definition: remake.cpp:515