summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Savchenko <bircoph@gmail.com>2013-06-17 05:25:24 +0400
committerAndrew Savchenko <bircoph@gmail.com>2013-06-17 05:25:24 +0400
commit4d3e97dbf8628d0ffadf1405787a3e22de67abb7 (patch)
tree706dff01d5286a17c3776747e3f18d72231c9b0c /sys-devel
parentdistcc: add support for native arguments (diff)
downloadbircoph-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.ebuild276
-rw-r--r--sys-devel/distcc/files/distcc-3.2_rc1-native.patch924
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.