ccontrol-1.0.orig/0000755000000000000000000000000011660237176012643 5ustar rootrootccontrol-1.0.orig/.gdbinit0000644000000000000000000000005010410404531014236 0ustar rootrootset environment HOME=/tmp/ccontrol-test ccontrol-1.0.orig/testsuite/0000755000000000000000000000000011256612221014661 5ustar rootrootccontrol-1.0.orig/testsuite/02distcc-fallback.test0000644000000000000000000000043110410404531020722 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/gcc # FAILDISTCC # EXEC DISTCC_HOSTS='snab/4 fort/6' /usr/bin/my-gcc -c -o hello.o world.c # Also tests parsing after distcc-hosts, which uses to_eol(). [*] distcc-hosts = snab/4 fort/6 distcc = /usr/bin/distcc cc = /usr/bin/my-gcc ccontrol-1.0.orig/testsuite/03ccache.test0000644000000000000000000000021610410404531017124 0ustar rootroot# ARGS hello world # NAME /usr/bin/gcc # EXEC /usr/bin/ccache /usr/bin/my-gcc hello world [*] cc = /usr/bin/my-gcc ccache = /usr/bin/ccache ccontrol-1.0.orig/testsuite/12ccontrol-print-complex5.test0000644000000000000000000000061110410404531022424 0ustar rootroot# NAME ccontrol # ARGS --section=test5 # EXEC \[test5\] # EXEC cc = /usr/bin/my-gcc # EXEC c++ = /usr/bin/my-c++ # EXEC make = /usr/bin/my-make # EXEC ld = /usr/bin/my-ld # EXEC ccache = /usr/bin/my-ccache # EXEC no-parallel = check [*] cc = /usr/bin/my-gcc c++ = /usr/bin/my-c++ ld = /usr/bin/my-ld make = /usr/bin/my-make ccache = /usr/bin/my-ccache [test5] no-parallel = check ccontrol-1.0.orig/testsuite/17unexpected-bracket.test0000644000000000000000000000030410410404531021476 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/gcc # EXIT 1 # EXEC ccontrol error: parse error on line 9: expected = targets [*module-init-tools*] no-parallel [*kernel/tmp] add make = ARCH=i386 ccontrol-1.0.orig/testsuite/10ccontrol-cc.test0000644000000000000000000000014410410404531020122 0ustar rootroot# ARGS gcc hello world # NAME ccontrol # EXEC /usr/bin/my-gcc hello world [*] cc = /usr/bin/my-gcc ccontrol-1.0.orig/testsuite/06include.include0000644000000000000000000000003710410404531020011 0ustar rootroot cc = /usr/bin/my-gcc-included ccontrol-1.0.orig/testsuite/01makeparallel.test0000644000000000000000000000016511256607002020360 0ustar rootroot# ARGS world # NAME make # EXEC /usr/bin/my-make -j20 world [*] no-parallel = pattern1 *x* make = /usr/bin/my-make ccontrol-1.0.orig/testsuite/02distcc-nohosts.test0000644000000000000000000000032010410404531020655 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/gcc # EXEC /usr/bin/my-gcc -c -o hello.o world.c # Also tests parsing after distcc-hosts, which uses to_eol(). [*] distcc = /usr/bin/distcc cc = /usr/bin/my-gcc ccontrol-1.0.orig/testsuite/02distcc-disable.test0000644000000000000000000000040410410404531020566 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/gcc # EXEC /usr/bin/my-gcc -c -o hello.o world.c # Also tests parsing after distcc-hosts, which uses to_eol(). [*] distcc-hosts = snab/4 fort/6 distcc = /usr/bin/distcc cc = /usr/bin/my-gcc [/*] distcc disable ccontrol-1.0.orig/testsuite/03ccache-disable.test0000644000000000000000000000022410410404531020524 0ustar rootroot# ARGS hello world # NAME /usr/bin/gcc # EXEC /usr/bin/my-gcc hello world [*] cc = /usr/bin/my-gcc ccache = /usr/bin/ccache [/*] ccache disable ccontrol-1.0.orig/testsuite/02distcplusplus.test0000644000000000000000000000047410410404531020641 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/g++ # EXEC DISTCC_HOSTS='snab/8 fort/6' /usr/bin/distcc /usr/bin/my-g++ -c -o hello.o world.c # Also tests parsing after distcc-hosts, which uses to_eol(). [*] distc++-hosts = snab/8 fort/6 distcc-hosts = snab/4 fort/6 distcc = /usr/bin/distcc c++ = /usr/bin/my-g++ ccontrol-1.0.orig/testsuite/10ccontrol-section.test0000644000000000000000000000034210410404531021201 0ustar rootroot# ARGS --section=magicpath gcc hello world # NAME ccontrol # EXEC /usr/bin/some-other-gcc hello world [/*] cc = /usr/bin/my-gcc # This can never be a real path, since it's relative. [magicpath] cc = /usr/bin/some-other-gcc ccontrol-1.0.orig/testsuite/10ccontrol-invoke.test.in0000644000000000000000000000011411256612221021440 0ustar rootroot# NAME ccontrol # ARGS --version # EXEC *version @VERSION@ (@VERSION_NAME@) ccontrol-1.0.orig/testsuite/04include.include0000644000000000000000000000004310410404531020004 0ustar rootroot[*] cc = /usr/bin/my-gcc-included ccontrol-1.0.orig/testsuite/02distcplusplushosts-off.test0000644000000000000000000000040710410404531022466 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/g++ # EXEC /usr/bin/my-g++ -c -o hello.o world.c # Also tests parsing after distcc-hosts, which uses to_eol(). [*] distc++-hosts disable distcc-hosts = snab/4 fort/6 distcc = /usr/bin/distcc c++ = /usr/bin/my-g++ ccontrol-1.0.orig/testsuite/04includesimple.test0000644000000000000000000000017010410404531020553 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /usr/bin/my-gcc-included hello world include = testsuite/04include.include ccontrol-1.0.orig/testsuite/12ccontrol-print-complex3.test0000644000000000000000000000074410410404531022431 0ustar rootroot# NAME ccontrol # ARGS --section=test3 # EXEC \[test3\] # EXEC cc = /usr/bin/my-gcc # EXEC c++ = /usr/bin/my-c++ # EXEC make = /usr/bin/my-make # EXEC ld = /usr/bin/my-ld # EXEC ccache = /usr/bin/my-ccache # EXEC distcc = /usr/bin/my-distcc # EXEC distc++-hosts = my distc++ hosts [*] cc = /usr/bin/my-gcc c++ = /usr/bin/my-c++ ld = /usr/bin/my-ld make = /usr/bin/my-make ccache = /usr/bin/my-ccache [test3] distcc = /usr/bin/my-distcc distc++-hosts = my distc++ hosts ccontrol-1.0.orig/testsuite/15lockfiles.test0000644000000000000000000000020410410404531017671 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /tmp/ccontrol-test/bin/my-gcc hello world [*] cc = ~/bin/my-gcc lock-file = ~/output ccontrol-1.0.orig/testsuite/04includeoverride.test0000644000000000000000000000023310410404531021101 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /usr/bin/my-gcc-override hello world include = testsuite/04include.include [*] cc = /usr/bin/my-gcc-override ccontrol-1.0.orig/testsuite/04includeoverrideafter.test0000644000000000000000000000023210410404531022122 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /usr/bin/my-gcc-included hello world [*] cc = /usr/bin/my-gcc-initial include = testsuite/04include.include ccontrol-1.0.orig/testsuite/12ccontrol-print-complex2.test0000644000000000000000000000105410410404531022423 0ustar rootroot# NAME ccontrol # ARGS --section=test2 # EXEC \[test2\] # EXEC cc = /usr/bin/my-gcc # EXEC c++ = /usr/bin/my-c++ # EXEC make = /usr/bin/my-make # EXEC ld = /usr/bin/my-ld # EXEC ccache = /usr/bin/my-ccache # EXEC distcc = /usr/bin/my-distcc # EXEC distcc-hosts = my distcc hosts # EXEC distc++-hosts = my distc++ hosts [*] cc = /usr/bin/my-gcc c++ = /usr/bin/my-c++ ld = /usr/bin/my-ld make = /usr/bin/my-make ccache = /usr/bin/my-ccache [test2] distcc = /usr/bin/my-distcc distcc-hosts = my distcc hosts distc++-hosts = my distc++ hosts ccontrol-1.0.orig/testsuite/09make-add.test0000644000000000000000000000021511256607151017402 0ustar rootroot# ARGS world # NAME make # EXEC /usr/bin/my-make ARG1=1 ARG2=2 -j20 world [*] make = /usr/bin/my-make add make = ARG1=1 add make = ARG2=2 ccontrol-1.0.orig/testsuite/16disable.test0000644000000000000000000000066610410404531017336 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/gcc # EXEC /usr/bin/my-gcc -c -o hello.o world.c [*] cc = /usr/bin/my-gcc distcc = /usr/bin/distcc ccache = /usr/bin/ccache distcc-hosts = snab/4 fort/6 distc++-hosts = snab/4 fort/6 no-parallel = check *foo add make = MAKE=1 add env = ENVVAR=1 [/*] distcc disable ccache disable distc++-hosts disable distcc-hosts disable no-parallel disable add make disable add env disable ccontrol-1.0.orig/testsuite/12ccontrol-print-complex4.test0000644000000000000000000000063210410404531022426 0ustar rootroot# NAME ccontrol # ARGS --section=test4 # EXEC \[test4\] # EXEC cc = /usr/bin/my-gcc # EXEC c++ = /usr/bin/my-c++ # EXEC make = /usr/bin/my-make # EXEC ld = /usr/bin/my-ld # EXEC ccache = /usr/bin/my-ccache [*] cc = /usr/bin/my-gcc c++ = /usr/bin/my-c++ ld = /usr/bin/my-ld make = /usr/bin/my-make ccache = /usr/bin/my-ccache [test4] distcc-hosts = my distcc hosts distc++-hosts = my distc++ hosts ccontrol-1.0.orig/testsuite/06includenull.test0000644000000000000000000000017010410404531020236 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /usr/bin/my-gcc hello world [*] cc = /usr/bin/my-gcc include = /dev/null ccontrol-1.0.orig/testsuite/vg-suppressions0000644000000000000000000000030310410404531017761 0ustar rootroot{ Glibc goes boom from _start (Debian glibc 2.3.5-3) Memcheck:Cond obj:/lib/ld-2.3.5.so obj:/lib/ld-2.3.5.so obj:/lib/ld-2.3.5.so obj:/lib/ld-2.3.5.so obj:/lib/ld-2.3.5.so } ccontrol-1.0.orig/testsuite/04includenull.test0000644000000000000000000000016710410404531020242 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /usr/bin/my-gcc hello world include = /dev/null [*] cc = /usr/bin/my-gcc ccontrol-1.0.orig/testsuite/12ccontrol-print-simple.test0000644000000000000000000000012510410404531022161 0ustar rootroot# NAME ccontrol # EXEC \[/*\] # EXEC cc = /usr/bin/my-gcc [*] cc = /usr/bin/my-gcc ccontrol-1.0.orig/testsuite/14tilde.test0000644000000000000000000000015610410404531017024 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /tmp/ccontrol-test/bin/my-gcc hello world [*] cc = ~/bin/my-gcc ccontrol-1.0.orig/testsuite/05cpus.test0000644000000000000000000000023610410404531016674 0ustar rootroot# ARGS hello world # NAME /usr/bin/gcc # EXEC /usr/bin/my-gcc hello world # FIXME: Doesn't actually check cpus are used. [*] cpus = 10 cc = /usr/bin/my-gcc ccontrol-1.0.orig/testsuite/07distcc-fail.test0000644000000000000000000000060010410404531020101 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/gcc # EXITCODE 103 # EXIT 103 # EXEC DISTCC_HOSTS='snab/4 fort/6' /usr/bin/distcc /usr/bin/my-gcc -c -o hello.o world.c # EXEC DISTCC_HOSTS='snab/4 fort/6' /usr/bin/my-gcc -c -o hello.o world.c # Also tests parsing after distcc-hosts, which uses to_eol(). [*] distcc-hosts = snab/4 fort/6 distcc = /usr/bin/distcc cc = /usr/bin/my-gcc ccontrol-1.0.orig/testsuite/12ccontrol-print-complex9.test0000644000000000000000000000065610410404531022441 0ustar rootroot# NAME ccontrol # ARGS --section=test9 # EXEC \[test9\] # EXEC cc = /usr/bin/my-gcc # EXEC c++ = /usr/bin/my-c++ # EXEC make = /usr/bin/my-make # EXEC ld = /usr/bin/my-ld # EXEC ccache = /usr/bin/my-ccache # EXEC add make = 1 2 3 # EXEC add make = 4 5 6 [*] cc = /usr/bin/my-gcc c++ = /usr/bin/my-c++ ld = /usr/bin/my-ld make = /usr/bin/my-make ccache = /usr/bin/my-ccache [test9] add make = 1 2 3 add make = 4 5 6 ccontrol-1.0.orig/testsuite/06includeoverrideafter.test0000644000000000000000000000023310410404531022125 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /usr/bin/my-gcc-included hello world [*] cc = /usr/bin/my-gcc-initial include = testsuite/06include.include ccontrol-1.0.orig/testsuite/12ccontrol-print-complex7.test0000644000000000000000000000054210410404531022431 0ustar rootroot# NAME ccontrol # ARGS --section=test7 # EXEC \[test7\] # EXEC cc = /usr/bin/my-gcc # EXEC c++ = /usr/bin/my-c++ # EXEC make = /usr/bin/my-make # EXEC ld = /usr/bin/my-ld # EXEC ccache = /usr/bin/my-ccache [*] cc = /usr/bin/my-gcc c++ = /usr/bin/my-c++ ld = /usr/bin/my-ld make = /usr/bin/my-make ccache = /usr/bin/my-ccache [test7] cpus = 1 ccontrol-1.0.orig/testsuite/12ccontrol-print-complex6.test0000644000000000000000000000060110410404531022424 0ustar rootroot# NAME ccontrol # ARGS --section=test6 # EXEC \[test6\] # EXEC cc = /usr/bin/my-gcc # EXEC c++ = /usr/bin/my-c++ # EXEC make = /usr/bin/my-make # EXEC ld = /usr/bin/my-ld # EXEC ccache = /usr/bin/my-ccache # EXEC no-parallel = * [*] cc = /usr/bin/my-gcc c++ = /usr/bin/my-c++ ld = /usr/bin/my-ld make = /usr/bin/my-make ccache = /usr/bin/my-ccache [test6] no-parallel = * ccontrol-1.0.orig/testsuite/01gccreplace.test0000644000000000000000000000013310410404531020002 0ustar rootroot# ARGS hello world # NAME gcc # EXEC /usr/bin/my-gcc hello world [*] cc = /usr/bin/my-gcc ccontrol-1.0.orig/testsuite/02distcc-hosts-off.test0000644000000000000000000000034310410404531021075 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/gcc # EXEC /usr/bin/my-gcc -c -o hello.o world.c [*] distcc-hosts = snab/4 fort/6 distcc = /usr/bin/distcc cc = /usr/bin/my-gcc1 [/*] distcc-hosts disable cc = /usr/bin/my-gcc ccontrol-1.0.orig/testsuite/01makeparallel-no-override.test0000644000000000000000000000013411256607417022615 0ustar rootroot# ARGS -j3 world # NAME make # EXEC /usr/bin/my-make -j3 world [*] make = /usr/bin/my-make ccontrol-1.0.orig/testsuite/01ccreplace.test0000644000000000000000000000014310410404531017634 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /usr/bin/my-gcc hello world [*] cc = /usr/bin/my-gcc ccontrol-1.0.orig/testsuite/12ccontrol-print-complex8.test0000644000000000000000000000056310410404531022435 0ustar rootroot# NAME ccontrol # ARGS --section=test8 # EXEC \[test8\] # EXEC cc = /usr/bin/my-gcc # EXEC c++ = /usr/bin/my-c++ # EXEC make = /usr/bin/my-make # EXEC ld = /usr/bin/my-ld # EXEC ccache = /usr/bin/my-ccache # EXEC cpus = 2 [*] cc = /usr/bin/my-gcc c++ = /usr/bin/my-c++ ld = /usr/bin/my-ld make = /usr/bin/my-make ccache = /usr/bin/my-ccache [test8] cpus = 2 ccontrol-1.0.orig/testsuite/06includeoverride.test0000644000000000000000000000023410410404531021104 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /usr/bin/my-gcc-override hello world [*] include = testsuite/06include.include cc = /usr/bin/my-gcc-override ccontrol-1.0.orig/testsuite/01makeparallel-pattern1.test0000644000000000000000000000016610410404531022106 0ustar rootroot# ARGS pattern1 # NAME make # EXEC /usr/bin/my-make pattern1 [*] no-parallel = pattern1 *x* make = /usr/bin/my-make ccontrol-1.0.orig/testsuite/01makeparallel-x.test0000644000000000000000000000021210410404531020607 0ustar rootroot# ARGS thing-with-x-in-it # NAME make # EXEC /usr/bin/my-make thing-with-x-in-it [*] no-parallel = pattern1 *x* make = /usr/bin/my-make ccontrol-1.0.orig/testsuite/01c++replace.test0000644000000000000000000000013410410404531017617 0ustar rootroot# ARGS hello world # NAME g++ # EXEC /usr/bin/my-g++ hello world [*] c++ = /usr/bin/my-g++ ccontrol-1.0.orig/testsuite/02distcplusplus-distcc.test0000644000000000000000000000043510410404531022105 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/g++ # EXEC DISTCC_HOSTS='snab/4 fort/6' /usr/bin/distcc /usr/bin/my-g++ -c -o hello.o world.c # Also tests parsing after distcc-hosts, which uses to_eol(). [*] distcc-hosts = snab/4 fort/6 distcc = /usr/bin/distcc c++ = /usr/bin/my-g++ ccontrol-1.0.orig/testsuite/test.sh0000755000000000000000000000332110410404531016170 0ustar rootroot#! /bin/sh -e TESTDIR=/tmp/ccontrol-test # We embed test info in comments get_args() { sed -n 's/^# *ARGS *\(.*\)/\1/p' $1 } get_argv0() { sed -n 's/^# *NAME *\(.*\)/\1/p' $1 } # EXIT is optional. get_exit_status() { STATUS=`sed -n 's/^# *EXIT *\(.*\)/\1/p' $1` if [ x"$STATUS" = x ]; then echo 0; else echo $STATUS; fi } get_output() { sed -n 's/^# *EXEC *\(.*\)/\1/p' $1 } get_exit_code() { CODE=`sed -n 's/^# *EXITCODE *\(.*\)/\1/p' $1` if [ x"$CODE" = x ]; then echo 0; else echo $CODE; fi } get_fail_distcc() { if grep -q FAILDISTCC $1; then echo 1; else echo 0; fi } run_test() { ln -sfn `pwd`/$1 $TESTDIR/.ccontrol/config HOME=$TESTDIR ARGV0=`get_argv0 $1` EXITCODE=`get_exit_code $1` FAILDISTCC=`get_fail_distcc $1` $VALGRIND ./ccontrol_test `get_args $1` > $TESTDIR/output 2>&1 RET=$? if [ $RET -ne `get_exit_status $1` ]; then cat $TESTDIR/output echo $1 failed: unexpected exit status $RET return 1 fi case "`sed 's/ *$//' $TESTDIR/output`" in `get_output $1`) : ;; *) cat $TESTDIR/output echo $1 failed: unexpected output return 1 ;; esac return 0 } rm -rf $TESTDIR mkdir $TESTDIR mkdir $TESTDIR/.ccontrol VALGRIND=`which valgrind` case "$1" in --valgrind=*) VALGRIND=`echo $1 | cut -d= -f2-`; shift;; esac if [ -n "$VALGRIND" ]; then VALGRIND="$VALGRIND --suppressions=testsuite/vg-suppressions -q" fi # Clean any *real* ccontrol env vars out of environment (make check) unset CCONTROL_LOCK unset CCONTROL_NO_PARALLEL MATCH=${1:-"*"} for f in testsuite/[0-9]*.test; do case `basename $f` in $MATCH) RUN=1;; esac [ -n "$RUN" ] || continue if run_test $f; then echo -n . else exit 1 fi done echo ccontrol-1.0.orig/testsuite/13env-add.test0000644000000000000000000000023510410404531017236 0ustar rootroot# ARGS world # NAME gcc # EXEC TESTING1='100' TESTING2='200' /usr/bin/my-gcc world [*] cc = /usr/bin/my-gcc add env = TESTING1=100 add env = TESTING2=200 ccontrol-1.0.orig/testsuite/15nice.test0000644000000000000000000000023210410404531016635 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /tmp/ccontrol-test/bin/my-gcc hello world # FIXME: Test more than parsing. [*] nice = 20 cc = ~/bin/my-gcc ccontrol-1.0.orig/testsuite/10ccontrol-invoke-V.test.in0000644000000000000000000000010511256612221021643 0ustar rootroot# NAME ccontrol # ARGS -V # EXEC *version @VERSION@ (@VERSION_NAME@) ccontrol-1.0.orig/testsuite/02distcc.test0000644000000000000000000000043410410404531017170 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/gcc # EXEC DISTCC_HOSTS='snab/4 fort/6' /usr/bin/distcc /usr/bin/my-gcc -c -o hello.o world.c # Also tests parsing after distcc-hosts, which uses to_eol(). [*] distcc-hosts = snab/4 fort/6 distcc = /usr/bin/distcc cc = /usr/bin/my-gcc ccontrol-1.0.orig/testsuite/06includesimple.test0000644000000000000000000000017510410404531020562 0ustar rootroot# ARGS hello world # NAME /usr/bin/cc # EXEC /usr/bin/my-gcc-included hello world [*] include = testsuite/06include.include ccontrol-1.0.orig/testsuite/11whitespace.test0000644000000000000000000000033110410404531020047 0ustar rootroot# Test that lines containing only whitespace are ignored. # ARGS hello world # NAME /usr/bin/cc # EXEC /usr/bin/my-gcc hello world [*] cc = /usr/bin/my-gcc c++ = /usr/bin/my-g++ # This is a comment ccontrol-1.0.orig/testsuite/10ccontrol-invoke.test0000644000000000000000000000010411374716655021053 0ustar rootroot# NAME ccontrol # ARGS --version # EXEC *version 1.0 (David Gibson) ccontrol-1.0.orig/testsuite/12ccontrol-print-complex1.test0000644000000000000000000000074010410404531022423 0ustar rootroot# NAME ccontrol # ARGS --section=test1 # EXEC \[test1\] # EXEC cc = /usr/bin/my-gcc # EXEC c++ = /usr/bin/my-c++ # EXEC make = /usr/bin/my-make # EXEC ld = /usr/bin/my-ld # EXEC ccache = /usr/bin/my-ccache # EXEC distcc = /usr/bin/my-distcc # EXEC distcc-hosts = my distcc hosts [*] cc = /usr/bin/my-gcc c++ = /usr/bin/my-c++ ld = /usr/bin/my-ld make = /usr/bin/my-make ccache = /usr/bin/my-ccache [test1] distcc = /usr/bin/my-distcc distcc-hosts = my distcc hosts ccontrol-1.0.orig/testsuite/10ccontrol-invoke-V.test0000644000000000000000000000007511374716655021265 0ustar rootroot# NAME ccontrol # ARGS -V # EXEC *version 1.0 (David Gibson) ccontrol-1.0.orig/testsuite/12ccontrol-print-complex10.test0000644000000000000000000000063211256612221022511 0ustar rootroot# NAME ccontrol # ARGS --section=test10 # EXEC *ccontrol*Using section test10 # EXEC \[test10\] # EXEC cc = /usr/bin/my-gcc # EXEC c++ = /usr/bin/my-c++ # EXEC make = /usr/bin/my-make # EXEC ld = /usr/bin/my-ld # EXEC ccache = /usr/bin/my-ccache # EXEC verbose [*] cc = /usr/bin/my-gcc c++ = /usr/bin/my-c++ ld = /usr/bin/my-ld make = /usr/bin/my-make ccache = /usr/bin/my-ccache [test10] verbose ccontrol-1.0.orig/testsuite/02distcc-off-machine.test0000644000000000000000000000036110410404531021341 0ustar rootroot# ARGS -c -o hello.o world.c # NAME /usr/bin/gcc # EXEC DISTCC_HOSTS='off/4' /usr/bin/distcc /usr/bin/my-gcc -c -o hello.o world.c [*] distcc-hosts = snab/4 fort/6 distcc = /usr/bin/distcc cc = /usr/bin/my-gcc [/*] distcc-hosts = off/4 ccontrol-1.0.orig/testsuite/07parallel.test0000644000000000000000000000021111256607106017525 0ustar rootroot# ARGS world # NAME make # EXEC /usr/bin/my-make -j20 world [*] no-parallel = world make = /usr/bin/my-make [/*] no-parallel disable ccontrol-1.0.orig/stdrusty.h0000644000000000000000000000307310410404531014677 0ustar rootroot#ifndef _STDRUSTY_H #define _STDRUSTY_H #include #include #include /* Is A == B ? */ #define streq(a,b) (strcmp((a),(b)) == 0) /* Does A start with B ? */ #define strstarts(a,b) (strncmp((a),(b),strlen(b)) == 0) /* Does A end in B ? */ static inline bool strends(const char *a, const char *b) { if (strlen(a) < strlen(b)) return false; return streq(a + strlen(a) - strlen(b), b); } #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #ifndef CHAR_BIT #define CHAR_BIT 8 #endif /* Upper bound to sprintf this simple type? Each 3 bits < 1 digit. */ #define CHAR_SIZE(type) (((sizeof(type)*CHAR_BIT + 2) / 3) + 1) typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; /* Convenient wrappers for malloc and realloc. Use them. */ #define new(type) ((type *)malloc_nofail(sizeof(type))) #define new_array(type, num) realloc_array((type *)0, (num)) #define realloc_array(ptr, num) ((__typeof__(ptr))_realloc_array((ptr), sizeof((*(ptr))), (num))) void *malloc_nofail(size_t size); void *realloc_nofail(void *ptr, size_t size); void *_realloc_array(void *ptr, size_t size, size_t num); const char *int_to_string(int val); void fatal(const char *msg, int err, ...) __attribute__((noreturn)); /* This version adds one byte (for nul term) */ void *suck_file(int fd, unsigned long *size); void release_file(void *data, unsigned long size); /* For writing daemons, based on Stevens. */ void daemonize(void); /* Signal handling: returns fd to listen on. */ int signal_to_fd(int signal); void close_signal(int fd); #endif /* _STDRUSTY_H */ ccontrol-1.0.orig/stdrusty.c0000644000000000000000000000417610465041533014710 0ustar rootroot#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "stdrusty.h" const char *int_to_string(int val) { unsigned int ord, i; static char buf[2][CHAR_SIZE(val)]; static int which; char *p = buf[which]; if (val < 0) { *(p++) = '-'; val = -val; } for (i = 0, ord = 1000000; ord; ord /= 10) { if (val >= ord || i != 0) { p[i++] = '0' + val / ord; val %= ord; } } if (i == 0) p[i++] = '0'; p[i] = '\0'; which = !which; return buf[!which]; } void fatal(const char *msg, int err, ...) { const char *str; va_list arglist; write(STDERR_FILENO, "ccontrol error: ", strlen("ccontrol error: ")); write(STDERR_FILENO, msg, strlen(msg)); va_start(arglist, err); while ((str = va_arg(arglist, const char *)) != NULL) write(STDERR_FILENO, str, strlen(str)); va_end(arglist); if (err) { str = int_to_string(err); write(STDERR_FILENO, ": ", strlen(": ")); write(STDERR_FILENO, str, strlen(str)); } write(STDERR_FILENO, "\n", 1); exit(1); } #ifndef SIZE_MAX #define SIZE_MAX UINT_MAX #endif void *_realloc_array(void *ptr, size_t size, size_t num) { if (num >= SIZE_MAX/size) return NULL; return realloc_nofail(ptr, size * num); } void *realloc_nofail(void *ptr, size_t size) { ptr = realloc(ptr, size); if (ptr) return ptr; fatal("realloc failed", errno, NULL); } /* This version adds one byte (for nul term) */ void *suck_file(int fd, unsigned long *size) { unsigned int max = 16384; int ret, savederr = 0; void *buffer; if (fd < 0) return NULL; buffer = malloc(max+1); *size = 0; while ((ret = read(fd, buffer + *size, max - *size)) > 0) { *size += ret; if (*size == max) buffer = realloc(buffer, max *= 2 + 1); else /* Should never get short reads on file. */ break; } if (ret < 0) { savederr = errno; free(buffer); buffer = NULL; } else ((char *)buffer)[*size] = '\0'; errno = savederr; return buffer; } void release_file(void *data, unsigned long size) { free(data); } ccontrol-1.0.orig/keywords.gperf0000644000000000000000000000120211256611104015516 0ustar rootroot%{ /* Keep -Wmissing-declarations happy: */ const struct ccontrol_command * find_keyword (register const char *str, register unsigned int len); %} %language=ANSI-C struct ccontrol_command { const char *name; enum token_type type; }; %define hash-function-name hash_keyword %define lookup-function-name find_keyword %readonly-tables %enum %struct-type %compare-strncmp %% no-parallel, NO_PARALLEL cc, CC c++, CPLUSPLUS ld, LD make, MAKE ccache, CCACHE distcc, DISTCC distcc-hosts, DISTCC_HOSTS distc++-hosts, DISTCPLUSPLUS_HOSTS verbose, VERBOSE nice, NICE include, INCLUDE cpus, CPUS disable, DISABLE add, ADD env, ENV lock-file, LOCK_FILE ccontrol-1.0.orig/keywords.c0000644000000000000000000001240611256611104014645 0ustar rootroot/* ANSI-C code produced by gperf version 3.0.1 */ /* Command-line: gperf keywords.gperf */ /* Computed positions: -k'1' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ #error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "keywords.gperf" /* Keep -Wmissing-declarations happy: */ const struct ccontrol_command * find_keyword (register const char *str, register unsigned int len); #line 7 "keywords.gperf" struct ccontrol_command { const char *name; enum token_type type; }; /* maximum key range = 26, duplicates = 0 */ #ifdef __GNUC__ __inline #else #ifdef __cplusplus inline #endif #endif static unsigned int hash_keyword (register const char *str, register unsigned int len) { static const unsigned char asso_values[] = { 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 10, 28, 0, 5, 5, 28, 28, 28, 20, 28, 28, 5, 15, 5, 28, 28, 28, 28, 28, 28, 28, 15, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28 }; return len + asso_values[(unsigned char)str[0]]; } #ifdef __GNUC__ __inline #endif const struct ccontrol_command * find_keyword (register const char *str, register unsigned int len) { enum { TOTAL_KEYWORDS = 17, MIN_WORD_LENGTH = 2, MAX_WORD_LENGTH = 13, MIN_HASH_VALUE = 2, MAX_HASH_VALUE = 27 }; static const struct ccontrol_command wordlist[] = { {""}, {""}, #line 19 "keywords.gperf" {"cc", CC}, #line 20 "keywords.gperf" {"c++", CPLUSPLUS}, #line 30 "keywords.gperf" {"cpus", CPUS}, {""}, #line 23 "keywords.gperf" {"ccache", CCACHE}, #line 21 "keywords.gperf" {"ld", LD}, #line 33 "keywords.gperf" {"env", ENV}, #line 28 "keywords.gperf" {"nice", NICE}, {""}, #line 24 "keywords.gperf" {"distcc", DISTCC}, #line 31 "keywords.gperf" {"disable", DISABLE}, #line 32 "keywords.gperf" {"add", ADD}, #line 34 "keywords.gperf" {"lock-file", LOCK_FILE}, {""}, #line 18 "keywords.gperf" {"no-parallel", NO_PARALLEL}, #line 25 "keywords.gperf" {"distcc-hosts", DISTCC_HOSTS}, #line 26 "keywords.gperf" {"distc++-hosts", DISTCPLUSPLUS_HOSTS}, #line 22 "keywords.gperf" {"make", MAKE}, {""}, {""}, #line 27 "keywords.gperf" {"verbose", VERBOSE}, {""}, {""}, {""}, {""}, #line 29 "keywords.gperf" {"include", INCLUDE} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register int key = hash_keyword (str, len); if (key <= MAX_HASH_VALUE && key >= 0) { register const char *s = wordlist[key].name; if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0') return &wordlist[key]; } } return 0; } ccontrol-1.0.orig/gui/0000755000000000000000000000000010410404555013414 5ustar rootrootccontrol-1.0.orig/gui/gccontrol.py0000755000000000000000000001270411374716655016010 0ustar rootroot#!/usr/bin/python # Simple GTK+ frontend to ccontrol import gtk import egg.trayicon import os import stat LIBDIR = "/usr/local/lib/ccontrol" def unexpand_user(path): home = os.getenv('HOME') common = os.path.commonprefix([path, home]) if len(common) > 0: return '~%s' % path[len(common):] else: return path class ConfigNotSymlinkException(Exception): def __init__(self, file, dir): self.file = file self.dir = dir class ConfigManager(object): CC_DIR = os.path.join(os.getenv('HOME'), '.ccontrol') CONFIG = os.path.join(CC_DIR, 'config') def config_files(self): try: for file in os.listdir(self.CC_DIR): path = os.path.join(self.CC_DIR, file) if not os.path.isfile(path) or os.path.islink(path) or \ file.startswith('.') or file.endswith('~'): continue yield file except OSError, e: if getattr(e, 'errno', None) == 2: return def get_config(self): if os.path.islink(self.CONFIG): return os.readlink(self.CONFIG) elif os.path.exists(self.CONFIG): raise ConfigNotSymlinkException(self.CONFIG, self.CC_DIR) else: return None def set_config(self, file): # Remove file if it already existed if os.access(self.CONFIG, os.F_OK): os.unlink(self.CONFIG) os.symlink(file, self.CONFIG) class ConfigEditor(gtk.Window): class ConfigTab(gtk.Frame): def __init__(self, file): gtk.Frame.__init__(self) self.file = file def save(self): pass def button_response(self, resp): if resp == "cancel": self.hide_all() elif resp == "ok": for p in range(0,self.notebook.get_n_pages()): self.notebook.get_nth_page(p).save() self.hide_all() def __init__(self): gtk.Window.__init__(self) self.set_title("Manage ccontrol profiles") self.set_default_size(400, 400) vbox = gtk.VBox() self.notebook = gtk.Notebook() cm = ConfigManager() for f in cm.config_files(): self.notebook.append_page(ConfigEditor.ConfigTab(f), gtk.Label(f)) vbox.add(self.notebook) buttons = gtk.HButtonBox() ok_button = gtk.Button(stock='gtk-ok') ok_button.connect_object("clicked", self.button_response, "ok") buttons.add(ok_button) cancel_button = gtk.Button(stock='gtk-cancel') cancel_button.connect_object("clicked", self.button_response, "cancel") buttons.add(cancel_button) vbox.add(buttons) self.add(vbox) class ProfilePopup(gtk.Menu): def menuitem_response(self, file): try: self._cm.set_config(file) except Exception, e: dialog = gtk.MessageDialog(None, flags=gtk.DIALOG_MODAL, type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_CLOSE) dialog.set_markup('An error occurred switching ' \ 'configuration files!') dialog.format_secondary_markup('The error was: %s' % e) dialog.connect("response", lambda dialog, resp: dialog.destroy()) dialog.show() raise e def __init__(self, event): gtk.Menu.__init__(self) self._cm = ConfigManager() try: current = self._cm.get_config() except ConfigNotSymlinkException, e: dialog = gtk.MessageDialog(None, flags=gtk.DIALOG_MODAL, type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_CLOSE) dialog.set_markup('%s is not a symbolic link.' % \ unexpand_user(e.file)) dialog.format_secondary_markup('To use the ccontrol GUI you ' \ 'must symlink %s to a configuration file ' \ 'in %s' % (unexpand_user(e.file), unexpand_user(e.dir))) dialog.connect("response", lambda dialog, resp: dialog.destroy()) dialog.show() return found = False for f in self._cm.config_files(): found = True item = gtk.CheckMenuItem(f) item.set_draw_as_radio(True) if f == current: item.set_active(True) self.append(item) item.connect_object("activate", self.menuitem_response, f) if not found: item = gtk.MenuItem('No config files found') item.set_property('sensitive', False) self.append(item) self.show_all() self.popup(None, None, None, event.button, event.time) class TrayIcon(egg.trayicon.TrayIcon): def __init__(self): egg.trayicon.TrayIcon.__init__(self, 'ccontrol') self._gui = ConfigEditor() img = gtk.Image() img.set_from_file("%s/ccontrol-key.png" % LIBDIR) eventbox = gtk.EventBox() eventbox.add(img) eventbox.set_events(gtk.gdk.BUTTON_PRESS_MASK) eventbox.connect_object("button_press_event", self.tray_clicked, self) self.add(eventbox) def tray_clicked(self, widget, event): if event.button == 1: ProfilePopup(event) elif event.button == 3: self._gui.show_all() def main(): tray_icon = TrayIcon() tray_icon.show_all() gtk.main() main() ccontrol-1.0.orig/gui/gccontrol.py.in0000755000000000000000000001266510410404531016374 0ustar rootroot#!/usr/bin/python # Simple GTK+ frontend to ccontrol import gtk import egg.trayicon import os import stat LIBDIR = "@LIBDIR@" def unexpand_user(path): home = os.getenv('HOME') common = os.path.commonprefix([path, home]) if len(common) > 0: return '~%s' % path[len(common):] else: return path class ConfigNotSymlinkException(Exception): def __init__(self, file, dir): self.file = file self.dir = dir class ConfigManager(object): CC_DIR = os.path.join(os.getenv('HOME'), '.ccontrol') CONFIG = os.path.join(CC_DIR, 'config') def config_files(self): try: for file in os.listdir(self.CC_DIR): path = os.path.join(self.CC_DIR, file) if not os.path.isfile(path) or os.path.islink(path) or \ file.startswith('.') or file.endswith('~'): continue yield file except OSError, e: if getattr(e, 'errno', None) == 2: return def get_config(self): if os.path.islink(self.CONFIG): return os.readlink(self.CONFIG) elif os.path.exists(self.CONFIG): raise ConfigNotSymlinkException(self.CONFIG, self.CC_DIR) else: return None def set_config(self, file): # Remove file if it already existed if os.access(self.CONFIG, os.F_OK): os.unlink(self.CONFIG) os.symlink(file, self.CONFIG) class ConfigEditor(gtk.Window): class ConfigTab(gtk.Frame): def __init__(self, file): gtk.Frame.__init__(self) self.file = file def save(self): pass def button_response(self, resp): if resp == "cancel": self.hide_all() elif resp == "ok": for p in range(0,self.notebook.get_n_pages()): self.notebook.get_nth_page(p).save() self.hide_all() def __init__(self): gtk.Window.__init__(self) self.set_title("Manage ccontrol profiles") self.set_default_size(400, 400) vbox = gtk.VBox() self.notebook = gtk.Notebook() cm = ConfigManager() for f in cm.config_files(): self.notebook.append_page(ConfigEditor.ConfigTab(f), gtk.Label(f)) vbox.add(self.notebook) buttons = gtk.HButtonBox() ok_button = gtk.Button(stock='gtk-ok') ok_button.connect_object("clicked", self.button_response, "ok") buttons.add(ok_button) cancel_button = gtk.Button(stock='gtk-cancel') cancel_button.connect_object("clicked", self.button_response, "cancel") buttons.add(cancel_button) vbox.add(buttons) self.add(vbox) class ProfilePopup(gtk.Menu): def menuitem_response(self, file): try: self._cm.set_config(file) except Exception, e: dialog = gtk.MessageDialog(None, flags=gtk.DIALOG_MODAL, type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_CLOSE) dialog.set_markup('An error occurred switching ' \ 'configuration files!') dialog.format_secondary_markup('The error was: %s' % e) dialog.connect("response", lambda dialog, resp: dialog.destroy()) dialog.show() raise e def __init__(self, event): gtk.Menu.__init__(self) self._cm = ConfigManager() try: current = self._cm.get_config() except ConfigNotSymlinkException, e: dialog = gtk.MessageDialog(None, flags=gtk.DIALOG_MODAL, type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_CLOSE) dialog.set_markup('%s is not a symbolic link.' % \ unexpand_user(e.file)) dialog.format_secondary_markup('To use the ccontrol GUI you ' \ 'must symlink %s to a configuration file ' \ 'in %s' % (unexpand_user(e.file), unexpand_user(e.dir))) dialog.connect("response", lambda dialog, resp: dialog.destroy()) dialog.show() return found = False for f in self._cm.config_files(): found = True item = gtk.CheckMenuItem(f) item.set_draw_as_radio(True) if f == current: item.set_active(True) self.append(item) item.connect_object("activate", self.menuitem_response, f) if not found: item = gtk.MenuItem('No config files found') item.set_property('sensitive', False) self.append(item) self.show_all() self.popup(None, None, None, event.button, event.time) class TrayIcon(egg.trayicon.TrayIcon): def __init__(self): egg.trayicon.TrayIcon.__init__(self, 'ccontrol') self._gui = ConfigEditor() img = gtk.Image() img.set_from_file("%s/ccontrol-key.png" % LIBDIR) eventbox = gtk.EventBox() eventbox.add(img) eventbox.set_events(gtk.gdk.BUTTON_PRESS_MASK) eventbox.connect_object("button_press_event", self.tray_clicked, self) self.add(eventbox) def tray_clicked(self, widget, event): if event.button == 1: ProfilePopup(event) elif event.button == 3: self._gui.show_all() def main(): tray_icon = TrayIcon() tray_icon.show_all() gtk.main() main() ccontrol-1.0.orig/gui/ccontrol-key.png0000644000000000000000000000300110410404531016517 0ustar rootroot‰PNG  IHDR&¬GîbKGDÿÿÿ ½§“ pHYs  šœtIMEÖ )…¡ªŽIDATHÇ—ïr×ÅÝ÷Îì®þª€Q¥„D‚]àEà¤\$Ä/Á#øƒŸ!ïÇåð1•²!;†DI‘*Ù1T ŽS¶Ø{»óáÎŒf”Ên©V«™é{úôéÓ-yÿ½÷~­ÞüjTG$D4DT±ªÜ+4‚ªª"„"€ˆP×51FPBŒsÆÝB¨*qT±´¼†Xäßßú=Ñó Á0×@–€‹â~ø«+«œ]?ËÞƒ=Þyçç|òÉ]N:Åêê lllpïÞ}.lm1Yšðí·ÿasóu?þW¯^aoowßý ÈPï*" ‚™¡9eT\QŒrÙD•ý¿î£ª¼ñæÜùÃnÞºÉÁÁ“É„GñðáCvvvøì³?³¶¶Æõ·¯±¿ÿ7nüâ»»¸|ù»»»ˆèœ èe¡Ò‚+Rˆ½vu+ºPUPQT•ó[çyþü9÷ïßgçÚüîμv†'OŸrvý,ç6_gý‡ëlþh“­ [üéã9¿užñdÄÅ‹?æîÝ»\¾|‰3gNÏëSޏvÖ4ˆF£†"Ú¦Æ\Çܾ}»<¤pýí¶.lÍUåæ­_"î€8f‰«ÛWØÞÞFD8wî\‰?óò« IM@+bÎFÎZawH¬:ú}xH¡o».ÓŠ˜çŽ]ÓÅè>UŠÆrÊ¥ÑDˆÉŠÏ¨ è]ð(Çú ‹¥йÃü*ËX*­Ø‡¬¹YÇ1h(-nÞ³%R={#Æì°KHUzÆA‹¾¤M±hÍñ!sA^î®Å2=7¼ªþŸ l™˜RîËØ•ÿY^bDú§ÕÂ|çòîGß_%…ò÷2] ÇEHÙÐÔ$rn5äÞƒêF¯ºX"–» (D¼ïÈR!3#VUi"mËœ+!gx¶cÐv Ç5ûQ#¿Ì »óúnTC›¾#¢Ä΃éoî†ñàD^Í–e¤øQù€¾ãÊýÞ¶êñ"B ¸;)5Dºí!çLJ3\k¬Ô}¥ÌÞ"Þ ¿£J sC¥3lé[ß[Ö¥Òy‹· %¦ÓC4Txš„h8¢Q%O¿çó{$¥ÄH•ÑhL 5q' == 62) && ('?' == 63) && ('A' == 65) \ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ #error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "extensions.gperf" /* Derived from distcc's dcc_is_source, Copyright (C) 2002, 2003, 2004 by Martin Pool */ /* Keep -Wmissing-declarations happy: */ const char * find_extension (register const char *str, register unsigned int len); /* maximum key range = 31, duplicates = 0 */ #ifdef __GNUC__ __inline #else #ifdef __cplusplus inline #endif #endif static unsigned int hash_extension (register const char *str, register unsigned int len) { static const unsigned char asso_values[] = { 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 30, 32, 32, 32, 32, 32, 32, 32, 32, 32, 25, 32, 32, 32, 32, 32, 20, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 5, 32, 32, 32, 0, 32, 32, 5, 32, 32, 15, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 }; register int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval; } #ifdef __GNUC__ __inline #endif const char * find_extension (register const char *str, register unsigned int len) { enum { TOTAL_KEYWORDS = 16, MIN_WORD_LENGTH = 1, MAX_WORD_LENGTH = 3, MIN_HASH_VALUE = 1, MAX_HASH_VALUE = 31 }; static const char * const wordlist[] = { "", "m", "mm", "", "", "", "i", "mi", "mii", "", "", "c", "ii", "cxx", "", "", "s", "cp", "cpp", "", "", "S", "cc", "c++", "", "", "M", "", "", "", "", "C" }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register int key = hash_extension (str, len); if (key <= MAX_HASH_VALUE && key >= 0) { register const char *s = wordlist[key]; if (*str == *s && !strcmp (str + 1, s + 1)) return s; } } return 0; } ccontrol-1.0.orig/configure0000755000000000000000000000626011374715546014562 0ustar rootroot#! /bin/sh reconfigure() { for f in `find . -name '*.in'`; do BASE=`echo $f | sed 's/.in$//'` sh config.status < $f > $BASE chmod --reference=$f $BASE done } if [ x"$1" = "x--reconfigure" ]; then reconfigure exit fi BINDIR=/usr/local/bin LIBDIR=/usr/local/lib/ccontrol MANDIR=/usr/local/share/man XMLTO=`which xmlto` ASCIIDOC=`which asciidoc` OPTFLAGS=-O3 VALGRIND=`which valgrind` VERSION=1.0 VERSION_NAME="David Gibson" while [ $# -gt 0 ]; do case "$1" in --bindir=*) BINDIR=`echo $1 | cut -d= -f2-`;; --libdir=*) LIBDIR=`echo $1 | cut -d= -f2-`;; --mandir=*) MANDIR=`echo $1 | cut -d= -f2-`;; --xmlto=*) XMLTO=`echo $1 | cut -d= -f2-`;; --asciidoc=*) ASCIIDOC=`echo $1 | cut -d= -f2-`;; --enable-debug) OPTFLAGS=-g; DEBUG=1; NODIET=1;; --disable-dietlibc) NODIET=1;; --disable-valgrind) VALGRIND="";; *) echo "Usage: configure []" >&2 echo "Valid options, with defaults:">&2 echo " --bindir=$BINDIR" >&2 echo " --libdir=$LIBDIR" >&2 echo " --mandir=$MANDIR" >&2 echo " --xmlto=$XMLTO" >&2 echo " --asciidoc=$ASCIIDOC" >&2 echo " --enable-debug" >&2 echo " --disable-dietlibc" >&2 echo " --disable-valgrind" >&2 exit 1 ;; esac shift done CC=${CC:-gcc} if [ -z "$NODIET" ]; then echo -n Looking for dietlibc... if type diet >/dev/null 2>&1; then echo yes CC="diet $CC" echo -n Checking for Ubuntu stack protector brain damage... trap 'rm -f $FILENAME $FILENAME.o' 0 FILENAME=`mktemp` echo 'int fn(char *); int foo(void) { char s[100]; return fn(s); }' > $FILENAME $CC -x c -O3 -c $FILENAME -o $FILENAME.o if objdump -d $FILENAME.o | grep -q '%gs:'; then echo yes OPTFLAGS="$OPTFLAGS -fno-stack-protector" else echo no fi else echo no fi fi echo -n Looking for compiler... echo $CC echo -n Looking for compiler flags... CFLAGS="${CFLAGS:--Wall -Wmissing-declarations} $OPTFLAGS" echo $CFLAGS echo -n Looking for xmlto... if [ -z "$XMLTO" ]; then echo not found '(no regenerating man pages for you)' XMLTO="@echo No xmlto found; exit 0; :" else echo $XMLTO fi echo -n Looking for asciidoc... if [ -z "$ASCIIDOC" ]; then echo not found '(no regenerating man pages for you)' ASCIIDOC="@echo No asciidoc found; exit 0; :" else echo $ASCIIDOC fi DEBVERSION=`head -n1 debian/changelog | sed -e 's/^ccontrol (\([0-9.]\+\)-[0-9]\+) .*$/\1/'` if [ "$DEBVERSION" != "$VERSION" ]; then echo "Fixing up debian/changelog as the version was changed." # FIXME: -R is GNU date specific DATE=`date -R` cp debian/changelog debian/changelog.old (sed -e "s/@VERSION@/$VERSION/" -e "s/@DATE@/$DATE/" debian/changelog.template.in ; cat debian/changelog.old) > debian/changelog rm debian/changelog.old fi echo sed -e \""s,@CC@,$CC,g\"" \ -e \""s,@BINDIR@,$BINDIR,g\"" \ -e \""s,@LIBDIR@,$LIBDIR,g\"" \ -e \""s,@MANDIR@,$MANDIR,g\"" \ -e \""s,@XMLTO@,$XMLTO,g\"" \ -e \""s,@ASCIIDOC@,$ASCIIDOC,g\"" \ -e \""s,@CFLAGS@,$CFLAGS,g\"" \ -e \""s,@VERSION@,$VERSION,g\"" \ -e \""s,@VERSION_NAME@,$VERSION_NAME,g\"" \ -e \""s,@VALGRIND@,$VALGRIND,g\"" > config.status reconfigure echo Configuration complete. Type \"make\" to build. ccontrol-1.0.orig/ccontrol.h0000644000000000000000000000315111374714066014637 0ustar rootroot/* Ccontrol goodness. */ #ifndef _CCONTROL_H #define _CCONTROL_H #include struct string { /* Start and end of this string, initial is pointer to beginning. */ char *start, *end; char *start_of_file; }; enum type { TYPE_CC, TYPE_CPLUSPLUS, TYPE_LD, TYPE_MAKE, TYPE_OTHER, /* TYPE_OTHER is special, it doesn't get a slot in tables */ LAST_TYPE = TYPE_MAKE, }; struct add { struct add *next; char *arg; }; struct section { struct string name; /* Run verbose (debugging) */ bool verbose; /* Priority level */ int nice; /* How many in parallel? */ unsigned int cpus; /* What (if any) targets do we suppress parallel make? */ char *no_parallel; /* Name of CC, C++, LD and MAKE replacements. */ char *names[LAST_TYPE + 1]; /* Path of ccache, distcc */ char *ccache, *distcc; /* Distcc hosts. */ char *distcc_hosts; bool distcplusplus_hosts_set; char *distcplusplus_hosts; /* Things to add to make line. */ struct add *make_add; /* Things to add to environment. */ struct add *env_add; /* lock file */ int lock_fd; char *lock_file; }; /* ccontrol.c */ #define verbose(v, fmt, ...) \ do { if (v) __verbose((fmt), ## __VA_ARGS__, NULL); } while(0) void __verbose(const char *fmt, ...) __attribute__((sentinel)); /* ccontrol-parse.c */ struct section read_config(const char *configname, const char *dir, int fd); /* ccontrol-lock.c */ typedef void (*undofn_t)(void); undofn_t grab_lock(int fd, struct section sec, enum type type); void drop_slow_lock(void); /* ccontrol-identify.c */ enum type what_am_i(char *argv[]); bool can_distcc(char *argv[]); #endif /* _CCONTROL_H */ ccontrol-1.0.orig/ccontrol.c0000644000000000000000000002576711374714066014653 0ustar rootroot#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stdrusty.h" #include "ccontrol.h" static void insert_arg(char *argv[], unsigned argc, unsigned pos, char *arg) { memmove(argv+pos+1, argv+pos, (argc+1) * sizeof(char *)); argv[pos] = arg; } static void write_string(int fd, const char *str) { write(fd, str, strlen(str)); } void __verbose(const char *msg, ...) { va_list arglist; const char *str; write_string(STDERR_FILENO, " ccontrol("); write_string(STDERR_FILENO, int_to_string(getpid())); write_string(STDERR_FILENO, ") "); write_string(STDERR_FILENO, msg); va_start(arglist, msg); while ((str = va_arg(arglist, char *)) != NULL) write_string(STDERR_FILENO, str); va_end(arglist); write_string(STDERR_FILENO, "\n"); } static bool make_target_match(char *argv[], char *targets) { char *p, *target = ""; unsigned int i; if (!targets) return false; if (!targets[0]) return true; /* Heuristic: choose last arg which is neither option nor variable */ for (i = 0; argv[i]; i++) { if (argv[i][0] == '-') continue; if (strchr(argv[i], '=')) continue; target = argv[i]; } for (p = strtok(targets, " \t"); p; p = strtok(NULL, " \t")) if (fnmatch(p, target, 0) == 0) return true; return false; } static bool already_has_dash_j(char *argv[]) { unsigned int i; const char *makeflags; for (i = 0; argv[i]; i++) if (strstarts(argv[i], "-j")) return true; /* Top-level -j sets --jobserver-fds for kids. */ makeflags = getenv("MAKEFLAGS"); return makeflags && strstr(makeflags, "--jobserver-fds") != NULL; } /* Earlier arg parsing might have revealed distcc is useless. */ static void adjust_args_and_environment(struct section sec, enum type type, char *argv[], unsigned argc) { struct add *add; if (type != TYPE_OTHER) argv[0] = sec.names[type]; switch (type) { case TYPE_MAKE: /* Ensure make calls us for children, not /usr/bin/make. */ setenv("MAKE", "make", 1); /* We insert -j to make in parallel. */ if (!make_target_match(argv, sec.no_parallel) && !already_has_dash_j(argv)) { const char *cpus = int_to_string(sec.cpus*20); char *arg = malloc(strlen(cpus) + 3); strcpy(arg, "-j"); strcpy(arg + strlen(arg), cpus); insert_arg(argv, argc++, 1, arg); } while (sec.make_add) { insert_arg(argv, argc++, 1, sec.make_add->arg); sec.make_add = sec.make_add->next; } break; case TYPE_CC: case TYPE_CPLUSPLUS: if (sec.ccache) { verbose(sec.verbose, "Prefixing ccache ", sec.ccache); insert_arg(argv, argc++, 0, sec.ccache); } else if (sec.distcc) { char *hosts; if (type == TYPE_CC) hosts = sec.distcc_hosts; else hosts = sec.distcplusplus_hosts; verbose(sec.verbose, "Setting DISTCC_HOSTS=", hosts); setenv("DISTCC_HOSTS", hosts, 1); verbose(sec.verbose, "Prefixing arg ", sec.distcc); insert_arg(argv, argc++, 0, sec.distcc); } case TYPE_LD: case TYPE_OTHER: break; } for (add = sec.env_add; add; add = add->next) { char *eq = strchr(add->arg, '='); if (!eq) fatal("Environment variable needs '=': ", 0, add->arg, NULL); *eq = '\0'; setenv(add->arg, eq+1, 1); } } #ifndef TESTING static void __attribute__((noreturn)) run_command(bool verbose, char *argv[]) { if (verbose) { unsigned int i; write_string(STDERR_FILENO, " ccontrol("); write_string(STDERR_FILENO, int_to_string(getpid())); write_string(STDERR_FILENO, ") execing "); for (i = 0; argv[i]; i++) { write_string(STDERR_FILENO, "'"); write_string(STDERR_FILENO, argv[i]); write_string(STDERR_FILENO, "' "); } write_string(STDERR_FILENO, "\n"); } execv(argv[0], argv); fatal("failed to exec '", errno, argv[0], "'", NULL); } #else static void __attribute__((noreturn)) run_command(bool verbose, char *argv[]) { /* Print out all TESTING= env vars. */ if (getenv("TESTING1")) printf("TESTING1='%s' ", getenv("TESTING1")); if (getenv("TESTING2")) printf("TESTING2='%s' ", getenv("TESTING2")); if (strcmp(getenv("FAILDISTCC") ?: "", "1") == 0) exit(103); if (getenv("DISTCC_HOSTS")) { printf("DISTCC_HOSTS='%s' ", getenv("DISTCC_HOSTS")); } while (argv[0]) { printf("%s ", argv[0]); argv++; } printf("\n"); exit(atoi(getenv("EXITCODE") ?: "0")); } #endif /* Fork command. */ static void fork_command(bool verbose, char *argv[]) { int pid = fork(); if (pid < 0) fatal("failed to fork", errno, NULL); if (pid == 0) run_command(verbose, argv); verbose(verbose, "Forked ", int_to_string(pid)); } /* Wait for forked command */ static int wait_for_child(bool verbose) { int status; wait(&status); status = WIFEXITED(status) ? WEXITSTATUS(status) : 255; verbose(verbose, "Child returned ", int_to_string(status)); return status; } static bool file_altered(const char *configname, const struct stat *st) { struct stat st2; stat(configname, &st2); return st2.st_ctime != st->st_ctime || st2.st_mtime != st->st_mtime || st2.st_ino != st->st_ino; } static void write_item(const char *a, const char *b) { if (b) { write_string(STDOUT_FILENO, "\t"); write_string(STDOUT_FILENO, a); write_string(STDOUT_FILENO, b); write_string(STDOUT_FILENO, "\n"); } } /* This list is backwards. Print it forwards. */ static void print_add(const char *str, struct add *add) { if (!add) return; print_add(str, add->next); write_item(str, add->arg); } static void print_section(const char *dir, struct section sec) { write_string(STDOUT_FILENO, "["); write_string(STDOUT_FILENO, dir); write_string(STDOUT_FILENO, "]\n"); write_item("cc = ", sec.names[TYPE_CC]); write_item("c++ = ", sec.names[TYPE_CPLUSPLUS]); write_item("make = ", sec.names[TYPE_MAKE]); write_item("ld = ", sec.names[TYPE_LD]); write_item("ccache = ", sec.ccache); if (sec.distcc && (sec.distcc_hosts || sec.distcplusplus_hosts)) { write_item("distcc = ", sec.distcc); write_item("distcc-hosts = ", sec.distcc_hosts); if (sec.distcplusplus_hosts_set) write_item("distc++-hosts = ",sec.distcplusplus_hosts); } print_add("add make = ", sec.make_add); print_add("add env = ", sec.env_add); if (sec.no_parallel && streq(sec.no_parallel, "")) write_item("no-parallel", ""); else write_item("no-parallel = ", sec.no_parallel); if (sec.cpus != 1) write_item("cpus = ", int_to_string(sec.cpus)); if (sec.verbose) write_item("verbose", ""); if (sec.nice != 10) write_item("nice = ", int_to_string(sec.nice)); write_item("lock-file = ", sec.lock_file); } static bool is_preprocess(char *argv[]) { char **arg; for (arg = argv; *arg; arg++) { if (streq(*arg, "-E")) return true; } return false; } int main(int orig_argc, char *orig_argv[]) { enum type type; struct section sec; int fd, ret, argc; bool nodistcc = false, noccache = false, ccache_then_distcc = false; struct stat st; char configname[PATH_MAX]; char dirname[PATH_MAX]; char *new_argv[orig_argc + 23], **argv; undofn_t undo; /* Have we already done ccache? */ if (getenv("CCONTROL_LOCK_FD")) { /* Ccache calls us once to pre-process. */ if (is_preprocess(orig_argv+1)) run_command(false, orig_argv+1); /* Write to fd to make grandparent (ccontrol) drop lock */ if (write(atoi(getenv("CCONTROL_LOCK_FD")), "#", 1) != 1) fatal("Could not write to ccontrol lock fd", errno, NULL); unsetenv("CCACHE_PREFIX"); unsetenv("CCONTROL_LOCK_FD"); noccache = true; } strcpy(configname, getenv("HOME")); strcat(configname, "/.ccontrol/config"); again_restore_args: /* Make room to add args. */ argc = orig_argc; memcpy(new_argv, orig_argv, (argc + 1)*sizeof(argv[0])); argv = new_argv; #ifdef TESTING if (getenv("ARGV0")) argv[0] = getenv("ARGV0"); #endif getcwd(dirname, sizeof(dirname)); if (strends(argv[0], "ccontrol")) { if (argv[1] && strstarts(argv[1], "--section=")) { strcpy(dirname, argv[1] + strlen("--section=")); argv++; argc--; } if (argv[1] && (streq(argv[1], "--version") || streq(argv[1], "-V"))) { __verbose("version " VERSION, NULL); exit(0); } if (!argv[1]) { print_section(dirname, read_config(configname, dirname, open(configname, O_RDWR))); exit(0); } argv++; argc--; } type = what_am_i(argv); again: /* Since we later grab an exclusive lock on this, must be writable */ fd = open(configname, O_RDWR); /* This handles open failure if fd < 0. */ sec = read_config(configname, dirname, fd); fstat(fd, &st); /* Run low priority; people like to use apps while compiling. */ setpriority(PRIO_PROCESS, 0, sec.nice); if (noccache) { verbose(sec.verbose, "ccache suppressed this time"); sec.ccache = NULL; } /* Check we really distcc if asked to. */ if (sec.distcc) { if (nodistcc || type == TYPE_LD || type == TYPE_MAKE) sec.distcc = NULL; else if (sec.ccache) { /* Don't put this in command line, but see below */ sec.distcc = NULL; ccache_then_distcc = true; } else { if (type == TYPE_CC && !sec.distcc_hosts) sec.distcc = NULL; else if (type == TYPE_CPLUSPLUS && !sec.distcplusplus_hosts) sec.distcc = NULL; else if (!can_distcc(argv)) { verbose(sec.verbose, "Cannot distcc this"); sec.distcc = NULL; } } } /* Grab lock returns undofn if it slept: restat config file. This * decreases latency when config file changed. */ undo = grab_lock(sec.lock_fd, sec, type); if (undo && file_altered(configname, &st)) { /* If we had distcc disabled, try again. */ nodistcc = 0; close(sec.lock_fd); undo(); goto again; } adjust_args_and_environment(sec, type, argv, argc); if (ccache_then_distcc) { /* We need to hold slow lock until either ccache pulls from cache and exits, OR ccontrol is invoked from ccache (cache miss). */ char c; int fd[2]; if (pipe(fd) != 0) fatal("Failed to create pipe", errno, NULL); verbose(sec.verbose, "Setting CCACHE_PREFIX=ccontrol"); setenv("CCONTROL_LOCK_FD", int_to_string(fd[1]), 1); setenv("CCACHE_PREFIX", "ccontrol", 1); fork_command(sec.verbose, argv); close(fd[1]); /* This will return when child exits, OR writes manually. */ if (read(fd[0], &c, 1) == 1) verbose(sec.verbose, "Grandchild said to drop lock"); else verbose(sec.verbose, "I guess ccache exited"); undo(); exit(wait_for_child(sec.verbose)); } if (!sec.distcc) run_command(sec.verbose, argv); /* We have to wait, to make sure distcc actually distributes, * otherwise we end up running 20 compiles locally. */ setenv("DISTCC_FALLBACK", "0", 1); fork_command(sec.verbose, argv); ret = wait_for_child(sec.verbose); if (ret == 103 || ret == 116 || ret == 107) { /* distcc failed to distribute: run single-thread. */ verbose(sec.verbose, "distcc failed, retrying without"); close(sec.lock_fd); undo(); nodistcc = 1; unsetenv("CCACHE_PREFIX"); #ifdef TESTING unsetenv("FAILDISTCC"); #endif while (sec.env_add) { unsetenv(sec.env_add->arg); sec.env_add = sec.env_add->next; } goto again_restore_args; } return ret; } ccontrol-1.0.orig/ccontrol.1.txt0000644000000000000000000002014511256611104015354 0ustar rootrootCCONTROL(1) =========== Rusty Russell v0.9 5 January 2006 NAME ---- ccontrol - wrapper to control distcc, ccache and more SYNOPSIS -------- 'gcc' ... 'cc' ... 'c++' ... 'make' ... 'ld' ... 'ccontrol' [--section=
] ... 'ccontrol' [--section=
] DESCRIPTION ----------- The ccontrol(1) program takes over the roles of the compiler and linker, and reads a configuration file to decide what to do before invoking them. This is particularly useful for centralized control over commands and options, such as enabling distcc(1) and ccache(1). When ccontrol(1) is invoked under its own name with no arguments, it prints out the settings which apply in this directory (unless '--section' is specified). Versions are named after the last person to report a bug. OPTIONS ------- Normally ccontrol(1) is invoked as a symboling link to cc, make, etc, so it can identify what is being invoked by examining its own name. It can also be invoked under its own name, in which case ccontrol-specific arguments can be supplied. The first non-option argument will be used to identify the invocation, eg. "ccontrol gcc ...". The following options are supported, when invoked as 'ccontrol': --section=
:: This is treated as the "current directory" for the purposes of evaluating the configuration file. As all real directories must begin with a "/" using an argument which does not, is a good way of overriding configuration for this particular invocation. CONFIGURATION FILE ------------------ ccontrol's configuration file is $HOME/.ccontrol/config. If this cannot be read (and written), your compilations will all fail. It is normal to have several different configuration files in this directory, and make default a symbolic link. SYNTAX ------ A configuration file consists of sections, led by a "[path]" header and followed by indented "name = value" entries. The first section is usually labelled "[*]" to set up the defaults. At the very least, you must set the "cc", "c++", "make" and "ld" values. ccontrol will read every section which matches the current directory, so you can override values on a per-directory basis. The "[path]" header of each section is a shell-style wildcard (see glob(7)) which indicates the directory or directories it applies to. Usually this will end in a "*" to include all subdirectories. All paths beginning with "~" are relative to the user's home directory. The following settings are available: cc:: Followed by '=' specifies the full path of the compiler to be invoked when ccontrol is invoked as "cc" or "gcc". ccontrol will fail to compile C programs if this is not set. c++:: Followed by '=' specifies the full path of the compiler to be invoked when ccontrol is invoked as "c++" or "g++". ccontrol will fail to compile C++ programs if this is not set. ld:: Followed by '=' specifies the full path of the linker to be invoked when ccontrol is invoked as "ld". ccontrol will fail to link programs if this is not set. make:: Followed by '=' specifies the full path of the binary to be invoked when ccontrol is invoked as "make". ccontrol will fail to make if this is not set. ccache:: Followed by '=' specifies the full path of "ccache", and indicates that ccache is to be used where appropriate. If followed by 'disable', or not set, ccache will not be used. distcc:: Followed by '=' specifies the full path of "distcc", and indicates that distcc is to be used where appropriate. If followed by 'disable', or not set, or distcc-hosts is not set, distcc will not be used. distcc-hosts:: Followed by '=' specifies the distcc servers to use, as per the DISTCC_HOSTS environment variable in distcc(1). Followed by 'disable' disables distcc. distc++-hosts:: The same as distcc-hosts, but only applies to C++ compilations. If not set, distcc-hosts is used. You can thus disable distcc for C++ compilations by setting "distc++-hosts disable". cpus:: Followed by '=' and a number of CPUs, set to the number of CPUs you have (the default is "1"). 'ccontrol' uses this to tune the degree of parallelism. no-parallel:: Followed by '=' and a space-separated list of wildcards, suppresses parallel make for any make target matching one of those. This option is needed because ccontrol(1) usually forces make(1) to perform all actions in parallel, but this can be confusing when an error occurs, and breaks poorly-written makefiles. Followed by 'disable', enables parallel make for all targets: this is useful to re-enable parallel make in a subdirectory. nice:: Followed by '=' and a priority level from -19 to 20, causes ccontrol to try to set its priority to this value. Default is 10. include:: Followed by '=' specifies a file to include at the current point. The effect is exactly as if the contents of the included file were literally inserted. Can be used at file level to include sections. Can also be used within sections to include section fragments. add make:: Followed by '=' specifies an argument to be added to each invocation of 'make'. This can be specified multiple times to add multiple arguments. Followed by 'disable' removes any arguments previously specified. add env:: Followed by '=' specifies an environment variable to be set, such as "add env = CCACHE_DIR=/tmp". This can be specified multiple times to set multiple environment variables. Followed by 'disable' removes any arguments previously specified. verbose:: By itself, indicates that ccontrol(1) is to spit lots of crap out to standard error about what it's doing to your innocent command line. lock-file:: Specify a particular lock file to use. EXAMPLES -------- This is the minimal configuration file: ......................... [*] cc = /usr/bin/gcc c++ = /usr/bin/g++ ld = /usr/bin/ld make = /usr/bin/make ......................... If you have multiple locations (such as a laptop) it is common to have a "global" file which is included from every configuration file, like so: ......................... # Configuration file for when I'm at work. Lots of distcc hosts! include = ~/.ccontrol/global [*] distcc-hosts = snab swarm1 swarm3 swarm4 swarm5 fandango2 mingo distc++-hosts = snab mingo ......................... Here is a complete configuration file with several common scenarios: ......................... [*] cc = /usr/bin/gcc-4.0 c++ = /usr/bin/g++-4.0 ld = /usr/bin/ld make = /usr/bin/make # Comment this back in for debugging # verbose distcc = /usr/bin/distcc distcc-hosts = snab swarm1 swarm3 swarm4 swarm5 fandango2 mingo distc++-hosts = snab mingo ccache = /usr/bin/ccache # make check should not generally be run in parallel no-parallel = check # Wesnoth doesn't compile with g++ 4.0 [*wesnoth*] c++ = /usr/bin/g++-3.4 # Stupid third-party modules don't build in parallel. [/usr/src/modules/*] no-parallel = * # Using distcc when testing module-init-tools causes strange effects. [*module-init-tools*/tests/*] distcc disable ......................... BUGS ---- The ~/.ccontrol/config file must be writable: ccontrol(1) needs to get an exclusive write lock on it, which means it needs to open the file for writing. Use 'include' to include read-only files. ccontrol will not immediately notice a change in included files, only in the toplevel file (ccontrol re-reads the config file if it changed while ccontrol was trying to grab a lock). The Linux (<= 2.6.15) cpufreq 'ondemand' governor (common on laptops) will not increase CPU speed when using ccontrol(1), because ccontrol re-nices compilations. This can make builds 2-3 times slower. Either use another governor, or tell 'ondemand' to ignore nice values: ......................... echo 1 > /sys/devices/system/cpu/cpu0/cpufreq/ondemand/ignore_nice ......................... If your code doesn't compile, ccontrol can only make it not compile faster. AUTHOR ------ Written by Rusty Russell LICENSE ------- Copyright (C) 2005 Rusty Russell. Free use of this software is granted under the terms of the GNU General Public License (GPL). SEE ALSO -------- make(1), cc(1), c++(1), ld(1), distcc(1), ccache(1), glob(7), cpufreq-set(1) ccontrol-1.0.orig/ccontrol.10000644000000000000000000002154311256611145014546 0ustar rootroot.\" Title: ccontrol .\" Author: .\" Generator: DocBook XSL Stylesheets v1.73.2 .\" Date: 09/24/2009 .\" Manual: .\" Source: .\" .TH "CCONTROL" "1" "09/24/2009" "" "" .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .SH "NAME" ccontrol \- wrapper to control distcc, ccache and more .SH "SYNOPSIS" \fIgcc\fR \&... .sp \fIcc\fR \&... .sp \fIc++\fR \&... .sp \fImake\fR \&... .sp \fIld\fR \&... .sp \fIccontrol\fR [\-\-section=
] \&... .sp \fIccontrol\fR [\-\-section=
] .sp .SH "DESCRIPTION" The ccontrol(1) program takes over the roles of the compiler and linker, and reads a configuration file to decide what to do before invoking them\&. This is particularly useful for centralized control over commands and options, such as enabling distcc(1) and ccache(1)\&. .sp When ccontrol(1) is invoked under its own name with no arguments, it prints out the settings which apply in this directory (unless \fI\-\-section\fR is specified)\&. .sp Versions are named after the last person to report a bug\&. .sp .SH "OPTIONS" Normally ccontrol(1) is invoked as a symboling link to cc, make, etc, so it can identify what is being invoked by examining its own name\&. It can also be invoked under its own name, in which case ccontrol\-specific arguments can be supplied\&. The first non\-option argument will be used to identify the invocation, eg\&. "ccontrol gcc \&..."\&. .sp The following options are supported, when invoked as \fIccontrol\fR: .PP \-\-section=
.RS 4 This is treated as the "current directory" for the purposes of evaluating the configuration file\&. As all real directories must begin with a "/" using an argument which does not, is a good way of overriding configuration for this particular invocation\&. .RE .SH "CONFIGURATION FILE" ccontrol\'s configuration file is $HOME/\&.ccontrol/config\&. If this cannot be read (and written), your compilations will all fail\&. It is normal to have several different configuration files in this directory, and make default a symbolic link\&. .sp .SH "SYNTAX" A configuration file consists of sections, led by a "[path]" header and followed by indented "name = value" entries\&. The first section is usually labelled "[*]" to set up the defaults\&. At the very least, you must set the "cc", "c++", "make" and "ld" values\&. .sp ccontrol will read every section which matches the current directory, so you can override values on a per\-directory basis\&. The "[path]" header of each section is a shell\-style wildcard (see glob(7)) which indicates the directory or directories it applies to\&. Usually this will end in a "*" to include all subdirectories\&. .sp All paths beginning with "~" are relative to the user\'s home directory\&. .sp The following settings are available: .PP cc .RS 4 Followed by \fI=\fR specifies the full path of the compiler to be invoked when ccontrol is invoked as "cc" or "gcc"\&. ccontrol will fail to compile C programs if this is not set\&. .RE .PP c++ .RS 4 Followed by \fI=\fR specifies the full path of the compiler to be invoked when ccontrol is invoked as "c" or "g"\&. ccontrol will fail to compile C++ programs if this is not set\&. .RE .PP ld .RS 4 Followed by \fI=\fR specifies the full path of the linker to be invoked when ccontrol is invoked as "ld"\&. ccontrol will fail to link programs if this is not set\&. .RE .PP make .RS 4 Followed by \fI=\fR specifies the full path of the binary to be invoked when ccontrol is invoked as "make"\&. ccontrol will fail to make if this is not set\&. .RE .PP ccache .RS 4 Followed by \fI=\fR specifies the full path of "ccache", and indicates that ccache is to be used where appropriate\&. If followed by \fIdisable\fR, or not set, ccache will not be used\&. .RE .PP distcc .RS 4 Followed by \fI=\fR specifies the full path of "distcc", and indicates that distcc is to be used where appropriate\&. If followed by \fIdisable\fR, or not set, or distcc\-hosts is not set, distcc will not be used\&. .RE .PP distcc\-hosts .RS 4 Followed by \fI=\fR specifies the distcc servers to use, as per the DISTCC_HOSTS environment variable in distcc(1)\&. Followed by \fIdisable\fR disables distcc\&. .RE .PP distc++\-hosts .RS 4 The same as distcc\-hosts, but only applies to Ccompilations\&. If not set, distcc\-hosts is used\&. You can thus disable distcc for C compilations by setting "distc++\-hosts disable"\&. .RE .PP cpus .RS 4 Followed by \fI=\fR and a number of CPUs, set to the number of CPUs you have (the default is "1")\&. \fIccontrol\fR uses this to tune the degree of parallelism\&. .RE .PP no\-parallel .RS 4 Followed by \fI=\fR and a space\-separated list of wildcards, suppresses parallel make for any make target matching one of those\&. This option is needed because ccontrol(1) usually forces make(1) to perform all actions in parallel, but this can be confusing when an error occurs, and breaks poorly\-written makefiles\&. Followed by \fIdisable\fR, enables parallel make for all targets: this is useful to re\-enable parallel make in a subdirectory\&. .RE .PP nice .RS 4 Followed by \fI=\fR and a priority level from \-19 to 20, causes ccontrol to try to set its priority to this value\&. Default is 10\&. .RE .PP include .RS 4 Followed by \fI=\fR specifies a file to include at the current point\&. The effect is exactly as if the contents of the included file were literally inserted\&. Can be used at file level to include sections\&. Can also be used within sections to include section fragments\&. .RE .PP add make .RS 4 Followed by \fI=\fR specifies an argument to be added to each invocation of \fImake\fR\&. This can be specified multiple times to add multiple arguments\&. Followed by \fIdisable\fR removes any arguments previously specified\&. .RE .PP add env .RS 4 Followed by \fI=\fR specifies an environment variable to be set, such as "add env = CCACHE_DIR=/tmp"\&. This can be specified multiple times to set multiple environment variables\&. Followed by \fIdisable\fR removes any arguments previously specified\&. .RE .PP verbose .RS 4 By itself, indicates that ccontrol(1) is to spit lots of crap out to standard error about what it\'s doing to your innocent command line\&. .RE .PP lock\-file .RS 4 Specify a particular lock file to use\&. .RE .SH "EXAMPLES" This is the minimal configuration file: .sp .sp .RS 4 .nf [*] cc = /usr/bin/gcc c++ = /usr/bin/g++ ld = /usr/bin/ld make = /usr/bin/make .fi .RE If you have multiple locations (such as a laptop) it is common to have a "global" file which is included from every configuration file, like so: .sp .sp .RS 4 .nf # Configuration file for when I\'m at work\&. Lots of distcc hosts! include = ~/\&.ccontrol/global [*] distcc\-hosts = snab swarm1 swarm3 swarm4 swarm5 fandango2 mingo distc++\-hosts = snab mingo .fi .RE Here is a complete configuration file with several common scenarios: .sp .sp .RS 4 .nf [*] cc = /usr/bin/gcc\-4\&.0 c++ = /usr/bin/g++\-4\&.0 ld = /usr/bin/ld make = /usr/bin/make # Comment this back in for debugging # verbose distcc = /usr/bin/distcc distcc\-hosts = snab swarm1 swarm3 swarm4 swarm5 fandango2 mingo distc++\-hosts = snab mingo ccache = /usr/bin/ccache # make check should not generally be run in parallel no\-parallel = check # Wesnoth doesn\'t compile with g++ 4\&.0 [*wesnoth*] c++ = /usr/bin/g++\-3\&.4 # Stupid third\-party modules don\'t build in parallel\&. [/usr/src/modules/*] no\-parallel = * # Using distcc when testing module\-init\-tools causes strange effects\&. [*module\-init\-tools*/tests/*] distcc disable .fi .RE .SH "BUGS" The ~/\&.ccontrol/config file must be writable: ccontrol(1) needs to get an exclusive write lock on it, which means it needs to open the file for writing\&. Use \fIinclude\fR to include read\-only files\&. .sp ccontrol will not immediately notice a change in included files, only in the toplevel file (ccontrol re\-reads the config file if it changed while ccontrol was trying to grab a lock)\&. .sp The Linux (\(la 2\&.6\&.15) cpufreq \fIondemand\fR governor (common on laptops) will not increase CPU speed when using ccontrol(1), because ccontrol re\-nices compilations\&. This can make builds 2\-3 times slower\&. Either use another governor, or tell \fIondemand\fR to ignore nice values: .sp .sp .RS 4 .nf echo 1 > /sys/devices/system/cpu/cpu0/cpufreq/ondemand/ignore_nice .fi .RE If your code doesn\'t compile, ccontrol can only make it not compile faster\&. .sp .SH "AUTHOR" Written by Rusty Russell <\fIrusty@rustcorp\&.com\&.au\fR\&[1]> .sp .SH "LICENSE" Copyright \(co 2005 Rusty Russell\&. Free use of this software is granted under the terms of the GNU General Public License (GPL)\&. .sp .SH "SEE ALSO" make(1), cc(1), c++(1), ld(1), distcc(1), ccache(1), glob(7), cpufreq\-set(1) .sp .SH "NOTES" .IP " 1." 4 rusty@rustcorp.com.au .RS 4 \%mailto:rusty@rustcorp.com.au .RE ccontrol-1.0.orig/ccontrol-parse.c0000644000000000000000000002527611256611104015742 0ustar rootroot#include "ccontrol.h" #include #include #include #include #include #include #include "stdrusty.h" enum token_type { CC = TYPE_CC, CPLUSPLUS = TYPE_CPLUSPLUS, LD = TYPE_LD, MAKE = TYPE_MAKE, NONE, INDENT, NO_PARALLEL, CCACHE, DISTCC, DISTCC_HOSTS, DISTCPLUSPLUS_HOSTS, LEFT_BRACKET, RIGHT_BRACKET, EQUALS, NUMBER, STRING, VERBOSE, NICE, INCLUDE, CPUS, DISABLE, ADD, ENV, LOCK_FILE, END, }; #include "keywords.c" struct token { enum token_type type; struct string string; }; static unsigned int slen(struct string string) { return string.end - string.start; } /* Length up to and including char. 0 == not found. */ static unsigned int upto(struct string data, char c) { char *end; end = memchr(data.start, c, slen(data)); if (end) return end - data.start + 1; return 0; } static void __attribute__((noreturn)) parse_error(struct token tok, const char *str) { unsigned int line = 1; char *input_head = tok.string.start_of_file; /* Count what line we're on. */ while (input_head < tok.string.start) { if (*input_head == '\n') line++; input_head++; } fatal("parse error on line ", 0, int_to_string(line), ": expected ", str, NULL); } static bool swallow_whitspace(struct string *data) { bool ret = false; while (data->start[0] == ' ' || data->start[0] == '\t') { ret = true; data->start++; if (data->start == data->end) break; } return ret; } static struct string get_string(struct string *data) { struct string str; str.start_of_file = data->start_of_file; str.start = data->start; while (data->start != data->end && *data->start != '=' && *data->start != '[' && *data->start != ']' && *data->start != ' ' && *data->start != '\n' && *data->start != '\t') data->start++; str.end = data->start; return str; } /* Token takes one character. */ static struct token single_digit_token(struct string data,enum token_type type) { return ((struct token) { type, { data.start, data.start+1, data.start_of_file } }); } static struct token peek_token(struct string data) { struct token tok; unsigned int num; bool new_line = (data.start == data.start_of_file); /* We might need to skip over comments. */ while (data.start != data.end) { const struct ccontrol_command *cmd; char *start; if (data.start[0] == '\n') { new_line = true; data.start++; continue; } /* Whitespace: only relevant if after nl, followed by stuff. */ start = data.start; if (swallow_whitspace(&data) && new_line) { if (slen(data)) { if (data.start[0] == '\n' || data.start[0] == '#') continue; tok.type = INDENT; tok.string.start_of_file = data.start_of_file; tok.string.start = start; tok.string.end = data.start; return tok; } } /* Comment? Ignore to end of line. */ if (data.start[0] == '#') { num = upto(data, '\n'); if (!num) break; data.start += num; new_line = true; continue; } if (data.start[0] == '[') return single_digit_token(data, LEFT_BRACKET); if (data.start[0] == ']') return single_digit_token(data, RIGHT_BRACKET); if (data.start[0] == '=') return single_digit_token(data, EQUALS); tok.string = get_string(&data); cmd = find_keyword(tok.string.start, slen(tok.string)); if (cmd) tok.type = cmd->type; else { /* Number or string? */ unsigned int i; for (i = 0; i < slen(tok.string); i++) if (!(isdigit(tok.string.start[i]) || tok.string.start[i] == '-')) break; if (i > 0 && i == slen(tok.string)) tok.type = NUMBER; else tok.type = STRING; } return tok; } tok.type = END; tok.string = data; return tok; } static void swallow_token(struct string *data, struct token tok) { data->start = tok.string.end; } static struct token get_token(struct string *data) { struct token tok = peek_token(*data); swallow_token(data, tok); return tok; } /* Optional = . */ static struct token get_value(struct string *data) { struct token tok; tok = peek_token(*data); if (tok.type == EQUALS) { swallow_token(data, tok); return get_token(data); } tok.type = NONE; tok.string.start = data->start; tok.string.end = data->start; return tok; } static int to_int(struct token tok, int min, int max) { int num; if (tok.type != NUMBER) parse_error(tok, "'= some-number'"); num = atoi(tok.string.start); if (num < min || num > max) parse_error(tok, "'= number in valid range'"); return num; } /* '=' */ static char *get_path(struct string *data) { char *p; const char *prefix = ""; struct token tok; tok = get_value(data); if (tok.type != STRING) parse_error(tok, "'= some-path'"); if (tok.string.start[0] == '~') { prefix = getenv("HOME") ?: ""; tok.string.start++; } p = new_array(char, strlen(prefix) + slen(tok.string) + 1); memcpy(p, prefix, strlen(prefix)); memcpy(p + strlen(prefix), tok.string.start, slen(tok.string)); p[strlen(prefix)+slen(tok.string)] = '\0'; return p; } static char *get_optional_path(struct string *data) { struct token tok; tok = peek_token(*data); if (tok.type == DISABLE) { swallow_token(data, tok); return NULL; } return get_path(data); } /* 'disable' or '=' */ static char *get_to_eol(struct string *data, const char *expect) { char *p; unsigned int len; struct token tok; tok = get_token(data); if (tok.type == DISABLE) return NULL; if (tok.type != EQUALS) parse_error(tok, expect); swallow_whitspace(data); len = upto(*data, '\n'); if (!len) { struct token tok; tok.type = END; tok.string = *data; parse_error(tok, "something"); } /* Turn \n into \0. */ p = malloc(len); memcpy(p, data->start, len); p[len-1] = '\0'; /* Leave \n so we can detect indent. */ data->start += len-1; return p; } static struct add *new_add(char *arg, struct add *next) { struct add *add = malloc(sizeof(struct add)); add->arg = arg; add->next = next; return add; } static void read_section_file(const char *configname, struct section *sec); /* With thanks to Joseph Heller. */ static void read_section_section(struct string *data, struct section *sec) { struct token tok; struct add **add; char *p; while ((tok = peek_token(*data)).type == INDENT) { swallow_token(data, tok); tok = get_token(data); /* Lines are of form "x" or "x = value". */ switch (tok.type) { case NO_PARALLEL: sec->no_parallel = get_to_eol(data, "= targets"); /* In case parent set it, and we're disabling it. */ if (!sec->no_parallel) unsetenv("CCONTROL_NO_PARALLEL"); break; case CPUS: sec->cpus = to_int(get_value(data), 1, 1000000); break; case CCACHE: sec->ccache = get_optional_path(data); break; case DISTCC: sec->distcc = get_optional_path(data); break; case DISTCC_HOSTS: sec->distcc_hosts = get_to_eol(data, "= some-hosts"); if (!sec->distcplusplus_hosts_set) sec->distcplusplus_hosts = sec->distcc_hosts; break; case DISTCPLUSPLUS_HOSTS: sec->distcplusplus_hosts = get_to_eol(data, "= some-hosts"); sec->distcplusplus_hosts_set = true; break; case VERBOSE: sec->verbose = true; break; case NICE: sec->nice = to_int(get_value(data), -19, 20); break; case CC: case CPLUSPLUS: case LD: case MAKE: sec->names[tok.type] = get_path(data); break; case INCLUDE: read_section_file(get_path(data), sec); break; case ADD: tok = get_token(data); switch (tok.type) { case MAKE: add = &sec->make_add; break; case ENV: add = &sec->env_add; break; default: parse_error(tok, "make or env"); } p = get_to_eol(data, "= argument"); if (!p) *add = NULL; else *add = new_add(p, *add); break; case LOCK_FILE: sec->lock_file = get_path(data); sec->lock_fd = open(sec->lock_file, O_RDWR|O_CREAT, 0600); if (sec->lock_fd < 0) fatal("could not open lock file: ", errno, sec->lock_file, NULL); break; default: parse_error(tok, "some instruction"); } } } static void read_section_file(const char *configname, struct section *sec) { unsigned long len; struct string data; data.start_of_file = suck_file(open(configname, O_RDONLY), &len); if (!data.start_of_file) fatal("reading included file ", errno, configname, NULL); data.start = data.start_of_file; data.end = data.start_of_file + len; read_section_section(&data, sec); } static bool read_section(struct string *data, struct section *sec) { struct token tok = get_token(data); if (tok.type == END) return false; if (tok.type != LEFT_BRACKET) parse_error(tok, "'[' to start new section"); tok = get_token(data); if (tok.type != STRING) parse_error(tok, "path after '[' in section start"); sec->name = tok.string; tok = get_token(data); if (tok.type != RIGHT_BRACKET) parse_error(tok, "']' after name in section start"); read_section_section(data, sec); return true; } #ifndef PATH_MAX #define PATH_MAX 4096 #endif static bool section_matches(const char *dirname, struct section sec) { unsigned int len = slen(sec.name); char pattern[len + 2]; /* Default always matches. */ if (len == 0) return true; memcpy(pattern, sec.name.start, len); /* Append a * if not there already. */ if (pattern[len-1] == '*') pattern[len] = '\0'; else { pattern[len] = '*'; pattern[len+1] = '\0'; } return (fnmatch(pattern, dirname, 0) == 0); } static struct section get_default(int fd) { struct section def = { .cpus = 1, .nice = 10, .distcplusplus_hosts_set = false }; def.lock_fd = fd; return def; } static void read_config_file(const char *configname, int fd, const char *dirname, struct section *result) { unsigned long len; struct string data; data.start_of_file = suck_file(fd, &len); if (!data.start_of_file) fatal("reading ", errno, configname, NULL); data.start = data.start_of_file; data.end = data.start_of_file + len; /* Trivial parser. */ for (;;) { struct section sec = *result; struct token tok; tok = peek_token(data); if (tok.type == INCLUDE) { char *included; int incfd; swallow_token(&data, tok); included = get_path(&data); incfd = open(included, O_RDONLY); read_config_file(included, incfd, dirname, result); close(incfd); continue; } if (!read_section(&data, &sec)) break; if (section_matches(dirname, sec)) { if (sec.verbose) { unsigned int len = slen(sec.name); char str[len+1]; memcpy(str, sec.name.start, len); str[len] = '\0'; verbose(sec.verbose, "Using section ", str); } *result = sec; } } free(data.start_of_file); } struct section read_config(const char *configname, const char *dir, int fd) { struct section result; result = get_default(fd); read_config_file(configname, fd, dir, &result); return result; } ccontrol-1.0.orig/ccontrol-lock.c0000644000000000000000000001165510465041533015560 0ustar rootroot/* Simple code to grab a lock to restrict number of parallel processes. */ #include #include #include #include #include #include #include #include #include #include "ccontrol.h" #include "stdrusty.h" /* We place much looser limits on "fast" operations: distcc-ables and make */ #define DISTCC_LIMIT 20 /* Make is limited separately for each depth, since it can recurse. */ #define MAKE_LIMIT 3 #define IPC_KEY 0xCCD1ED static void fcntl_lock(int fd, bool lock, unsigned int offset) { struct flock fl; fl.l_type = lock ? F_WRLCK : F_UNLCK; fl.l_whence = SEEK_SET; fl.l_start = offset; fl.l_len = 1; if (fcntl(fd, F_SETLKW, &fl) < 0) fatal("cannot lock config file", errno, NULL); } /* To create an initialized semaphore, we need a lock. Use fcntl lock. */ static int make_semaphore(int fd, struct section sec, int key) { int id; fcntl_lock(fd, true, 0); id = semget(key, 1, IPC_CREAT|IPC_EXCL); if (id < 0) { int saved_errno = errno; /* We might have raced, try again. */ id = semget(key, 1, 0); errno = saved_errno; } else { union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; } semctl_arg; struct semid_ds ds; verbose(sec.verbose, "Created slow lock"); memset(&ds, 0, sizeof(ds)); ds.sem_perm.uid = geteuid(); ds.sem_perm.gid = getegid(); ds.sem_perm.mode = 0600; semctl_arg.buf = &ds; if (semctl(id, 0, IPC_SET, semctl_arg) < 0) fatal("cannot set semaphore permissions", errno, NULL); semctl_arg.val = sec.cpus; if (semctl(id, 0, SETVAL, semctl_arg) < 0) fatal("cannot set semaphore value", errno, NULL); } fcntl_lock(fd, false, 0); return id; } /* Semaphores give us exact control over rate, but SEM_UNDO space is * often limited (not on Linux tho AFAICT). */ static void grab_sem(int fd, struct section sec) { struct sembuf sop; int id, key; key = IPC_KEY + geteuid(); id = semget(key, 1, 0); if (id < 0 && errno == ENOENT) id = make_semaphore(fd, sec, key); if (id < 0) fatal("cannot get semaphore", errno, NULL); again: sop.sem_num = 0; sop.sem_op = -1; sop.sem_flg = SEM_UNDO; if (semop(id, &sop, 1) != 0) { if (errno == EINTR) goto again; fatal("cannot decrement semaphore", errno, NULL); } } /* Since we have lots of these, we use fcntl locks as an approximate * means to limit them. */ static void grab_fcntl_lock(int fd, unsigned int base, unsigned int max) { srand(getpid()); fcntl_lock(fd, true, base + rand()%max); } static void set_lock_envvar(char depth) { char locktype[2]; locktype[0] = depth; locktype[1] = '\0'; setenv("CCONTROL_LOCK", locktype, 1); } static void undo_decrement_envvar(void) { char *depth = getenv("CCONTROL_LOCK"); set_lock_envvar(depth[0] - 1); } void drop_slow_lock(void) { struct sembuf sop; int id, key; assert(getenv("CCONTROL_LOCK")); assert(getenv("CCONTROL_LOCK")[0] == '0'); key = IPC_KEY + geteuid(); id = semget(key, 1, 0); if (id < 0) fatal("cannot re-get semaphore", errno, NULL); sop.sem_num = 0; sop.sem_op = 1; sop.sem_flg = SEM_UNDO; if (semop(id, &sop, 1) != 0) fatal("cannot increment semaphore", errno, NULL); unsetenv("CCONTROL_LOCK"); } static void undo_unset_envvar(void) { unsetenv("CCONTROL_LOCK"); } static void undo_nothing(void) { } undofn_t grab_lock(int fd, struct section sec, enum type type) { char *lock = getenv("CCONTROL_LOCK"); unsigned int distcc_lim, make_off, make_lim; verbose(sec.verbose, "Grabbing lock for ", type == TYPE_CC ? "CC" : type == TYPE_CPLUSPLUS ? "C++" : type == TYPE_MAKE ? "MAKE" : type == TYPE_LD ? "LD" : "UNKNOWN"); /* If we already have slow lock, don't grab again (gcc calls ld). */ if (lock && lock[0] == '0') { verbose(sec.verbose, "Already got it"); return undo_nothing; } /* Position 0 is used to initialize slow semaphore. * Next range is used by distcc-able builds. * Then a series of ranges for each makefile depth. */ make_off = distcc_lim = sec.cpus*DISTCC_LIMIT; make_lim = sec.cpus*MAKE_LIMIT; /* Make can run in parallel. */ if (type == TYPE_MAKE) { unsigned int depth = 0; /* Each level of make limited separately (tends to recurse) */ if (lock) depth = lock[0] - 'A' + 1; verbose(sec.verbose, "Getting fast lock for make"); /* No locks for top-level make. */ if (lock) grab_fcntl_lock(fd, 1 + make_off + depth * make_lim, make_lim); set_lock_envvar('A' + depth); return undo_decrement_envvar; } else if (!sec.distcc) { verbose(sec.verbose, "Getting slow lock for non-distcc"); /* LD or non-distcc compile. Single file! */ grab_sem(fd, sec); set_lock_envvar('0'); return drop_slow_lock; } else { /* gcc & g++ are limited together. */ if (lock && lock[0] == '1') fatal("called myself?", 0, NULL); verbose(sec.verbose, "Getting fast lock for compile"); grab_fcntl_lock(fd, 1, distcc_lim); set_lock_envvar('1'); return undo_unset_envvar; } } ccontrol-1.0.orig/ccontrol-init.1.txt0000644000000000000000000000157110410404531016312 0ustar rootrootCCONTROL-INIT(1) ================ Rusty Russell v0.3, 17 September 2005 NAME ---- ccontrol-init - initialize ccontrol setup SYNOPSIS -------- 'ccontrol-init' [file] DESCRIPTION ----------- The ccontrol-init(1) program creates a sensible default configuration file for ccontrol, and then installs ccontrol in your $HOME/bin directory if you have one (otherwise, you can simply put /usr/lib/ccontrol early in your path). If no filename is specified on the command line, $HOME/.ccontrol/default is used. If $DISTCC_HOSTS is set, then that is used as the distcc host list, otherwise a simple scan of the local network is attempted to find likely hosts. AUTHOR ------ Written by Rusty Russell LICENSE ------- Copyright (C) 2005 Rusty Russell. Free use of this software is granted under the terms of the GNU General Public License (GPL). ccontrol-1.0.orig/ccontrol-init.10000644000000000000000000000214310424063421015474 0ustar rootroot.\"Generated by db2man.xsl. Don't modify this, modify the source. .de Sh \" Subsection .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Ip \" List item .br .ie \\n(.$>=3 .ne \\$3 .el .ne 3 .IP "\\$1" \\$2 .. .TH "CCONTROL-INIT" 1 "" "" "" .SH NAME ccontrol-init \- initialize ccontrol setup .SH "SYNOPSIS" \fIccontrol\-init\fR [file] .SH "DESCRIPTION" The ccontrol\-init(1) program creates a sensible default configuration file for ccontrol, and then installs ccontrol in your $HOME/bin directory if you have one (otherwise, you can simply put /usr/lib/ccontrol early in your path)\&. If no filename is specified on the command line, $HOME/\&.ccontrol/default is used\&. If $DISTCC_HOSTS is set, then that is used as the distcc host list, otherwise a simple scan of the local network is attempted to find likely hosts\&. .SH "AUTHOR" Written by Rusty Russell .SH "LICENSE" Copyright © 2005 Rusty Russell\&. Free use of this software is granted under the terms of the GNU General Public License (GPL)\&. ccontrol-1.0.orig/ccontrol-init0000755000000000000000000000664610410404531015350 0ustar rootroot#! /bin/sh set -e find() { echo -n Looking for $1... >&2 ANS=`which $2 || true` if [ -z "$ANS" ]; then if [ $# = 3 ]; then echo None found\! Giving up. >&2 exit 1 fi echo "Not found." >&2 elif [ -L "$ANS" ]; then echo -n "$ANS"... >&2 while [ -L "$ANS" ]; do LINK=`readlink $ANS` case $LINK in /*) ANS=$LINK;; *) ANS=`dirname $ANS`/$LINK;; esac done echo which is a link to $ANS >&2 else echo Found "$ANS" >&2 fi echo $ANS } test_net() { for host; do echo -n . >&2 if ifconfig | fgrep -q $host; then :; elif echo | telnet $host distcc 2>&1 | grep -q Connected; then $GETENT hosts $host | awk '{print $2}' fi done } ping_network() { # There are two kinds of ping (netkit ping and iputils ping) (ping -w1 -b $1.255 2>/dev/null; ping -c2 $1.255 2>/dev/null) | fgrep "from $1." | sed 's/.*from \([0-9.]*\):.*/\1/' | sort -u } scan_for_distcc() { NET=`route -n | grep '^[123456789]' | awk '{print $1}' | head -1` case "$NET" in *[123456789].0) echo -n scanning network $NET... set +e NET=`echo $NET | sed 's/\.0$//'` HOSTS=`ping_network $NET` DISTCC_HOSTS=`test_net $HOSTS` set -e echo $DISTCC_HOSTS if [ -z "$DISTCC_HOSTS" ]; then echo no hosts found, disabling distcc. return 1 fi ;; *) echo no obvious network to scan, disabling distcc. return 1 ;; esac return 0 } # Let's not confuse ourselves: take ~/bin and ccontrol dirs out of path. PATH=`echo :${PATH}: | sed -e "s,:~/bin:,," -e "s,:$HOME/bin:,," -e "s,:[^:]*ccontrol[^:]*,," | sed -e 's/^://' -e 's/:$//'` DEST=${1:-$HOME/.ccontrol/default} CC=`find "C compiler" cc needed` CPLUSPLUS=`find "C++ compiler" c++ needed` LD=`find "linker" ld needed` MAKE=`find "make" make needed` DISTCC=`find "distcc" distcc` CCACHE=`find "ccache" ccache` GETENT=`which getent || echo echo` echo -n Counting CPUs on this machine... PROCESSORS=`grep -cw ^processor < /proc/cpuinfo` 2>/dev/null if [ "$PROCESSORS" -gt 0 ] 2> /dev/null; then echo found $PROCESSORS else echo too hard, assuming 1 PROCESSORS=1 fi if [ -n "$DISTCC" ]; then echo -n Looking for distcc hosts... if [ -n "$DISTCC_HOSTS" ]; then echo $DISTCC_HOSTS else if scan_for_distcc; then :; else DISTCC="" fi fi fi if [ "$DEST" = $HOME/.ccontrol/default ]; then if [ -f $HOME/.ccontrol ]; then echo Found old .ccontrol file, moving to .ccontrol.old mv -i $HOME/.ccontrol $HOME/.ccontrol.old if [ -f $HOME/.ccontrol ]; then echo Aborting. exit 1 fi fi fi DIRNAME=`dirname "$DEST"` if [ ! -d "$DIRNAME" ]; then echo Creating $DIRNAME directory mkdir -p "$DIRNAME" elif [ -L "$DEST" ]; then rm $DEST elif [ -f "$DEST" ]; then echo Moving existing "$DEST" to "$DEST".old mv -i "$DEST" "$DEST".old if [ -f $DEST ]; then echo Aborting. exit 1 fi fi echo '[*]' > "$DEST" echo " cc = $CC" >> "$DEST" echo " c++ = $CPLUSPLUS" >> "$DEST" echo " ld = $LD" >> "$DEST" echo " make = $MAKE" >> "$DEST" if [ $PROCESSORS -gt 1 ]; then echo " cpus = $PROCESSORS" >> "$DEST" fi if [ -n "$DISTCC" ]; then echo " distcc = $DISTCC" >> "$DEST" echo " distcc-hosts = `echo $DISTCC_HOSTS`" >> "$DEST" fi if [ -n "$CCACHE" ]; then echo " ccache = $CCACHE" >> "$DEST" fi echo echo Created "$DEST" if [ "$DEST" = $HOME/.ccontrol/default ]; then ln -s $DEST ~/.ccontrol/config echo Created symlink ~/.ccontrol/config fi ccontrol-1.0.orig/ccontrol-identify.c0000644000000000000000000000441211374714066016444 0ustar rootroot/* Routine to figure out what is running, whether distcc will parallelise. */ #include "ccontrol.h" #include "stdrusty.h" #include "extensions.c" #include static bool is_source(const char *sfile) { const char *dot; dot = strrchr(sfile, '.'); if (!dot) return false; return find_extension(dot+1, strlen(dot+1)) != NULL; } /* Tries to guess whether distcc will be able to distribute it. */ bool can_distcc(char **argv) { unsigned int i; char *a, *input_file = NULL, *output_file = NULL; bool seen_opt_s = false, seen_opt_c = false; /* Stolen from distcc's dcc_scan_args, Copyright (C) 2002, 2003, 2004 by Martin Pool */ for (i = 1; (a = argv[i]); i++) { if (a[0] != '-') { if (is_source(a)) { if (input_file) return false; input_file = a; } else if (strends(a, ".o")) { got_output: if (output_file) return false; output_file = a; } } else { if (streq(a, "-E")) { return false; } else if (streq(a, "-MD") || streq(a, "-MMD") || streq(a, "-MG") || streq(a, "-MP")) { ; } else if (streq(a, "-MF") || streq(a, "-MT") || streq(a,"-MQ")) { i++; } else if (a[1] == 'M') { return false; } else if (strstr(a, "-Wa,")) { if (strstr(a, ",-a") || strstr(a, "--MD")) return false; } else if (strstarts(a, "-specs=")) { return false; } else if (streq(a, "-S")) { seen_opt_s = true; } else if (streq(a, "-fprofile-arcs") || streq(a, "-ftest-coverage") || streq(a, "-frepo")) { return false; } else if (strstarts(a, "-x")) { return false; } else if (streq(a, "-c")) { seen_opt_c = true; } else if (streq(a, "-o")) { a = argv[++i]; goto got_output; } else if (strstarts(a, "-o")) { a += 2; /* skip "-o" */ goto got_output; } } } if (!seen_opt_c && !seen_opt_s) return false; if (!input_file) return false; return true; } /* What am I? */ enum type what_am_i(char *argv[]) { const char *basename = strrchr(argv[0], '/'); if (!basename) basename = argv[0]; if (strstr(basename, "cc")) return TYPE_CC; else if (strstr(basename, "++")) return TYPE_CPLUSPLUS; else if (strstr(basename, "ld")) return TYPE_LD; else if (strstr(basename, "make")) return TYPE_MAKE; else return TYPE_OTHER; } ccontrol-1.0.orig/TODO0000644000000000000000000000005110410404531013306 0ustar rootroot* GUI to set/manipulate .ccontrol/ files ccontrol-1.0.orig/TIME-lockbreak0000644000000000000000000000044710424331300015241 0ustar rootrootlockbreak + distccmon: real 2m38.739s user 0m58.967s sys 0m18.567s lockbreak: real 2m42.981s user 1m4.416s sys 0m19.706s no lockbreak + distccmon: real 2m58.364s user 1m23.504s sys 0m23.019s no lockbreak: real 2m18.197s user 1m6.078s sys 0m19.869s ccontrol-1.0.orig/README0000644000000000000000000000552410410404531013510 0ustar rootrootCCONTROL: Control your compiles =============================== Distcc is great, so is ccache. But they are a PITA to control, as you need to change environment variables and/or set CC, add -j to make lines, etc. Explicit control of which compiler you want to use is hard. Enter ccontrol: it takes over invocations of make(1), gcc(1), cc(1), g++(1), c++(1) and ld(1), and decides intelligently what to do, based on a config file in the .ccontrol subdirectory of your home directory. In particular, it allows different options depending on which directory it is run in: you can suppress parallel builds for projects (or particular make targets) which don't have correctly-working Makefiles, use a different compiler, or not use ccache or distcc, depending on where you are. GETTING STARTED --------------- I recommend installing dietlibc if you can; the resulting (static) binary is smaller and cuts a few percent off compile times. If you want to install ccontrol in your home directory (eg. ~/bin), use "./configure --bindir=$HOME/bin". Otherwise, a simple "./configure" will place the binaries in /usr/local/lib/ccontrol, which you will have to put in your PATH. The type "make" and "make install". Now run "./ccontrol-init" to create a ".ccontrol/default" file. If you want to avoid probing for distcc hosts on the local network, set DISTCC_HOSTS first, eg: DISTCC_HOSTS="one two three" ./ccontrol-init TROUBLESHOOTING --------------- Use distccmon-gnome (or "watch distccmon-text") to check if distcc is working as expected. Use "watch ccache -s" to check if ccache is working as expected. If top reveals lots of "cc1", or your load is over 10, make sure your Makefiles are invoking "gcc" or "cc" (ie. ccontrol), not the compiler directly. If your configuration file isn't working, try putting "verbose" in the section you think should apply, or if all else fails, in the "[*]" section. MORE FUN -------- You can read the ccontrol(1) man page and configure your .ccontrol/config file, which is usually a symlink to .ccontrol/default. HACKING ------- Feedback on documentation, problems you had, and distribution variations gratefully accepted! Changes to ccontrol code will only be accepted with an accompanying testcase or a really good excuse. To make changes, install the following packages: asciidoc (to regenerate the manpages) xmlto (to regenerate the manpages) gperf (to regenerate the keyword list) And I recommend the following: mercurial (to view and commit changes) valgrind (to improve testsuite thoroughness) After your changes, configure with "./configure --enable-debug" and run "make check". Testcases are fairly simple to write: the comments at the top control how the test is activated, and the output expected. Use "hg bundle" to send a convenient ball of changes back to me for merging. Happy hacking! Rusty Russell ccontrol-1.0.orig/Makefile.in0000644000000000000000000000473711256612510014711 0ustar rootrootCC=@CC@ CFLAGS=@CFLAGS@ C_FILES:=ccontrol.c ccontrol-parse.c ccontrol-lock.c ccontrol-identify.c stdrusty.c GPERF_C_FILES:=extensions.c keywords.c # Everything depends on makefile, headers, gperf files ALLFILES=$(C_FILES) $(GPERF_C_FILES) Makefile stdrusty.h ccontrol.h BINDIR=@BINDIR@ LIBDIR=@LIBDIR@ MANDIR=@MANDIR@ XMLTO=@XMLTO@ ASCIIDOC=@ASCIIDOC@ VALGRIND=@VALGRIND@ VERSION=@VERSION@ LINKS=$(foreach l,gcc cc g++ c++ ld make,$(prefix)$(LIBDIR)/$l) ccontrol: $(ALLFILES) ccontrol.1 $(CC) $(CFLAGS) -DVERSION='"$(VERSION) (@VERSION_NAME@)"' -o $@ $(C_FILES) Makefile: Makefile.in configure config.status ./configure --reconfigure config.status: @echo Please rerun ./configure.; exit 1 ccontrol_test: $(ALLFILES) $(CC) $(CFLAGS) -DVERSION='"$(VERSION) (@VERSION_NAME@)"' -DTESTING -o $@ $(C_FILES) $(GPERF_C_FILES): %.c: %.gperf gperf $< > $@ install: installbin installlib installman installgui $(prefix)$(BINDIR) $(prefix)$(MANDIR)/man1 $(prefix)$(LIBDIR): mkdir -p $@ installgui: $(prefix)$(LIBDIR)/ccontrol-key.png $(prefix)$(BINDIR)/gccontrol.py $(prefix)$(LIBDIR)/ccontrol-key.png: gui/ccontrol-key.png $(prefix)$(LIBDIR) cp $< $@ $(prefix)$(BINDIR)/gccontrol.py: gui/gccontrol.py $(prefix)$(BINDIR) cp $< $@ installman: $(prefix)$(MANDIR)/man1 $(prefix)$(MANDIR)/man1/ccontrol.1 $(prefix)$(MANDIR)/man1/ccontrol-init.1 installbin: $(prefix)$(BINDIR)/ccontrol $(prefix)$(BINDIR)/ccontrol-init $(prefix)$(BINDIR)/ccontrol: ccontrol $(prefix)$(BINDIR) cp $< $@.tmp && mv $@.tmp $@ $(prefix)$(BINDIR)/ccontrol-init: ccontrol-init cp $< $@ installlib: $(LINKS) $(LINKS): %: ccontrol $(prefix)$(LIBDIR) ln -sf $(BINDIR)/ccontrol $@ $(prefix)$(MANDIR)/man1/ccontrol.1: ccontrol.1 cp $< $@ $(prefix)$(MANDIR)/man1/ccontrol-init.1: ccontrol-init.1 cp $< $@ uninstall: rm -f $(prefix)$(BINDIR)/ccontrol $(prefix)$(BINDIR)/ccontrol-init $(LINKS) $(prefix)$(MANDIR)/man1/ccontrol.1 $(prefix)$(MANDIR)/man1/ccontrol-init.1 check: ccontrol_test @unset MAKEFLAGS; testsuite/test.sh --valgrind=$(VALGRIND) dist: clean rm -f Makefile config.status DIR=`basename \`pwd\``; cd .. && ln -sf $$DIR ccontrol-$(VERSION) && tar -c -v -j --exclude='*~' --exclude='*.orig' --exclude='*.rej' -f ccontrol-$(VERSION).tar.bz2 ccontrol-$(VERSION)/* ccontrol-$(VERSION)/.??* && rm -f ccontrol-$(VERSION) clean: rm -f *.o ccontrol ccontrol_test %: %.xml $(XMLTO) man $*.xml %.xml: %.txt $(ASCIIDOC) -d manpage -b docbook $*.txt distclean: clean rm -f Makefile config.status ccontrol.1 ccontrol-init.1 ccontrol-1.0.orig/COPYING0000644000000000000000000004310510410404531013660 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ccontrol-1.0.orig/CHANGES0000644000000000000000000000535011374715617013644 0ustar rootrootChanges for 1.0 --------------- Configuration language changes: - config file now called ~/.ccontrol/config not default - '= off' now replaced with 'disable', can be used in more places - 'parallel' keyword gone: use 'no-parallel disable' - 'no-parallel' by itself no longer valid, use 'no-parallel = *' - 'nice' keyword to change priority - 'lock-file' keyword to have some builds not block others. - Paths (include, distcc, etc) expand ~ so you can use "~/bin/gcc". - Unknown argv[0] is passed through, so you can ccontrol anything. Changes for 0.9 --------------- New features: - Gnome panel control-area applet which sets symlink Other changes: - Fix PowerPC (and other?) locking (patch from Tony Breeds) - Intercept ccache misses, so we don't have to guess chance of hit - Fix error reporting (also patched by Olof Johansson) Changes for 0.8 --------------- Other changes: - fix bug where all complations done at once if distcc failed. - invoking ccontrol by itself gives current settings. - invoking ccontrol -V/--version gives current version. - debian packaging - fix bug where ccontrol didn't reread config file when re-symlinked Changes for 0.7 --------------- Configuration language changes: - "add make" to add makefile arguments (eg. for cross-compiling). Other changes: - ccontrol can now be invoked with --section= for special effects. - Faster response to config changes if distcc fails. - Examples and troubleshooting in documentation Changes for 0.6 --------------- Configuration language changes: - distc++-hosts for c++ compiles (defaults to distcc-hosts) - parallel keyword to override no-parallel - distcc only used if distcc-hosts/distc++-hosts set Other changes: - toplevel make now shouldn't queue behind other makes. Changes for 0.5 --------------- Configuration language changes: - off keyword for distcc and ccache to turn them off locally Other changes: - ccontrol: "cannot decrement semaphore" fixed: support multiple users. - distcc: detect when distcc is failing, don't flood localhost. - documentation: fix cpus entry in ccontrol(1) - ccontrol-init: human names, fix some failure cases - ccache: don't limit distcc so hard when ccache enabled - install: create directories if necessary Changes for 0.4 --------------- IMPORTANT: Configuration language changes: - "parallel" renamed to "cpus" (other was confusing) - "[global]" must be replaced by "[*]": we now process *every* matching section, in order. Other changes: ccontrol-init: better handling for probing distcc hosts configuration: "include" keyword for including files documentation: clarifications, hacking advice, license distribution: include .hg dir to help users become developers users: ccontrol now has a second user (and developer)