summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShawn Landden <slandden@gmail.com>2018-02-26 15:24:42 -0800
committerGitHub <noreply@github.com>2018-02-26 15:24:42 -0800
commit3f510877feb4f66a3674bc1b8d2b1fbb96232d9b (patch)
tree84fdc0df1b8ab8bdcc35bfb512b5d6b305eea06f
parent5110e667123be49ec874fbc7d47f02331c3b4922 (diff)
parentf2c978714cab690c2a41580095c72f058c2e53da (diff)
downloaddistcc-git-3f510877feb4f66a3674bc1b8d2b1fbb96232d9b.tar.gz
Merge pull request #244 from shawnl/cross
cross-compilation support
-rw-r--r--man/distcc.15
-rw-r--r--src/compile.c172
-rw-r--r--src/util.c28
-rw-r--r--src/util.h1
4 files changed, 206 insertions, 0 deletions
diff --git a/man/distcc.1 b/man/distcc.1
index 18e5737..fb93615 100644
--- a/man/distcc.1
+++ b/man/distcc.1
@@ -767,6 +767,11 @@ variable is set to 0 then fallbacks are disabled and those
compilations will simply fail. Note that this does not affect jobs
which must always be local such as linking.
.TP
+.B "DISTCC_NO_CROSS_REWRITE"
+By default distcc will rewrite calls gcc to use fully qualified names
+(like x86_64-linux-gnu-gcc), and clang to use the -target option. Setting this
+turns that off.
+.TP
.B "DISTCC_BACKOFF_PERIOD"
Specifies how long (in seconds) distcc will avoid trying to use a
particular compilation server after that server yields a compile
diff --git a/src/compile.c b/src/compile.c
index dc854f4..dc458fb 100644
--- a/src/compile.c
+++ b/src/compile.c
@@ -25,6 +25,7 @@
#include <config.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -445,6 +446,172 @@ static int dcc_please_send_email_after_investigation(
return dcc_note_discrepancy(discrepancy_filename);
}
+/* Re-write "cc" to directly call gcc or clang
+ */
+static void dcc_rewrite_generic_compiler(char **argv)
+{
+#ifdef __APPLE__ /* FIXME */
+
+ assert(argv);
+
+ return;
+#else
+ char linkbuf[MAXPATHLEN + 1], *link = NULL, *t;
+ int ret, dir;
+ ssize_t ssz;
+ struct stat st;
+ bool cpp = false;
+
+ assert(argv);
+
+ if (strcmp(argv[0], "cc") == 0)
+ ;
+ else if (strcmp(argv[0], "c++") == 0)
+ cpp = true;
+ else
+ return;
+
+ ret = dcc_which(cpp ? "c++" : "cc", &link);
+ if (ret < 0)
+ return;
+ t = strrchr(link, '/');
+ if (!t)
+ return;
+ *t = '\0';
+ dir = open(link, O_RDONLY);
+ if (dir < 0)
+ return;
+ *t = '/';
+ ret = fstatat(dir, t + 1, &st, AT_SYMLINK_NOFOLLOW);
+ if (ret < 0)
+ return;
+ if ((st.st_mode & S_IFMT) != S_IFLNK)
+ /* TODO use cc -v */
+ return;
+ ssz = readlinkat(dir, t + 1, linkbuf, sizeof(linkbuf) - 1);
+ if (ssz < 0)
+ return;
+ linkbuf[ssz] = '\0';
+ fstatat(dir, linkbuf, &st, AT_SYMLINK_NOFOLLOW);
+ if ((st.st_mode & S_IFMT) == S_IFLNK) {
+ /* this is a Debian thing. Fedora just has /usr/bin/cc -> gcc */
+ if (strcmp(linkbuf, cpp ? "/etc/alternatives/c++" : "/etc/alternatives/cc") == 0) {
+ ssz = readlinkat(dir, linkbuf, linkbuf, sizeof(linkbuf) - 1);
+ linkbuf[ssz] = '\0';
+ }
+ }
+ ret = faccessat(dir, linkbuf, X_OK, 0);
+ if (ret < 0)
+ return;
+
+ if ( cpp && strcmp(strrchr(linkbuf, '/') ? strrchr(linkbuf, '/') + 1 : linkbuf, "clang++") == 0) {
+ free(argv[0]);
+ argv[0] = strdup("clang++");
+ rs_trace("Rewriting '%s' to '%s'", "c++", "clang++");
+ } else if ( cpp && strcmp(strrchr(linkbuf, '/') ? strrchr(linkbuf, '/') + 1 : linkbuf, "g++") == 0) {
+ free(argv[0]);
+ argv[0] = strdup("g++");
+ rs_trace("Rewriting '%s' to '%s'", "c++", "g++");
+ } else if (!cpp && strcmp(strrchr(linkbuf, '/') ? strrchr(linkbuf, '/') + 1 : linkbuf, "clang") == 0) {
+ free(argv[0]);
+ argv[0] = strdup("clang");
+ rs_trace("Rewriting '%s' to '%s'", "cc", "clang");
+ } else if (!cpp && strcmp(strrchr(linkbuf, '/') ? strrchr(linkbuf, '/') + 1 : linkbuf, "gcc") == 0) {
+ free(argv[0]);
+ argv[0] = strdup("gcc");
+ rs_trace("Rewriting '%s' to '%s'", "cc", "gcc");
+ } else
+ return;
+#endif
+}
+
+
+/* Clang is a native cross-compiler, but needs to be told to what target it is
+ * building.
+ * TODO: actually probe clang with clang --version, instead of trusting
+ * autoheader.
+ */
+static void dcc_add_clang_target(char **argv)
+{
+ /* defined by autoheader */
+ const char *target = GNU_HOST;
+
+ if (strcmp(argv[0], "clang") == 0 || strncmp(argv[0], "clang-", strlen("clang-")) == 0 ||
+ strcmp(argv[0], "clang++") == 0 || strncmp(argv[0], "clang++-", strlen("clang++-")) == 0)
+ ;
+ else
+ return;
+
+ if (dcc_argv_search(argv, "-target"))
+ return;
+
+ rs_log_info("Adding '-target %s' to support clang cross-compilation.",
+ target);
+ dcc_argv_append(argv, strdup("-target"));
+ dcc_argv_append(argv, strdup(target));
+}
+
+/*
+ * Cross compilation for gcc
+*/
+static int dcc_gcc_rewrite_fqn(char **argv)
+{
+ /* defined by autoheader */
+ const char *target_with_vendor = GNU_HOST;
+ char *newcmd, *t, *path;
+ int pathlen = 0;
+
+ if (strcmp(argv[0], "gcc") == 0 || strncmp(argv[0], "gcc-", strlen("gcc-")) == 0 ||
+ strcmp(argv[0], "g++") == 0 || strncmp(argv[0], "g++-", strlen("g++-")) == 0)
+ ;
+ else
+ return -ENOENT;
+
+
+ newcmd = malloc(strlen(target_with_vendor) + 1 + strlen(argv[0] + 1));
+ if (!newcmd)
+ return -ENOMEM;
+
+ if ((t = strstr(target_with_vendor, "-pc-"))) {
+ strncpy(newcmd, target_with_vendor, t - target_with_vendor);
+ strcat(newcmd, t + strlen("-pc"));
+ } else
+ strcpy(newcmd, target_with_vendor);
+
+
+ strcat(newcmd, "-");
+ strcat(newcmd, argv[0]);
+
+ /* TODO, is this the right PATH? */
+ path = getenv("PATH");
+ do {
+ char binname[strlen(path) + 1 + strlen(newcmd) + 1];
+ int r;
+
+ /* emulate strchrnul() */
+ t = strchr(path, ':');
+ if (!t)
+ t = path + strlen(path);
+ pathlen = t - path;
+ if (*path == '\0')
+ return -ENOENT;
+ strncpy(binname, path, pathlen);
+ binname[pathlen] = '\0';
+ strcat(binname, "/");
+ strcat(binname, newcmd);
+ r = access(binname, X_OK);
+ if (r < 0)
+ continue;
+ /* good!, now rewrite */
+ rs_log_info("Re-writing call to '%s' to '%s' to support cross-compilation.",
+ argv[0], newcmd);
+ free(argv[0]);
+ argv[0] = newcmd;
+ return 0;
+ } while ((path += pathlen + 1));
+ return -ENOENT;
+}
+
/**
* Execute the commands in argv remotely or locally as appropriate.
*
@@ -521,6 +688,11 @@ dcc_build_somewhere(char *argv[],
ret = dcc_scan_args(argv, &input_fname, &output_fname, &new_argv);
dcc_free_argv(argv);
argv = new_argv;
+ if (!getenv("DISTCC_NO_REWRITE_CROSS")) {
+ dcc_rewrite_generic_compiler(new_argv); /* does not work on Mac FIXME */
+ dcc_add_clang_target(new_argv);
+ dcc_gcc_rewrite_fqn(new_argv);
+ }
if (ret != 0) {
/* we need to scan the arguments even if we already know it's
* local, so that we can pick up distcc client options. */
diff --git a/src/util.c b/src/util.c
index 4b26fa7..c4939cd 100644
--- a/src/util.c
+++ b/src/util.c
@@ -574,6 +574,34 @@ int dcc_remove_if_exists(const char *fname)
return 0;
}
+int dcc_which(const char *command, char **out)
+{
+ char *loc = NULL, *path, *t;
+ int ret;
+
+ path = getenv("PATH");
+ if (!path)
+ return -ENOENT;
+ do {
+ /* emulate strchrnul() */
+ t = strchr(path, ':');
+ if (!t)
+ t = path + strlen(path);
+ loc = realloc(loc, t - path + 1 + strlen(command) + 1);
+ if (!loc)
+ return -ENOMEM;
+ strncpy(loc, path, t - path);
+ loc[t - path] = '\0';
+ strcat(loc, "/");
+ strcat(loc, command);
+ ret = access(loc, X_OK);
+ if (ret < 0)
+ continue;
+ *out = loc;
+ return 0;
+ } while ((path = strchr(path, ':') + 1));
+ return -ENOENT;
+}
/* Returns the number of processes in state D, the max non-cc/c++ RSS in kb and
* the max RSS program's name */
diff --git a/src/util.h b/src/util.h
index 463a383..92017c5 100644
--- a/src/util.h
+++ b/src/util.h
@@ -49,6 +49,7 @@ int dcc_get_dns_domain(const char **domain_name);
void dcc_get_proc_stats(int *num_D, int *max_RSS, char **max_RSS_name);
void dcc_get_disk_io_stats(int *n_reads, int *n_writes);
+int dcc_which(const char *cmd, char **out);
int dcc_dup_part(const char **psrc, char **pdst, const char *sep);