diff options
author | Shawn Landden <slandden@gmail.com> | 2018-02-26 15:24:42 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-02-26 15:24:42 -0800 |
commit | 3f510877feb4f66a3674bc1b8d2b1fbb96232d9b (patch) | |
tree | 84fdc0df1b8ab8bdcc35bfb512b5d6b305eea06f | |
parent | 5110e667123be49ec874fbc7d47f02331c3b4922 (diff) | |
parent | f2c978714cab690c2a41580095c72f058c2e53da (diff) | |
download | distcc-git-3f510877feb4f66a3674bc1b8d2b1fbb96232d9b.tar.gz |
Merge pull request #244 from shawnl/cross
cross-compilation support
-rw-r--r-- | man/distcc.1 | 5 | ||||
-rw-r--r-- | src/compile.c | 172 | ||||
-rw-r--r-- | src/util.c | 28 | ||||
-rw-r--r-- | src/util.h | 1 |
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. */ @@ -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 */ @@ -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); |