diff options
author | Andrew Savchenko <bircoph@gmail.com> | 2013-06-17 05:25:24 +0400 |
---|---|---|
committer | Andrew Savchenko <bircoph@gmail.com> | 2013-06-17 05:25:24 +0400 |
commit | 4d3e97dbf8628d0ffadf1405787a3e22de67abb7 (patch) | |
tree | 706dff01d5286a17c3776747e3f18d72231c9b0c /sys-devel | |
parent | distcc: add support for native arguments (diff) | |
download | bircoph-4d3e97dbf8628d0ffadf1405787a3e22de67abb7.tar.gz bircoph-4d3e97dbf8628d0ffadf1405787a3e22de67abb7.tar.bz2 bircoph-4d3e97dbf8628d0ffadf1405787a3e22de67abb7.zip |
distcc: add files to git from previous update
Diffstat (limited to 'sys-devel')
-rw-r--r-- | sys-devel/distcc/distcc-3.2_rc1-r1.ebuild | 276 | ||||
-rw-r--r-- | sys-devel/distcc/files/distcc-3.2_rc1-native.patch | 924 |
2 files changed, 1200 insertions, 0 deletions
diff --git a/sys-devel/distcc/distcc-3.2_rc1-r1.ebuild b/sys-devel/distcc/distcc-3.2_rc1-r1.ebuild new file mode 100644 index 0000000..87549bf --- /dev/null +++ b/sys-devel/distcc/distcc-3.2_rc1-r1.ebuild @@ -0,0 +1,276 @@ +# Copyright 1999-2013 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-x86/sys-devel/distcc/distcc-3.2_rc1.ebuild,v 1.5 2013/02/12 09:09:20 armin76 Exp $ + +EAPI="4" +PYTHON_DEPEND="2:2.5" + +inherit autotools eutils fdo-mime flag-o-matic multilib python toolchain-funcs user + +MY_P="${P/_}" +DESCRIPTION="a program to distribute compilation of C code across several machines on a network" +HOMEPAGE="http://distcc.org/" +SRC_URI="http://distcc.googlecode.com/files/${MY_P}.tar.bz2" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd" +IUSE="avahi cc32_64 crossdev gnome gssapi gtk hardened ipv6 +secure selinux xinetd" + +RESTRICT="test" + +RDEPEND="dev-libs/popt + avahi? ( >=net-dns/avahi-0.6[dbus] ) + cc32_64? ( + amd64? ( sys-devel/gcc[multilib] ) + x86? ( cross-x86_64-pc-linux-gnu/gcc ) + ) + gnome? ( + >=gnome-base/libgnome-2 + >=gnome-base/libgnomeui-2 + x11-libs/gtk+:2 + x11-libs/pango + ) + gssapi? ( net-libs/libgssglue ) + gtk? ( x11-libs/gtk+:2 )" +DEPEND="${RDEPEND} + virtual/pkgconfig" +RDEPEND="${RDEPEND} + !net-misc/pump + >=sys-devel/gcc-config-1.4.1 + selinux? ( sec-policy/selinux-distcc ) + xinetd? ( sys-apps/xinetd )" + +# crosscompilation requirements +REQUIRED_USE=" + cc32_64? ( + !crossdev + ^^ ( amd64 x86 ) + )" + +S="${WORKDIR}/${MY_P}" + +DCCC_PATH="/usr/$(get_libdir)/distcc/bin" + +pkg_setup() { + enewuser distcc 240 -1 -1 daemon + python_set_active_version 2 + python_pkg_setup + + if use cc32_64; then + use x86 && arch="x86_64" + use amd64 && arch="i686" + fi +} + +src_prepare() { + epatch "${FILESDIR}/${PN}-3.0-xinetd.patch" + # bug #253786 + epatch "${FILESDIR}/${PN}-3.0-fix-fortify.patch" + # bug #255188 + epatch "${FILESDIR}/${P}-freedesktop.patch" + # bug #258364 + epatch "${FILESDIR}/${P}-python.patch" + # for net-libs/libgssglue + epatch "${FILESDIR}/${P}-gssapi.patch" + # for cross-compiling + epatch "${FILESDIR}/${P}-crosscompile.patch" + # to support native arguments + epatch "${FILESDIR}/${P}-native.patch" + + # Bugs #120001, #167844 and probably more. See patch for description. + use hardened && epatch "${FILESDIR}/distcc-hardened.patch" + + python_convert_shebangs -r $(python_get_version) . + sed -i \ + -e "/PATH/s:\$distcc_location:${EPREFIX}${DCCC_PATH}:" \ + -e "s:@PYTHON@:${EPREFIX}$(PYTHON -a):" \ + pump.in || die "sed failed" + + sed \ + -e "s:@EPREFIX@:${EPREFIX:-/}:" \ + -e "s:@libdir@:/usr/$(get_libdir):" \ + "${FILESDIR}/distcc-config-3.2_rc1-v2" > "${T}/distcc-config" || die "sed failed" + + # prepare conf.d before we fork it + cp "${FILESDIR}/distccd.confd" "${T}/distccd.confd-base" || die "cp failed" + if use avahi; then + cat >> "${T}/distccd.confd" <<-EOF + + # Enable zeroconf support in distccd + DISTCCD_OPTS="\${DISTCCD_OPTS} --zeroconf" + EOF + fi + + # conf.d and init.d arch deps + sed -e "s%@DCCBIN@%distccd%" \ + -e "s%@DCCPORT@%3632%" \ + "${T}/distccd.confd-base" > "${T}/distccd.confd" || die "sed failed" + + sed -e "s%@DCCPATH@%%" \ + "${FILESDIR}/distccd.initd" > "${T}/distccd.initd" || die "sed failed" + + # conf.d and init.d common deps + if use cc32_64; then + sed -e "s%@DCCBIN@%distccd-${arch}%" \ + -e "s%@DCCPORT@%3633%" \ + "${T}/distccd.confd-base" > "${T}/distccd-${arch}.confd" || die "sed failed" + + sed -e "s%@DCCPATH@%${EPREFIX}${DCCC_PATH}-${arch}:%" \ + "${FILESDIR}/distccd.initd" > "${T}/distccd-${arch}.initd" || die "sed failed" + fi + + eaclocal -Im4 --output=aclocal.m4 + eautoconf + + if use cc32_64; then + mkdir "${S}/bin-${arch}" + cp "${FILESDIR}"/cross-${arch}/* "${S}/bin-${arch}" || die "helper cp failed" + fi +} + +src_configure() { + local myconf="--disable-Werror" + # More legacy stuff? + [ "$(gcc-major-version)" = "2" ] && filter-lfs-flags + + # --disable-rfc2553 b0rked, bug #254176 + use ipv6 && myconf="${myconf} --enable-rfc2553" + + econf \ + $(use_with avahi) \ + $(use_with gtk) \ + $(use_with gnome) \ + $(use_with gssapi auth) \ + --with-docdir="${EPREFIX}/usr/share/doc/${PF}" \ + ${myconf} || die "econf failed" +} + +src_compile() { + default + use cc32_64 && emake -C "${S}/bin-${arch}" +} + +src_install() { + emake DESTDIR="${D}" install + + if use cc32_64 ; then + exeinto "${DCCC_PATH}-${arch}" + doexe "${S}/bin-${arch}/"{c++,g++,gcc} + fi + + newinitd "${T}/distccd.initd" distccd + newconfd "${T}/distccd.confd" distccd + if use cc32_64; then + newinitd "${T}/distccd-${arch}.initd" "distccd-${arch}" + newconfd "${T}/distccd-${arch}.confd" "distccd-${arch}" + fi + + cat > "${T}/02distcc" <<-EOF + # This file is managed by distcc-config; use it to change these settings. + # DISTCC_LOG and DISTCC_DIR should not be set. + DISTCC_VERBOSE="${DISTCC_VERBOSE:-0}" + DISTCC_FALLBACK="${DISTCC_FALLBACK:-1}" + DISTCC_SAVE_TEMPS="${DISTCC_SAVE_TEMPS:-0}" + DISTCC_TCP_CORK="${DISTCC_TCP_CORK:-1}" + DISTCC_SSH="${DISTCC_SSH}" + UNCACHED_ERR_FD="${UNCACHED_ERR_FD}" + DISTCC_ENABLE_DISCREPANCY_EMAIL="${DISTCC_ENABLE_DISCREPANCY_EMAIL}" + DCC_EMAILLOG_WHOM_TO_BLAME="${DCC_EMAILLOG_WHOM_TO_BLAME}" + EOF + if use secure; then + cat >> "${T}/02distcc" <<-EOF + # Enable list of allowed commands + DISTCC_CMDLIST_NUMWORDS="1" + DISTCC_CMDLIST="${EPREFIX}/etc/distcc/commands.allow" + EOF + fi + doenvd "${T}/02distcc" + + keepdir "${DCCC_PATH}" + + dobin "${T}/distcc-config" + + # create the distccd pid directory + keepdir /var/run/distccd + fowners distcc:daemon /var/run/distccd + + if use gnome || use gtk; then + einfo "Renaming /usr/bin/distccmon-gnome to /usr/bin/distccmon-gui" + einfo "This is to have a little sensability in naming schemes between distccmon programs" + mv "${ED}/usr/bin/distccmon-gnome" "${ED}/usr/bin/distccmon-gui" || die + dosym distccmon-gui /usr/bin/distccmon-gnome + fi + + if use xinetd; then + insinto /etc/xinetd.d || die + newins "doc/example/xinetd" distcc + fi + + rm -r "${ED}/etc/default" || die + rm "${ED}/etc/distcc/clients.allow" || die + rm "${ED}/etc/distcc/commands.allow.sh" || die +} + +pkg_postinst() { + pkg_config + + python_mod_optimize include_server + use gnome && fdo-mime_desktop_database_update + + elog + elog "Tips on using distcc with Gentoo can be found at" + elog "http://www.gentoo.org/doc/en/distcc.xml" + elog + elog "How to use pump mode with Gentoo:" + elog "# distcc-config --set-hosts \"foo,cpp,lzo bar,cpp,lzo baz,cpp,lzo\"" + elog "# echo 'FEATURES=\"\${FEATURES} distcc distcc-pump\"' >> /etc/make.conf" + elog "# emerge -u world" + elog + elog "To use the distccmon programs with Gentoo you should use this command:" + elog "# DISTCC_DIR=\"${DISTCC_DIR:-${BUILD_PREFIX}/.distcc}\" distccmon-text 5" + + if use gnome || use gtk; then + elog "Or:" + elog "# DISTCC_DIR=\"${DISTCC_DIR:-${BUILD_PREFIX}/.distcc}\" distccmon-gnome" + fi + + elog + elog "***SECURITY NOTICE***" + elog "If you are upgrading distcc please make sure to run etc-update to" + elog "update your /etc/conf.d/distccd and /etc/init.d/distccd files with" + elog "added security precautions (the --listen and --allow directives)" + elog + + ewarn "On gcc version bump please do not forget to update distcc setup." + ewarn "You may do this by running emerge --config distcc" +} + +pkg_postrm() { + # delete the masquerade directory + if [ ! -f "${EPREFIX}/usr/bin/distcc" ] ; then + einfo "Remove masquerade symbolic links." + rm "${EPREFIX}${DCCC_PATH}/"*{cc,c++,gcc,g++}* + rmdir "${EPREFIX}${DCCC_PATH}" + fi + + python_mod_cleanup include_server + use gnome && fdo-mime_desktop_database_update +} + +pkg_config() { + if [ -x "${EPREFIX}/usr/bin/distcc-config" ] ; then + if use crossdev; then + "${EPREFIX}/usr/bin/distcc-config" --update-masquerade-with-crossdev + elif use cc32_64; then + "${EPREFIX}/usr/bin/distcc-config" --update-masquerade-with-cc32_64 + else + "${EPREFIX}/usr/bin/distcc-config" --update-masquerade + fi + + use secure && "${EPREFIX}/usr/bin/distcc-config" --generate-cmdlist + else + eerror "${EPREFIX}/usr/bin/distcc-config was not found!" + eerror "Your distcc installation is broken." + fi +} diff --git a/sys-devel/distcc/files/distcc-3.2_rc1-native.patch b/sys-devel/distcc/files/distcc-3.2_rc1-native.patch new file mode 100644 index 0000000..638434c --- /dev/null +++ b/sys-devel/distcc/files/distcc-3.2_rc1-native.patch @@ -0,0 +1,924 @@ +diff -Naurd -x '*.swp' -x '*.o' -x '*.d' -x config.h distcc-3.2rc1.orig/src/arg.c distcc-3.2rc1/src/arg.c +--- distcc-3.2rc1.orig/src/arg.c 2008-12-03 00:50:24.000000000 +0300 ++++ distcc-3.2rc1/src/arg.c 2013-06-17 05:23:22.575994582 +0400 +@@ -4,6 +4,7 @@ + * + * Copyright (C) 2002, 2003, 2004 by Martin Pool <mbp@samba.org> + * Copyright 2007 Google Inc. ++ * Copyright 2013 Andrew Savchenko <bircoph@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License +@@ -134,6 +135,8 @@ + int i; + char *a; + int ret; ++ int arg_native = 0; ++ int input_idx, output_idx; // indices of input and output arguments + + /* allow for -o foo.o */ + if ((ret = dcc_copy_argv(argv, ret_newargv, 2)) != 0) +@@ -182,13 +185,11 @@ + rs_trace("%s implies -E (maybe) and must be local", a); + return EXIT_DISTCC_FAILED; + } else if (!strcmp(a, "-march=native")) { +- rs_trace("-march=native generates code for local machine; " +- "must be local"); +- return EXIT_DISTCC_FAILED; ++ arg_native |= DCC_ARG_MARCH; + } else if (!strcmp(a, "-mtune=native")) { +- rs_trace("-mtune=native optimizes for local machine; " +- "must be local"); +- return EXIT_DISTCC_FAILED; ++ arg_native |= DCC_ARG_MTUNE; ++ } else if (!strcmp(a, "-mcpu=native")) { ++ arg_native |= DCC_ARG_MCPU; + } else if (str_startswith("-Wa,", a)) { + /* Look for assembler options that would produce output + * files and must be local. +@@ -236,6 +237,7 @@ + return EXIT_DISTCC_FAILED; + } + *input_file = a; ++ input_idx = i; + } else if (str_endswith(".o", a)) { + GOT_OUTPUT: + rs_trace("found object/output file \"%s\"", a); +@@ -244,6 +246,7 @@ + return EXIT_DISTCC_FAILED; + } + *output_file = a; ++ output_idx = i; + } + } + } +@@ -265,6 +268,14 @@ + if (dcc_source_needs_local(*input_file)) + return EXIT_DISTCC_FAILED; + ++ /* Expand -m$arg=native using local compiler */ ++ if (arg_native && (ret = dcc_expand_native(ret_newargv, arg_native, ++ &input_idx, &output_idx))) ++ return ret; ++ // correct input filename index ++ argv = *ret_newargv; ++ *input_file = argv[input_idx]; ++ + if (!*output_file) { + /* This is a commandline like "gcc -c hello.c". They want + * hello.o, but they don't say so. For example, the Ethereal +@@ -293,6 +304,9 @@ + dcc_argv_append(argv, strdup("-o")); + dcc_argv_append(argv, ofile); + *output_file = ofile; ++ } else { ++ // correct output filename index ++ *output_file = argv[output_idx]; + } + + dcc_note_compiled(*input_file, *output_file); +diff -Naurd -x '*.swp' -x '*.o' -x '*.d' -x config.h distcc-3.2rc1.orig/src/arg_native.c distcc-3.2rc1/src/arg_native.c +--- distcc-3.2rc1.orig/src/arg_native.c 1970-01-01 03:00:00.000000000 +0300 ++++ distcc-3.2rc1/src/arg_native.c 2013-06-17 05:14:27.704941669 +0400 +@@ -0,0 +1,705 @@ ++/* -*- c-file-style: "java"; indent-tabs-mode: nil; tab-width: 4; fill-column: 78 -*- ++ * ++ * distcc -- A simple distributed compiler system ++ * ++ * Copyright 2013 Andrew Savchenko <bircoph@gmail.com> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ */ ++ ++ ++ /* The chief enemy of creativity is "good" sense ++ * -- Picasso */ ++ ++ ++/** ++ * @file ++ * ++ * Functions for -m$arg=native argument expansion. ++ * ++ * Instead of compiling -march=native locally (as well as -mtune=native and ++ * -mcpu=native) we can extract expansion of native parameter from ++ * gcc output in a way similar to: ++ * $ gcc -march=native -E -v - < /dev/null 2>&1 | egrep "\-E -quiet -v" | ++ * sed 's/.*-E -quiet -v - //' ++ * ++ * Results are per-compiler cached. Compiler is identified by mtime ++ * and size. ++ * ++ * -march supersedes -mcpu and -mcpu supersedes -mtune, so all args ++ * should be parsed before actual expansion. ++ **/ ++ ++ ++#include <config.h> ++ ++#include <stdio.h> ++#include <stdlib.h> ++#include <unistd.h> ++#include <string.h> ++#include <fcntl.h> ++#include <errno.h> ++ ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <sys/wait.h> ++ ++#include "distcc.h" ++#include "trace.h" ++#include "exitcode.h" ++#include "snprintf.h" ++ ++/** ++ * Search compiler based on its basename. ++ * ++ * First try to get PATH. If it is undefined or emphy, try to get ++ * system defaults from confstr(). ++ * ++ * @param name Compiler base name. ++ * @param fullname Returned found full name. ++ * ++ * @return dcc standard exit code ++ **/ ++static int ++dcc_search_compiler_in_path(const char *name, char **fullname) ++{ ++ char *envpath; ++ char *token, *saveptr, *str; ++ int envpath_size = 256; ++ int ret; ++ int need4free = 0; // do we need to free envpath? ++ // it can't be touched after getenv, but must ++ // be after confstr ++ ++ /* Look for PATH */ ++ if (!(envpath = getenv("PATH")) || !strlen(envpath)) { ++ // Strange, but legal: we have PATH undefined or empty ++ rs_trace("PATH seems to be not defined"); ++ ++ // try to get from standard configuration, ++ // will result in something like "/bin:/usr/bin" ++ envpath = malloc(envpath_size * sizeof(char)); ++ if (!(envpath = malloc(envpath_size * sizeof(char)))) { ++ rs_log_error("failed to allocate PATH buffer"); ++ return EXIT_OUT_OF_MEMORY; ++ } ++ need4free = 1; ++ ++ ret = confstr(_CS_PATH, envpath, envpath_size); ++ if (ret > envpath_size) { ++ // Strange, but legal: too long default PATH ++ if (!(envpath = realloc(envpath, ret * sizeof(char)))) { ++ rs_log_error("failed to reallocate PATH buffer"); ++ return EXIT_OUT_OF_MEMORY; ++ } ++ envpath_size = ret; ++ // Recall with full path ++ confstr(_CS_PATH, envpath, envpath_size); ++ } ++ if (!(confstr(_CS_PATH, envpath, ret))) { ++ rs_log_error("confstr() says PATH have no value"); ++ return EXIT_COMPILER_MISSING; ++ } ++ } ++ ++ /* Parse PATH string */ ++ ret = EXIT_COMPILER_MISSING; ++ str = strdup(envpath); // copy as will need to modify ++ while ((token = strtok_r(str, ":", &saveptr))) { ++ // construct full name to check ++ if (asprintf(fullname, "%s/%s", token, name) == -1) { ++ rs_log_error("asprintf failed for compiler fullname"); ++ return EXIT_OUT_OF_MEMORY; ++ } ++ ++ if (!access(*fullname, X_OK)) { ++ // found it! ++ rs_trace("compiler found: %s", *fullname); ++ ret = 0; ++ break; ++ } ++ str = NULL; ++ } ++ ++ if (need4free) ++ free(envpath); ++ return ret; ++} ++ ++/** ++ * Search compiler and get its ID: size, mtime in form of a hash ++ * string. This should be reliable enough for compiler change ++ * detection. ++ * ++ * @param name Compiler name. ++ * @param hash Found hash value. Must be freed by caller. ++ * ++ * @return dcc standard exit code ++ **/ ++static inline int ++dcc_get_compiler_id(const char *name, char **hash) ++{ ++ char *fullname; ++ int ret; ++ int need4free = 1; // do we need to free fullname? ++ struct stat statbuf; ++ ++ /* Look for full name first */ ++ if (name[0] == '/') { ++ if (!access(name, X_OK)) { ++ fullname = (char*)name; ++ need4free = 0; ++ } else { ++ rs_log_warning("compiler is not accessible by its full name '%s' : %s", ++ name, strerror(errno)); ++ // If compiler is not found by a full name, it may be ++ // somewhere else, so try PATH as fallback. ++ // Strip dirname before proceed further. ++ if (( ret = dcc_search_compiler_in_path(basename(name), &fullname) )) ++ return ret; ++ } ++ } else { ++ if (( ret = dcc_search_compiler_in_path(name, &fullname) )) ++ return ret; ++ } ++ ++ /* Get ID data */ ++ if (stat(fullname, &statbuf) == -1) { ++ rs_log_error("failed to stat compiler '%s': %s", fullname, strerror(errno)); ++ return EXIT_COMPILER_MISSING; ++ } ++ ++ /* Generate compiler hash: hex(size, mtime)*/ ++ if (asprintf(hash, "%lx%lx", statbuf.st_size, statbuf.st_mtime) == -1) { ++ rs_log_error("can't generate compiler's hash"); ++ return EXIT_OUT_OF_MEMORY; ++ } ++ rs_trace("compiler is %s, hash is %s", fullname, *hash); ++ ++ if (need4free) ++ free(fullname); ++ return 0; ++} ++ ++/** ++ * Generate cache path and file name based on compiler's hash and ++ * native type. ++ * ++ * Cache is stored at $DISTCC_DIR/cache/$hash-$type , ++ * where type is a charfield "act" corresponding to -march, -mcpu ++ * and -mtune options present (with native argument). ++ * ++ * @note ++ * A complicate thing here is that on different architectures ++ * different combinations of these options may provide different ++ * non-additive results. E.g. expanded options from -march, -mcpu ++ * and -march -mcpu (together) may yield three different results - ++ * that's why we need to cache them separately. Though order of ++ * options doesn't matter, thus we have seven cases left :) ++ * ++ * @param ret_hash Compiler hash. Full hash name is returned by this ++ * variable too. ++ * @param type Bitmask of requested native type. ++ * ++ * @return dcc standard exit code ++ **/ ++static inline int ++dcc_get_hash_filename(char **ret_hash, const int type) ++{ ++ int hash_len, ret; ++ char *hash = *ret_hash; ++ char *cachedir; ++ char *hashname; ++ ++ // Grow hash array to adapt the longest possible extension: ++ // "-act" ++ hash_len = strlen(hash); ++ // 4 bytes for extension + 1 for '\0' ++ if (!(hash = realloc(hash, (hash_len + 5) * sizeof(char)))) { ++ rs_log_error("failed to reallocate PATH buffer"); ++ return EXIT_OUT_OF_MEMORY; ++ } ++ ++ /* Add hash name extension based on its type. */ ++ hash[hash_len++] = '-'; ++ if (type & DCC_ARG_MARCH) ++ hash[hash_len++] = 'a'; ++ if (type & DCC_ARG_MCPU) ++ hash[hash_len++] = 'c'; ++ if (type & DCC_ARG_MTUNE) ++ hash[hash_len++] = 't'; ++ hash[hash_len++] = '\0'; ++ ++ /* Get cache directory */ ++ if (dcc_get_cache_dir(&cachedir)) { ++ rs_log_error("can't get cache directory"); ++ *ret_hash = hash; ++ return EXIT_IO_ERROR; ++ } ++ ++ // construct full hash name ++ if (asprintf(&hashname, "%s/%s", cachedir, hash) == -1) { ++ rs_log_error("asprintf failed for hash full name"); ++ *ret_hash = hash; ++ ret = EXIT_OUT_OF_MEMORY; ++ } else { ++ free(hash); ++ *ret_hash = hashname; ++ ret = 0; ++ } ++ ++ free(cachedir); ++ ++ return ret; ++} ++ ++/** ++ * Split string into argv array. ++ * ++ * @param str String to split ++ * @param ret_argv Pointer to argv array ++ * @param ret_argc Pointer to argv size (not including last NULL ++ * element) ++ * ++ * @return dcc standard exit code ++ * Error is also returned if no elements were found, ++ * argv is cleared in this case. ++ **/ ++static int ++dcc_str_to_argv(char *str, char ***ret_argv, int *ret_argc) ++{ ++ const char *delim = " \t\v\n\r"; // possible field delimitors ++ char *saveptr, *token; ++ int argv_size = 64; // memory allocation size for argv, may be larger argc ++ char **argv; ++ int argc; ++ ++ argc = 0; ++ // allocate initial argv buffer ++ if (!(argv = malloc(sizeof(char*) * argv_size))) { ++ rs_log_error("failed to allocate argv buffer"); ++ return EXIT_OUT_OF_MEMORY; ++ } ++ ++ /* Convert string to argv array */ ++ for (argc = 0; (token = strtok_r(str, delim, &saveptr)); argc++, str = NULL) { ++ // grow array as needed ++ if (argc + 2 > argv_size) { ++ argv_size *= 2; ++ if (!(argv = realloc(argv, sizeof(char*) * argv_size))) { ++ rs_log_error("failed to reallocate argv buffer"); ++ return EXIT_OUT_OF_MEMORY; ++ } ++ } ++ // record new element ++ argv[argc] = strdup(token); ++ } ++ argv[argc] = NULL; ++ ++ /* If list is empty, emit an error */ ++ if (!argc) { ++ rs_log_error("argv string is empty!"); ++ dcc_free_argv(argv); ++ argv = NULL; ++ return EXIT_DISTCC_FAILED; ++ } ++ ++ // return values ++ *ret_argv = argv; ++ *ret_argc = argc; ++ ++ return 0; ++} ++ ++/** ++ * Look for a cache file for present compiler and flag type. ++ * ++ * This function errors should be non-fatal for further proceeding. ++ * Cache miss is OK. ++ * ++ * @param hash Compiler hash ++ * @param argv Where to write argv ++ * @param argc Where to write argc, terminating NULL is not ++ * counted. ++ * ++ * @return dcc standard exit code ++ **/ ++static inline int ++dcc_cache_query(const char *hashname, char ***argv, int *argc) ++{ ++ int ret; ++ FILE *cache ; ++ char *buf = NULL; ++ size_t buf_size; ++ ++ if (!(cache = fopen(hashname, "r"))) { ++ // File is not readable (or absent) ++ rs_trace("can't open cache file %s : %s", hashname, strerror(errno)); ++ return EXIT_NO_SUCH_FILE; ++ } ++ ++ // Get cache string ++ if (getline(&buf, &buf_size, cache) == -1) { ++ rs_log_error("cache %s is empty!", hashname); ++ ret = EXIT_DISTCC_FAILED; ++ } else { ++ // split parameter list into argv array ++ rs_log_info("cache found : %s", hashname); ++ ret = dcc_str_to_argv(buf, argv, argc); ++ } ++ ++ // cleanup ++ if (fclose(cache)) ++ rs_log_error("can't close cache file %s : %s", hashname, strerror(errno)); ++ free(buf); ++ ++ return ret; ++} ++ ++/** ++ * Child process intended to run compiler sample in order to ++ * extract -m<arg> expansion. ++ * ++ * @param name Compiler name ++ * @param type -m<arg> type ++ * @param pipefd pipe descriptors ++ * ++ * @return never ++ **/ ++static void NORETURN ++dcc_compiler_query_child(const char *name, const int type, const int pipefd[]) ++{ ++ const char *args[8] = {NULL, "-E", "-v", "-", NULL, NULL, NULL, NULL}; ++ int argsidx; ++ int devnull; ++ ++ // do not read from pipe ++ if (close(pipefd[0])) { ++ rs_log_error("can't close child's pipe: %s", strerror(errno)); ++ exit(EXIT_IO_ERROR); ++ } ++ ++ // redirect stdin and stdout to /dev/null ++ if ((devnull = open("/dev/null", O_RDWR)) == -1) { ++ rs_log_error("can't open /dev/null: %s", strerror(errno)); ++ exit(EXIT_IO_ERROR); ++ } ++ if (dup2(devnull, 0) == -1) { ++ rs_log_error("can't redirect /dev/null to stdin: %s", strerror(errno)); ++ exit(EXIT_IO_ERROR); ++ } ++ if (dup2(devnull, 1) == -1) { ++ rs_log_error("can't redirect stdout to /dev/null: %s", strerror(errno)); ++ exit(EXIT_IO_ERROR); ++ } ++ ++ /* Prepare compiler args */ ++ args[0] = basename(name); ++ argsidx = 4; // first empty slot; ++ if (type & DCC_ARG_MARCH) ++ args[argsidx++] = strdup("-march=native"); ++ if (type & DCC_ARG_MCPU) ++ args[argsidx++] = strdup("-mcpu=native"); ++ if (type & DCC_ARG_MTUNE) ++ args[argsidx++] = strdup("-mtune=native"); ++ ++ if (rs_trace_level >= RS_LOG_INFO) ++ rs_log_info("running compiler query: %s %s", name, dcc_argv_tostr((char**)args)); ++ ++ // write stderr to pipe ++ if (dup2(pipefd[1], 2) == -1) { ++ rs_log_error("can't dup2() stderr: %s", strerror(errno)); ++ exit(EXIT_IO_ERROR); ++ } ++ ++ /* Run compiler */ ++ execvp(name, (char *const *)args); ++ // We've failed. Try based on basename. ++ execvp(args[0], (char *const *)args); ++ exit(EXIT_COMPILER_MISSING); ++} ++ ++/** ++ * Get -m<arg>=native expansion from compiler output alike: ++ * $ gcc -E -v - -march=native < /dev/null 2>&1 ++ * We need to parse stderr. ++ * ++ * @param name Compiler name ++ * @param argv argv storage ++ * @param argc argv size ++ * @param type Type of query to perform (march, mtune, mcpu) ++ * ++ * @return dcc standard exit code ++ **/ ++static inline int ++dcc_compiler_query(const char *name, char ***argv, int *argc, const int type) ++{ ++ int pipefd[2]; // pipe for gcc stderr ++ pid_t cpid; // child pid ++ ++ FILE *out; // stderr output data from gcc ++ char *buf = NULL; // stderr buffer ++ size_t buf_size; ++ char *match; ++ int ret; ++ ++ // prepare pipe to get stderr ++ if (pipe(pipefd) == -1) { ++ rs_log_error("failed to create pipe for compiler: %s", strerror(errno)); ++ return EXIT_IO_ERROR; ++ } ++ ++ // here we go.. ++ if ((cpid = fork()) == -1) { ++ rs_log_error("failed to fork for compiler: %s", strerror(errno)); ++ return EXIT_DISTCC_FAILED; ++ } ++ ++ if (!cpid) { ++ /* Child is here */ ++ dcc_compiler_query_child(name, type, pipefd); ++ } else { ++ /* Parent is here */ ++ ++ // do not write to pipe ++ if (close(pipefd[1])) { ++ rs_log_error("can't close parent's pipe: %s", strerror(errno)); ++ return EXIT_IO_ERROR; ++ } ++ ++ // open pipe from child ++ if (!(out = fdopen(pipefd[0], "r"))) { ++ rs_trace("can't open pipe from child : %s", strerror(errno)); ++ return EXIT_NO_SUCH_FILE; ++ } ++ ++ /* Parse GCC output */ ++ ret = EXIT_DISTCC_FAILED; // in case we'll found nothing ++ while (getline(&buf, &buf_size, out) != -1) { ++ // grep for unique pattern ++ if (!(match = strstr(buf, " -v - "))) ++ continue; ++ // convert to argv ++ ret = dcc_str_to_argv(match + 6, argv, argc); ++ break; ++ } ++ ++ // cleanup ++ if (fclose(out)) ++ rs_log_error("can't close pipe : %s", strerror(errno)); ++ free(buf); ++ ++ if (wait(NULL) == -1) ++ rs_log_error("wait failed : %s", strerror(errno)); ++ } ++ ++ return ret; ++} ++ ++/** ++ * Save expanded "native" arguments to cache ++ * ++ * @param hash Filename to save hash to ++ * @param argv argv array to save ++ * ++ * @return dcc standard exit code ++ **/ ++static inline int ++dcc_cache_write(const char *hash, const char **argv) ++{ ++ FILE *cache; ++ char *astr; ++ int ret = 0; ++ ++ if (!(cache = fopen(hash, "w"))) { ++ rs_trace("unable to create cache file %s : %s", hash, strerror(errno)); ++ return EXIT_IO_ERROR; ++ } ++ ++ // write data to the file ++ astr = dcc_argv_tostr((char**)argv); ++ if (fputs(astr, cache) == EOF) { ++ rs_log_error("can't write to cache file %s : %s", hash, strerror(errno)); ++ ret = EXIT_IO_ERROR; ++ } ++ ++ if (fclose(cache)) { ++ rs_log_error("can't close cache file %s : %s", hash, strerror(errno)); ++ ret = EXIT_IO_ERROR; ++ } ++ free(astr); ++ ++ if (!ret) ++ rs_log_info("cache %s saved", hash); ++ ++ return ret; ++} ++ ++/** ++ * Insert native expansion into argv instead of first occurrence of ++ * native flags, remove all other native encounters. ++ * ++ * A special pain here is that input and output filename indices in ++ * the argv will be changed and must be recalculated. ++ * ++ * @param argv Original list of arguments, must be freed by the caller. ++ * @param exp_argv Expanded list of arguments. ++ * @param exp_argc Size of exp_argv array. ++ * @param input_idx index of input file name in the argv ++ * @param output_idx index of output file name in the argv ++ * ++ * @return dcc standard exit code ++ **/ ++static inline int ++dcc_insert_expansion(char ***argv, const char **exp_argv, const int exp_argc, ++ int *input_idx, int *output_idx) ++{ ++ char **new_argv; // buffer for new argv ++ char **old_argv; // pointer to old argv ++ char *astr; ++ const char *a; ++ int inserted = 0; // flag is set if expansion data is already inserted ++ int i, j, k; ++ ++ // new array length <= old - 1(at least) + exp + 1 ++ if (!(new_argv = malloc((dcc_argv_len(*argv) + exp_argc) * sizeof(char*)))) { ++ rs_log_error("failed to allocate argv for native expansion"); ++ return EXIT_OUT_OF_MEMORY; ++ } ++ ++ // generate new argv ++ old_argv = *argv; ++ for (i = j = 0; (a = old_argv[i]); i++) { ++ if (!strcmp(a, "-march=native") || ++ !strcmp(a, "-mtune=native") || ++ !strcmp(a, "-mcpu=native")) { ++ // insert expansion only once ++ if (!inserted) { ++ for (k = 0; k < exp_argc; k++) { ++ // insert expansion in place of native arg ++ if ((new_argv[j++] = strdup(exp_argv[k])) == NULL) { ++ rs_log_error("failed to duplicate element %d", k); ++ return EXIT_OUT_OF_MEMORY; ++ } ++ } ++ inserted = 1; ++ ++ // recalculate indices ++ if (*input_idx > i) ++ *input_idx += exp_argc - 1; ++ if (*output_idx > i) ++ *output_idx += exp_argc - 1; ++ } else { ++ // skip argument, recalculate indices ++ if (*input_idx > i) ++ (*input_idx)--; ++ if (*output_idx > i) ++ (*output_idx)--; ++ ++ continue; ++ } ++ } else { ++ // copy all other args untouched ++ if ((new_argv[j++] = strdup(a)) == NULL) { ++ rs_log_error("failed to duplicate element %d", j); ++ return EXIT_OUT_OF_MEMORY; ++ } ++ } ++ } ++ // terminate new argv by NULL element ++ new_argv[j] = NULL; ++ ++ dcc_free_argv(*argv); ++ *argv = new_argv; ++ ++ // check for log level to avoid useless string manipulations ++ if (rs_trace_level >= RS_LOG_INFO) { ++ astr = dcc_argv_tostr(*argv); ++ rs_log_info("argv after native expansion: %s", astr); ++ free(astr); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Replace -m<arg>=native flag by its expansion string from ++ * local compiler output or cache from previous queries, if ++ * available. ++ * ++ * Supported args are: -march, -mcpu, -mtune. The former flag ++ * supersedes the latter. ++ * ++ * All -m$arg=native arguments are removed from argv and the first ++ * one is replaced by expansion of the highest priority flag found. ++ * ++ * A special pain here is that input and output filename indices in ++ * the argv will be changed and must be recalculated. ++ * ++ * @param argv argv, must be freed by the caller ++ * @param type denotes type of native argument (bitfield) ++ * @param input_idx index of input file name in the argv ++ * @param output_idx index of output file name in the argv ++ * ++ * @return dcc standard exit code ++ **/ ++int ++dcc_expand_native(char ***argv, const int type, ++ int *input_idx, int *output_idx) ++{ ++ char **exp_argv = NULL; // buffer for expanded native data ++ int exp_argc = 0; // length of expanded data ++ char *compiler_name, *hash; ++ int ret; ++ ++ // Lookup compiler ID (size + mtime) ++ compiler_name = (*argv)[0]; ++ if ((ret = dcc_get_compiler_id(compiler_name, &hash))) { ++ rs_log_error("failed to get compiler ID"); ++ return ret; ++ } ++ ++ // Construct hash filename ++ if ((ret = dcc_get_hash_filename(&hash, type))) { ++ rs_log_error("failed to supply hash filename"); ++ goto cleanup; ++ } ++ ++ /* Query expanded arguments from cache */ ++ if (dcc_cache_query(hash, &exp_argv, &exp_argc)) { ++ // If cache is not available, grep compiler's output ++ if (!(ret = dcc_compiler_query(compiler_name, &exp_argv, &exp_argc, type))) { ++ // If grep is successful, record results to cache ++ if (dcc_cache_write(hash, (const char**)exp_argv)) { ++ rs_log_warning("can't save cache for compiler %s", compiler_name); ++ } ++ } else { ++ rs_log_error("failed to expand native argument using compiler"); ++ goto cleanup; ++ } ++ } ++ ++ // Replace native args by found expansion ++ ret = dcc_insert_expansion(argv, (const char**)exp_argv, exp_argc, ++ input_idx, output_idx); ++ ++cleanup: ++ free(hash); ++ if (exp_argv) ++ dcc_free_argv(exp_argv); ++ ++ return ret; ++} +diff -Naurd -x '*.swp' -x '*.o' -x '*.d' -x config.h distcc-3.2rc1.orig/src/distcc.h distcc-3.2rc1/src/distcc.h +--- distcc-3.2rc1.orig/src/distcc.h 2011-04-05 22:58:58.000000000 +0400 ++++ distcc-3.2rc1/src/distcc.h 2013-06-16 22:11:06.768761656 +0400 +@@ -4,6 +4,7 @@ + * + * Copyright (C) 2002, 2003, 2004 by Martin Pool + * Copyright 2007 Google Inc. ++ * Copyright 2013 Andrew Savchenko <bircoph@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License +@@ -256,6 +257,14 @@ + char **orig_i, char ***ret_newargv); + int dcc_expand_preprocessor_options(char ***argv_ptr); + ++/* arg_native.c */ ++/* Type of "native" param expansion */ ++#define DCC_ARG_MTUNE (1 << 0) ++#define DCC_ARG_MCPU (1 << 1) ++#define DCC_ARG_MARCH (1 << 2) ++int dcc_expand_native(char ***argv, const int type, ++ int *input_idx, int *output_idx); ++ + /* argutil.c */ + unsigned int dcc_argv_len(char **a); + int dcc_argv_search(char **a, const char *); +@@ -272,6 +281,7 @@ + int dcc_mkdir(const char *path); + int dcc_get_subdir(const char *name, char **path_ret) WARN_UNUSED; + ++int dcc_get_cache_dir(char **path_ret) WARN_UNUSED; + int dcc_get_lock_dir(char **path_ret) WARN_UNUSED; + int dcc_get_state_dir(char **path_ret) WARN_UNUSED; + int dcc_get_top_dir(char **path_ret) WARN_UNUSED; +diff -Naurd -x '*.swp' -x '*.o' -x '*.d' -x config.h distcc-3.2rc1.orig/src/tempfile.c distcc-3.2rc1/src/tempfile.c +--- distcc-3.2rc1.orig/src/tempfile.c 2008-12-03 00:50:24.000000000 +0300 ++++ distcc-3.2rc1/src/tempfile.c 2013-06-16 03:37:21.807183537 +0400 +@@ -4,6 +4,7 @@ + * + * Copyright (C) 2002, 2003, 2004 by Martin Pool <mbp@samba.org> + * Copyright 2007 Google Inc. ++ * Copyright 2013 Andrew Savchenko <bircoph@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License +@@ -333,39 +334,26 @@ + return dcc_mkdir(*dir_ret); + } + +-int dcc_get_lock_dir(char **dir_ret) +-{ +- static char *cached; +- int ret; +- +- if (cached) { +- *dir_ret = cached; +- return 0; +- } else { +- ret = dcc_get_subdir("lock", dir_ret); +- if (ret == 0) +- cached = *dir_ret; +- return ret; +- } +-} +- +-int dcc_get_state_dir(char **dir_ret) +-{ +- static char *cached; +- int ret; +- +- if (cached) { +- *dir_ret = cached; +- return 0; +- } else { +- ret = dcc_get_subdir("state", dir_ret); +- if (ret == 0) +- cached = *dir_ret; +- return ret; +- } ++#define DCC_GET_DIR(NAME) \ ++int dcc_get_## NAME ##_dir(char **dir_ret) \ ++{ \ ++ static char *cached; \ ++ int ret; \ ++\ ++ if (cached) { \ ++ *dir_ret = cached; \ ++ return 0; \ ++ } else { \ ++ ret = dcc_get_subdir(#NAME, dir_ret); \ ++ if (ret == 0) \ ++ cached = *dir_ret; \ ++ return ret; \ ++ } \ + } + +- ++DCC_GET_DIR(cache) ++DCC_GET_DIR(lock) ++DCC_GET_DIR(state) + + /** + * Create a file inside the temporary directory and register it for +--- distcc-3.2rc1.orig/Makefile.in 2011-10-26 06:07:15.000000000 +0400 ++++ distcc-3.2rc1/Makefile.in 2013-06-15 18:54:50.254612420 +0400 +@@ -227,7 +227,7 @@ + rpm_glob_pattern = "$(PACKAGE)"*[-_.]"$(VERSION)"[-_.]*.deb + deb_glob_pattern = "$(PACKAGE)"*[-_.]"$(VERSION)"[-_.]*.rpm + +-common_obj = src/arg.o src/argutil.o \ ++common_obj = src/arg.o src/arg_native.o src/argutil.o \ + src/cleanup.o src/compress.o \ + src/trace.o src/util.o src/io.o src/exec.o \ + src/rpc.o src/tempfile.o src/bulk.o src/help.o src/filename.o \ +@@ -309,7 +309,7 @@ + + # All source files, for the purposes of building the distribution + SRC = src/stats.c \ +- src/access.c src/arg.c src/argutil.c \ ++ src/access.c src/arg.c src/arg_native.c src/argutil.c \ + src/auth_common.c src/auth_distcc.c src/auth_distccd.c \ + src/backoff.c src/bulk.c \ + src/cleanup.c \ +diff -Naurd distcc-3.2rc1.orig/man/distcc.1 distcc-3.2rc1/man/distcc.1 +--- distcc-3.2rc1.orig/man/distcc.1 2011-10-25 03:36:12.000000000 +0400 ++++ distcc-3.2rc1/man/distcc.1 2013-06-16 04:03:36.087697023 +0400 +@@ -789,7 +789,8 @@ + recompile that file locally. + .TP + .B "DISTCC_DIR" +-Per-user configuration directory to store lock files and state files. ++Per-user configuration directory to store lock files, state files ++and cache files. + By default + .B ~/.distcc/ + is used. |