Skip to content

Instantly share code, notes, and snippets.

@Tasssadar
Last active August 29, 2015 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Tasssadar/f9f38d27253e88aa740b to your computer and use it in GitHub Desktop.
Save Tasssadar/f9f38d27253e88aa740b to your computer and use it in GitHub Desktop.
Force make to stop just after parsing all makefiles and wait for SIGUSR1, then fork and continue in build. Eliminates parsing of makefiles on each build, thus saves a fuckton of time if you're building something in Android tree over and over.
diff --git a/main.c b/main.c
index f60e6be..bef5652 100644
--- a/main.c
+++ b/main.c
@@ -302,6 +302,18 @@ struct variable shell_var;
char cmd_prefix = '\t';
+/* For the -F/--fork-hack option */
+#define FORK_HACK_PID_FILE ".make-fork-hack.pid"
+static int is_forked = 0;
+static int use_fork_hack = 0;
+static void fork_hack_signal_handler(int sig)
+{
+ if(use_fork_hack && !is_forked)
+ remove(FORK_HACK_PID_FILE);
+ exit(130);
+}
+
+
/* The usage output. We write it this way to make life easier for the
translators, especially those trying to translate to right-to-left
@@ -330,6 +342,12 @@ static const char *const usage[] =
-f FILE, --file=FILE, --makefile=FILE\n\
Read FILE as a makefile.\n"),
N_("\
+ -F, --fork-hack Parses all makefiles, then waits for SIGUSR1, forks\n\
+ when it arrives and continues in the forked child.\n\
+ This eliminates parsing the same makefiles on every\n\
+ 'make' over and over again.\n\
+ WARNING: THIS IS A HACK AND MIGHT BREAK EVERYTHING!\n"),
+ N_("\
-h, --help Print this message and exit.\n"),
N_("\
-i, --ignore-errors Ignore errors from recipes.\n"),
@@ -398,6 +416,7 @@ static const struct command_switch switches[] =
{ 'D', flag, &suspend_flag, 1, 1, 0, 0, 0, "suspend-for-debug" },
#endif
{ 'e', flag, &env_overrides, 1, 1, 0, 0, 0, "environment-overrides", },
+ { 'F', flag, &use_fork_hack, 1, 0, 0, 0, 0, "fork-hack", },
{ 'h', flag, &print_usage_flag, 0, 0, 0, 0, 0, "help" },
{ 'i', flag, &ignore_errors_flag, 1, 1, 0, 0, 0, "ignore-errors" },
{ 'k', flag, &keep_going_flag, 1, 1, 0, 0, &default_keep_going_flag,
@@ -1038,6 +1057,73 @@ msdos_return_to_initial_directory (void)
}
#endif /* __MSDOS__ */
+static void
+init_top_level_jobserver(void)
+{
+ char *cp;
+
+#ifdef WINDOWS32
+ /* sub_proc.c cannot wait for more than MAXIMUM_WAIT_OBJECTS objects
+ * and one of them is the job-server semaphore object. Limit the
+ * number of available job slots to (MAXIMUM_WAIT_OBJECTS - 1). */
+
+ if (job_slots >= MAXIMUM_WAIT_OBJECTS)
+ {
+ job_slots = MAXIMUM_WAIT_OBJECTS - 1;
+ DB (DB_JOBS, (_("Jobserver slots limited to %d\n"), job_slots));
+ }
+
+ if (! create_jobserver_semaphore (job_slots - 1))
+ {
+ DWORD err = GetLastError ();
+ fatal (NILF, _("creating jobserver semaphore: (Error %ld: %s)"),
+ err, map_windows32_error_to_string (err));
+ }
+#else
+ char c = '+';
+
+ if (pipe (job_fds) < 0 || (job_rfd = dup (job_fds[0])) < 0)
+ pfatal_with_name (_("creating jobs pipe"));
+#endif
+
+ /* Every make assumes that it always has one job it can run. For the
+ submakes it's the token they were given by their parent. For the
+ top make, we just subtract one from the number the user wants. We
+ want job_slots to be 0 to indicate we're using the jobserver. */
+
+ master_job_slots = job_slots;
+
+#ifdef WINDOWS32
+ /* We're using the jobserver so set job_slots to 0. */
+ job_slots = 0;
+#else
+ while (--job_slots)
+ {
+ int r;
+
+ EINTRLOOP (r, write (job_fds[1], &c, 1));
+ if (r != 1)
+ pfatal_with_name (_("init jobserver pipe"));
+ }
+#endif
+
+ /* Fill in the jobserver_fds struct for our children. */
+
+#ifdef WINDOWS32
+ cp = xmalloc (MAX_PATH + 1);
+ strcpy (cp, get_jobserver_semaphore_name ());
+#else
+ cp = xmalloc ((CSTRLEN ("1024") * 2) + 2);
+ sprintf (cp, "%d,%d", job_fds[0], job_fds[1]);
+#endif
+
+ jobserver_fds = xmalloc (sizeof (struct stringlist));
+ jobserver_fds->list = xmalloc (sizeof (char *));
+ jobserver_fds->list[0] = cp;
+ jobserver_fds->idx = 1;
+ jobserver_fds->max = 1;
+}
+
#ifdef _AMIGA
int
main (int argc, char **argv)
@@ -1460,6 +1546,27 @@ main (int argc, char **argv, char **envp)
die (0);
}
+ /* Trigger build on fork-hack daemon */
+ if(use_fork_hack)
+ {
+ FILE *f = fopen(FORK_HACK_PID_FILE, "r");
+ if(f)
+ {
+ char buf[64] = { 0 };
+ char *res UNUSED = fgets(buf, sizeof(buf), f);
+ fclose(f);
+
+ pid_t daemon_pid = atoi(buf);
+ if(daemon_pid > 0 && kill(daemon_pid, SIGUSR1) == 0)
+ {
+ printf("Sent SIGUSR to %d, exiting.\n", daemon_pid);
+ die(0);
+ }
+ }
+
+ signal(SIGINT, fork_hack_signal_handler);
+ }
+
if (ISDB (DB_BASIC))
print_version ();
@@ -1991,71 +2098,8 @@ main (int argc, char **argv, char **envp)
/* If we have >1 slot but no jobserver-fds, then we're a top-level make.
Set up the pipe and install the fds option for our children. */
- if (job_slots > 1)
- {
- char *cp;
-
-#ifdef WINDOWS32
- /* sub_proc.c cannot wait for more than MAXIMUM_WAIT_OBJECTS objects
- * and one of them is the job-server semaphore object. Limit the
- * number of available job slots to (MAXIMUM_WAIT_OBJECTS - 1). */
-
- if (job_slots >= MAXIMUM_WAIT_OBJECTS)
- {
- job_slots = MAXIMUM_WAIT_OBJECTS - 1;
- DB (DB_JOBS, (_("Jobserver slots limited to %d\n"), job_slots));
- }
-
- if (! create_jobserver_semaphore (job_slots - 1))
- {
- DWORD err = GetLastError ();
- fatal (NILF, _("creating jobserver semaphore: (Error %ld: %s)"),
- err, map_windows32_error_to_string (err));
- }
-#else
- char c = '+';
-
- if (pipe (job_fds) < 0 || (job_rfd = dup (job_fds[0])) < 0)
- pfatal_with_name (_("creating jobs pipe"));
-#endif
-
- /* Every make assumes that it always has one job it can run. For the
- submakes it's the token they were given by their parent. For the
- top make, we just subtract one from the number the user wants. We
- want job_slots to be 0 to indicate we're using the jobserver. */
-
- master_job_slots = job_slots;
-
-#ifdef WINDOWS32
- /* We're using the jobserver so set job_slots to 0. */
- job_slots = 0;
-#else
- while (--job_slots)
- {
- int r;
-
- EINTRLOOP (r, write (job_fds[1], &c, 1));
- if (r != 1)
- pfatal_with_name (_("init jobserver pipe"));
- }
-#endif
-
- /* Fill in the jobserver_fds struct for our children. */
-
-#ifdef WINDOWS32
- cp = xmalloc (MAX_PATH + 1);
- strcpy (cp, get_jobserver_semaphore_name ());
-#else
- cp = xmalloc ((CSTRLEN ("1024") * 2) + 2);
- sprintf (cp, "%d,%d", job_fds[0], job_fds[1]);
-#endif
-
- jobserver_fds = xmalloc (sizeof (struct stringlist));
- jobserver_fds->list = xmalloc (sizeof (char *));
- jobserver_fds->list[0] = cp;
- jobserver_fds->idx = 1;
- jobserver_fds->max = 1;
- }
+ if (job_slots > 1 && (!use_fork_hack || is_forked))
+ init_top_level_jobserver();
#endif
#ifndef MAKE_SYMLINKS
@@ -2132,6 +2176,47 @@ main (int argc, char **argv, char **envp)
OUTPUT_UNSET ();
output_close (&make_sync);
+ /* FORK HACK: wait for SIGUSR1 here, then fork and continue
+ with build in the child.*/
+ if(use_fork_hack)
+ {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ FILE *f = fopen(FORK_HACK_PID_FILE, "w");
+ if(f)
+ {
+ fprintf(f, "%d\n", getpid());
+ fclose(f);
+ }
+
+ while(1)
+ {
+ int recv;
+ printf("\n\aWaiting for SIGUSR1, command to spawn child and continue: "
+ "kill -USR1 %d or %s -F in the same folder\n\n", getpid(), argv[0]);
+ if(sigwait(&mask, &recv) == 0)
+ {
+ pid_t p = fork();
+ if(p == 0) // child
+ {
+ if(job_slots > 1)
+ init_top_level_jobserver();
+ is_forked = 1;
+ break;
+ }
+ else
+ {
+ printf("Started child with pid %d, waiting for it to finish\n", p);
+ waitpid(p, &recv, 0);
+ }
+ }
+ }
+ }
+
+
if (read_files != 0)
{
/* Update any makefiles if necessary. */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment