diff options
author | bubulle <bubulle@alioth.debian.org> | 2011-11-12 13:00:54 +0000 |
---|---|---|
committer | bubulle <bubulle@alioth.debian.org> | 2011-11-12 13:00:54 +0000 |
commit | 6fba685eec3a1165ec0b82d72d3ae71e946a1404 (patch) | |
tree | f3c0543c8f9df4a22ed62e3bd99d9d7bc1054c14 /buildtools | |
parent | 77a7925c0509068d5cd2affd94a3996d0a86035a (diff) | |
download | samba-6fba685eec3a1165ec0b82d72d3ae71e946a1404.tar.gz |
Merge upstream 3.6.1 source
git-svn-id: svn://svn.debian.org/svn/pkg-samba/trunk/samba@3972 fc4039ab-9d04-0410-8cac-899223bdd6b0
Diffstat (limited to 'buildtools')
45 files changed, 8475 insertions, 0 deletions
diff --git a/buildtools/bin/README b/buildtools/bin/README new file mode 100644 index 0000000000..9ef8a1f651 --- /dev/null +++ b/buildtools/bin/README @@ -0,0 +1,16 @@ +This copy of waf-svn is taken from the git mirror of waf +at: + + git://git.samba.org/tridge/waf-svn.git + +using the waf-samba branch + +It was built using the command: + + ./waf-light --zip-type=gz --make-waf + +See http://code.google.com/p/waf/ for more information on waf + +You can get a svn copy of the upstream source with: + + svn checkout http://waf.googlecode.com/svn/trunk/ waf-read-only diff --git a/buildtools/bin/waf b/buildtools/bin/waf new file mode 120000 index 0000000000..1e5b242062 --- /dev/null +++ b/buildtools/bin/waf @@ -0,0 +1 @@ +waf-svn
\ No newline at end of file diff --git a/buildtools/bin/waf-svn b/buildtools/bin/waf-svn Binary files differnew file mode 100755 index 0000000000..b2e4885840 --- /dev/null +++ b/buildtools/bin/waf-svn diff --git a/buildtools/compare_config_h3.sh b/buildtools/compare_config_h3.sh new file mode 100755 index 0000000000..294af30930 --- /dev/null +++ b/buildtools/compare_config_h3.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# compare the generated config.h from a waf build with existing samba +# build + +OLD_CONFIG=$HOME/samba_old/source3/include/config.h +if test "x$1" != "x" ; then + OLD_CONFIG=$1 +fi + +if test "x$DIFF" = "x" ; then + DIFF="comm -23" +fi + +grep "^.define" bin/default/source3/include/config.h | sort > waf-config.h +grep "^.define" $OLD_CONFIG | sort > old-config.h + +$DIFF old-config.h waf-config.h + diff --git a/buildtools/compare_config_h4.sh b/buildtools/compare_config_h4.sh new file mode 100755 index 0000000000..b78b36fdd0 --- /dev/null +++ b/buildtools/compare_config_h4.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# compare the generated config.h from a waf build with existing samba +# build + +grep "^.define" bin/default/source4/include/config.h | sort > waf-config.h +grep "^.define" $HOME/samba_old/source4/include/config.h | sort > old-config.h + +comm -23 old-config.h waf-config.h + +#echo +#diff -u old-config.h waf-config.h diff --git a/buildtools/compare_generated.sh b/buildtools/compare_generated.sh new file mode 100755 index 0000000000..ebef8a979b --- /dev/null +++ b/buildtools/compare_generated.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +# compare the generated files from a waf + +old_build=$HOME/samba_old + +gen_files=$(cd bin/default && find . -type f -name '*.[ch]') + +2>&1 + +strip_file() +{ + in_file=$1 + out_file=$2 + cat $in_file | + grep -v 'The following definitions come from' | + grep -v 'Automatically generated at' | + grep -v 'Generated from' | + sed 's|/home/tnagy/samba/source4||g' | + sed 's|/home/tnagy/samba/|../|g' | + sed 's|bin/default/source4/||g' | + sed 's|bin/default/|../|g' | + sed 's/define _____/define ___/g' | + sed 's/define __*/define _/g' | + sed 's/define _DEFAULT_/define _/g' | + sed 's/define _SOURCE4_/define ___/g' | + sed 's/define ___/define _/g' | + sed 's/ifndef ___/ifndef _/g' | + sed 's|endif /* ____|endif /* __|g' | + sed s/__DEFAULT_SOURCE4/__/ | + sed s/__DEFAULT_SOURCE4/__/ | + sed s/__DEFAULT/____/ > $out_file +} + +compare_file() +{ + f=$f + bname=$(basename $f) + t1=/tmp/$bname.old.$$ + t2=/tmp/$bname.new.$$ + strip_file $old_build/$f $t1 + strip_file bin/default/$f $t2 + diff -u -b $t1 $t2 2>&1 + rm -f $t1 $t2 +} + +for f in $gen_files; do + compare_file $f +done + diff --git a/buildtools/compare_install.sh b/buildtools/compare_install.sh new file mode 100755 index 0000000000..b964117550 --- /dev/null +++ b/buildtools/compare_install.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +prefix1="$1" +prefix2="$2" + +(cd $prefix1 && find . ) | sort > p1.txt +(cd $prefix2 && find . ) | sort > p2.txt +diff -u p[12].txt diff --git a/buildtools/mktowscript/mklist.txt b/buildtools/mktowscript/mklist.txt new file mode 100644 index 0000000000..ee77bb99df --- /dev/null +++ b/buildtools/mktowscript/mklist.txt @@ -0,0 +1,86 @@ +source4/main.mk +source4/lib/basic.mk +pidl/config.mk +nsswitch/config.mk +nsswitch/libwbclient/config.mk +source4/heimdal_build/internal.mk +source4/lib/ldb-samba/config.mk +source4/librpc/config.mk +source4/utils/config.mk +source4/utils/net/config.mk +source4/scripting/python/config.mk +source4/auth/config.mk +source4/auth/gensec/config.mk +source4/auth/kerberos/config.mk +source4/auth/ntlm/config.mk +source4/auth/credentials/config.mk +source4/auth/ntlmssp/config.mk +source4/libnet/config.mk +source4/nbt_server/config.mk +source4/wrepl_server/config.mk +source4/ntvfs/config.mk +source4/ntvfs/unixuid/config.mk +source4/ntvfs/sysdep/config.mk +source4/ntvfs/common/config.mk +source4/ntvfs/posix/config.mk +source4/selftest/config.mk +source4/cldap_server/config.mk +source4/smb_server/config.mk +source4/smb_server/smb2/config.mk +source4/smb_server/smb/config.mk +source4/smbd/config.mk source4/smbd/process_model.mk +source4/kdc/config.mk +source4/dsdb/config.mk +source4/dsdb/samdb/ldb_modules/config.mk +source4/web_server/config.mk +source4/param/config.mk +source4/winbind/config.mk +source4/cluster/config.mk +source4/client/config.mk +source4/ntptr/config.mk +source4/rpc_server/config.mk +source4/libcli/config.mk +source4/libcli/smb2/config.mk +source4/libcli/wbclient/config.mk +source4/libcli/security/config.mk +source4/libcli/ldap/config.mk +source4/ntp_signd/config.mk +source4/torture/config.mk +source4/torture/smb2/config.mk +source4/torture/local/config.mk +source4/torture/drs/config.mk +source4/torture/winbind/config.mk +source4/torture/libsmbclient/config.mk +source4/torture/libnetapi/config.mk +source4/lib/messaging/config.mk +source4/lib/events/config.mk +source4/lib/stream/config.mk +source4/lib/cmdline/config.mk +source4/lib/com/config.mk +source4/lib/registry/config.mk +source4/lib/wmi/config.mk +source4/lib/socket/config.mk +source4/lib/samba3/config.mk +source4/ldap_server/config.mk +libgpo/config.mk +libcli/cldap/config.mk +libcli/samsync/config.mk +libcli/nbt/config.mk +libcli/auth/config.mk +libcli/drsuapi/config.mk +libcli/security/config.mk +libcli/smb/config.mk +libcli/named_pipe_auth/config.mk +libcli/ldap/config.mk +lib/uid_wrapper/config.mk +lib/crypto/config.mk +lib/socket_wrapper/config.mk +lib/util/config.mk +lib/util/charset/config.mk +lib/nss_wrapper/config.mk +lib/tsocket/config.mk +lib/popt/config.mk +lib/async_req/config.mk +lib/tdr/config.mk +lib/torture/config.mk +lib/smbconf/config.mk diff --git a/buildtools/mktowscript/mktowscript.pl b/buildtools/mktowscript/mktowscript.pl new file mode 100755 index 0000000000..a05506bcbd --- /dev/null +++ b/buildtools/mktowscript/mktowscript.pl @@ -0,0 +1,451 @@ +#!/usr/bin/perl -w + +use strict; +use Data::Dumper; +use File::Basename; +use List::MoreUtils qw(uniq); + +my $globals; +my $dname; + +sub read_file($) +{ + my $filename = shift; + open(CONFIG_MK, "$filename"); + my @lines = <CONFIG_MK>; + close(CONFIG_MK); + return @lines; +} + +sub trim($) +{ + my $string = shift; + $string =~ s/^\s+//; + $string =~ s/\s+$//; + return $string; +} + +sub strlist($) +{ + my $s = shift; + $s =~ s/\$\(SHLIBEXT\)/so/g; + $s =~ s/\$\(heimdalsrcdir\)/..\/heimdal/g; + $s =~ s/\$\(heimdalbuildsrcdir\)/..\/heimdal_build/g; + $s =~ s/\$\(nsswitchsrcdir\)/..\/nsswitch/g; + $s =~ s/\$\(param_OBJ_FILES\)/..\/pyparam.c/g; + $s =~ s/\$\(libclisrcdir\)\///g; + $s =~ s/\$\(socketwrappersrcdir\)\///g; + $s =~ s/\$\(libcompressionsrcdir\)\///g; + $s =~ s/\$\(\w*srcdir\)\///g; + $s =~ s/\$\(libgpodir\)\///g; + $s =~ s/\:\.o=\.ho//g; + $s =~ s/\:\.o=\.d//g; + + # this one doesn't exist? + $s =~ s/\bLDAP_ENCODE\b//g; + + # these need to use the library names + $s =~ s/\bLIBLDB\b/ldb/g; + $s =~ s/\bLDB\b/ldb/g; + $s =~ s/\bLIBTALLOC\b/talloc/g; + $s =~ s/\bTALLOC\b/talloc/g; + $s =~ s/\bLIBTEVENT\b/tevent/g; + $s =~ s/\bTEVENT\b/tevent/g; + $s =~ s/\bTSOCKET\b/LIBTSOCKET/g; + $s =~ s/\bGENSEC\b/gensec/g; + $s =~ s/\bLIBTDB\b/tdb/g; + $s =~ s/\bRESOLV\b/resolv/g; + + return trim(join(' ', split(/\s+/, $s))); +} + +sub expand_vars($$) +{ + my $vars = shift; + my $s = shift; + foreach my $v (keys %{$vars}) { + if ($s =~ /\$\($v\)/) { + $s =~ s/\$\($v\)/$vars->{$v}/g; + delete($vars->{$v}); + } + } + foreach my $v (keys %{$globals}) { + if ($s =~ /\$\($v\)/) { + $s =~ s/\$\($v\)/$globals->{$v}/g; + } + } + return $s; +} + +sub find_file($) +{ + my $f = shift; + my $orig = $f; + + if ($f =~ /\$/) { + printf(STDERR "bad variable expansion for file $orig in $dname\n"); + exit(1); + } + + my $b = basename($f); + return $b if (-e $b); + + return $f if (-e $f); + + while ($f =~ /\//) { + $f =~ s/^[^\/]+\///g; + return $f if (-e $f); + } + my $f2; + $f2 = `find . -name "$f" -type f`; + return $f2 unless ($f2 eq ""); + $f2 = `find .. -name "$f" -type f`; + return $f2 unless ($f2 eq ""); + $f2 = `find ../.. -name "$f" -type f`; + return $f2 unless ($f2 eq ""); + $f2 = `find ../../.. -name "$f" -type f`; + return $f2 unless ($f2 eq ""); + printf(STDERR "Failed to find $orig in $dname\n"); + exit(1); + return ''; +} + +sub find_files($) +{ + my $list = shift; + my $ret = ''; + foreach my $f (split(/\s+/, $list)) { + if ($f =~ /\.[0-9]$/) { + # a man page + my $m = find_file($f . ".xml"); + die("Unable to find man page $f\n") if ($m eq ""); + $m =~ s/\.xml$//; + return $m; + } + $f = find_file($f); + $f =~ s/^[.]\///; + $ret .= ' ' . $f; + } + $ret = strlist($ret); + my @list = split(/\s+/, $ret); + @list = uniq(@list); + $ret = trim(join(' ', @list)); + return $ret; +} + +sub read_config_mk($) +{ + my $filename = shift; + my @lines = read_file($filename); + my $prev = ""; + my $linenum = 1; + my $section = "GLOBAL"; + my $infragment; + my $result; + my $line = ""; + my $secnumber = 1; + + $result->{"GLOBAL"}->{SECNUMBER} = $secnumber++; + + foreach (@lines) { + $linenum++; + + # lines beginning with '#' are ignored + next if (/^\#.*$/); + + if (/^(.*)\\$/) { + $prev .= $1; + next; + } else { + $line = "$prev$_"; + $prev = ""; + } + + if ($line =~ /^mkinclude.*asn1_deps.pl\s+([^\s]+)\s+([^\s]+)\s+\\\$\\\(\w+\\\)\/([^\s|]+)\s*([^|]*)\|$/) { + my $src = $1; + $section = $2; + my $dir = $3; + my $options = $4; + $section = "HEIMDAL_" . uc($section); + $result->{$section}->{TYPE} = 'ASN1'; + $result->{$section}->{SECNUMBER} = $secnumber++; + if ($options ne '') { + $result->{$section}->{OPTIONS} = $options; + } + $result->{$section}->{DIRECTORY} = $dir; + $result->{$section}->{$section . '_OBJ_FILES'} = $src; + next; + } + + if ($line =~ /^mkinclude.*et_deps.pl\s+([^\s]+)\s+\\\$\\\(\w+\\\)\/([^\s|]+)\|$/) { + my $src = $1; + my $dir = $2; + $section = basename($src); + $section =~ s/\./_/g; + $section = "HEIMDAL_" . uc($section); + $result->{$section}->{TYPE} = 'ERRTABLE'; + $result->{$section}->{SECNUMBER} = $secnumber++; + $result->{$section}->{DIRECTORY} = "$dir"; + $result->{$section}->{$section . '_OBJ_FILES'} = $src; + next; + } + + if ($line =~ /^\[(\w+)::([\w-]+)\]/) + { + my $type = $1; + $section = $2; + $infragment = 0; + + $result->{$section}->{TYPE} = $type; + $result->{$section}->{SECNUMBER} = $secnumber++; + next; + } + + # include + if ($line =~ /^mkinclude (.*)$/) { + my $subfile = $1; + $result->{$subfile}->{TYPE} = 'SUBCONFIG'; + $result->{$subfile}->{SECNUMBER} = $secnumber++; + next; + } + + # empty line + if ($line =~ /^[ \t]*$/) { + next; + } + + # global stuff is considered part of the makefile + if ($section eq "GLOBAL") { + $infragment = 1; + next; + } + + # Assignment + if ($line =~ /^([a-zA-Z0-9_-]+)[\t ]*=(.*)$/) { + $result->{$section}->{$1} = expand_vars($result->{$section}, strlist($2)); + $globals->{$1} = $result->{$section}->{$1}; + next; + } + + # += + if ($line =~ /^([a-zA-Z0-9_-]+)[\t ]*\+=(.*)$/) { + if (!$result->{$section}->{$1}) { + $result->{$section}->{$1}=""; + } + $result->{$section}->{$1} .= " " . expand_vars($result->{$section}, strlist($2)); + $globals->{$1} = $result->{$section}->{$1}; + next; + } + + if ($line =~ /\$\(eval.\$\(call.proto_header_template.*,(.*),.*/) { + $result->{$section}->{AUTOPROTO} = $1; + } + if ($line =~ /^\$\(eval/) { + # skip eval lines for now + next; + } + + printf(STDERR "$linenum: Bad line: $line"); + } + + return $result; +} + + +sub process_results($) +{ + my $result = shift; + + foreach my $s (sort {$result->{$a}->{SECNUMBER} <=> $result->{$b}->{SECNUMBER}} keys %{$result}) { + next if ($s eq "GLOBAL"); + my $sec = $result->{$s}; + if ($sec->{TYPE} eq "SUBCONFIG") { + my $d = dirname($s); + next if ($d eq "."); + printf "bld.BUILD_SUBDIR('%s')\n", dirname($s); + } else { + printf "\nbld.SAMBA_%s('%s'", $sec->{TYPE}, $s; + my $trailer=""; + my $got_src = 0; + my $got_private_deps = 0; + + foreach my $k (keys %{$sec}) { + #print "key=$k\n"; + + next if ($k eq "SECNUMBER"); + next if ($k eq "TYPE"); + if ($k eq "INIT_FUNCTION") { + $trailer .= sprintf(",\n\tinit_function='%s'", trim($sec->{$k})); + next; + } + if ($k eq "INIT_FUNCTION_SENTINEL") { + $trailer .= sprintf(",\n\tinit_function_sentinal='%s'", trim($sec->{$k})); + next; + } + if ($k eq "_PY_FILES" || + $k eq "EPYDOC_OPTIONS" || + $k eq "COV_TARGET" || + $k eq "GCOV" || + $k eq "PC_FILES" || + $k eq "CONFIG4FILE" || + $k eq "LMHOSTSFILE4") { + $trailer .= sprintf(",\n\t# %s='%s'", $k, trim($sec->{$k})); + next; + } + if ($k eq "SUBSYSTEM") { + $trailer .= sprintf(",\n\tsubsystem='%s'", trim($sec->{$k})); + next; + } + if ($k eq "PRIVATE_DEPENDENCIES") { + $trailer .= sprintf(",\n\tdeps='%s'", strlist($sec->{$k})); + $got_private_deps = 1; + next; + } + if ($k eq "PUBLIC_DEPENDENCIES") { + $trailer .= sprintf(",\n\tpublic_deps='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "ALIASES") { + $trailer .= sprintf(",\n\taliases='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "CFLAGS") { + $trailer .= sprintf(",\n\tcflags='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "OPTIONS") { + $trailer .= sprintf(",\n\toptions='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "DIRECTORY") { + $trailer .= sprintf(",\n\tdirectory='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "LDFLAGS") { + $trailer .= sprintf(",\n\tldflags='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "INSTALLDIR") { + $trailer .= sprintf(",\n\tinstalldir='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "ASN1C") { + $trailer .= sprintf(",\n\tcompiler='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "ET_COMPILER") { + $trailer .= sprintf(",\n\tcompiler='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "ENABLE") { + my $v = strlist($sec->{$k}); + if ($v eq "NO") { + $trailer .= sprintf(",\n\tenabled=False"); + next; + } + next if ($v eq "YES"); + die("Unknown ENABLE value $v in $s\n"); + } + if ($k eq "USE_HOSTCC") { + my $v = strlist($sec->{$k}); + if ($v eq "YES") { + $trailer .= sprintf(",\n\tuse_hostcc=True"); + next; + } + next if ($v eq "NO"); + die("Unknown HOST_CC value $v in $s\n"); + } + if ($k eq "$s" . "_VERSION") { + $trailer .= sprintf(",\n\tvnum='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "$s" . "_SOVERSION") { + next; + } + if ($k eq "LIBRARY_REALNAME") { + $trailer .= sprintf(",\n\trealname='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "OUTPUT_TYPE") { + $trailer .= sprintf(",\n\toutput_type='%s'", strlist($sec->{$k})); + next; + } + if ($k eq "AUTOPROTO") { + my $list = trim(find_files(strlist($sec->{$k}))); + $trailer .= sprintf(",\n\tautoproto='%s'", $list); + next; + } + if ($k eq "PUBLIC_HEADERS") { + my $list = trim(strlist($sec->{$k})); + if ($list =~ /\$\(addprefix .*,(.*)\)(.*)$/) { + $list = trim("$1 $2"); + $list = find_files($list); + } else { + $list = trim(find_files(strlist($sec->{$k}))); + } + $trailer .= sprintf(",\n\tpublic_headers='%s'", $list); + next; + } + if ($k eq "MANPAGES") { + my $list = trim(find_files(strlist($sec->{$k}))); + $trailer .= sprintf(",\n\tmanpages='%s'", $list); + next; + } + if ($k eq "$s" . "_OBJ_FILES") { + my $list = trim(strlist($sec->{$k})); + $list =~ s/\.o/.c/g; + $list =~ s/\.ho/.c/g; + if ($list =~ /\$\(addprefix .*,(.*)\)(.*)$/) { + $list = trim("$1 $2"); + $list = find_files($list); + $list = "'$list'"; + } elsif ($list =~ /\$\(addprefix \$\((\w+)\)(.*),(.*)\)(.*)$/) { + my $src = trim($3); + my $dir = "$1$2"; + $dir =~ s/\/$//; + my $res = "bld.SUBDIR('$dir', '$src')"; + if ($4) { + $res = "$res + '$4'"; + } + $list = $res; + } else { + $list = find_files($list); + $list="'$list'"; + } + $list =~ s/\$\(\w+srcdir\)\///g; + printf(",\n\tsource=%s", $list); + $got_src = 1; + next; + } + if ($k eq "HEIMDAL_GSSAPI_KRB5_OBJ_FILES" || + $k eq "HEIMDAL_GSSAPI_SPNEGO_OBJ_FILES" || + $k eq "HEIMDAL_HEIM_ASN1_DER_OBJ_FILES" || + $k eq "HEIMDAL_HX509_OBJH_FILES" || + $k eq "HEIMDAL_HX509_OBJG_FILES" || + $k eq "HEIMDAL_ROKEN_OBJ_FILES" + ) { + next; + } + die("Unknown keyword $k in $s\n"); + } + die("No source list in $s\n") unless $got_src or $got_private_deps; + if (! $got_src) { + printf(",source=''\n\t"); + } + printf("%s\n\t)\n\n", $trailer); + } + } +} + +for (my $i=0; $i <= $#ARGV; $i++) { + my $filename=$ARGV[$i]; + $dname=dirname($filename); + my $result = read_config_mk($filename); + if ($i != 0) { + print "\n\n\n"; + } + print "# AUTOGENERATED by mktowscript.pl from $filename\n# Please remove this notice if hand editing\n\n"; + die("Unable to chdir to $dname\n") unless chdir($dname); + process_results($result); +} + diff --git a/buildtools/mktowscript/rebuild_all.sh b/buildtools/mktowscript/rebuild_all.sh new file mode 100755 index 0000000000..e3ed7cfd24 --- /dev/null +++ b/buildtools/mktowscript/rebuild_all.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +cat mklist.txt | +while read line; do + ws="" + list="" + for f in $line; do + echo "Processing $f" + f="../../$f" + test -f $f || { + echo "$f doesn't exist" + exit 1 + } + ws="$(dirname $f)/wscript_build" + if [ -f $ws ]; then + if test -s $ws && ! grep "AUTOGENERATED.by.mktowscript" $ws > /dev/null; then + echo "Skipping manually edited file $ws" + continue + fi + fi + list="$list $f" + done + if [ "$list" = "" ]; then + continue + fi + ./mktowscript.pl $list > wscript_build.$$ || { + echo "Failed on $f" + rm -f wscript_build.$$ + exit 1 + } + if cmp wscript_build.$$ $ws > /dev/null 2>&1; then + rm -f wscript_build.$$ + else + mv wscript_build.$$ $ws || exit 1 + fi + #exit 1 +done diff --git a/buildtools/scripts/Makefile.waf b/buildtools/scripts/Makefile.waf new file mode 100644 index 0000000000..716ab93270 --- /dev/null +++ b/buildtools/scripts/Makefile.waf @@ -0,0 +1,72 @@ +# simple makefile wrapper to run waf + +WAF_BINARY=BUILDTOOLS/bin/waf +WAF=WAF_MAKE=1 $(WAF_BINARY) + +all: + $(WAF) build + +install: + $(WAF) install + +uninstall: + $(WAF) uninstall + +test: + $(WAF) test $(TEST_OPTIONS) + +help: + @echo NOTE: to run extended waf options use $(WAF_BINARY) or modify your PATH + $(WAF) --help + +testenv: + $(WAF) test --testenv $(TEST_OPTIONS) + +quicktest: + $(WAF) test --quick $(TEST_OPTIONS) + +dist: + $(WAF) dist + +distcheck: + $(WAF) distcheck + +clean: + $(WAF) clean + +distclean: + $(WAF) distclean + +reconfigure: configure + $(WAF) reconfigure + +show_waf_options: + $(WAF) --help + +# some compatibility make targets +everything: all + +testsuite: all + +check: test + +torture: all + +# this should do an install as well, once install is finished +installcheck: test + +etags: + $(WAF) etags + +ctags: + $(WAF) ctags + +bin/%:: FORCE + $(WAF) --targets=`basename $@` +FORCE: + +configure: autogen-waf.sh BUILDTOOLS/scripts/configure.waf + ./autogen-waf.sh + +Makefile: autogen-waf.sh configure BUILDTOOLS/scripts/Makefile.waf + ./autogen-waf.sh diff --git a/buildtools/scripts/abi_gen.sh b/buildtools/scripts/abi_gen.sh new file mode 100755 index 0000000000..ed6f445519 --- /dev/null +++ b/buildtools/scripts/abi_gen.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# generate a set of ABI signatures from a shared library + +SHAREDLIB="$1" + +GDBSCRIPT="gdb_syms.$$" + +( +cat <<EOF +set height 0 +set width 0 +EOF +nm "$SHAREDLIB" | cut -d' ' -f2- | egrep '^[BDGTRVWS]' | grep -v @ | cut -c3- | sort | while read s; do + echo "echo $s: " + echo p $s +done +) > $GDBSCRIPT + +# forcing the terminal avoids a problem on Fedora12 +TERM=none gdb -batch -x $GDBSCRIPT "$SHAREDLIB" < /dev/null +rm -f $GDBSCRIPT diff --git a/buildtools/scripts/autogen-waf.sh b/buildtools/scripts/autogen-waf.sh new file mode 100755 index 0000000000..7a6e94c5ec --- /dev/null +++ b/buildtools/scripts/autogen-waf.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +p=`dirname $0` + +echo "Setting up for waf build" + +echo "Looking for the buildtools directory" + +d="buildtools" +while test \! -d "$p/$d"; do d="../$d"; done + +echo "Found buildtools in $p/$d" + +echo "Setting up configure" +rm -f $p/configure $p/include/config*.h* +sed "s|BUILDTOOLS|$d|g;s|BUILDPATH|$p|g" < "$p/$d/scripts/configure.waf" > $p/configure +chmod +x $p/configure + +echo "Setting up Makefile" +rm -f $p/makefile $p/Makefile +sed "s|BUILDTOOLS|$d|g" < "$p/$d/scripts/Makefile.waf" > $p/Makefile + +echo "done. Now run $p/configure or $p/configure.developer then make." +if [ $p != "." ]; then + echo "Notice: The build invoke path is not 'source4'! Use make with the parameter" + echo "-C <'source4' path>. Example: make -C source4 all" +fi diff --git a/buildtools/scripts/configure.waf b/buildtools/scripts/configure.waf new file mode 100755 index 0000000000..a7d8d1dbd6 --- /dev/null +++ b/buildtools/scripts/configure.waf @@ -0,0 +1,14 @@ +#!/bin/sh + +PREVPATH=`dirname $0` + +WAF=BUILDTOOLS/bin/waf + +# using JOBS=1 gives maximum compatibility with +# systems like AIX which have broken threading in python +JOBS=1 +export JOBS + +cd BUILDPATH || exit 1 +$WAF configure "$@" || exit 1 +cd $PREVPATH diff --git a/buildtools/testwaf.sh b/buildtools/testwaf.sh new file mode 100755 index 0000000000..8b65af2c10 --- /dev/null +++ b/buildtools/testwaf.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +set -e +set -x + +d=$(dirname $0) + +cd $d/.. +PREFIX=$HOME/testprefix + +if [ $# -gt 0 ]; then + tests="$*" +else + tests="lib/replace lib/talloc lib/tevent lib/tdb source4/lib/ldb" +fi + +echo "testing in dirs $tests" + +for d in $tests; do + echo "`date`: testing $d" + pushd $d + rm -rf bin + type waf + waf dist + ./configure -C --enable-developer --prefix=$PREFIX + time make + make install + make distcheck + case $d in + "source4/lib/ldb") + ldd bin/ldbadd + ;; + "lib/replace") + ldd bin/replace_testsuite + ;; + "lib/talloc") + ldd bin/talloc_testsuite + ;; + "lib/tdb") + ldd bin/tdbtool + ;; + esac + popd +done + +echo "testing python portability" +pushd lib/talloc +versions="python2.4 python2.5 python2.6 python3.0 python3.1" +for p in $versions; do + ret=$(which $p || echo "failed") + if [ $ret = "failed" ]; then + echo "$p not found, skipping" + continue + fi + echo "Testing $p" + $p ../../buildtools/bin/waf configure -C --enable-developer --prefix=$PREFIX + $p ../../buildtools/bin/waf build install +done +popd + +echo "testing cross compiling" +pushd lib/talloc +ret=$(which arm-linux-gnueabi-gcc || echo "failed") +if [ $ret != "failed" ]; then + CC=arm-linux-gnueabi-gcc ./configure -C --prefix=$PREFIX --cross-compile --cross-execute='runarm' + make && make install +else + echo "Cross-compiler not installed, skipping test" +fi +popd diff --git a/buildtools/wafsamba/README b/buildtools/wafsamba/README new file mode 100644 index 0000000000..1968b55453 --- /dev/null +++ b/buildtools/wafsamba/README @@ -0,0 +1,8 @@ +This is a set of waf 'tools' to help make building the Samba +components easier, by having common functions in one place. This gives +us a more consistent build, and ensures that our project rules are +obeyed + + +TODO: + see http://wiki.samba.org/index.php/Waf diff --git a/buildtools/wafsamba/gccdeps.py b/buildtools/wafsamba/gccdeps.py new file mode 100644 index 0000000000..6600c9ca3b --- /dev/null +++ b/buildtools/wafsamba/gccdeps.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2008-2010 (ita) + +""" +Execute the tasks with gcc -MD, read the dependencies from the .d file +and prepare the dependency calculation for the next run +""" + +import os, re, threading +import Task, Logs, Utils, preproc +from TaskGen import before, after, feature + +lock = threading.Lock() + +preprocessor_flag = '-MD' + +@feature('cc') +@before('apply_core') +def add_mmd_cc(self): + if self.env.get_flat('CCFLAGS').find(preprocessor_flag) < 0: + self.env.append_value('CCFLAGS', preprocessor_flag) + +@feature('cxx') +@before('apply_core') +def add_mmd_cxx(self): + if self.env.get_flat('CXXFLAGS').find(preprocessor_flag) < 0: + self.env.append_value('CXXFLAGS', preprocessor_flag) + +def scan(self): + "the scanner does not do anything initially" + nodes = self.generator.bld.node_deps.get(self.unique_id(), []) + names = [] + return (nodes, names) + +re_o = re.compile("\.o$") +re_src = re.compile("^(\.\.)[\\/](.*)$") + +def post_run(self): + # The following code is executed by threads, it is not safe, so a lock is needed... + + if getattr(self, 'cached', None): + return Task.Task.post_run(self) + + name = self.outputs[0].abspath(self.env) + name = re_o.sub('.d', name) + txt = Utils.readf(name) + #os.unlink(name) + + txt = txt.replace('\\\n', '') + + lst = txt.strip().split(':') + val = ":".join(lst[1:]) + val = val.split() + + nodes = [] + bld = self.generator.bld + + f = re.compile("^("+self.env.variant()+"|\.\.)[\\/](.*)$") + for x in val: + if os.path.isabs(x): + + if not preproc.go_absolute: + continue + + lock.acquire() + try: + node = bld.root.find_resource(x) + finally: + lock.release() + else: + g = re.search(re_src, x) + if g: + x = g.group(2) + lock.acquire() + try: + node = bld.bldnode.parent.find_resource(x) + finally: + lock.release() + else: + g = re.search(f, x) + if g: + x = g.group(2) + lock.acquire() + try: + node = bld.srcnode.find_resource(x) + finally: + lock.release() + + if id(node) == id(self.inputs[0]): + # ignore the source file, it is already in the dependencies + # this way, successful config tests may be retrieved from the cache + continue + + if not node: + raise ValueError('could not find %r for %r' % (x, self)) + else: + nodes.append(node) + + Logs.debug('deps: real scanner for %s returned %s' % (str(self), str(nodes))) + + bld.node_deps[self.unique_id()] = nodes + bld.raw_deps[self.unique_id()] = [] + + try: + del self.cache_sig + except: + pass + + Task.Task.post_run(self) + +import Constants, Utils +def sig_implicit_deps(self): + try: + return Task.Task.sig_implicit_deps(self) + except Utils.WafError: + return Constants.SIG_NIL + +for name in 'cc cxx'.split(): + try: + cls = Task.TaskBase.classes[name] + except KeyError: + pass + else: + cls.post_run = post_run + cls.scan = scan + cls.sig_implicit_deps = sig_implicit_deps + diff --git a/buildtools/wafsamba/generic_cc.py b/buildtools/wafsamba/generic_cc.py new file mode 100644 index 0000000000..ea277dc655 --- /dev/null +++ b/buildtools/wafsamba/generic_cc.py @@ -0,0 +1,71 @@ + +# compiler definition for a generic C compiler +# based on suncc.py from waf + +import os, optparse +import Utils, Options, Configure +import ccroot, ar +from Configure import conftest + +from compiler_cc import c_compiler + +c_compiler['default'] = ['gcc', 'generic_cc'] +c_compiler['hpux'] = ['gcc', 'generic_cc'] + +@conftest +def find_generic_cc(conf): + v = conf.env + cc = None + if v['CC']: cc = v['CC'] + elif 'CC' in conf.environ: cc = conf.environ['CC'] + if not cc: cc = conf.find_program('cc', var='CC') + if not cc: conf.fatal('generic_cc was not found') + cc = conf.cmd_to_list(cc) + v['CC'] = cc + v['CC_NAME'] = 'generic' + +@conftest +def generic_cc_common_flags(conf): + v = conf.env + + v['CC_SRC_F'] = '' + v['CC_TGT_F'] = ['-c', '-o', ''] + v['CPPPATH_ST'] = '-I%s' # template for adding include paths + + # linker + if not v['LINK_CC']: v['LINK_CC'] = v['CC'] + v['CCLNK_SRC_F'] = '' + v['CCLNK_TGT_F'] = ['-o', ''] + + v['LIB_ST'] = '-l%s' # template for adding libs + v['LIBPATH_ST'] = '-L%s' # template for adding libpaths + v['STATICLIB_ST'] = '-l%s' + v['STATICLIBPATH_ST'] = '-L%s' + v['CCDEFINES_ST'] = '-D%s' + +# v['SONAME_ST'] = '-Wl,-h -Wl,%s' +# v['SHLIB_MARKER'] = '-Bdynamic' +# v['STATICLIB_MARKER'] = '-Bstatic' + + # program + v['program_PATTERN'] = '%s' + + # shared library +# v['shlib_CCFLAGS'] = ['-Kpic', '-DPIC'] +# v['shlib_LINKFLAGS'] = ['-G'] + v['shlib_PATTERN'] = 'lib%s.so' + + # static lib +# v['staticlib_LINKFLAGS'] = ['-Bstatic'] +# v['staticlib_PATTERN'] = 'lib%s.a' + +detect = ''' +find_generic_cc +find_cpp +find_ar +generic_cc_common_flags +cc_load_tools +cc_add_flags +link_add_flags +''' + diff --git a/buildtools/wafsamba/hpuxcc.py b/buildtools/wafsamba/hpuxcc.py new file mode 100644 index 0000000000..c263556cd8 --- /dev/null +++ b/buildtools/wafsamba/hpuxcc.py @@ -0,0 +1,56 @@ +# compiler definition for HPUX +# based on suncc.py from waf + +import os, optparse, sys +import Utils, Options, Configure +import ccroot, ar +from Configure import conftest +import gcc + + +@conftest +def gcc_modifier_hpux(conf): + v=conf.env + v['CCFLAGS_DEBUG']=['-g'] + v['CCFLAGS_RELEASE']=['-O2'] + v['CC_SRC_F']='' + v['CC_TGT_F']=['-c','-o',''] + v['CPPPATH_ST']='-I%s' + if not v['LINK_CC']:v['LINK_CC']=v['CC'] + v['CCLNK_SRC_F']='' + v['CCLNK_TGT_F']=['-o',''] + v['LIB_ST']='-l%s' + v['LIBPATH_ST']='-L%s' + v['STATICLIB_ST']='-l%s' + v['STATICLIBPATH_ST']='-L%s' + v['RPATH_ST']='-Wl,-rpath,%s' + v['CCDEFINES_ST']='-D%s' + v['SONAME_ST']='-Wl,-h,%s' + v['SHLIB_MARKER']=[] +# v['STATICLIB_MARKER']='-Wl,-Bstatic' + v['FULLSTATIC_MARKER']='-static' + v['program_PATTERN']='%s' + v['shlib_CCFLAGS']=['-fPIC','-DPIC'] + v['shlib_LINKFLAGS']=['-shared'] + v['shlib_PATTERN']='lib%s.sl' +# v['staticlib_LINKFLAGS']=['-Wl,-Bstatic'] + v['staticlib_PATTERN']='lib%s.a' + +gcc.gcc_modifier_hpux = gcc_modifier_hpux + +from TaskGen import feature, after +@feature('cprogram', 'cshlib') +@after('apply_link', 'apply_lib_vars', 'apply_obj_vars') +def hpux_addfullpath(self): + if sys.platform == 'hp-ux11': + link = getattr(self, 'link_task', None) + if link: + lst = link.env.LINKFLAGS + buf = [] + for x in lst: + if x.startswith('-L'): + p2 = x[2:] + if not os.path.isabs(p2): + x = x[:2] + self.bld.srcnode.abspath(link.env) + "/../" + x[2:].lstrip('.') + buf.append(x) + link.env.LINKFLAGS = buf diff --git a/buildtools/wafsamba/irixcc.py b/buildtools/wafsamba/irixcc.py new file mode 100644 index 0000000000..1461bf8c83 --- /dev/null +++ b/buildtools/wafsamba/irixcc.py @@ -0,0 +1,77 @@ + +# compiler definition for irix/MIPSpro cc compiler +# based on suncc.py from waf + +import os, optparse +import Utils, Options, Configure +import ccroot, ar +from Configure import conftest + +from compiler_cc import c_compiler + +c_compiler['irix'] = ['gcc', 'irixcc'] + +@conftest +def find_irixcc(conf): + v = conf.env + cc = None + if v['CC']: cc = v['CC'] + elif 'CC' in conf.environ: cc = conf.environ['CC'] + if not cc: cc = conf.find_program('cc', var='CC') + if not cc: conf.fatal('irixcc was not found') + cc = conf.cmd_to_list(cc) + + try: + if Utils.cmd_output(cc + ['-version']) != '': + conf.fatal('irixcc %r was not found' % cc) + except ValueError: + conf.fatal('irixcc -v could not be executed') + + v['CC'] = cc + v['CC_NAME'] = 'irix' + +@conftest +def irixcc_common_flags(conf): + v = conf.env + + v['CC_SRC_F'] = '' + v['CC_TGT_F'] = ['-c', '-o', ''] + v['CPPPATH_ST'] = '-I%s' # template for adding include paths + + # linker + if not v['LINK_CC']: v['LINK_CC'] = v['CC'] + v['CCLNK_SRC_F'] = '' + v['CCLNK_TGT_F'] = ['-o', ''] + + v['LIB_ST'] = '-l%s' # template for adding libs + v['LIBPATH_ST'] = '-L%s' # template for adding libpaths + v['STATICLIB_ST'] = '-l%s' + v['STATICLIBPATH_ST'] = '-L%s' + v['CCDEFINES_ST'] = '-D%s' + +# v['SONAME_ST'] = '-Wl,-h -Wl,%s' +# v['SHLIB_MARKER'] = '-Bdynamic' +# v['STATICLIB_MARKER'] = '-Bstatic' + + # program + v['program_PATTERN'] = '%s' + + # shared library +# v['shlib_CCFLAGS'] = ['-Kpic', '-DPIC'] +# v['shlib_LINKFLAGS'] = ['-G'] + v['shlib_PATTERN'] = 'lib%s.so' + + # static lib +# v['staticlib_LINKFLAGS'] = ['-Bstatic'] +# v['staticlib_PATTERN'] = 'lib%s.a' + +detect = ''' +find_irixcc +find_cpp +find_ar +irixcc_common_flags +cc_load_tools +cc_add_flags +link_add_flags +''' + diff --git a/buildtools/wafsamba/nothreads.py b/buildtools/wafsamba/nothreads.py new file mode 100644 index 0000000000..9054a57af5 --- /dev/null +++ b/buildtools/wafsamba/nothreads.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2008 (ita) + +# this replaces the core of Runner.py in waf with a varient that works +# on systems with completely broken threading (such as Python 2.5.x on +# AIX). For simplicity we enable this when JOBS=1, which is triggered +# by the compatibility makefile used for the waf build. That also ensures +# this code is tested, as it means it is used in the build farm, and by +# anyone using 'make' to build Samba with waf + +"Execute the tasks" + +import sys, random, time, threading, traceback, os +try: from Queue import Queue +except ImportError: from queue import Queue +import Build, Utils, Logs, Options +from Logs import debug, error +from Constants import * + +GAP = 15 + +run_old = threading.Thread.run +def run(*args, **kwargs): + try: + run_old(*args, **kwargs) + except (KeyboardInterrupt, SystemExit): + raise + except: + sys.excepthook(*sys.exc_info()) +threading.Thread.run = run + + +class TaskConsumer(object): + consumers = 1 + +def process(tsk): + m = tsk.master + if m.stop: + m.out.put(tsk) + return + + try: + tsk.generator.bld.printout(tsk.display()) + if tsk.__class__.stat: ret = tsk.__class__.stat(tsk) + # actual call to task's run() function + else: ret = tsk.call_run() + except Exception, e: + tsk.err_msg = Utils.ex_stack() + tsk.hasrun = EXCEPTION + + # TODO cleanup + m.error_handler(tsk) + m.out.put(tsk) + return + + if ret: + tsk.err_code = ret + tsk.hasrun = CRASHED + else: + try: + tsk.post_run() + except Utils.WafError: + pass + except Exception: + tsk.err_msg = Utils.ex_stack() + tsk.hasrun = EXCEPTION + else: + tsk.hasrun = SUCCESS + if tsk.hasrun != SUCCESS: + m.error_handler(tsk) + + m.out.put(tsk) + +class Parallel(object): + """ + keep the consumer threads busy, and avoid consuming cpu cycles + when no more tasks can be added (end of the build, etc) + """ + def __init__(self, bld, j=2): + + # number of consumers + self.numjobs = j + + self.manager = bld.task_manager + self.manager.current_group = 0 + + self.total = self.manager.total() + + # tasks waiting to be processed - IMPORTANT + self.outstanding = [] + self.maxjobs = MAXJOBS + + # tasks that are awaiting for another task to complete + self.frozen = [] + + # tasks returned by the consumers + self.out = Queue(0) + + self.count = 0 # tasks not in the producer area + + self.processed = 1 # progress indicator + + self.stop = False # error condition to stop the build + self.error = False # error flag + + def get_next(self): + "override this method to schedule the tasks in a particular order" + if not self.outstanding: + return None + return self.outstanding.pop(0) + + def postpone(self, tsk): + "override this method to schedule the tasks in a particular order" + # TODO consider using a deque instead + if random.randint(0, 1): + self.frozen.insert(0, tsk) + else: + self.frozen.append(tsk) + + def refill_task_list(self): + "called to set the next group of tasks" + + while self.count > self.numjobs + GAP or self.count >= self.maxjobs: + self.get_out() + + while not self.outstanding: + if self.count: + self.get_out() + + if self.frozen: + self.outstanding += self.frozen + self.frozen = [] + elif not self.count: + (jobs, tmp) = self.manager.get_next_set() + if jobs != None: self.maxjobs = jobs + if tmp: self.outstanding += tmp + break + + def get_out(self): + "the tasks that are put to execute are all collected using get_out" + ret = self.out.get() + self.manager.add_finished(ret) + if not self.stop and getattr(ret, 'more_tasks', None): + self.outstanding += ret.more_tasks + self.total += len(ret.more_tasks) + self.count -= 1 + + def error_handler(self, tsk): + "by default, errors make the build stop (not thread safe so be careful)" + if not Options.options.keep: + self.stop = True + self.error = True + + def start(self): + "execute the tasks" + + while not self.stop: + + self.refill_task_list() + + # consider the next task + tsk = self.get_next() + if not tsk: + if self.count: + # tasks may add new ones after they are run + continue + else: + # no tasks to run, no tasks running, time to exit + break + + if tsk.hasrun: + # if the task is marked as "run", just skip it + self.processed += 1 + self.manager.add_finished(tsk) + continue + + try: + st = tsk.runnable_status() + except Exception, e: + self.processed += 1 + if self.stop and not Options.options.keep: + tsk.hasrun = SKIPPED + self.manager.add_finished(tsk) + continue + self.error_handler(tsk) + self.manager.add_finished(tsk) + tsk.hasrun = EXCEPTION + tsk.err_msg = Utils.ex_stack() + continue + + if st == ASK_LATER: + self.postpone(tsk) + elif st == SKIP_ME: + self.processed += 1 + tsk.hasrun = SKIPPED + self.manager.add_finished(tsk) + else: + # run me: put the task in ready queue + tsk.position = (self.processed, self.total) + self.count += 1 + self.processed += 1 + tsk.master = self + + process(tsk) + + # self.count represents the tasks that have been made available to the consumer threads + # collect all the tasks after an error else the message may be incomplete + while self.error and self.count: + self.get_out() + + #print loop + assert (self.count == 0 or self.stop) + + +# enable nothreads +import Runner +Runner.process = process +Runner.Parallel = Parallel diff --git a/buildtools/wafsamba/pkgconfig.py b/buildtools/wafsamba/pkgconfig.py new file mode 100644 index 0000000000..09bfcb9c6b --- /dev/null +++ b/buildtools/wafsamba/pkgconfig.py @@ -0,0 +1,68 @@ +# handle substitution of variables in pc files + +import Build, sys, Logs +from samba_utils import * + +def subst_at_vars(task): + '''substiture @VAR@ style variables in a file''' + src = task.inputs[0].srcpath(task.env) + tgt = task.outputs[0].bldpath(task.env) + + f = open(src, 'r') + s = f.read() + f.close() + # split on the vars + a = re.split('(@\w+@)', s) + out = [] + done_var = {} + back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')] + for v in a: + if re.match('@\w+@', v): + vname = v[1:-1] + if not vname in task.env and vname.upper() in task.env: + vname = vname.upper() + if not vname in task.env: + Logs.error("Unknown substitution %s in %s" % (v, task.name)) + sys.exit(1) + v = SUBST_VARS_RECURSIVE(task.env[vname], task.env) + # now we back substitute the allowed pc vars + for (b, m) in back_sub: + s = task.env[b] + if s == v[0:len(s)]: + if not b in done_var: + # we don't want to substitute the first usage + done_var[b] = True + else: + v = m + v[len(s):] + break + out.append(v) + contents = ''.join(out) + f = open(tgt, 'w') + s = f.write(contents) + f.close() + return 0 + + +def PKG_CONFIG_FILES(bld, pc_files, vnum=None): + '''install some pkg_config pc files''' + dest = '${PKGCONFIGDIR}' + dest = bld.EXPAND_VARIABLES(dest) + for f in TO_LIST(pc_files): + base=os.path.basename(f) + t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base, + rule=subst_at_vars, + source=f+'.in', + target=f) + t.vars = [] + if t.env.RPATH_ON_INSTALL: + t.env.LIB_RPATH = t.env.RPATH_ST % t.env.LIBDIR + else: + t.env.LIB_RPATH = '' + if vnum: + t.env.PACKAGE_VERSION = vnum + for v in [ 'PREFIX', 'EXEC_PREFIX', 'LIB_RPATH' ]: + t.vars.append(t.env[v]) + bld.INSTALL_FILES(dest, f, flat=True, destname=base) +Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES + + diff --git a/buildtools/wafsamba/samba3.py b/buildtools/wafsamba/samba3.py new file mode 100644 index 0000000000..cb459ad115 --- /dev/null +++ b/buildtools/wafsamba/samba3.py @@ -0,0 +1,110 @@ +# a waf tool to add autoconf-like macros to the configure section +# and for SAMBA_ macros for building libraries, binaries etc + +import Options, Build, os +from optparse import SUPPRESS_HELP +from samba_utils import os_path_relpath, TO_LIST + +def SAMBA3_ADD_OPTION(opt, option, help=(), dest=None, default=True, + with_name="with", without_name="without"): + if help == (): + help = ("Build with %s support" % option) + if dest is None: + dest = "with_%s" % option.replace('-', '_') + + with_val = "--%s-%s" % (with_name, option) + without_val = "--%s-%s" % (without_name, option) + + #FIXME: This is broken and will always default to "default" no matter if + # --with or --without is chosen. + opt.add_option(with_val, help=help, action="store_true", dest=dest, + default=default) + opt.add_option(without_val, help=SUPPRESS_HELP, action="store_false", + dest=dest) +Options.Handler.SAMBA3_ADD_OPTION = SAMBA3_ADD_OPTION + +def SAMBA3_IS_STATIC_MODULE(bld, module): + '''Check whether module is in static list''' + if module in bld.env['static_modules']: + return True + return False +Build.BuildContext.SAMBA3_IS_STATIC_MODULE = SAMBA3_IS_STATIC_MODULE + +def SAMBA3_IS_SHARED_MODULE(bld, module): + '''Check whether module is in shared list''' + if module in bld.env['shared_modules']: + return True + return False +Build.BuildContext.SAMBA3_IS_SHARED_MODULE = SAMBA3_IS_SHARED_MODULE + +def SAMBA3_IS_ENABLED_MODULE(bld, module): + '''Check whether module is in either shared or static list ''' + return SAMBA3_IS_STATIC_MODULE(bld, module) or SAMBA3_IS_SHARED_MODULE(bld, module) +Build.BuildContext.SAMBA3_IS_ENABLED_MODULE = SAMBA3_IS_ENABLED_MODULE + + + +def s3_fix_kwargs(bld, kwargs): + '''fix the build arguments for s3 build rules to include the + necessary includes, subdir and cflags options ''' + s3dir = os.path.join(bld.env.srcdir, 'source3') + s3reldir = os_path_relpath(s3dir, bld.curdir) + + # the extra_includes list is relative to the source3 directory + extra_includes = [ '.', 'include', 'lib' ] + if bld.env.use_intree_heimdal: + extra_includes += [ '../source4/heimdal/lib/com_err', + '../source4/heimdal/lib/gssapi', + '../source4/heimdal_build' ] + + if not bld.CONFIG_SET('USING_SYSTEM_TDB'): + extra_includes += [ '../lib/tdb/include' ] + + if not bld.CONFIG_SET('USING_SYSTEM_TEVENT'): + extra_includes += [ '../lib/tevent' ] + + if not bld.CONFIG_SET('USING_SYSTEM_TALLOC'): + extra_includes += [ '../lib/talloc' ] + + if not bld.CONFIG_SET('USING_SYSTEM_POPT'): + extra_includes += [ '../lib/popt' ] + + # s3 builds assume that they will have a bunch of extra include paths + includes = [] + for d in extra_includes: + includes += [ os.path.join(s3reldir, d) ] + + # the rule may already have some includes listed + if 'includes' in kwargs: + includes += TO_LIST(kwargs['includes']) + kwargs['includes'] = includes + + # some S3 code assumes that CONFIGFILE is set + cflags = ['-DCONFIGFILE="%s"' % bld.env['CONFIGFILE']] + if 'cflags' in kwargs: + cflags += TO_LIST(kwargs['cflags']) + kwargs['cflags'] = cflags + +# these wrappers allow for mixing of S3 and S4 build rules in the one build + +def SAMBA3_LIBRARY(bld, name, *args, **kwargs): + s3_fix_kwargs(bld, kwargs) + kwargs['allow_undefined_symbols'] = True + return bld.SAMBA_LIBRARY(name, *args, **kwargs) +Build.BuildContext.SAMBA3_LIBRARY = SAMBA3_LIBRARY + +def SAMBA3_MODULE(bld, name, *args, **kwargs): + s3_fix_kwargs(bld, kwargs) + kwargs['allow_undefined_symbols'] = True + return bld.SAMBA_MODULE(name, *args, **kwargs) +Build.BuildContext.SAMBA3_MODULE = SAMBA3_MODULE + +def SAMBA3_SUBSYSTEM(bld, name, *args, **kwargs): + s3_fix_kwargs(bld, kwargs) + return bld.SAMBA_SUBSYSTEM(name, *args, **kwargs) +Build.BuildContext.SAMBA3_SUBSYSTEM = SAMBA3_SUBSYSTEM + +def SAMBA3_BINARY(bld, name, *args, **kwargs): + s3_fix_kwargs(bld, kwargs) + return bld.SAMBA_BINARY(name, *args, **kwargs) +Build.BuildContext.SAMBA3_BINARY = SAMBA3_BINARY diff --git a/buildtools/wafsamba/samba_abi.py b/buildtools/wafsamba/samba_abi.py new file mode 100644 index 0000000000..990e1e5fdf --- /dev/null +++ b/buildtools/wafsamba/samba_abi.py @@ -0,0 +1,234 @@ +# functions for handling ABI checking of libraries + +import Options, Utils, os, Logs, samba_utils, sys, Task, fnmatch, re, Build +from TaskGen import feature, before, after + +# these type maps cope with platform specific names for common types +# please add new type mappings into the list below +abi_type_maps = { + '_Bool' : 'bool', + 'struct __va_list_tag *' : 'va_list' + } + +version_key = lambda x: map(int, x.split(".")) + +def normalise_signature(sig): + '''normalise a signature from gdb''' + sig = sig.strip() + sig = re.sub('^\$[0-9]+\s=\s\{*', '', sig) + sig = re.sub('\}(\s0x[0-9a-f]+\s<\w+>)?$', '', sig) + sig = re.sub('0x[0-9a-f]+', '0xXXXX', sig) + + for t in abi_type_maps: + # we need to cope with non-word characters in mapped types + m = t + m = m.replace('*', '\*') + if m[-1].isalnum() or m[-1] == '_': + m += '\\b' + if m[0].isalnum() or m[0] == '_': + m = '\\b' + m + sig = re.sub(m, abi_type_maps[t], sig) + return sig + +def normalise_varargs(sig): + '''cope with older versions of gdb''' + sig = re.sub(',\s\.\.\.', '', sig) + return sig + +def parse_sigs(sigs, abi_match): + '''parse ABI signatures file''' + abi_match = samba_utils.TO_LIST(abi_match) + ret = {} + a = sigs.split('\n') + for s in a: + if s.find(':') == -1: + continue + sa = s.split(':') + if abi_match: + matched = False + for p in abi_match: + if p[0] == '!' and fnmatch.fnmatch(sa[0], p[1:]): + break + elif fnmatch.fnmatch(sa[0], p): + matched = True + break + if not matched: + continue + ret[sa[0]] = normalise_signature(sa[1]) + return ret + +def save_sigs(sig_file, parsed_sigs): + '''save ABI signatures to a file''' + sigs = '' + for s in sorted(parsed_sigs.keys()): + sigs += '%s: %s\n' % (s, parsed_sigs[s]) + return samba_utils.save_file(sig_file, sigs, create_dir=True) + + +def abi_check_task(self): + '''check if the ABI has changed''' + abi_gen = self.ABI_GEN + + libpath = self.inputs[0].abspath(self.env) + libname = os.path.basename(libpath) + + sigs = Utils.cmd_output([abi_gen, libpath]) + parsed_sigs = parse_sigs(sigs, self.ABI_MATCH) + + sig_file = self.ABI_FILE + + old_sigs = samba_utils.load_file(sig_file) + if old_sigs is None or Options.options.ABI_UPDATE: + if not save_sigs(sig_file, parsed_sigs): + raise Utils.WafError('Failed to save ABI file "%s"' % sig_file) + Logs.warn('Generated ABI signatures %s' % sig_file) + return + + parsed_old_sigs = parse_sigs(old_sigs, self.ABI_MATCH) + + # check all old sigs + got_error = False + for s in parsed_old_sigs: + if not s in parsed_sigs: + Logs.error('%s: symbol %s has been removed - please update major version\n\tsignature: %s' % ( + libname, s, parsed_old_sigs[s])) + got_error = True + elif normalise_varargs(parsed_old_sigs[s]) != normalise_varargs(parsed_sigs[s]): + Logs.error('%s: symbol %s has changed - please update major version\n\told_signature: %s\n\tnew_signature: %s' % ( + libname, s, parsed_old_sigs[s], parsed_sigs[s])) + got_error = True + + for s in parsed_sigs: + if not s in parsed_old_sigs: + Logs.error('%s: symbol %s has been added - please mark it _PRIVATE_ or update minor version\n\tsignature: %s' % ( + libname, s, parsed_sigs[s])) + got_error = True + + if got_error: + raise Utils.WafError('ABI for %s has changed - please fix library version then build with --abi-update\nSee http://wiki.samba.org/index.php/Waf#ABI_Checking for more information' % libname) + + +t = Task.task_type_from_func('abi_check', abi_check_task, color='BLUE', ext_in='.bin') +t.quiet = True +# allow "waf --abi-check" to force re-checking the ABI +if '--abi-check' in sys.argv: + Task.always_run(t) + +@after('apply_link') +@feature('abi_check') +def abi_check(self): + '''check that ABI matches saved signatures''' + env = self.bld.env + if not env.ABI_CHECK or self.abi_directory is None: + return + + # if the platform doesn't support -fvisibility=hidden then the ABI + # checks become fairly meaningless + if not env.HAVE_VISIBILITY_ATTR: + return + + topsrc = self.bld.srcnode.abspath() + abi_gen = os.path.join(topsrc, 'buildtools/scripts/abi_gen.sh') + + abi_file = "%s/%s-%s.sigs" % (self.abi_directory, self.name, self.vnum) + + tsk = self.create_task('abi_check', self.link_task.outputs[0]) + tsk.ABI_FILE = abi_file + tsk.ABI_MATCH = self.abi_match + tsk.ABI_GEN = abi_gen + + +def abi_process_file(fname, version, symmap): + '''process one ABI file, adding new symbols to the symmap''' + f = open(fname, mode='r') + for line in f: + symname = line.split(":")[0] + if not symname in symmap: + symmap[symname] = version + f.close() + +def abi_write_vscript(vscript, libname, current_version, versions, symmap, abi_match): + '''write a vscript file for a library in --version-script format + + :param vscript: Path to the vscript file + :param libname: Name of the library, uppercased + :param current_version: Current version + :param versions: Versions to consider + :param symmap: Dictionary mapping symbols -> version + :param abi_match: List of symbols considered to be public in the current version + ''' + + invmap = {} + for s in symmap: + invmap.setdefault(symmap[s], []).append(s) + + f = open(vscript, mode='w') + last_key = "" + versions = sorted(versions, key=version_key) + for k in versions: + symver = "%s_%s" % (libname, k) + if symver == current_version: + break + f.write("%s {\n" % symver) + if k in invmap: + f.write("\tglobal: \n") + for s in invmap.get(k, []): + f.write("\t\t%s;\n" % s); + f.write("}%s;\n\n" % last_key) + last_key = " %s" % symver + f.write("%s {\n" % current_version) + f.write("\tglobal:\n") + for x in abi_match: + f.write("\t\t%s;\n" % x) + if abi_match != ["*"]: + f.write("\tlocal: *;\n") + f.write("};\n") + f.close() + + +def abi_build_vscript(task): + '''generate a vscript file for our public libraries''' + + tgt = task.outputs[0].bldpath(task.env) + + symmap = {} + versions = [] + for f in task.inputs: + fname = f.abspath(task.env) + basename = os.path.basename(fname) + version = basename[len(task.env.LIBNAME)+1:-len(".sigs")] + versions.append(version) + abi_process_file(fname, version, symmap) + abi_write_vscript(tgt, task.env.LIBNAME, task.env.VERSION, versions, symmap, + task.env.ABI_MATCH) + + +def ABI_VSCRIPT(bld, libname, abi_directory, version, vscript, abi_match=None): + '''generate a vscript file for our public libraries''' + if abi_directory: + source = bld.path.ant_glob('%s/%s-[0-9]*.sigs' % (abi_directory, libname)) + def abi_file_key(path): + return version_key(path[:-len(".sigs")].rsplit("-")[-1]) + source = sorted(source.split(), key=abi_file_key) + else: + source = '' + + libname = os.path.basename(libname) + version = os.path.basename(version) + libname = libname.replace("-", "_").replace("+","_").upper() + version = version.replace("-", "_").replace("+","_").upper() + + t = bld.SAMBA_GENERATOR(vscript, + rule=abi_build_vscript, + source=source, + group='vscripts', + target=vscript) + if abi_match is None: + abi_match = ["*"] + else: + abi_match = samba_utils.TO_LIST(abi_match) + t.env.ABI_MATCH = abi_match + t.env.VERSION = version + t.env.LIBNAME = libname + t.vars = ['LIBNAME', 'VERSION', 'ABI_MATCH'] +Build.BuildContext.ABI_VSCRIPT = ABI_VSCRIPT diff --git a/buildtools/wafsamba/samba_autoconf.py b/buildtools/wafsamba/samba_autoconf.py new file mode 100644 index 0000000000..174ca14210 --- /dev/null +++ b/buildtools/wafsamba/samba_autoconf.py @@ -0,0 +1,715 @@ +# a waf tool to add autoconf-like macros to the configure section + +import Build, os, sys, Options, preproc, Logs +import string +from Configure import conf +from samba_utils import * +import samba_cross + +missing_headers = set() + +#################################################### +# some autoconf like helpers, to make the transition +# to waf a bit easier for those used to autoconf +# m4 files + +@runonce +@conf +def DEFINE(conf, d, v, add_to_cflags=False, quote=False): + '''define a config option''' + conf.define(d, v, quote=quote) + if add_to_cflags: + conf.env.append_value('CCDEFINES', d + '=' + str(v)) + +def hlist_to_string(conf, headers=None): + '''convert a headers list to a set of #include lines''' + hdrs='' + hlist = conf.env.hlist + if headers: + hlist = hlist[:] + hlist.extend(TO_LIST(headers)) + for h in hlist: + hdrs += '#include <%s>\n' % h + return hdrs + + +@conf +def COMPOUND_START(conf, msg): + '''start a compound test''' + def null_check_message_1(self,*k,**kw): + return + def null_check_message_2(self,*k,**kw): + return + + v = getattr(conf.env, 'in_compound', []) + if v != [] and v != 0: + conf.env.in_compound = v + 1 + return + conf.check_message_1(msg) + conf.saved_check_message_1 = conf.check_message_1 + conf.check_message_1 = null_check_message_1 + conf.saved_check_message_2 = conf.check_message_2 + conf.check_message_2 = null_check_message_2 + conf.env.in_compound = 1 + + +@conf +def COMPOUND_END(conf, result): + '''start a compound test''' + conf.env.in_compound -= 1 + if conf.env.in_compound != 0: + return + conf.check_message_1 = conf.saved_check_message_1 + conf.check_message_2 = conf.saved_check_message_2 + p = conf.check_message_2 + if result == True: + p('ok ') + elif result == False: + p('not found', 'YELLOW') + else: + p(result) + + +@feature('nolink') +def nolink(self): + '''using the nolink type in conf.check() allows us to avoid + the link stage of a test, thus speeding it up for tests + that where linking is not needed''' + pass + + +def CHECK_HEADER(conf, h, add_headers=False, lib=None): + '''check for a header''' + if h in missing_headers and lib is None: + return False + d = h.upper().replace('/', '_') + d = d.replace('.', '_') + d = d.replace('-', '_') + d = 'HAVE_%s' % d + if CONFIG_SET(conf, d): + if add_headers: + if not h in conf.env.hlist: + conf.env.hlist.append(h) + return True + + (ccflags, ldflags) = library_flags(conf, lib) + + hdrs = hlist_to_string(conf, headers=h) + ret = conf.check(fragment='%s\nint main(void) { return 0; }' % hdrs, + type='nolink', + execute=0, + ccflags=ccflags, + msg="Checking for header %s" % h) + if not ret: + missing_headers.add(h) + return False + + conf.DEFINE(d, 1) + if add_headers and not h in conf.env.hlist: + conf.env.hlist.append(h) + return ret + + +@conf +def CHECK_HEADERS(conf, headers, add_headers=False, together=False, lib=None): + '''check for a list of headers + + when together==True, then the headers accumulate within this test. + This is useful for interdependent headers + ''' + ret = True + if not add_headers and together: + saved_hlist = conf.env.hlist[:] + set_add_headers = True + else: + set_add_headers = add_headers + for hdr in TO_LIST(headers): + if not CHECK_HEADER(conf, hdr, set_add_headers, lib=lib): + ret = False + if not add_headers and together: + conf.env.hlist = saved_hlist + return ret + + +def header_list(conf, headers=None, lib=None): + '''form a list of headers which exist, as a string''' + hlist=[] + if headers is not None: + for h in TO_LIST(headers): + if CHECK_HEADER(conf, h, add_headers=False, lib=lib): + hlist.append(h) + return hlist_to_string(conf, headers=hlist) + + +@conf +def CHECK_TYPE(conf, t, alternate=None, headers=None, define=None, lib=None, msg=None): + '''check for a single type''' + if define is None: + define = 'HAVE_' + t.upper().replace(' ', '_') + if msg is None: + msg='Checking for %s' % t + ret = CHECK_CODE(conf, '%s _x' % t, + define, + execute=False, + headers=headers, + local_include=False, + msg=msg, + lib=lib, + link=False) + if not ret and alternate: + conf.DEFINE(t, alternate) + return ret + + +@conf +def CHECK_TYPES(conf, list, headers=None, define=None, alternate=None, lib=None): + '''check for a list of types''' + ret = True + for t in TO_LIST(list): + if not CHECK_TYPE(conf, t, headers=headers, + define=define, alternate=alternate, lib=lib): + ret = False + return ret + + +@conf +def CHECK_TYPE_IN(conf, t, headers=None, alternate=None, define=None): + '''check for a single type with a header''' + return CHECK_TYPE(conf, t, headers=headers, alternate=alternate, define=define) + + +@conf +def CHECK_VARIABLE(conf, v, define=None, always=False, + headers=None, msg=None, lib=None): + '''check for a variable declaration (or define)''' + if define is None: + define = 'HAVE_%s' % v.upper() + + if msg is None: + msg="Checking for variable %s" % v + + return CHECK_CODE(conf, + # we need to make sure the compiler doesn't + # optimize it out... + ''' + #ifndef %s + void *_x; _x=(void *)&%s; return (int)_x; + #endif + return 0 + ''' % (v, v), + execute=False, + link=False, + msg=msg, + local_include=False, + lib=lib, + headers=headers, + define=define, + always=always) + + +@conf +def CHECK_DECLS(conf, vars, reverse=False, headers=None, always=False): + '''check a list of variable declarations, using the HAVE_DECL_xxx form + of define + + When reverse==True then use HAVE_xxx_DECL instead of HAVE_DECL_xxx + ''' + ret = True + for v in TO_LIST(vars): + if not reverse: + define='HAVE_DECL_%s' % v.upper() + else: + define='HAVE_%s_DECL' % v.upper() + if not CHECK_VARIABLE(conf, v, + define=define, + headers=headers, + msg='Checking for declaration of %s' % v, + always=always): + ret = False + return ret + + +def CHECK_FUNC(conf, f, link=True, lib=None, headers=None): + '''check for a function''' + define='HAVE_%s' % f.upper() + + ret = False + + conf.COMPOUND_START('Checking for %s' % f) + + if link is None or link == True: + ret = CHECK_CODE(conf, + # this is based on the autoconf strategy + ''' + #define %s __fake__%s + #ifdef HAVE_LIMITS_H + # include <limits.h> + #else + # include <assert.h> + #endif + #undef %s + #if defined __stub_%s || defined __stub___%s + #error "bad glibc stub" + #endif + extern char %s(); + int main() { return %s(); } + ''' % (f, f, f, f, f, f, f), + execute=False, + link=True, + addmain=False, + add_headers=False, + define=define, + local_include=False, + lib=lib, + headers=headers, + msg='Checking for %s' % f) + + if not ret: + ret = CHECK_CODE(conf, + # it might be a macro + # we need to make sure the compiler doesn't + # optimize it out... + 'void *__x = (void *)%s; return (int)__x' % f, + execute=False, + link=True, + addmain=True, + add_headers=True, + define=define, + local_include=False, + lib=lib, + headers=headers, + msg='Checking for macro %s' % f) + + if not ret and (link is None or link == False): + ret = CHECK_VARIABLE(conf, f, + define=define, + headers=headers, + msg='Checking for declaration of %s' % f) + conf.COMPOUND_END(ret) + return ret + + +@conf +def CHECK_FUNCS(conf, list, link=True, lib=None, headers=None): + '''check for a list of functions''' + ret = True + for f in TO_LIST(list): + if not CHECK_FUNC(conf, f, link=link, lib=lib, headers=headers): + ret = False + return ret + + +@conf +def CHECK_SIZEOF(conf, vars, headers=None, define=None): + '''check the size of a type''' + ret = True + for v in TO_LIST(vars): + v_define = define + if v_define is None: + v_define = 'SIZEOF_%s' % v.upper().replace(' ', '_') + if not CHECK_CODE(conf, + 'printf("%%u", (unsigned)sizeof(%s))' % v, + define=v_define, + execute=True, + define_ret=True, + quote=False, + headers=headers, + local_include=False, + msg="Checking size of %s" % v): + ret = False + return ret + + + +@conf +def CHECK_CODE(conf, code, define, + always=False, execute=False, addmain=True, + add_headers=True, mandatory=False, + headers=None, msg=None, cflags='', includes='# .', + local_include=True, lib=None, link=True, + define_ret=False, quote=False, + on_target=True): + '''check if some code compiles and/or runs''' + + if CONFIG_SET(conf, define): + return True + + if headers is not None: + CHECK_HEADERS(conf, headers=headers, lib=lib) + + if add_headers: + hdrs = header_list(conf, headers=headers, lib=lib) + else: + hdrs = '' + if execute: + execute = 1 + else: + execute = 0 + + defs = conf.get_config_header() + + if addmain: + fragment='%s\n%s\n int main(void) { %s; return 0; }\n' % (defs, hdrs, code) + else: + fragment='%s\n%s\n%s\n' % (defs, hdrs, code) + + if msg is None: + msg="Checking for %s" % define + + cflags = TO_LIST(cflags) + + if local_include: + cflags.append('-I%s' % conf.curdir) + + if not link: + type='nolink' + else: + type='cprogram' + + uselib = TO_LIST(lib) + + (ccflags, ldflags) = library_flags(conf, uselib) + + cflags.extend(ccflags) + + if on_target: + exec_args = conf.SAMBA_CROSS_ARGS(msg=msg) + else: + exec_args = [] + + conf.COMPOUND_START(msg) + + ret = conf.check(fragment=fragment, + execute=execute, + define_name = define, + mandatory = mandatory, + ccflags=cflags, + ldflags=ldflags, + includes=includes, + uselib=uselib, + type=type, + msg=msg, + quote=quote, + exec_args=exec_args, + define_ret=define_ret) + if not ret and CONFIG_SET(conf, define): + # sometimes conf.check() returns false, but it + # sets the define. Maybe a waf bug? + ret = True + if ret: + if not define_ret: + conf.DEFINE(define, 1) + conf.COMPOUND_END(True) + else: + conf.COMPOUND_END(conf.env[define]) + return True + if always: + conf.DEFINE(define, 0) + conf.COMPOUND_END(False) + return False + + + +@conf +def CHECK_STRUCTURE_MEMBER(conf, structname, member, + always=False, define=None, headers=None): + '''check for a structure member''' + if define is None: + define = 'HAVE_%s' % member.upper() + return CHECK_CODE(conf, + '%s s; void *_x; _x=(void *)&s.%s' % (structname, member), + define, + execute=False, + link=False, + always=always, + headers=headers, + local_include=False, + msg="Checking for member %s in %s" % (member, structname)) + + +@conf +def CHECK_CFLAGS(conf, cflags): + '''check if the given cflags are accepted by the compiler + ''' + return conf.check(fragment='int main(void) { return 0; }\n', + execute=0, + type='nolink', + ccflags=cflags, + msg="Checking compiler accepts %s" % cflags) + +@conf +def CHECK_LDFLAGS(conf, ldflags): + '''check if the given ldflags are accepted by the linker + ''' + return conf.check(fragment='int main(void) { return 0; }\n', + execute=0, + ldflags=ldflags, + msg="Checking linker accepts %s" % ldflags) + + +@conf +def CONFIG_GET(conf, option): + '''return True if a configuration option was found''' + if (option in conf.env): + return conf.env[option] + else: + return None + +@conf +def CONFIG_SET(conf, option): + '''return True if a configuration option was found''' + return (option in conf.env) and (conf.env[option] != ()) +Build.BuildContext.CONFIG_SET = CONFIG_SET +Build.BuildContext.CONFIG_GET = CONFIG_GET + + +def library_flags(self, libs): + '''work out flags from pkg_config''' + ccflags = [] + ldflags = [] + for lib in TO_LIST(libs): + inc_path = getattr(self.env, 'CPPPATH_%s' % lib.upper(), []) + lib_path = getattr(self.env, 'LIBPATH_%s' % lib.upper(), []) + ccflags.extend(['-I%s' % i for i in inc_path]) + ldflags.extend(['-L%s' % l for l in lib_path]) + extra_ccflags = TO_LIST(getattr(self.env, 'CCFLAGS_%s' % lib.upper(), [])) + extra_ldflags = TO_LIST(getattr(self.env, 'LDFLAGS_%s' % lib.upper(), [])) + ccflags.extend(extra_ccflags) + ldflags.extend(extra_ldflags) + if 'EXTRA_LDFLAGS' in self.env: + ldflags.extend(self.env['EXTRA_LDFLAGS']) + + ccflags = unique_list(ccflags) + ldflags = unique_list(ldflags) + return (ccflags, ldflags) + + +@conf +def CHECK_LIB(conf, libs, mandatory=False, empty_decl=True, set_target=True, shlib=False): + '''check if a set of libraries exist as system libraries + + returns the sublist of libs that do exist as a syslib or [] + ''' + + fragment= ''' +int foo() +{ + int v = 2; + return v*2; +} +''' + ret = [] + liblist = TO_LIST(libs) + for lib in liblist[:]: + if GET_TARGET_TYPE(conf, lib) == 'SYSLIB': + ret.append(lib) + continue + + (ccflags, ldflags) = library_flags(conf, lib) + if shlib: + res = conf.check(features='cc cshlib', fragment=fragment, lib=lib, uselib_store=lib, ccflags=ccflags, ldflags=ldflags) + else: + res = conf.check(lib=lib, uselib_store=lib, ccflags=ccflags, ldflags=ldflags) + + if not res: + if mandatory: + Logs.error("Mandatory library '%s' not found for functions '%s'" % (lib, list)) + sys.exit(1) + if empty_decl: + # if it isn't a mandatory library, then remove it from dependency lists + if set_target: + SET_TARGET_TYPE(conf, lib, 'EMPTY') + else: + conf.define('HAVE_LIB%s' % lib.upper().replace('-','_'), 1) + conf.env['LIB_' + lib.upper()] = lib + if set_target: + conf.SET_TARGET_TYPE(lib, 'SYSLIB') + ret.append(lib) + + return ret + + + +@conf +def CHECK_FUNCS_IN(conf, list, library, mandatory=False, checklibc=False, + headers=None, link=True, empty_decl=True, set_target=True): + """ + check that the functions in 'list' are available in 'library' + if they are, then make that library available as a dependency + + if the library is not available and mandatory==True, then + raise an error. + + If the library is not available and mandatory==False, then + add the library to the list of dependencies to remove from + build rules + + optionally check for the functions first in libc + """ + remaining = TO_LIST(list) + liblist = TO_LIST(library) + + # check if some already found + for f in remaining[:]: + if CONFIG_SET(conf, 'HAVE_%s' % f.upper()): + remaining.remove(f) + + # see if the functions are in libc + if checklibc: + for f in remaining[:]: + if CHECK_FUNC(conf, f, link=True, headers=headers): + remaining.remove(f) + + if remaining == []: + for lib in liblist: + if GET_TARGET_TYPE(conf, lib) != 'SYSLIB' and empty_decl: + SET_TARGET_TYPE(conf, lib, 'EMPTY') + return True + + checklist = conf.CHECK_LIB(liblist, empty_decl=empty_decl, set_target=set_target) + for lib in liblist[:]: + if not lib in checklist and mandatory: + Logs.error("Mandatory library '%s' not found for functions '%s'" % (lib, list)) + sys.exit(1) + + ret = True + for f in remaining: + if not CHECK_FUNC(conf, f, lib=' '.join(checklist), headers=headers, link=link): + ret = False + + return ret + + +@conf +def IN_LAUNCH_DIR(conf): + '''return True if this rule is being run from the launch directory''' + return os.path.realpath(conf.curdir) == os.path.realpath(Options.launch_dir) +Options.Handler.IN_LAUNCH_DIR = IN_LAUNCH_DIR + + +@conf +def SAMBA_CONFIG_H(conf, path=None): + '''write out config.h in the right directory''' + # we don't want to produce a config.h in places like lib/replace + # when we are building projects that depend on lib/replace + if not IN_LAUNCH_DIR(conf): + return + + if Options.options.developer: + # we add these here to ensure that -Wstrict-prototypes is not set during configure + conf.ADD_CFLAGS('-Wall -g -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-align -Wwrite-strings -Werror-implicit-function-declaration -Wformat=2 -Wno-format-y2k -Wmissing-prototypes', + testflags=True) + if os.getenv('TOPLEVEL_BUILD'): + conf.ADD_CFLAGS('-Wcast-qual', testflags=True) + conf.env.DEVELOPER_MODE = True + + if Options.options.picky_developer: + conf.ADD_CFLAGS('-Werror', testflags=True) + + if Options.options.fatal_errors: + conf.ADD_CFLAGS('-Wfatal-errors', testflags=True) + + if Options.options.pedantic: + conf.ADD_CFLAGS('-W', testflags=True) + + if path is None: + conf.write_config_header('config.h', top=True) + else: + conf.write_config_header(path) + conf.SAMBA_CROSS_CHECK_COMPLETE() + + +@conf +def CONFIG_PATH(conf, name, default): + '''setup a configurable path''' + if not name in conf.env: + if default[0] == '/': + conf.env[name] = default + else: + conf.env[name] = conf.env['PREFIX'] + default + +@conf +def ADD_CFLAGS(conf, flags, testflags=False): + '''add some CFLAGS to the command line + optionally set testflags to ensure all the flags work + ''' + if testflags: + ok_flags=[] + for f in flags.split(): + if CHECK_CFLAGS(conf, f): + ok_flags.append(f) + flags = ok_flags + if not 'EXTRA_CFLAGS' in conf.env: + conf.env['EXTRA_CFLAGS'] = [] + conf.env['EXTRA_CFLAGS'].extend(TO_LIST(flags)) + +@conf +def ADD_LDFLAGS(conf, flags, testflags=False): + '''add some LDFLAGS to the command line + optionally set testflags to ensure all the flags work + + this will return the flags that are added, if any + ''' + if testflags: + ok_flags=[] + for f in flags.split(): + if CHECK_LDFLAGS(conf, f): + ok_flags.append(f) + flags = ok_flags + if not 'EXTRA_LDFLAGS' in conf.env: + conf.env['EXTRA_LDFLAGS'] = [] + conf.env['EXTRA_LDFLAGS'].extend(TO_LIST(flags)) + return flags + + +@conf +def ADD_EXTRA_INCLUDES(conf, includes): + '''add some extra include directories to all builds''' + if not 'EXTRA_INCLUDES' in conf.env: + conf.env['EXTRA_INCLUDES'] = [] + conf.env['EXTRA_INCLUDES'].extend(TO_LIST(includes)) + + + +def CURRENT_CFLAGS(bld, target, cflags, hide_symbols=False): + '''work out the current flags. local flags are added first''' + if not 'EXTRA_CFLAGS' in bld.env: + list = [] + else: + list = bld.env['EXTRA_CFLAGS']; + ret = TO_LIST(cflags) + ret.extend(list) + if hide_symbols and bld.env.HAVE_VISIBILITY_ATTR: + ret.append('-fvisibility=hidden') + return ret + + +@conf +def CHECK_CC_ENV(conf): + """trim whitespaces from 'CC'. + The build farm sometimes puts a space at the start""" + if os.environ.get('CC'): + conf.env.CC = TO_LIST(os.environ.get('CC')) + if len(conf.env.CC) == 1: + # make for nicer logs if just a single command + conf.env.CC = conf.env.CC[0] + + +@conf +def SETUP_CONFIGURE_CACHE(conf, enable): + '''enable/disable cache of configure results''' + if enable: + # when -C is chosen, we will use a private cache and will + # not look into system includes. This roughtly matches what + # autoconf does with -C + cache_path = os.path.join(conf.blddir, '.confcache') + mkdir_p(cache_path) + Options.cache_global = os.environ['WAFCACHE'] = cache_path + else: + # when -C is not chosen we will not cache configure checks + # We set the recursion limit low to prevent waf from spending + # a lot of time on the signatures of the files. + Options.cache_global = os.environ['WAFCACHE'] = '' + preproc.recursion_limit = 1 + # in either case we don't need to scan system includes + preproc.go_absolute = False diff --git a/buildtools/wafsamba/samba_autoproto.py b/buildtools/wafsamba/samba_autoproto.py new file mode 100644 index 0000000000..2d8ea546ab --- /dev/null +++ b/buildtools/wafsamba/samba_autoproto.py @@ -0,0 +1,23 @@ +# waf build tool for building automatic prototypes from C source + +import Build +from samba_utils import * + +def SAMBA_AUTOPROTO(bld, header, source): + '''rule for samba prototype generation''' + bld.SET_BUILD_GROUP('prototypes') + relpath = os_path_relpath(bld.curdir, bld.srcnode.abspath()) + name = os.path.join(relpath, header) + SET_TARGET_TYPE(bld, name, 'PROTOTYPE') + t = bld( + name = name, + source = source, + target = header, + on_results=True, + ext_out='.c', + before ='cc', + rule = '${PERL} "${SCRIPT}/mkproto.pl" --srcdir=.. --builddir=. --public=/dev/null --private="${TGT}" ${SRC}' + ) + t.env.SCRIPT = os.path.join(bld.srcnode.abspath(), 'source4/script') +Build.BuildContext.SAMBA_AUTOPROTO = SAMBA_AUTOPROTO + diff --git a/buildtools/wafsamba/samba_bundled.py b/buildtools/wafsamba/samba_bundled.py new file mode 100644 index 0000000000..39edad060d --- /dev/null +++ b/buildtools/wafsamba/samba_bundled.py @@ -0,0 +1,191 @@ +# functions to support bundled libraries + +from Configure import conf +import sys, Logs +from samba_utils import * + +def PRIVATE_NAME(bld, name, private_extension, private_library): + '''possibly rename a library to include a bundled extension''' + + # we now use the same private name for libraries as the public name. + # see http://git.samba.org/?p=tridge/junkcode.git;a=tree;f=shlib for a + # demonstration that this is the right thing to do + # also see http://lists.samba.org/archive/samba-technical/2011-January/075816.html + return name + + +def target_in_list(target, lst, default): + for l in lst: + if target == l: + return True + if '!' + target == l: + return False + if l == 'ALL': + return True + if l == 'NONE': + return False + return default + + +def BUILTIN_LIBRARY(bld, name): + '''return True if a library should be builtin + instead of being built as a shared lib''' + if bld.env.DISABLE_SHARED: + return True + return target_in_list(name, bld.env.BUILTIN_LIBRARIES, False) +Build.BuildContext.BUILTIN_LIBRARY = BUILTIN_LIBRARY + + +def BUILTIN_DEFAULT(opt, builtins): + '''set a comma separated default list of builtin libraries for this package''' + if 'BUILTIN_LIBRARIES_DEFAULT' in Options.options: + return + Options.options['BUILTIN_LIBRARIES_DEFAULT'] = builtins +Options.Handler.BUILTIN_DEFAULT = BUILTIN_DEFAULT + + +def PRIVATE_EXTENSION_DEFAULT(opt, extension, noextension=''): + '''set a default private library extension''' + if 'PRIVATE_EXTENSION_DEFAULT' in Options.options: + return + Options.options['PRIVATE_EXTENSION_DEFAULT'] = extension + Options.options['PRIVATE_EXTENSION_EXCEPTION'] = noextension +Options.Handler.PRIVATE_EXTENSION_DEFAULT = PRIVATE_EXTENSION_DEFAULT + + +def minimum_library_version(conf, libname, default): + '''allow override of mininum system library version''' + + minlist = Options.options.MINIMUM_LIBRARY_VERSION + if not minlist: + return default + + for m in minlist.split(','): + a = m.split(':') + if len(a) != 2: + Logs.error("Bad syntax for --minimum-library-version of %s" % m) + sys.exit(1) + if a[0] == libname: + return a[1] + return default + + +@conf +def LIB_MAY_BE_BUNDLED(conf, libname): + return ('NONE' not in conf.env.BUNDLED_LIBS and + '!%s' % libname not in conf.env.BUNDLED_LIBS) + + +@conf +def LIB_MUST_BE_BUNDLED(conf, libname): + return ('ALL' in conf.env.BUNDLED_LIBS or + libname in conf.env.BUNDLED_LIBS) + + +@runonce +@conf +def CHECK_BUNDLED_SYSTEM(conf, libname, minversion='0.0.0', + checkfunctions=None, headers=None, + onlyif=None, implied_deps=None, + require_headers=True): + '''check if a library is available as a system library. + this first tries via pkg-config, then if that fails + tries by testing for a specified function in the specified lib + ''' + if conf.LIB_MUST_BE_BUNDLED(libname): + return False + found = 'FOUND_SYSTEMLIB_%s' % libname + if found in conf.env: + return conf.env[found] + + def check_functions_headers(): + '''helper function for CHECK_BUNDLED_SYSTEM''' + if checkfunctions is None: + return True + if require_headers and headers and not conf.CHECK_HEADERS(headers, lib=libname): + return False + return conf.CHECK_FUNCS_IN(checkfunctions, libname, headers=headers, + empty_decl=False, set_target=False) + + # see if the library should only use a system version if another dependent + # system version is found. That prevents possible use of mixed library + # versions + if onlyif: + for syslib in TO_LIST(onlyif): + f = 'FOUND_SYSTEMLIB_%s' % syslib + if not f in conf.env: + if not conf.LIB_MAY_BE_BUNDLED(libname): + Logs.error('ERROR: Use of system library %s depends on missing system library %s' % (libname, syslib)) + sys.exit(1) + conf.env[found] = False + return False + + minversion = minimum_library_version(conf, libname, minversion) + + msg = 'Checking for system %s' % libname + if minversion != '0.0.0': + msg += ' >= %s' % minversion + + # try pkgconfig first + if (conf.check_cfg(package=libname, + args='"%s >= %s" --cflags --libs' % (libname, minversion), + msg=msg) and + check_functions_headers()): + conf.SET_TARGET_TYPE(libname, 'SYSLIB') + conf.env[found] = True + if implied_deps: + conf.SET_SYSLIB_DEPS(libname, implied_deps) + return True + if checkfunctions is not None: + if check_functions_headers(): + conf.env[found] = True + if implied_deps: + conf.SET_SYSLIB_DEPS(libname, implied_deps) + conf.SET_TARGET_TYPE(libname, 'SYSLIB') + return True + conf.env[found] = False + if not conf.LIB_MAY_BE_BUNDLED(libname): + Logs.error('ERROR: System library %s of version %s not found, and bundling disabled' % (libname, minversion)) + sys.exit(1) + return False + + +@runonce +@conf +def CHECK_BUNDLED_SYSTEM_PYTHON(conf, libname, modulename, minversion='0.0.0'): + '''check if a python module is available on the system and + has the specified minimum version. + ''' + if conf.LIB_MUST_BE_BUNDLED(libname): + return False + + # see if the library should only use a system version if another dependent + # system version is found. That prevents possible use of mixed library + # versions + minversion = minimum_library_version(conf, libname, minversion) + + try: + m = __import__(modulename) + except ImportError: + found = False + else: + try: + version = m.__version__ + except AttributeError: + found = False + else: + found = tuple(version.split(".")) >= tuple(minversion.split(".")) + if not found and not conf.LIB_MAY_BE_BUNDLED(libname): + Logs.error('ERROR: Python module %s of version %s not found, and bundling disabled' % (libname, minversion)) + sys.exit(1) + return found + + +def NONSHARED_BINARY(bld, name): + '''return True if a binary should be built without non-system shared libs''' + if bld.env.DISABLE_SHARED: + return True + return target_in_list(name, bld.env.NONSHARED_BINARIES, False) +Build.BuildContext.NONSHARED_BINARY = NONSHARED_BINARY + + diff --git a/buildtools/wafsamba/samba_conftests.py b/buildtools/wafsamba/samba_conftests.py new file mode 100644 index 0000000000..c7e596d27a --- /dev/null +++ b/buildtools/wafsamba/samba_conftests.py @@ -0,0 +1,414 @@ +# a set of config tests that use the samba_autoconf functions +# to test for commonly needed configuration options + +import os, Build, shutil, Utils, re +from Configure import conf +from samba_utils import * + +@conf +def CHECK_ICONV(conf, define='HAVE_NATIVE_ICONV'): + '''check if the iconv library is installed + optionally pass a define''' + if conf.CHECK_FUNCS_IN('iconv_open', 'iconv', checklibc=True, headers='iconv.h'): + conf.DEFINE(define, 1) + return True + return False + + +@conf +def CHECK_LARGEFILE(conf, define='HAVE_LARGEFILE'): + '''see what we need for largefile support''' + if conf.CHECK_CODE('return !(sizeof(off_t) >= 8)', + define, + execute=True, + msg='Checking for large file support'): + return True + if conf.CHECK_CODE('return !(sizeof(off_t) >= 8)', + define, + execute=True, + cflags='-D_FILE_OFFSET_BITS=64', + msg='Checking for -D_FILE_OFFSET_BITS=64'): + conf.DEFINE('_FILE_OFFSET_BITS', 64) + return True + return False + + +@conf +def CHECK_C_PROTOTYPE(conf, function, prototype, define, headers=None, msg=None): + '''verify that a C prototype matches the one on the current system''' + if not conf.CHECK_DECLS(function, headers=headers): + return False + if not msg: + msg = 'Checking C prototype for %s' % function + return conf.CHECK_CODE('%s; void *_x = (void *)%s' % (prototype, function), + define=define, + local_include=False, + headers=headers, + link=False, + execute=False, + msg=msg) + + +@conf +def CHECK_CHARSET_EXISTS(conf, charset, outcharset='UCS-2LE', headers=None, define=None): + '''check that a named charset is able to be used with iconv_open() for conversion + to a target charset + ''' + msg = 'Checking if can we convert from %s to %s' % (charset, outcharset) + if define is None: + define = 'HAVE_CHARSET_%s' % charset.upper().replace('-','_') + return conf.CHECK_CODE(''' + iconv_t cd = iconv_open("%s", "%s"); + if (cd == 0 || cd == (iconv_t)-1) return -1; + ''' % (charset, outcharset), + define=define, + execute=True, + msg=msg, + lib='iconv', + headers=headers) + +def find_config_dir(conf): + '''find a directory to run tests in''' + k = 0 + while k < 10000: + dir = os.path.join(conf.blddir, '.conf_check_%d' % k) + try: + shutil.rmtree(dir) + except OSError: + pass + try: + os.stat(dir) + except: + break + k += 1 + + try: + os.makedirs(dir) + except: + conf.fatal('cannot create a configuration test folder %r' % dir) + + try: + os.stat(dir) + except: + conf.fatal('cannot use the configuration test folder %r' % dir) + return dir + +@conf +def CHECK_SHLIB_INTRASINC_NAME_FLAGS(conf, msg): + ''' + check if the waf default flags for setting the name of lib + are ok + ''' + + snip = ''' +int foo(int v) { + return v * 2; +} +''' + return conf.check(features='cc cshlib',vnum="1",fragment=snip,msg=msg) + +@conf +def CHECK_NEED_LC(conf, msg): + '''check if we need -lc''' + + dir = find_config_dir(conf) + + env = conf.env + + bdir = os.path.join(dir, 'testbuild2') + if not os.path.exists(bdir): + os.makedirs(bdir) + + + subdir = os.path.join(dir, "liblctest") + + os.makedirs(subdir) + + dest = open(os.path.join(subdir, 'liblc1.c'), 'w') + dest.write('#include <stdio.h>\nint lib_func(void) { FILE *f = fopen("foo", "r");}\n') + dest.close() + + bld = Build.BuildContext() + bld.log = conf.log + bld.all_envs.update(conf.all_envs) + bld.all_envs['default'] = env + bld.lst_variants = bld.all_envs.keys() + bld.load_dirs(dir, bdir) + + bld.rescan(bld.srcnode) + + bld(features='cc cshlib', + source='liblctest/liblc1.c', + ldflags=conf.env['EXTRA_LDFLAGS'], + target='liblc', + name='liblc') + + try: + bld.compile() + conf.check_message(msg, '', True) + return True + except: + conf.check_message(msg, '', False) + return False + + +@conf +def CHECK_SHLIB_W_PYTHON(conf, msg): + '''check if we need -undefined dynamic_lookup''' + + dir = find_config_dir(conf) + + env = conf.env + + snip = ''' +#include <Python.h> +#include <crt_externs.h> +#define environ (*_NSGetEnviron()) + +static PyObject *ldb_module = NULL; +int foo(int v) { + extern char **environ; + environ[0] = 1; + ldb_module = PyImport_ImportModule("ldb"); + return v * 2; +}''' + return conf.check(features='cc cshlib',uselib='PYEMBED',fragment=snip,msg=msg) + +# this one is quite complex, and should probably be broken up +# into several parts. I'd quite like to create a set of CHECK_COMPOUND() +# functions that make writing complex compound tests like this much easier +@conf +def CHECK_LIBRARY_SUPPORT(conf, rpath=False, version_script=False, msg=None): + '''see if the platform supports building libraries''' + + if msg is None: + if rpath: + msg = "rpath library support" + else: + msg = "building library support" + + dir = find_config_dir(conf) + + bdir = os.path.join(dir, 'testbuild') + if not os.path.exists(bdir): + os.makedirs(bdir) + + env = conf.env + + subdir = os.path.join(dir, "libdir") + + os.makedirs(subdir) + + dest = open(os.path.join(subdir, 'lib1.c'), 'w') + dest.write('int lib_func(void) { return 42; }\n') + dest.close() + + dest = open(os.path.join(dir, 'main.c'), 'w') + dest.write('int main(void) {return !(lib_func() == 42);}\n') + dest.close() + + bld = Build.BuildContext() + bld.log = conf.log + bld.all_envs.update(conf.all_envs) + bld.all_envs['default'] = env + bld.lst_variants = bld.all_envs.keys() + bld.load_dirs(dir, bdir) + + bld.rescan(bld.srcnode) + + ldflags = [] + if version_script: + ldflags.append("-Wl,--version-script=%s/vscript" % bld.path.abspath()) + dest = open(os.path.join(dir,'vscript'), 'w') + dest.write('TEST_1.0A2 { global: *; };\n') + dest.close() + + bld(features='cc cshlib', + source='libdir/lib1.c', + target='libdir/lib1', + ldflags=ldflags, + name='lib1') + + o = bld(features='cc cprogram', + source='main.c', + target='prog1', + uselib_local='lib1') + + if rpath: + o.rpath=os.path.join(bdir, 'default/libdir') + + # compile the program + try: + bld.compile() + except: + conf.check_message(msg, '', False) + return False + + # path for execution + lastprog = o.link_task.outputs[0].abspath(env) + + if not rpath: + if 'LD_LIBRARY_PATH' in os.environ: + old_ld_library_path = os.environ['LD_LIBRARY_PATH'] + else: + old_ld_library_path = None + ADD_LD_LIBRARY_PATH(os.path.join(bdir, 'default/libdir')) + + # we need to run the program, try to get its result + args = conf.SAMBA_CROSS_ARGS(msg=msg) + proc = Utils.pproc.Popen([lastprog] + args, stdout=Utils.pproc.PIPE, stderr=Utils.pproc.PIPE) + (out, err) = proc.communicate() + w = conf.log.write + w(str(out)) + w('\n') + w(str(err)) + w('\nreturncode %r\n' % proc.returncode) + ret = (proc.returncode == 0) + + if not rpath: + os.environ['LD_LIBRARY_PATH'] = old_ld_library_path or '' + + conf.check_message(msg, '', ret) + return ret + + + +@conf +def CHECK_PERL_MANPAGE(conf, msg=None, section=None): + '''work out what extension perl uses for manpages''' + + if msg is None: + if section: + msg = "perl man%s extension" % section + else: + msg = "perl manpage generation" + + conf.check_message_1(msg) + + dir = find_config_dir(conf) + + bdir = os.path.join(dir, 'testbuild') + if not os.path.exists(bdir): + os.makedirs(bdir) + + dest = open(os.path.join(bdir, 'Makefile.PL'), 'w') + dest.write(""" +use ExtUtils::MakeMaker; +WriteMakefile( + 'NAME' => 'WafTest', + 'EXE_FILES' => [ 'WafTest' ] +); +""") + dest.close() + back = os.path.abspath('.') + os.chdir(bdir) + proc = Utils.pproc.Popen(['perl', 'Makefile.PL'], + stdout=Utils.pproc.PIPE, + stderr=Utils.pproc.PIPE) + (out, err) = proc.communicate() + os.chdir(back) + + ret = (proc.returncode == 0) + if not ret: + conf.check_message_2('not found', color='YELLOW') + return + + if section: + f = open(os.path.join(bdir,'Makefile'), 'r') + man = f.read() + f.close() + m = re.search('MAN%sEXT\s+=\s+(\w+)' % section, man) + if not m: + conf.check_message_2('not found', color='YELLOW') + return + ext = m.group(1) + conf.check_message_2(ext) + return ext + + conf.check_message_2('ok') + return True + + +@conf +def CHECK_COMMAND(conf, cmd, msg=None, define=None, on_target=True, boolean=False): + '''run a command and return result''' + if msg is None: + msg = 'Checking %s' % ' '.join(cmd) + conf.COMPOUND_START(msg) + cmd = cmd[:] + if on_target: + cmd.extend(conf.SAMBA_CROSS_ARGS(msg=msg)) + try: + ret = Utils.cmd_output(cmd) + except: + conf.COMPOUND_END(False) + return False + if boolean: + conf.COMPOUND_END('ok') + if define: + conf.DEFINE(define, '1') + else: + ret = ret.strip() + conf.COMPOUND_END(ret) + if define: + conf.DEFINE(define, ret, quote=True) + return ret + + +@conf +def CHECK_UNAME(conf): + '''setup SYSTEM_UNAME_* defines''' + ret = True + for v in "sysname machine release version".split(): + if not conf.CHECK_CODE(''' + struct utsname n; + if (uname(&n) == -1) return -1; + printf("%%s", n.%s); + ''' % v, + define='SYSTEM_UNAME_%s' % v.upper(), + execute=True, + define_ret=True, + quote=True, + headers='sys/utsname.h', + local_include=False, + msg="Checking uname %s type" % v): + ret = False + return ret + +@conf +def CHECK_INLINE(conf): + '''check for the right value for inline''' + conf.COMPOUND_START('Checking for inline') + for i in ['inline', '__inline__', '__inline']: + ret = conf.CHECK_CODE(''' + typedef int foo_t; + static %s foo_t static_foo () {return 0; } + %s foo_t foo () {return 0; }''' % (i, i), + define='INLINE_MACRO', + addmain=False, + link=False) + if ret: + if i != 'inline': + conf.DEFINE('inline', i, quote=False) + break + if not ret: + conf.COMPOUND_END(ret) + else: + conf.COMPOUND_END(i) + return ret + +@conf +def CHECK_XSLTPROC_MANPAGES(conf): + '''check if xsltproc can run with the given stylesheets''' + + + if not conf.CONFIG_SET('XSLTPROC'): + conf.find_program('xsltproc', var='XSLTPROC') + if not conf.CONFIG_SET('XSLTPROC'): + return False + + s='http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl' + conf.CHECK_COMMAND('%s --nonet %s 2> /dev/null' % (conf.env.XSLTPROC, s), + msg='Checking for stylesheet %s' % s, + define='XSLTPROC_MANPAGES', on_target=False, + boolean=True) diff --git a/buildtools/wafsamba/samba_cross.py b/buildtools/wafsamba/samba_cross.py new file mode 100644 index 0000000000..3838e34ec4 --- /dev/null +++ b/buildtools/wafsamba/samba_cross.py @@ -0,0 +1,134 @@ +# functions for handling cross-compilation + +import Utils, Logs, sys, os, Options, re +from Configure import conf + +real_Popen = None + +ANSWER_UNKNOWN = (254, "") +ANSWER_FAIL = (255, "") +ANSWER_OK = (0, "") + +cross_answers_incomplete = False + + +def add_answer(ca_file, msg, answer): + '''add an answer to a set of cross answers''' + try: + f = open(ca_file, 'a') + except: + Logs.error("Unable to open cross-answers file %s" % ca_file) + sys.exit(1) + if answer == ANSWER_OK: + f.write('%s: OK\n' % msg) + elif answer == ANSWER_UNKNOWN: + f.write('%s: UNKNOWN\n' % msg) + elif answer == ANSWER_FAIL: + f.write('%s: FAIL\n' % msg) + else: + (retcode, retstring) = answer + f.write('%s: (%d, "%s")' % (msg, retcode, retstring)) + f.close() + + +def cross_answer(ca_file, msg): + '''return a (retcode,retstring) tuple from a answers file''' + try: + f = open(ca_file, 'r') + except: + add_answer(ca_file, msg, ANSWER_UNKNOWN) + return ANSWER_UNKNOWN + for line in f: + line = line.strip() + if line == '' or line[0] == '#': + continue + if line.find(':') != -1: + a = line.split(':') + thismsg = a[0].strip() + if thismsg != msg: + continue + ans = a[1].strip() + if ans == "OK" or ans == "YES": + f.close() + return ANSWER_OK + elif ans == "UNKNOWN": + f.close() + return ANSWER_UNKNOWN + elif ans == "FAIL" or ans == "NO": + f.close() + return ANSWER_FAIL + elif ans[0] == '"': + return (0, ans.strip('"')) + elif ans[0] == "'": + return (0, ans.strip("'")) + else: + m = re.match('\(\s*(-?\d+)\s*,\s*\"(.*)\"\s*\)', ans) + if m: + f.close() + return (int(m.group(1)), m.group(2)) + else: + raise Utils.WafError("Bad answer format '%s' in %s" % (line, ca_file)) + f.close() + add_answer(ca_file, msg, ANSWER_UNKNOWN) + return ANSWER_UNKNOWN + + +class cross_Popen(Utils.pproc.Popen): + '''cross-compilation wrapper for Popen''' + def __init__(*k, **kw): + (obj, args) = k + + if '--cross-execute' in args: + # when --cross-execute is set, then change the arguments + # to use the cross emulator + i = args.index('--cross-execute') + newargs = args[i+1].split() + newargs.extend(args[0:i]) + args = newargs + elif '--cross-answers' in args: + # when --cross-answers is set, then change the arguments + # to use the cross answers if available + i = args.index('--cross-answers') + ca_file = args[i+1] + msg = args[i+2] + ans = cross_answer(ca_file, msg) + if ans == ANSWER_UNKNOWN: + global cross_answers_incomplete + cross_answers_incomplete = True + (retcode, retstring) = ans + args = ['/bin/sh', '-c', "echo -n '%s'; exit %d" % (retstring, retcode)] + real_Popen.__init__(*(obj, args), **kw) + + +@conf +def SAMBA_CROSS_ARGS(conf, msg=None): + '''get exec_args to pass when running cross compiled binaries''' + if not conf.env.CROSS_COMPILE: + return [] + + global real_Popen + if real_Popen is None: + real_Popen = Utils.pproc.Popen + Utils.pproc.Popen = cross_Popen + + ret = [] + + if conf.env.CROSS_EXECUTE: + ret.extend(['--cross-execute', conf.env.CROSS_EXECUTE]) + elif conf.env.CROSS_ANSWERS: + if msg is None: + raise Utils.WafError("Cannot have NULL msg in cross-answers") + ret.extend(['--cross-answers', os.path.join(Options.launch_dir, conf.env.CROSS_ANSWERS), msg]) + + if ret == []: + raise Utils.WafError("Cannot cross-compile without either --cross-execute or --cross-answers") + + return ret + +@conf +def SAMBA_CROSS_CHECK_COMPLETE(conf): + '''check if we have some unanswered questions''' + global cross_answers_incomplete + if conf.env.CROSS_COMPILE and cross_answers_incomplete: + raise Utils.WafError("Cross answers file %s is incomplete" % conf.env.CROSS_ANSWERS) + return True diff --git a/buildtools/wafsamba/samba_deps.py b/buildtools/wafsamba/samba_deps.py new file mode 100644 index 0000000000..adeb3645ce --- /dev/null +++ b/buildtools/wafsamba/samba_deps.py @@ -0,0 +1,1184 @@ +# Samba automatic dependency handling and project rules + +import Build, os, sys, re, Environment, Logs, time +from samba_utils import * +from samba_autoconf import * +from samba_bundled import BUILTIN_LIBRARY + +@conf +def ADD_GLOBAL_DEPENDENCY(ctx, dep): + '''add a dependency for all binaries and libraries''' + if not 'GLOBAL_DEPENDENCIES' in ctx.env: + ctx.env.GLOBAL_DEPENDENCIES = [] + ctx.env.GLOBAL_DEPENDENCIES.append(dep) + + +@conf +def BREAK_CIRCULAR_LIBRARY_DEPENDENCIES(ctx): + '''indicate that circular dependencies between libraries should be broken.''' + ctx.env.ALLOW_CIRCULAR_LIB_DEPENDENCIES = True + + +@conf +def SET_SYSLIB_DEPS(conf, target, deps): + '''setup some implied dependencies for a SYSLIB''' + cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS') + cache[target] = deps + + +def expand_subsystem_deps(bld): + '''expand the reverse dependencies resulting from subsystem + attributes of modules. This is walking over the complete list + of declared subsystems, and expands the samba_deps_extended list for any + module<->subsystem dependencies''' + + subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS') + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + for subsystem_name in subsystem_list: + bld.ASSERT(subsystem_name in targets, "Subsystem target %s not declared" % subsystem_name) + type = targets[subsystem_name] + if type == 'DISABLED' or type == 'EMPTY': + continue + + # for example, + # subsystem_name = dcerpc_server (a subsystem) + # subsystem = dcerpc_server (a subsystem object) + # module_name = rpc_epmapper (a module within the dcerpc_server subsystem) + # module = rpc_epmapper (a module object within the dcerpc_server subsystem) + + subsystem = bld.name_to_obj(subsystem_name, bld.env) + bld.ASSERT(subsystem is not None, "Unable to find subsystem %s" % subsystem_name) + for d in subsystem_list[subsystem_name]: + module_name = d['TARGET'] + module_type = targets[module_name] + if module_type in ['DISABLED', 'EMPTY']: + continue + bld.ASSERT(subsystem is not None, + "Subsystem target %s for %s (%s) not found" % (subsystem_name, module_name, module_type)) + if module_type in ['SUBSYSTEM']: + # if a module is a plain object type (not a library) then the + # subsystem it is part of needs to have it as a dependency, so targets + # that depend on this subsystem get the modules of that subsystem + subsystem.samba_deps_extended.append(module_name) + subsystem.samba_deps_extended = unique_list(subsystem.samba_deps_extended) + + + +def build_dependencies(self): + '''This builds the dependency list for a target. It runs after all the targets are declared + + The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing + the full dependency list for a target until we have all of the targets declared. + ''' + + if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']: + self.uselib = list(self.final_syslibs) + self.uselib_local = list(self.final_libs) + self.add_objects = list(self.final_objects) + + # extra link flags from pkg_config + libs = self.final_syslibs.copy() + + (ccflags, ldflags) = library_flags(self, list(libs)) + new_ldflags = getattr(self, 'samba_ldflags', [])[:] + new_ldflags.extend(ldflags) + self.ldflags = new_ldflags + + if getattr(self, 'allow_undefined_symbols', False) and self.env.undefined_ldflags: + for f in self.env.undefined_ldflags: + self.ldflags.remove(f) + + debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s', + self.sname, self.uselib, self.uselib_local, self.add_objects) + + if self.samba_type in ['SUBSYSTEM']: + # this is needed for the ccflags of libs that come from pkg_config + self.uselib = list(self.final_syslibs) + self.uselib.extend(list(self.direct_syslibs)) + for lib in self.final_libs: + t = self.bld.name_to_obj(lib, self.bld.env) + self.uselib.extend(list(t.final_syslibs)) + self.uselib = unique_list(self.uselib) + + if getattr(self, 'uselib', None): + up_list = [] + for l in self.uselib: + up_list.append(l.upper()) + self.uselib = up_list + + +def build_includes(self): + '''This builds the right set of includes for a target. + + One tricky part of this is that the includes= attribute for a + target needs to use paths which are relative to that targets + declaration directory (which we can get at via t.path). + + The way this works is the includes list gets added as + samba_includes in the main build task declaration. Then this + function runs after all of the tasks are declared, and it + processes the samba_includes attribute to produce a includes= + attribute + ''' + + if getattr(self, 'samba_includes', None) is None: + return + + bld = self.bld + + inc_deps = includes_objects(bld, self, set(), {}) + + includes = [] + + # maybe add local includes + if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True): + includes.append('.') + + includes.extend(self.samba_includes_extended) + + if 'EXTRA_INCLUDES' in bld.env and getattr(self, 'global_include', True): + includes.extend(bld.env['EXTRA_INCLUDES']) + + includes.append('#') + + inc_set = set() + inc_abs = [] + + for d in inc_deps: + t = bld.name_to_obj(d, bld.env) + bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname)) + inclist = getattr(t, 'samba_includes_extended', [])[:] + if getattr(t, 'local_include', True) == True: + inclist.append('.') + if inclist == []: + continue + tpath = t.samba_abspath + for inc in inclist: + npath = tpath + '/' + inc + if not npath in inc_set: + inc_abs.append(npath) + inc_set.add(npath) + + mypath = self.path.abspath(bld.env) + for inc in inc_abs: + relpath = os_path_relpath(inc, mypath) + includes.append(relpath) + + if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True): + includes.append('.') + + # now transform the includes list to be relative to the top directory + # which is represented by '#' in waf. This allows waf to cache the + # includes lists more efficiently + includes_top = [] + for i in includes: + if i[0] == '#': + # some are already top based + includes_top.append(i) + continue + absinc = os.path.join(self.path.abspath(), i) + relinc = os_path_relpath(absinc, self.bld.srcnode.abspath()) + includes_top.append('#' + relinc) + + self.includes = unique_list(includes_top) + debug('deps: includes for target %s: includes=%s', + self.sname, self.includes) + + + + +def add_init_functions(self): + '''This builds the right set of init functions''' + + bld = self.bld + + subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS') + + # cope with the separated object lists from BINARY and LIBRARY targets + sname = self.sname + if sname.endswith('.objlist'): + sname = sname[0:-8] + + modules = [] + if sname in subsystems: + modules.append(sname) + + m = getattr(self, 'samba_modules', None) + if m is not None: + modules.extend(TO_LIST(m)) + + m = getattr(self, 'samba_subsystem', None) + if m is not None: + modules.append(m) + + sentinal = getattr(self, 'init_function_sentinal', 'NULL') + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + cflags = getattr(self, 'samba_cflags', [])[:] + + if modules == []: + sname = sname.replace('-','_') + sname = sname.replace('/','_') + cflags.append('-DSTATIC_%s_MODULES=%s' % (sname, sentinal)) + if sentinal == 'NULL': + cflags.append('-DSTATIC_%s_MODULES_PROTO' % sname) + self.ccflags = cflags + return + + for m in modules: + bld.ASSERT(m in subsystems, + "No init_function defined for module '%s' in target '%s'" % (m, self.sname)) + init_fn_list = [] + for d in subsystems[m]: + if targets[d['TARGET']] != 'DISABLED': + init_fn_list.append(d['INIT_FUNCTION']) + if init_fn_list == []: + cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinal)) + if sentinal == 'NULL': + cflags.append('-DSTATIC_%s_MODULES_PROTO' % m) + else: + cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal)) + proto='' + for f in init_fn_list: + proto = proto + '_MODULE_PROTO(%s)' % f + cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m, proto)) + self.ccflags = cflags + + + +def check_duplicate_sources(bld, tgt_list): + '''see if we are compiling the same source file more than once + without an allow_duplicates attribute''' + + debug('deps: checking for duplicate sources') + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + ret = True + + global tstart + + for t in tgt_list: + source_list = TO_LIST(getattr(t, 'source', '')) + tpath = os.path.normpath(os_path_relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default')) + obj_sources = set() + for s in source_list: + p = os.path.normpath(os.path.join(tpath, s)) + if p in obj_sources: + Logs.error("ERROR: source %s appears twice in target '%s'" % (p, t.sname)) + sys.exit(1) + obj_sources.add(p) + t.samba_source_set = obj_sources + + subsystems = {} + + # build a list of targets that each source file is part of + for t in tgt_list: + sources = [] + if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]: + continue + for obj in t.add_objects: + t2 = t.bld.name_to_obj(obj, bld.env) + source_set = getattr(t2, 'samba_source_set', set()) + for s in source_set: + if not s in subsystems: + subsystems[s] = {} + if not t.sname in subsystems[s]: + subsystems[s][t.sname] = [] + subsystems[s][t.sname].append(t2.sname) + + for s in subsystems: + if len(subsystems[s]) > 1 and Options.options.SHOW_DUPLICATES: + Logs.warn("WARNING: source %s is in more than one target: %s" % (s, subsystems[s].keys())) + for tname in subsystems[s]: + if len(subsystems[s][tname]) > 1: + raise Utils.WafError("ERROR: source %s is in more than one subsystem of target '%s': %s" % (s, tname, subsystems[s][tname])) + + return ret + + +def check_orpaned_targets(bld, tgt_list): + '''check if any build targets are orphaned''' + + target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE') + + debug('deps: checking for orphaned targets') + + for t in tgt_list: + if getattr(t, 'samba_used', False) == True: + continue + type = target_dict[t.sname] + if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']: + if re.search('^PIDL_', t.sname) is None: + Logs.warn("Target %s of type %s is unused by any other target" % (t.sname, type)) + + +def check_group_ordering(bld, tgt_list): + '''see if we have any dependencies that violate the group ordering + + It is an error for a target to depend on a target from a later + build group + ''' + + def group_name(g): + tm = bld.task_manager + return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0] + + for g in bld.task_manager.groups: + gname = group_name(g) + for t in g.tasks_gen: + t.samba_group = gname + + grp_map = {} + idx = 0 + for g in bld.task_manager.groups: + name = group_name(g) + grp_map[name] = idx + idx += 1 + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + ret = True + for t in tgt_list: + tdeps = getattr(t, 'add_objects', []) + getattr(t, 'uselib_local', []) + for d in tdeps: + t2 = bld.name_to_obj(d, bld.env) + if t2 is None: + continue + map1 = grp_map[t.samba_group] + map2 = grp_map[t2.samba_group] + + if map2 > map1: + Logs.error("Target %r in build group %r depends on target %r from later build group %r" % ( + t.sname, t.samba_group, t2.sname, t2.samba_group)) + ret = False + + return ret + + +def show_final_deps(bld, tgt_list): + '''show the final dependencies for all targets''' + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + for t in tgt_list: + if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON', 'SUBSYSTEM']: + continue + debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s', + t.sname, t.uselib, getattr(t, 'uselib_local', []), getattr(t, 'add_objects', [])) + + +def add_samba_attributes(bld, tgt_list): + '''ensure a target has a the required samba attributes''' + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + for t in tgt_list: + if t.name != '': + t.sname = t.name + else: + t.sname = t.target + t.samba_type = targets[t.sname] + t.samba_abspath = t.path.abspath(bld.env) + t.samba_deps_extended = t.samba_deps[:] + t.samba_includes_extended = TO_LIST(t.samba_includes)[:] + t.ccflags = getattr(t, 'samba_cflags', '') + +def replace_grouping_libraries(bld, tgt_list): + '''replace dependencies based on grouping libraries + + If a library is marked as a grouping library, then any target that + depends on a subsystem that is part of that grouping library gets + that dependency replaced with a dependency on the grouping library + ''' + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + grouping = {} + + # find our list of grouping libraries, mapped from the subsystems they depend on + for t in tgt_list: + if not getattr(t, 'grouping_library', False): + continue + for dep in t.samba_deps_extended: + bld.ASSERT(dep in targets, "grouping library target %s not declared in %s" % (dep, t.sname)) + if targets[dep] == 'SUBSYSTEM': + grouping[dep] = t.sname + + # now replace any dependencies on elements of grouping libraries + for t in tgt_list: + for i in range(len(t.samba_deps_extended)): + dep = t.samba_deps_extended[i] + if dep in grouping: + if t.sname != grouping[dep]: + debug("deps: target %s: replacing dependency %s with grouping library %s" % (t.sname, dep, grouping[dep])) + t.samba_deps_extended[i] = grouping[dep] + + + +def build_direct_deps(bld, tgt_list): + '''build the direct_objects and direct_libs sets for each target''' + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + syslib_deps = LOCAL_CACHE(bld, 'SYSLIB_DEPS') + + global_deps = bld.env.GLOBAL_DEPENDENCIES + global_deps_exclude = set() + for dep in global_deps: + t = bld.name_to_obj(dep, bld.env) + for d in t.samba_deps: + # prevent loops from the global dependencies list + global_deps_exclude.add(d) + global_deps_exclude.add(d + '.objlist') + + for t in tgt_list: + t.direct_objects = set() + t.direct_libs = set() + t.direct_syslibs = set() + deps = t.samba_deps_extended[:] + if getattr(t, 'samba_use_global_deps', False) and not t.sname in global_deps_exclude: + deps.extend(global_deps) + for d in deps: + if d == t.sname: continue + if not d in targets: + Logs.error("Unknown dependency '%s' in '%s'" % (d, t.sname)) + sys.exit(1) + if targets[d] in [ 'EMPTY', 'DISABLED' ]: + continue + if targets[d] == 'PYTHON' and targets[t.sname] != 'PYTHON' and t.sname.find('.objlist') == -1: + # this check should be more restrictive, but for now we have pidl-generated python + # code that directly depends on other python modules + Logs.error('ERROR: Target %s has dependency on python module %s' % (t.sname, d)) + sys.exit(1) + if targets[d] == 'SYSLIB': + t.direct_syslibs.add(d) + if d in syslib_deps: + for implied in TO_LIST(syslib_deps[d]): + if BUILTIN_LIBRARY(bld, implied): + t.direct_objects.add(implied) + elif targets[implied] == 'SYSLIB': + t.direct_syslibs.add(implied) + elif targets[implied] in ['LIBRARY', 'MODULE']: + t.direct_libs.add(implied) + else: + Logs.error('Implied dependency %s in %s is of type %s' % ( + implied, t.sname, targets[implied])) + sys.exit(1) + continue + t2 = bld.name_to_obj(d, bld.env) + if t2 is None: + Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname)) + sys.exit(1) + if t2.samba_type in [ 'LIBRARY', 'MODULE' ]: + t.direct_libs.add(d) + elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]: + t.direct_objects.add(d) + debug('deps: built direct dependencies') + + +def dependency_loop(loops, t, target): + '''add a dependency loop to the loops dictionary''' + if t.sname == target: + return + if not target in loops: + loops[target] = set() + if not t.sname in loops[target]: + loops[target].add(t.sname) + + +def indirect_libs(bld, t, chain, loops): + '''recursively calculate the indirect library dependencies for a target + + An indirect library is a library that results from a dependency on + a subsystem + ''' + + ret = getattr(t, 'indirect_libs', None) + if ret is not None: + return ret + + ret = set() + for obj in t.direct_objects: + if obj in chain: + dependency_loop(loops, t, obj) + continue + chain.add(obj) + t2 = bld.name_to_obj(obj, bld.env) + r2 = indirect_libs(bld, t2, chain, loops) + chain.remove(obj) + ret = ret.union(t2.direct_libs) + ret = ret.union(r2) + + for obj in indirect_objects(bld, t, set(), loops): + if obj in chain: + dependency_loop(loops, t, obj) + continue + chain.add(obj) + t2 = bld.name_to_obj(obj, bld.env) + r2 = indirect_libs(bld, t2, chain, loops) + chain.remove(obj) + ret = ret.union(t2.direct_libs) + ret = ret.union(r2) + + t.indirect_libs = ret + + return ret + + +def indirect_objects(bld, t, chain, loops): + '''recursively calculate the indirect object dependencies for a target + + indirect objects are the set of objects from expanding the + subsystem dependencies + ''' + + ret = getattr(t, 'indirect_objects', None) + if ret is not None: return ret + + ret = set() + for lib in t.direct_objects: + if lib in chain: + dependency_loop(loops, t, lib) + continue + chain.add(lib) + t2 = bld.name_to_obj(lib, bld.env) + r2 = indirect_objects(bld, t2, chain, loops) + chain.remove(lib) + ret = ret.union(t2.direct_objects) + ret = ret.union(r2) + + t.indirect_objects = ret + return ret + + +def extended_objects(bld, t, chain): + '''recursively calculate the extended object dependencies for a target + + extended objects are the union of: + - direct objects + - indirect objects + - direct and indirect objects of all direct and indirect libraries + ''' + + ret = getattr(t, 'extended_objects', None) + if ret is not None: return ret + + ret = set() + ret = ret.union(t.final_objects) + + for lib in t.final_libs: + if lib in chain: + continue + t2 = bld.name_to_obj(lib, bld.env) + chain.add(lib) + r2 = extended_objects(bld, t2, chain) + chain.remove(lib) + ret = ret.union(t2.final_objects) + ret = ret.union(r2) + + t.extended_objects = ret + return ret + + +def includes_objects(bld, t, chain, inc_loops): + '''recursively calculate the includes object dependencies for a target + + includes dependencies come from either library or object dependencies + ''' + ret = getattr(t, 'includes_objects', None) + if ret is not None: + return ret + + ret = t.direct_objects.copy() + ret = ret.union(t.direct_libs) + + for obj in t.direct_objects: + if obj in chain: + dependency_loop(inc_loops, t, obj) + continue + chain.add(obj) + t2 = bld.name_to_obj(obj, bld.env) + r2 = includes_objects(bld, t2, chain, inc_loops) + chain.remove(obj) + ret = ret.union(t2.direct_objects) + ret = ret.union(r2) + + for lib in t.direct_libs: + if lib in chain: + dependency_loop(inc_loops, t, lib) + continue + chain.add(lib) + t2 = bld.name_to_obj(lib, bld.env) + if t2 is None: + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + Logs.error('Target %s of type %s not found in direct_libs for %s' % ( + lib, targets[lib], t.sname)) + sys.exit(1) + r2 = includes_objects(bld, t2, chain, inc_loops) + chain.remove(lib) + ret = ret.union(t2.direct_objects) + ret = ret.union(r2) + + t.includes_objects = ret + return ret + + +def break_dependency_loops(bld, tgt_list): + '''find and break dependency loops''' + loops = {} + inc_loops = {} + + # build up the list of loops + for t in tgt_list: + indirect_objects(bld, t, set(), loops) + indirect_libs(bld, t, set(), loops) + includes_objects(bld, t, set(), inc_loops) + + # break the loops + for t in tgt_list: + if t.sname in loops: + for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']: + objs = getattr(t, attr, set()) + setattr(t, attr, objs.difference(loops[t.sname])) + + for loop in loops: + debug('deps: Found dependency loops for target %s : %s', loop, loops[loop]) + + for loop in inc_loops: + debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop]) + + # expand the loops mapping by one level + for loop in loops.copy(): + for tgt in loops[loop]: + if tgt in loops: + loops[loop] = loops[loop].union(loops[tgt]) + + for loop in inc_loops.copy(): + for tgt in inc_loops[loop]: + if tgt in inc_loops: + inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt]) + + + # expand indirect subsystem and library loops + for loop in loops.copy(): + t = bld.name_to_obj(loop, bld.env) + if t.samba_type in ['SUBSYSTEM']: + loops[loop] = loops[loop].union(t.indirect_objects) + loops[loop] = loops[loop].union(t.direct_objects) + if t.samba_type in ['LIBRARY','PYTHON']: + loops[loop] = loops[loop].union(t.indirect_libs) + loops[loop] = loops[loop].union(t.direct_libs) + if loop in loops[loop]: + loops[loop].remove(loop) + + # expand indirect includes loops + for loop in inc_loops.copy(): + t = bld.name_to_obj(loop, bld.env) + inc_loops[loop] = inc_loops[loop].union(t.includes_objects) + if loop in inc_loops[loop]: + inc_loops[loop].remove(loop) + + # add in the replacement dependencies + for t in tgt_list: + for loop in loops: + for attr in ['indirect_objects', 'indirect_libs']: + objs = getattr(t, attr, set()) + if loop in objs: + diff = loops[loop].difference(objs) + if t.sname in diff: + diff.remove(t.sname) + if diff: + debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff) + objs = objs.union(diff) + setattr(t, attr, objs) + + for loop in inc_loops: + objs = getattr(t, 'includes_objects', set()) + if loop in objs: + diff = inc_loops[loop].difference(objs) + if t.sname in diff: + diff.remove(t.sname) + if diff: + debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff) + objs = objs.union(diff) + setattr(t, 'includes_objects', objs) + + +def reduce_objects(bld, tgt_list): + '''reduce objects by looking for indirect object dependencies''' + rely_on = {} + + for t in tgt_list: + t.extended_objects = None + + changed = False + + for type in ['BINARY', 'PYTHON', 'LIBRARY']: + for t in tgt_list: + if t.samba_type != type: continue + # if we will indirectly link to a target then we don't need it + new = t.final_objects.copy() + for l in t.final_libs: + t2 = bld.name_to_obj(l, bld.env) + t2_obj = extended_objects(bld, t2, set()) + dup = new.intersection(t2_obj) + if t.sname in rely_on: + dup = dup.difference(rely_on[t.sname]) + if dup: + debug('deps: removing dups from %s of type %s: %s also in %s %s', + t.sname, t.samba_type, dup, t2.samba_type, l) + new = new.difference(dup) + changed = True + if not l in rely_on: + rely_on[l] = set() + rely_on[l] = rely_on[l].union(dup) + t.final_objects = new + + if not changed: + return False + + # add back in any objects that were relied upon by the reduction rules + for r in rely_on: + t = bld.name_to_obj(r, bld.env) + t.final_objects = t.final_objects.union(rely_on[r]) + + return True + + +def show_library_loop(bld, lib1, lib2, path, seen): + '''show the detailed path of a library loop between lib1 and lib2''' + + t = bld.name_to_obj(lib1, bld.env) + if not lib2 in getattr(t, 'final_libs', set()): + return + + for d in t.samba_deps_extended: + if d in seen: + continue + seen.add(d) + path2 = path + '=>' + d + if d == lib2: + Logs.warn('library loop path: ' + path2) + return + show_library_loop(bld, d, lib2, path2, seen) + seen.remove(d) + + +def calculate_final_deps(bld, tgt_list, loops): + '''calculate the final library and object dependencies''' + for t in tgt_list: + # start with the maximum possible list + t.final_libs = t.direct_libs.union(indirect_libs(bld, t, set(), loops)) + t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops)) + + for t in tgt_list: + # don't depend on ourselves + if t.sname in t.final_libs: + t.final_libs.remove(t.sname) + if t.sname in t.final_objects: + t.final_objects.remove(t.sname) + + # handle any non-shared binaries + for t in tgt_list: + if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname): + subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS') + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + # replace lib deps with objlist deps + for l in t.final_libs: + objname = l + '.objlist' + t2 = bld.name_to_obj(objname, bld.env) + if t2 is None: + Logs.error('ERROR: subsystem %s not found' % objname) + sys.exit(1) + t.final_objects.add(objname) + t.final_objects = t.final_objects.union(extended_objects(bld, t2, set())) + if l in subsystem_list: + # its a subsystem - we also need the contents of any modules + for d in subsystem_list[l]: + module_name = d['TARGET'] + if targets[module_name] == 'LIBRARY': + objname = module_name + '.objlist' + elif targets[module_name] == 'SUBSYSTEM': + objname = module_name + else: + continue + t2 = bld.name_to_obj(objname, bld.env) + if t2 is None: + Logs.error('ERROR: subsystem %s not found' % objname) + sys.exit(1) + t.final_objects.add(objname) + t.final_objects = t.final_objects.union(extended_objects(bld, t2, set())) + t.final_libs = set() + + # find any library loops + for t in tgt_list: + if t.samba_type in ['LIBRARY', 'PYTHON']: + for l in t.final_libs.copy(): + t2 = bld.name_to_obj(l, bld.env) + if t.sname in t2.final_libs: + if getattr(bld.env, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False): + # we could break this in either direction. If one of the libraries + # has a version number, and will this be distributed publicly, then + # we should make it the lower level library in the DAG + Logs.warn('deps: removing library loop %s from %s' % (t.sname, t2.sname)) + dependency_loop(loops, t, t2.sname) + t2.final_libs.remove(t.sname) + else: + Logs.error('ERROR: circular library dependency between %s and %s' + % (t.sname, t2.sname)) + show_library_loop(bld, t.sname, t2.sname, t.sname, set()) + show_library_loop(bld, t2.sname, t.sname, t2.sname, set()) + sys.exit(1) + + for loop in loops: + debug('deps: Found dependency loops for target %s : %s', loop, loops[loop]) + + # we now need to make corrections for any library loops we broke up + # any target that depended on the target of the loop and doesn't + # depend on the source of the loop needs to get the loop source added + for type in ['BINARY','PYTHON','LIBRARY','BINARY']: + for t in tgt_list: + if t.samba_type != type: continue + for loop in loops: + if loop in t.final_libs: + diff = loops[loop].difference(t.final_libs) + if t.sname in diff: + diff.remove(t.sname) + if t.sname in diff: + diff.remove(t.sname) + # make sure we don't recreate the loop again! + for d in diff.copy(): + t2 = bld.name_to_obj(d, bld.env) + if t2.samba_type == 'LIBRARY': + if t.sname in t2.final_libs: + debug('deps: removing expansion %s from %s', d, t.sname) + diff.remove(d) + if diff: + debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop, + loops[loop], diff) + t.final_libs = t.final_libs.union(diff) + + # remove objects that are also available in linked libs + count = 0 + while reduce_objects(bld, tgt_list): + count += 1 + if count > 100: + Logs.warn("WARNING: Unable to remove all inter-target object duplicates") + break + debug('deps: Object reduction took %u iterations', count) + + # add in any syslib dependencies + for t in tgt_list: + if not t.samba_type in ['BINARY','PYTHON','LIBRARY','SUBSYSTEM']: + continue + syslibs = set() + for d in t.final_objects: + t2 = bld.name_to_obj(d, bld.env) + syslibs = syslibs.union(t2.direct_syslibs) + # this adds the indirect syslibs as well, which may not be needed + # depending on the linker flags + for d in t.final_libs: + t2 = bld.name_to_obj(d, bld.env) + syslibs = syslibs.union(t2.direct_syslibs) + t.final_syslibs = syslibs + + + # find any unresolved library loops + lib_loop_error = False + for t in tgt_list: + if t.samba_type in ['LIBRARY', 'PYTHON']: + for l in t.final_libs.copy(): + t2 = bld.name_to_obj(l, bld.env) + if t.sname in t2.final_libs: + Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname)) + lib_loop_error = True + if lib_loop_error: + sys.exit(1) + + debug('deps: removed duplicate dependencies') + + +def show_dependencies(bld, target, seen): + '''recursively show the dependencies of target''' + + if target in seen: + return + + t = bld.name_to_obj(target, bld.env) + if t is None: + Logs.error("ERROR: Unable to find target '%s'" % target) + sys.exit(1) + + Logs.info('%s(OBJECTS): %s' % (target, t.direct_objects)) + Logs.info('%s(LIBS): %s' % (target, t.direct_libs)) + Logs.info('%s(SYSLIBS): %s' % (target, t.direct_syslibs)) + + seen.add(target) + + for t2 in t.direct_objects: + show_dependencies(bld, t2, seen) + + +def show_object_duplicates(bld, tgt_list): + '''show a list of object files that are included in more than + one library or binary''' + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + used_by = {} + + Logs.info("showing duplicate objects") + + for t in tgt_list: + if not targets[t.sname] in [ 'LIBRARY', 'PYTHON' ]: + continue + for n in getattr(t, 'final_objects', set()): + t2 = bld.name_to_obj(n, bld.env) + if not n in used_by: + used_by[n] = set() + used_by[n].add(t.sname) + + for n in used_by: + if len(used_by[n]) > 1: + Logs.info("target '%s' is used by %s" % (n, used_by[n])) + + Logs.info("showing indirect dependency counts (sorted by count)") + + def indirect_count(t1, t2): + return len(t2.indirect_objects) - len(t1.indirect_objects) + + sorted_list = sorted(tgt_list, cmp=indirect_count) + for t in sorted_list: + if len(t.indirect_objects) > 1: + Logs.info("%s depends on %u indirect objects" % (t.sname, len(t.indirect_objects))) + + +###################################################################### +# this provides a way to save our dependency calculations between runs +savedeps_version = 3 +savedeps_inputs = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', + 'source', 'grouping_library', 'samba_ldflags', 'allow_undefined_symbols', + 'use_global_deps', 'global_include' ] +savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags', 'ldflags', 'samba_deps_extended'] +savedeps_outenv = ['INC_PATHS'] +savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES', 'EXTRA_CFLAGS', 'EXTRA_LDFLAGS', 'EXTRA_INCLUDES' ] +savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS'] +savedeps_files = ['buildtools/wafsamba/samba_deps.py'] + +def save_samba_deps(bld, tgt_list): + '''save the dependency calculations between builds, to make + further builds faster''' + denv = Environment.Environment() + + denv.version = savedeps_version + denv.savedeps_inputs = savedeps_inputs + denv.savedeps_outputs = savedeps_outputs + denv.input = {} + denv.output = {} + denv.outenv = {} + denv.caches = {} + denv.envvar = {} + denv.files = {} + + for f in savedeps_files: + denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime + + for c in savedeps_caches: + denv.caches[c] = LOCAL_CACHE(bld, c) + + for e in savedeps_envvars: + denv.envvar[e] = bld.env[e] + + for t in tgt_list: + # save all the input attributes for each target + tdeps = {} + for attr in savedeps_inputs: + v = getattr(t, attr, None) + if v is not None: + tdeps[attr] = v + if tdeps != {}: + denv.input[t.sname] = tdeps + + # save all the output attributes for each target + tdeps = {} + for attr in savedeps_outputs: + v = getattr(t, attr, None) + if v is not None: + tdeps[attr] = v + if tdeps != {}: + denv.output[t.sname] = tdeps + + tdeps = {} + for attr in savedeps_outenv: + if attr in t.env: + tdeps[attr] = t.env[attr] + if tdeps != {}: + denv.outenv[t.sname] = tdeps + + depsfile = os.path.join(bld.bdir, "sambadeps") + denv.store(depsfile) + + + +def load_samba_deps(bld, tgt_list): + '''load a previous set of build dependencies if possible''' + depsfile = os.path.join(bld.bdir, "sambadeps") + denv = Environment.Environment() + try: + debug('deps: checking saved dependencies') + denv.load(depsfile) + if (denv.version != savedeps_version or + denv.savedeps_inputs != savedeps_inputs or + denv.savedeps_outputs != savedeps_outputs): + return False + except: + return False + + # check if critical files have changed + for f in savedeps_files: + if f not in denv.files: + return False + if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime: + return False + + # check if caches are the same + for c in savedeps_caches: + if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c): + return False + + # check if caches are the same + for e in savedeps_envvars: + if e not in denv.envvar or denv.envvar[e] != bld.env[e]: + return False + + # check inputs are the same + for t in tgt_list: + tdeps = {} + for attr in savedeps_inputs: + v = getattr(t, attr, None) + if v is not None: + tdeps[attr] = v + if t.sname in denv.input: + olddeps = denv.input[t.sname] + else: + olddeps = {} + if tdeps != olddeps: + #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps) + return False + + # put outputs in place + for t in tgt_list: + if not t.sname in denv.output: continue + tdeps = denv.output[t.sname] + for a in tdeps: + setattr(t, a, tdeps[a]) + + # put output env vars in place + for t in tgt_list: + if not t.sname in denv.outenv: continue + tdeps = denv.outenv[t.sname] + for a in tdeps: + t.env[a] = tdeps[a] + + debug('deps: loaded saved dependencies') + return True + + + +def check_project_rules(bld): + '''check the project rules - ensuring the targets are sane''' + + loops = {} + inc_loops = {} + + tgt_list = get_tgt_list(bld) + + add_samba_attributes(bld, tgt_list) + + force_project_rules = (Options.options.SHOWDEPS or + Options.options.SHOW_DUPLICATES) + + if not force_project_rules and load_samba_deps(bld, tgt_list): + return + + global tstart + tstart = time.clock() + + bld.new_rules = True + Logs.info("Checking project rules ...") + + debug('deps: project rules checking started') + + expand_subsystem_deps(bld) + + debug("deps: expand_subsystem_deps: %f" % (time.clock() - tstart)) + + replace_grouping_libraries(bld, tgt_list) + + debug("deps: replace_grouping_libraries: %f" % (time.clock() - tstart)) + + build_direct_deps(bld, tgt_list) + + debug("deps: build_direct_deps: %f" % (time.clock() - tstart)) + + break_dependency_loops(bld, tgt_list) + + debug("deps: break_dependency_loops: %f" % (time.clock() - tstart)) + + if Options.options.SHOWDEPS: + show_dependencies(bld, Options.options.SHOWDEPS, set()) + + calculate_final_deps(bld, tgt_list, loops) + + debug("deps: calculate_final_deps: %f" % (time.clock() - tstart)) + + if Options.options.SHOW_DUPLICATES: + show_object_duplicates(bld, tgt_list) + + # run the various attribute generators + for f in [ build_dependencies, build_includes, add_init_functions ]: + debug('deps: project rules checking %s', f) + for t in tgt_list: f(t) + debug("deps: %s: %f" % (f, time.clock() - tstart)) + + debug('deps: project rules stage1 completed') + + #check_orpaned_targets(bld, tgt_list) + + if not check_duplicate_sources(bld, tgt_list): + Logs.error("Duplicate sources present - aborting") + sys.exit(1) + + debug("deps: check_duplicate_sources: %f" % (time.clock() - tstart)) + + if not check_group_ordering(bld, tgt_list): + Logs.error("Bad group ordering - aborting") + sys.exit(1) + + debug("deps: check_group_ordering: %f" % (time.clock() - tstart)) + + show_final_deps(bld, tgt_list) + + debug("deps: show_final_deps: %f" % (time.clock() - tstart)) + + debug('deps: project rules checking completed - %u targets checked', + len(tgt_list)) + + if not bld.is_install: + save_samba_deps(bld, tgt_list) + + debug("deps: save_samba_deps: %f" % (time.clock() - tstart)) + + Logs.info("Project rules pass") + + +def CHECK_PROJECT_RULES(bld): + '''enable checking of project targets for sanity''' + if bld.env.added_project_rules: + return + bld.env.added_project_rules = True + bld.add_pre_fun(check_project_rules) +Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES + + diff --git a/buildtools/wafsamba/samba_dist.py b/buildtools/wafsamba/samba_dist.py new file mode 100644 index 0000000000..a11a37cc1b --- /dev/null +++ b/buildtools/wafsamba/samba_dist.py @@ -0,0 +1,204 @@ +# customised version of 'waf dist' for Samba tools +# uses git ls-files to get file lists + +import Utils, os, sys, tarfile, stat, Scripting, Logs, Options +from samba_utils import * + +dist_dirs = None +dist_blacklist = "" + +def add_symlink(tar, fname, abspath, basedir): + '''handle symlinks to directories that may move during packaging''' + if not os.path.islink(abspath): + return False + tinfo = tar.gettarinfo(name=abspath, arcname=fname) + tgt = os.readlink(abspath) + + if dist_dirs: + # we need to find the target relative to the main directory + # this is here to cope with symlinks into the buildtools + # directory from within the standalone libraries in Samba. For example, + # a symlink to ../../builtools/scripts/autogen-waf.sh needs + # to be rewritten as a symlink to buildtools/scripts/autogen-waf.sh + # when the tarball for talloc is built + + # the filename without the appname-version + rel_fname = '/'.join(fname.split('/')[1:]) + + # join this with the symlink target + tgt_full = os.path.join(os.path.dirname(rel_fname), tgt) + + # join with the base directory + tgt_base = os.path.normpath(os.path.join(basedir, tgt_full)) + + # see if this is inside one of our dist_dirs + for dir in dist_dirs.split(): + if dir.find(':') != -1: + destdir=dir.split(':')[1] + dir=dir.split(':')[0] + else: + destdir = '.' + if dir == basedir: + # internal links don't get rewritten + continue + if dir == tgt_base[0:len(dir)] and tgt_base[len(dir)] == '/': + new_tgt = destdir + tgt_base[len(dir):] + tinfo.linkname = new_tgt + break + + tinfo.uid = 0 + tinfo.gid = 0 + tinfo.uname = 'root' + tinfo.gname = 'root' + tar.addfile(tinfo) + return True + +def add_tarfile(tar, fname, abspath, basedir): + '''add a file to the tarball''' + if add_symlink(tar, fname, abspath, basedir): + return + try: + tinfo = tar.gettarinfo(name=abspath, arcname=fname) + except OSError: + Logs.error('Unable to find file %s - missing from git checkout?' % abspath) + sys.exit(1) + tinfo.uid = 0 + tinfo.gid = 0 + tinfo.uname = 'root' + tinfo.gname = 'root' + fh = open(abspath) + tar.addfile(tinfo, fileobj=fh) + fh.close() + + +def vcs_dir_contents(path): + """Return the versioned files under a path. + + :return: List of paths relative to path + """ + repo = path + while repo != "/": + if os.path.isdir(os.path.join(repo, ".git")): + ls_files_cmd = [ 'git', 'ls-files', '--full-name', + os_path_relpath(path, repo) ] + cwd = None + env = dict(os.environ) + env["GIT_DIR"] = os.path.join(repo, ".git") + break + elif os.path.isdir(os.path.join(repo, ".bzr")): + ls_files_cmd = [ 'bzr', 'ls', '--recursive', '--versioned', + os_path_relpath(path, repo)] + cwd = repo + env = None + break + repo = os.path.dirname(repo) + if repo == "/": + raise Exception("unsupported or no vcs for %s" % path) + return Utils.cmd_output(ls_files_cmd, cwd=cwd, env=env).split() + + +def dist(appname='',version=''): + if not isinstance(appname, str) or not appname: + # this copes with a mismatch in the calling arguments for dist() + appname = Utils.g_module.APPNAME + version = Utils.g_module.VERSION + if not version: + version = Utils.g_module.VERSION + + srcdir = os.path.normpath(os.path.join(os.path.dirname(Utils.g_module.root_path), Utils.g_module.srcdir)) + + if not dist_dirs: + Logs.error('You must use samba_dist.DIST_DIRS() to set which directories to package') + sys.exit(1) + + dist_base = '%s-%s' % (appname, version) + + if Options.options.SIGN_RELEASE: + dist_name = '%s.tar' % (dist_base) + tar = tarfile.open(dist_name, 'w') + else: + dist_name = '%s.tar.gz' % (dist_base) + tar = tarfile.open(dist_name, 'w:gz') + + blacklist = dist_blacklist.split() + + for dir in dist_dirs.split(): + if dir.find(':') != -1: + destdir=dir.split(':')[1] + dir=dir.split(':')[0] + else: + destdir = '.' + absdir = os.path.join(srcdir, dir) + try: + files = vcs_dir_contents(absdir) + except Exception, e: + Logs.error('unable to get contents of %s: %s' % (absdir, e)) + sys.exit(1) + for f in files: + abspath = os.path.join(srcdir, f) + + if dir != '.': + f = f[len(dir)+1:] + + # Remove files in the blacklist + if f in dist_blacklist: + continue + blacklisted = False + # Remove directories in the blacklist + for d in blacklist: + if f.startswith(d): + blacklisted = True + if blacklisted: + continue + if os.path.isdir(abspath): + continue + if destdir != '.': + f = destdir + '/' + f + fname = dist_base + '/' + f + add_tarfile(tar, fname, abspath, dir) + + tar.close() + + if Options.options.SIGN_RELEASE: + import gzip + try: + os.unlink(dist_name + '.asc') + except OSError: + pass + + cmd = "gpg --detach-sign --armor " + dist_name + os.system(cmd) + uncompressed_tar = open(dist_name, 'rb') + compressed_tar = gzip.open(dist_name + '.gz', 'wb') + while 1: + buffer = uncompressed_tar.read(1048576) + if buffer: + compressed_tar.write(buffer) + else: + break + uncompressed_tar.close() + compressed_tar.close() + os.unlink(dist_name) + Logs.info('Created %s.gz %s.asc' % (dist_name, dist_name)) + dist_name = dist_name + '.gz' + else: + Logs.info('Created %s' % dist_name) + + return dist_name + + +@conf +def DIST_DIRS(dirs): + '''set the directories to package, relative to top srcdir''' + global dist_dirs + if not dist_dirs: + dist_dirs = dirs + +@conf +def DIST_BLACKLIST(blacklist): + '''set the files to exclude from packaging, relative to top srcdir''' + global dist_blacklist + if not dist_blacklist: + dist_blacklist = blacklist + +Scripting.dist = dist diff --git a/buildtools/wafsamba/samba_headers.py b/buildtools/wafsamba/samba_headers.py new file mode 100644 index 0000000000..cca6420b6c --- /dev/null +++ b/buildtools/wafsamba/samba_headers.py @@ -0,0 +1,180 @@ +# specialist handling of header files for Samba + +import Build, re, Task, TaskGen, shutil, sys, Logs +from samba_utils import * + + +def header_install_path(header, header_path): + '''find the installation path for a header, given a header_path option''' + if not header_path: + return '' + if not isinstance(header_path, list): + return header_path + for (p1, dir) in header_path: + for p2 in TO_LIST(p1): + if fnmatch.fnmatch(header, p2): + return dir + # default to current path + return '' + + +re_header = re.compile('^\s*#\s*include[ \t]*"([^"]+)"', re.I | re.M) + +# a dictionary mapping source header paths to public header paths +header_map = {} + +def find_suggested_header(hpath): + '''find a suggested header path to use''' + base = os.path.basename(hpath) + ret = [] + for h in header_map: + if os.path.basename(h) == base: + ret.append('<%s>' % header_map[h]) + ret.append('"%s"' % h) + return ret + +def create_public_header(task): + '''create a public header from a private one, output within the build tree''' + src = task.inputs[0].abspath(task.env) + tgt = task.outputs[0].bldpath(task.env) + + if os.path.exists(tgt): + os.unlink(tgt) + + relsrc = os_path_relpath(src, task.env.TOPDIR) + + infile = open(src, mode='r') + outfile = open(tgt, mode='w') + linenumber = 0 + + search_paths = [ '', task.env.RELPATH ] + for i in task.env.EXTRA_INCLUDES: + if i.startswith('#'): + search_paths.append(i[1:]) + + for line in infile: + linenumber += 1 + + # allow some straight substitutions + if task.env.public_headers_replace and line.strip() in task.env.public_headers_replace: + outfile.write(task.env.public_headers_replace[line.strip()] + '\n') + continue + + # see if its an include line + m = re_header.match(line) + if m is None: + outfile.write(line) + continue + + # its an include, get the header path + hpath = m.group(1) + if hpath.startswith("bin/default/"): + hpath = hpath[12:] + + # some are always allowed + if task.env.public_headers_skip and hpath in task.env.public_headers_skip: + outfile.write(line) + continue + + # work out the header this refers to + found = False + for s in search_paths: + p = os.path.normpath(os.path.join(s, hpath)) + if p in header_map: + outfile.write("#include <%s>\n" % header_map[p]) + found = True + break + if found: + continue + + if task.env.public_headers_allow_broken: + Logs.warn("Broken public header include '%s' in '%s'" % (hpath, relsrc)) + outfile.write(line) + continue + + # try to be nice to the developer by suggesting an alternative + suggested = find_suggested_header(hpath) + outfile.close() + os.unlink(tgt) + sys.stderr.write("%s:%u:Error: unable to resolve public header %s (maybe try one of %s)\n" % ( + os.path.relpath(src, os.getcwd()), linenumber, hpath, suggested)) + raise Utils.WafError("Unable to resolve header path '%s' in public header '%s' in directory %s" % ( + hpath, relsrc, task.env.RELPATH)) + infile.close() + outfile.close() + + +def public_headers_simple(bld, public_headers, header_path=None, public_headers_install=True): + '''install some headers - simple version, no munging needed + ''' + if not public_headers_install: + return + for h in TO_LIST(public_headers): + inst_path = header_install_path(h, header_path) + if h.find(':') != -1: + s = h.split(":") + h_name = s[0] + inst_name = s[1] + else: + h_name = h + inst_name = os.path.basename(h) + bld.INSTALL_FILES('${INCLUDEDIR}', h_name, destname=inst_name) + + + +def PUBLIC_HEADERS(bld, public_headers, header_path=None, public_headers_install=True): + '''install some headers + + header_path may either be a string that is added to the INCLUDEDIR, + or it can be a dictionary of wildcard patterns which map to destination + directories relative to INCLUDEDIR + ''' + bld.SET_BUILD_GROUP('final') + + if not bld.env.build_public_headers: + # in this case no header munging neeeded. Used for tdb, talloc etc + public_headers_simple(bld, public_headers, header_path=header_path, + public_headers_install=public_headers_install) + return + + # create the public header in the given path + # in the build tree + for h in TO_LIST(public_headers): + inst_path = header_install_path(h, header_path) + if h.find(':') != -1: + s = h.split(":") + h_name = s[0] + inst_name = s[1] + else: + h_name = h + inst_name = os.path.basename(h) + relpath1 = os_path_relpath(bld.srcnode.abspath(), bld.curdir) + relpath2 = os_path_relpath(bld.curdir, bld.srcnode.abspath()) + targetdir = os.path.normpath(os.path.join(relpath1, bld.env.build_public_headers, inst_path)) + if not os.path.exists(os.path.join(bld.curdir, targetdir)): + raise Utils.WafError("missing source directory %s for public header %s" % (targetdir, inst_name)) + target = os.path.join(targetdir, inst_name) + + # the source path of the header, relative to the top of the source tree + src_path = os.path.normpath(os.path.join(relpath2, h_name)) + + # the install path of the header, relative to the public include directory + target_path = os.path.normpath(os.path.join(inst_path, inst_name)) + + header_map[src_path] = target_path + + t = bld.SAMBA_GENERATOR('HEADER_%s/%s/%s' % (relpath2, inst_path, inst_name), + group='headers', + rule=create_public_header, + source=h_name, + target=target) + t.env.RELPATH = relpath2 + t.env.TOPDIR = bld.srcnode.abspath() + if not bld.env.public_headers_list: + bld.env.public_headers_list = [] + bld.env.public_headers_list.append(os.path.join(inst_path, inst_name)) + if public_headers_install: + bld.INSTALL_FILES('${INCLUDEDIR}', + target, + destname=os.path.join(inst_path, inst_name), flat=True) +Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS diff --git a/buildtools/wafsamba/samba_install.py b/buildtools/wafsamba/samba_install.py new file mode 100644 index 0000000000..d755d01078 --- /dev/null +++ b/buildtools/wafsamba/samba_install.py @@ -0,0 +1,227 @@ +########################### +# this handles the magic we need to do for installing +# with all the configure options that affect rpath and shared +# library use + +import Options +from TaskGen import feature, before, after +from samba_utils import * + +@feature('install_bin') +@after('apply_core') +@before('apply_link', 'apply_obj_vars') +def install_binary(self): + '''install a binary, taking account of the different rpath varients''' + bld = self.bld + + # get the ldflags we will use for install and build + install_ldflags = install_rpath(self) + build_ldflags = build_rpath(bld) + + if not Options.is_install: + # just need to set rpath if we are not installing + self.env.RPATH = build_ldflags + return + + # work out the install path, expanding variables + install_path = getattr(self, 'samba_inst_path', None) or '${BINDIR}' + install_path = bld.EXPAND_VARIABLES(install_path) + + orig_target = os.path.basename(self.target) + + if install_ldflags != build_ldflags: + # we will be creating a new target name, and using that for the + # install link. That stops us from overwriting the existing build + # target, which has different ldflags + self.target += '.inst' + + # setup the right rpath link flags for the install + self.env.RPATH = install_ldflags + + if not self.samba_install: + # this binary is marked not to be installed + return + + # tell waf to install the right binary + bld.install_as(os.path.join(install_path, orig_target), + os.path.join(self.path.abspath(bld.env), self.target), + chmod=MODE_755) + + + +@feature('install_lib') +@after('apply_core') +@before('apply_link', 'apply_obj_vars') +def install_library(self): + '''install a library, taking account of the different rpath varients''' + if getattr(self, 'done_install_library', False): + return + + bld = self.bld + + install_ldflags = install_rpath(self) + build_ldflags = build_rpath(bld) + + if not Options.is_install or not getattr(self, 'samba_install', True): + # just need to set the build rpath if we are not installing + self.env.RPATH = build_ldflags + return + + # setup the install path, expanding variables + install_path = getattr(self, 'samba_inst_path', None) + if install_path is None: + if getattr(self, 'private_library', False): + install_path = '${PRIVATELIBDIR}' + else: + install_path = '${LIBDIR}' + install_path = bld.EXPAND_VARIABLES(install_path) + + target_name = self.target + + if install_ldflags != build_ldflags: + # we will be creating a new target name, and using that for the + # install link. That stops us from overwriting the existing build + # target, which has different ldflags + self.done_install_library = True + t = self.clone('default') + t.posted = False + t.target += '.inst' + self.env.RPATH = build_ldflags + else: + t = self + + t.env.RPATH = install_ldflags + + dev_link = None + + # in the following the names are: + # - inst_name is the name with .inst. in it, in the build + # directory + # - install_name is the name in the install directory + # - install_link is a symlink in the install directory, to install_name + + if getattr(self, 'samba_realname', None): + install_name = self.samba_realname + install_link = None + if getattr(self, 'samba_type', None) == 'PYTHON': + inst_name = bld.make_libname(t.target, nolibprefix=True, python=True) + else: + inst_name = bld.make_libname(t.target) + elif self.vnum: + vnum_base = self.vnum.split('.')[0] + install_name = bld.make_libname(target_name, version=self.vnum) + install_link = bld.make_libname(target_name, version=vnum_base) + inst_name = bld.make_libname(t.target) + if not self.private_library: + # only generate the dev link for non-bundled libs + dev_link = bld.make_libname(target_name) + elif getattr(self, 'soname', ''): + install_name = bld.make_libname(target_name) + install_link = self.soname + inst_name = bld.make_libname(t.target) + else: + install_name = bld.make_libname(target_name) + install_link = None + inst_name = bld.make_libname(t.target) + + if t.env.SONAME_ST: + # ensure we get the right names in the library + if install_link: + t.env.append_value('LINKFLAGS', t.env.SONAME_ST % install_link) + else: + t.env.append_value('LINKFLAGS', t.env.SONAME_ST % install_name) + t.env.SONAME_ST = '' + + # tell waf to install the library + bld.install_as(os.path.join(install_path, install_name), + os.path.join(self.path.abspath(bld.env), inst_name)) + if install_link and install_link != install_name: + # and the symlink if needed + bld.symlink_as(os.path.join(install_path, install_link), install_name) + if dev_link: + bld.symlink_as(os.path.join(install_path, dev_link), install_name) + + +@feature('cshlib') +@after('apply_implib') +@before('apply_vnum') +def apply_soname(self): + '''install a library, taking account of the different rpath varients''' + + if self.env.SONAME_ST and getattr(self, 'soname', ''): + self.env.append_value('LINKFLAGS', self.env.SONAME_ST % self.soname) + self.env.SONAME_ST = '' + +@feature('cshlib') +@after('apply_implib') +@before('apply_vnum') +def apply_vscript(self): + '''add version-script arguments to library build''' + + if self.env.HAVE_LD_VERSION_SCRIPT and getattr(self, 'version_script', ''): + self.env.append_value('LINKFLAGS', "-Wl,--version-script=%s" % + self.version_script) + self.version_script = None + + +############################## +# handle the creation of links for libraries and binaries in the build tree + +@feature('symlink_lib') +@after('apply_link') +def symlink_lib(self): + '''symlink a shared lib''' + + if self.target.endswith('.inst'): + return + + blddir = os.path.dirname(self.bld.srcnode.abspath(self.bld.env)) + libpath = self.link_task.outputs[0].abspath(self.env) + + # calculat the link target and put it in the environment + soext="" + vnum = getattr(self, 'vnum', None) + if vnum is not None: + soext = '.' + vnum.split('.')[0] + + link_target = getattr(self, 'link_name', '') + if link_target == '': + basename = os.path.basename(self.bld.make_libname(self.target, version=soext)) + if getattr(self, "private_library", False): + link_target = '%s/private/%s' % (LIB_PATH, basename) + else: + link_target = '%s/%s' % (LIB_PATH, basename) + + link_target = os.path.join(blddir, link_target) + + if os.path.lexists(link_target): + if os.path.islink(link_target) and os.readlink(link_target) == libpath: + return + os.unlink(link_target) + + link_container = os.path.dirname(link_target) + if not os.path.isdir(link_container): + os.makedirs(link_container) + + os.symlink(libpath, link_target) + + +@feature('symlink_bin') +@after('apply_link') +def symlink_bin(self): + '''symlink a binary into the build directory''' + + if self.target.endswith('.inst'): + return + + blddir = os.path.dirname(self.bld.srcnode.abspath(self.bld.env)) + if not self.link_task.outputs or not self.link_task.outputs[0]: + raise Utils.WafError('no outputs found for %s in symlink_bin' % self.name) + binpath = self.link_task.outputs[0].abspath(self.env) + bldpath = os.path.join(self.bld.env.BUILD_DIRECTORY, self.link_task.outputs[0].name) + + if os.path.lexists(bldpath): + if os.path.islink(bldpath) and os.readlink(bldpath) == binpath: + return + os.unlink(bldpath) + os.symlink(binpath, bldpath) diff --git a/buildtools/wafsamba/samba_optimisation.py b/buildtools/wafsamba/samba_optimisation.py new file mode 100644 index 0000000000..951fd4c1f6 --- /dev/null +++ b/buildtools/wafsamba/samba_optimisation.py @@ -0,0 +1,165 @@ +# This file contains waf optimisations for Samba + +# most of these optimisations are possible because of the restricted build environment +# that Samba has. For example, Samba doesn't attempt to cope with Win32 paths during the +# build, and Samba doesn't need build varients + +# overall this makes some build tasks quite a bit faster + +from TaskGen import feature, after +import preproc, Task + +@feature('cc', 'cxx') +@after('apply_type_vars', 'apply_lib_vars', 'apply_core') +def apply_incpaths(self): + lst = [] + + try: + kak = self.bld.kak + except AttributeError: + kak = self.bld.kak = {} + + # TODO move the uselib processing out of here + for lib in self.to_list(self.uselib): + for path in self.env['CPPPATH_' + lib]: + if not path in lst: + lst.append(path) + if preproc.go_absolute: + for path in preproc.standard_includes: + if not path in lst: + lst.append(path) + + for path in self.to_list(self.includes): + if not path in lst: + if preproc.go_absolute or path[0] != '/': #os.path.isabs(path): + lst.append(path) + else: + self.env.prepend_value('CPPPATH', path) + + for path in lst: + node = None + if path[0] == '/': # os.path.isabs(path): + if preproc.go_absolute: + node = self.bld.root.find_dir(path) + elif path[0] == '#': + node = self.bld.srcnode + if len(path) > 1: + try: + node = kak[path] + except KeyError: + kak[path] = node = node.find_dir(path[1:]) + else: + try: + node = kak[(self.path.id, path)] + except KeyError: + kak[(self.path.id, path)] = node = self.path.find_dir(path) + + if node: + self.env.append_value('INC_PATHS', node) + +@feature('cc') +@after('apply_incpaths') +def apply_obj_vars_cc(self): + """after apply_incpaths for INC_PATHS""" + env = self.env + app = env.append_unique + cpppath_st = env['CPPPATH_ST'] + + lss = env['_CCINCFLAGS'] + + try: + cac = self.bld.cac + except AttributeError: + cac = self.bld.cac = {} + + # local flags come first + # set the user-defined includes paths + for i in env['INC_PATHS']: + + try: + lss.extend(cac[i.id]) + except KeyError: + + cac[i.id] = [cpppath_st % i.bldpath(env), cpppath_st % i.srcpath(env)] + lss.extend(cac[i.id]) + + env['_CCINCFLAGS'] = lss + # set the library include paths + for i in env['CPPPATH']: + app('_CCINCFLAGS', cpppath_st % i) + +import Node, Environment + +def vari(self): + return "default" +Environment.Environment.variant = vari + +def variant(self, env): + if not env: return 0 + elif self.id & 3 == Node.FILE: return 0 + else: return "default" +Node.Node.variant = variant + + +import TaskGen, Task + +def create_task(self, name, src=None, tgt=None): + task = Task.TaskBase.classes[name](self.env, generator=self) + if src: + task.set_inputs(src) + if tgt: + task.set_outputs(tgt) + return task +TaskGen.task_gen.create_task = create_task + +def hash_constraints(self): + a = self.attr + sum = hash((str(a('before', '')), + str(a('after', '')), + str(a('ext_in', '')), + str(a('ext_out', '')), + self.__class__.maxjobs)) + return sum +Task.TaskBase.hash_constraints = hash_constraints + + +# import cc +# from TaskGen import extension +# import Utils + +# @extension(cc.EXT_CC) +# def c_hook(self, node): +# task = self.create_task('cc', node, node.change_ext('.o')) +# try: +# self.compiled_tasks.append(task) +# except AttributeError: +# raise Utils.WafError('Have you forgotten to set the feature "cc" on %s?' % str(self)) + +# bld = self.bld +# try: +# dc = bld.dc +# except AttributeError: +# dc = bld.dc = {} + +# if task.outputs[0].id in dc: +# raise Utils.WafError('Samba, you are doing it wrong %r %s %s' % (task.outputs, task.generator, dc[task.outputs[0].id].generator)) +# else: +# dc[task.outputs[0].id] = task + +# return task + + +def suncc_wrap(cls): + '''work around a problem with cc on solaris not handling module aliases + which have empty libs''' + if getattr(cls, 'solaris_wrap', False): + return + cls.solaris_wrap = True + oldrun = cls.run + def run(self): + if self.env.CC_NAME == "sun" and not self.inputs: + self.env = self.env.copy() + self.env.append_value('LINKFLAGS', '-') + return oldrun(self) + cls.run = run +suncc_wrap(Task.TaskBase.classes['cc_link']) diff --git a/buildtools/wafsamba/samba_patterns.py b/buildtools/wafsamba/samba_patterns.py new file mode 100644 index 0000000000..37ef4198a6 --- /dev/null +++ b/buildtools/wafsamba/samba_patterns.py @@ -0,0 +1,29 @@ +# a waf tool to add extension based build patterns for Samba + +import Task +from TaskGen import extension +from samba_utils import * +from wafsamba import samba_version_file + +def write_version_header(task): + '''print version.h contents''' + src = task.inputs[0].srcpath(task.env) + tgt = task.outputs[0].bldpath(task.env) + + version = samba_version_file(src, task.env.srcdir, env=task.env) + string = str(version) + + f = open(tgt, 'w') + s = f.write(string) + f.close() + return 0 + + +def SAMBA_MKVERSION(bld, target): + '''generate the version.h header for Samba''' + t = bld.SAMBA_GENERATOR('VERSION', + rule=write_version_header, + source= 'VERSION', + target=target, + always=True) +Build.BuildContext.SAMBA_MKVERSION = SAMBA_MKVERSION diff --git a/buildtools/wafsamba/samba_pidl.py b/buildtools/wafsamba/samba_pidl.py new file mode 100644 index 0000000000..f40066eb69 --- /dev/null +++ b/buildtools/wafsamba/samba_pidl.py @@ -0,0 +1,167 @@ +# waf build tool for building IDL files with pidl + +from TaskGen import before +import Build, os, sys, Logs +from samba_utils import * + +def SAMBA_PIDL(bld, pname, source, + options='', + output_dir='.', + symlink=False, + generate_tables=True): + '''Build a IDL file using pidl. + This will produce up to 13 output files depending on the options used''' + + bname = source[0:-4]; # strip off the .idl suffix + bname = os.path.basename(bname) + name = "%s_%s" % (pname, bname.upper()) + + if not SET_TARGET_TYPE(bld, name, 'PIDL'): + return + + bld.SET_BUILD_GROUP('build_source') + + # the output files depend on the options used. Use this dictionary + # to map between the options and the resulting file names + options_map = { '--header' : '%s.h', + '--ndr-parser' : 'ndr_%s.c ndr_%s.h', + '--samba3-ndr-server' : 'srv_%s.c srv_%s.h', + '--samba3-ndr-client' : 'cli_%s.c cli_%s.h', + '--server' : 'ndr_%s_s.c', + '--client' : 'ndr_%s_c.c ndr_%s_c.h', + '--python' : 'py_%s.c', + '--tdr-parser' : 'tdr_%s.c tdr_%s.h', + '--dcom-proxy' : '%s_p.c', + '--com-header' : 'com_%s.h' + } + + table_header_idx = None + out_files = [] + options_list = TO_LIST(options) + + for o in options_list: + if o in options_map: + ofiles = TO_LIST(options_map[o]) + for f in ofiles: + out_files.append(os.path.join(output_dir, f % bname)) + if f == 'ndr_%s.h': + # remember this one for the tables generation + table_header_idx = len(out_files) - 1 + + # depend on the full pidl sources + source = TO_LIST(source) + try: + pidl_src_nodes = bld.pidl_files_cache + except AttributeError: + bld.pidl_files_cache = bld.srcnode.ant_glob('pidl/lib/Parse/**/*.pm', flat=False) + bld.pidl_files_cache.extend(bld.srcnode.ant_glob('pidl', flat=False)) + pidl_src_nodes = bld.pidl_files_cache + + # the cd .. is needed because pidl currently is sensitive to the directory it is run in + cpp = "" + cc = "" + if bld.CONFIG_SET("CPP"): + if isinstance(bld.CONFIG_GET("CPP"), list): + cpp = 'CPP="%s"' % bld.CONFIG_GET("CPP")[0] + else: + cpp = 'CPP="%s"' % bld.CONFIG_GET("CPP") + + if cpp == "CPP=xlc_r": + cpp = "" + + + if bld.CONFIG_SET("CC"): + if isinstance(bld.CONFIG_GET("CC"), list): + cc = 'CC="%s"' % bld.CONFIG_GET("CC")[0] + else: + cc = 'CC="%s"' % bld.CONFIG_GET("CC") + + t = bld(rule='cd .. && %s %s ${PERL} "${PIDL}" --quiet ${OPTIONS} --outputdir ${OUTPUTDIR} -- "${SRC[0].abspath(env)}"' % (cpp, cc), + ext_out = '.c', + before = 'cc', + on_results = True, + shell = True, + source = source, + target = out_files, + name = name, + samba_type = 'PIDL') + + # prime the list of nodes we are dependent on with the cached pidl sources + t.allnodes = pidl_src_nodes + + t.env.PIDL = os.path.join(bld.srcnode.abspath(), 'pidl/pidl') + t.env.OPTIONS = TO_LIST(options) + + # this rather convoluted set of path calculations is to cope with the possibility + # that gen_ndr is a symlink into the source tree. By doing this for the source3 + # gen_ndr directory we end up generating identical output in gen_ndr for the old + # build system and the new one. That makes keeping things in sync much easier. + # eventually we should drop the gen_ndr files in git, but in the meanwhile this works + + found_dir = bld.path.find_dir(output_dir) + if not 'abspath' in dir(found_dir): + Logs.error('Unable to find pidl output directory %s' % + os.path.normpath(os.path.join(bld.curdir, output_dir))) + sys.exit(1) + + outdir = bld.path.find_dir(output_dir).abspath(t.env) + + if symlink and not os.path.lexists(outdir): + link_source = os.path.normpath(os.path.join(bld.curdir,output_dir)) + os.symlink(link_source, outdir) + + real_outputdir = os.path.realpath(outdir) + t.env.OUTPUTDIR = os_path_relpath(real_outputdir, os.path.dirname(bld.env.BUILD_DIRECTORY)) + + if generate_tables and table_header_idx is not None: + pidl_headers = LOCAL_CACHE(bld, 'PIDL_HEADERS') + pidl_headers[name] = [bld.path.find_or_declare(out_files[table_header_idx])] + + t.more_includes = '#' + bld.path.relpath_gen(bld.srcnode) +Build.BuildContext.SAMBA_PIDL = SAMBA_PIDL + + +def SAMBA_PIDL_LIST(bld, name, source, + options='', + output_dir='.', + symlink=False, + generate_tables=True): + '''A wrapper for building a set of IDL files''' + for p in TO_LIST(source): + bld.SAMBA_PIDL(name, p, options=options, output_dir=output_dir, symlink=symlink, generate_tables=generate_tables) +Build.BuildContext.SAMBA_PIDL_LIST = SAMBA_PIDL_LIST + + +################################################################# +# the rule for generating the NDR tables +from TaskGen import feature, before +@feature('collect') +@before('exec_rule') +def collect(self): + pidl_headers = LOCAL_CACHE(self.bld, 'PIDL_HEADERS') + for (name, hd) in pidl_headers.items(): + y = self.bld.name_to_obj(name, self.env) + self.bld.ASSERT(y is not None, 'Failed to find PIDL header %s' % name) + y.post() + for node in hd: + self.bld.ASSERT(node is not None, 'Got None as build node generating PIDL table for %s' % name) + self.source += " " + node.relpath_gen(self.path) + + +def SAMBA_PIDL_TABLES(bld, name, target): + '''generate the pidl NDR tables file''' + headers = bld.env.PIDL_HEADERS + bld.SET_BUILD_GROUP('main') + t = bld( + features = 'collect', + rule = '${PERL} ${SRC} --output ${TGT} | sed "s|default/||" > ${TGT}', + ext_out = '.c', + before = 'cc', + on_results = True, + shell = True, + source = '../../librpc/tables.pl', + target = target, + name = name) + t.env.LIBRPC = os.path.join(bld.srcnode.abspath(), 'librpc') +Build.BuildContext.SAMBA_PIDL_TABLES = SAMBA_PIDL_TABLES + diff --git a/buildtools/wafsamba/samba_python.py b/buildtools/wafsamba/samba_python.py new file mode 100644 index 0000000000..e9afa939a3 --- /dev/null +++ b/buildtools/wafsamba/samba_python.py @@ -0,0 +1,69 @@ +# waf build tool for building IDL files with pidl + +import Build +from samba_utils import * +from samba_autoconf import * + +from Configure import conf +@conf +def SAMBA_CHECK_PYTHON_HEADERS(conf, mandatory=True): + if conf.env["python_headers_checked"] == []: + conf.check_python_headers(mandatory) + conf.env["python_headers_checked"] = "yes" + else: + conf.msg("python headers", "using cache") + + +def SAMBA_PYTHON(bld, name, + source='', + deps='', + public_deps='', + realname=None, + cflags='', + includes='', + init_function_sentinal=None, + local_include=True, + vars=None, + enabled=True): + '''build a python extension for Samba''' + + # when we support static python modules we'll need to gather + # the list from all the SAMBA_PYTHON() targets + if init_function_sentinal is not None: + cflags += '-DSTATIC_LIBPYTHON_MODULES=%s' % init_function_sentinal + + source = bld.EXPAND_VARIABLES(source, vars=vars) + + if realname is None: + # a SAMBA_PYTHON target without a realname is just a + # library with pyembed=True + bld.SAMBA_LIBRARY(name, + source=source, + deps=deps, + public_deps=public_deps, + includes=includes, + cflags=cflags, + local_include=local_include, + vars=vars, + pyembed=True, + enabled=enabled) + return + + link_name = 'python/%s' % realname + + bld.SAMBA_LIBRARY(name, + source=source, + deps=deps, + public_deps=public_deps, + includes=includes, + cflags=cflags, + realname=realname, + local_include=local_include, + vars=vars, + link_name=link_name, + pyembed=True, + target_type='PYTHON', + install_path='${PYTHONARCHDIR}', + enabled=enabled) + +Build.BuildContext.SAMBA_PYTHON = SAMBA_PYTHON diff --git a/buildtools/wafsamba/samba_utils.py b/buildtools/wafsamba/samba_utils.py new file mode 100644 index 0000000000..3adf533b0e --- /dev/null +++ b/buildtools/wafsamba/samba_utils.py @@ -0,0 +1,620 @@ +# a waf tool to add autoconf-like macros to the configure section +# and for SAMBA_ macros for building libraries, binaries etc + +import Build, os, sys, Options, Utils, Task, re, fnmatch, Logs +from TaskGen import feature, before +from Configure import conf +from Logs import debug +import shlex + +# TODO: make this a --option +LIB_PATH="shared" + + +# sigh, python octal constants are a mess +MODE_644 = int('644', 8) +MODE_755 = int('755', 8) + +@conf +def SET_TARGET_TYPE(ctx, target, value): + '''set the target type of a target''' + cache = LOCAL_CACHE(ctx, 'TARGET_TYPE') + if target in cache and cache[target] != 'EMPTY': + Logs.error("ERROR: Target '%s' in directory %s re-defined as %s - was %s" % (target, ctx.curdir, value, cache[target])) + sys.exit(1) + LOCAL_CACHE_SET(ctx, 'TARGET_TYPE', target, value) + debug("task_gen: Target '%s' created of type '%s' in %s" % (target, value, ctx.curdir)) + return True + + +def GET_TARGET_TYPE(ctx, target): + '''get target type from cache''' + cache = LOCAL_CACHE(ctx, 'TARGET_TYPE') + if not target in cache: + return None + return cache[target] + + +###################################################### +# this is used as a decorator to make functions only +# run once. Based on the idea from +# http://stackoverflow.com/questions/815110/is-there-a-decorator-to-simply-cache-function-return-values +runonce_ret = {} +def runonce(function): + def runonce_wrapper(*args): + if args in runonce_ret: + return runonce_ret[args] + else: + ret = function(*args) + runonce_ret[args] = ret + return ret + return runonce_wrapper + + +def ADD_LD_LIBRARY_PATH(path): + '''add something to LD_LIBRARY_PATH''' + if 'LD_LIBRARY_PATH' in os.environ: + oldpath = os.environ['LD_LIBRARY_PATH'] + else: + oldpath = '' + newpath = oldpath.split(':') + if not path in newpath: + newpath.append(path) + os.environ['LD_LIBRARY_PATH'] = ':'.join(newpath) + + +def needs_private_lib(bld, target): + '''return True if a target links to a private library''' + for lib in getattr(target, "uselib_local", []): + t = bld.name_to_obj(lib, bld.env) + if t and getattr(t, 'private_library', False): + return True + return False + + +def install_rpath(target): + '''the rpath value for installation''' + bld = target.bld + bld.env['RPATH'] = [] + ret = set() + if bld.env.RPATH_ON_INSTALL: + ret.add(bld.EXPAND_VARIABLES(bld.env.LIBDIR)) + if bld.env.RPATH_ON_INSTALL_PRIVATE and needs_private_lib(bld, target): + ret.add(bld.EXPAND_VARIABLES(bld.env.PRIVATELIBDIR)) + return list(ret) + + +def build_rpath(bld): + '''the rpath value for build''' + rpaths = [os.path.normpath('%s/%s' % (bld.env.BUILD_DIRECTORY, d)) for d in ("shared", "shared/private")] + bld.env['RPATH'] = [] + if bld.env.RPATH_ON_BUILD: + return rpaths + for rpath in rpaths: + ADD_LD_LIBRARY_PATH(rpath) + return [] + + +@conf +def LOCAL_CACHE(ctx, name): + '''return a named build cache dictionary, used to store + state inside other functions''' + if name in ctx.env: + return ctx.env[name] + ctx.env[name] = {} + return ctx.env[name] + + +@conf +def LOCAL_CACHE_SET(ctx, cachename, key, value): + '''set a value in a local cache''' + cache = LOCAL_CACHE(ctx, cachename) + cache[key] = value + + +@conf +def ASSERT(ctx, expression, msg): + '''a build assert call''' + if not expression: + raise Utils.WafError("ERROR: %s\n" % msg) +Build.BuildContext.ASSERT = ASSERT + + +def SUBDIR(bld, subdir, list): + '''create a list of files by pre-pending each with a subdir name''' + ret = '' + for l in TO_LIST(list): + ret = ret + os.path.normpath(os.path.join(subdir, l)) + ' ' + return ret +Build.BuildContext.SUBDIR = SUBDIR + + +def dict_concat(d1, d2): + '''concatenate two dictionaries d1 += d2''' + for t in d2: + if t not in d1: + d1[t] = d2[t] + + +def exec_command(self, cmd, **kw): + '''this overrides the 'waf -v' debug output to be in a nice + unix like format instead of a python list. + Thanks to ita on #waf for this''' + import Utils, Logs + _cmd = cmd + if isinstance(cmd, list): + _cmd = ' '.join(cmd) + debug('runner: %s' % _cmd) + if self.log: + self.log.write('%s\n' % cmd) + kw['log'] = self.log + try: + if not kw.get('cwd', None): + kw['cwd'] = self.cwd + except AttributeError: + self.cwd = kw['cwd'] = self.bldnode.abspath() + return Utils.exec_command(cmd, **kw) +Build.BuildContext.exec_command = exec_command + + +def ADD_COMMAND(opt, name, function): + '''add a new top level command to waf''' + Utils.g_module.__dict__[name] = function + opt.name = function +Options.Handler.ADD_COMMAND = ADD_COMMAND + + +@feature('cc', 'cshlib', 'cprogram') +@before('apply_core','exec_rule') +def process_depends_on(self): + '''The new depends_on attribute for build rules + allow us to specify a dependency on output from + a source generation rule''' + if getattr(self , 'depends_on', None): + lst = self.to_list(self.depends_on) + for x in lst: + y = self.bld.name_to_obj(x, self.env) + self.bld.ASSERT(y is not None, "Failed to find dependency %s of %s" % (x, self.name)) + y.post() + if getattr(y, 'more_includes', None): + self.includes += " " + y.more_includes + + +os_path_relpath = getattr(os.path, 'relpath', None) +if os_path_relpath is None: + # Python < 2.6 does not have os.path.relpath, provide a replacement + # (imported from Python2.6.5~rc2) + def os_path_relpath(path, start): + """Return a relative version of a path""" + start_list = os.path.abspath(start).split("/") + path_list = os.path.abspath(path).split("/") + + # Work out how much of the filepath is shared by start and path. + i = len(os.path.commonprefix([start_list, path_list])) + + rel_list = ['..'] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return start + return os.path.join(*rel_list) + + +def unique_list(seq): + '''return a uniquified list in the same order as the existing list''' + seen = {} + result = [] + for item in seq: + if item in seen: continue + seen[item] = True + result.append(item) + return result + + +def TO_LIST(str, delimiter=None): + '''Split a list, preserving quoted strings and existing lists''' + if str is None: + return [] + if isinstance(str, list): + return str + lst = str.split(delimiter) + # the string may have had quotes in it, now we + # check if we did have quotes, and use the slower shlex + # if we need to + for e in lst: + if e[0] == '"': + return shlex.split(str) + return lst + + +def subst_vars_error(string, env): + '''substitute vars, throw an error if a variable is not defined''' + lst = re.split('(\$\{\w+\})', string) + out = [] + for v in lst: + if re.match('\$\{\w+\}', v): + vname = v[2:-1] + if not vname in env: + Logs.error("Failed to find variable %s in %s" % (vname, string)) + sys.exit(1) + v = env[vname] + out.append(v) + return ''.join(out) + + +@conf +def SUBST_ENV_VAR(ctx, varname): + '''Substitute an environment variable for any embedded variables''' + return subst_vars_error(ctx.env[varname], ctx.env) +Build.BuildContext.SUBST_ENV_VAR = SUBST_ENV_VAR + + +def ENFORCE_GROUP_ORDERING(bld): + '''enforce group ordering for the project. This + makes the group ordering apply only when you specify + a target with --target''' + if Options.options.compile_targets: + @feature('*') + @before('exec_rule', 'apply_core', 'collect') + def force_previous_groups(self): + if getattr(self.bld, 'enforced_group_ordering', False) == True: + return + self.bld.enforced_group_ordering = True + + def group_name(g): + tm = self.bld.task_manager + return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0] + + my_id = id(self) + bld = self.bld + stop = None + for g in bld.task_manager.groups: + for t in g.tasks_gen: + if id(t) == my_id: + stop = id(g) + debug('group: Forcing up to group %s for target %s', + group_name(g), self.name or self.target) + break + if stop != None: + break + if stop is None: + return + + for i in xrange(len(bld.task_manager.groups)): + g = bld.task_manager.groups[i] + bld.task_manager.current_group = i + if id(g) == stop: + break + debug('group: Forcing group %s', group_name(g)) + for t in g.tasks_gen: + if not getattr(t, 'forced_groups', False): + debug('group: Posting %s', t.name or t.target) + t.forced_groups = True + t.post() +Build.BuildContext.ENFORCE_GROUP_ORDERING = ENFORCE_GROUP_ORDERING + + +def recursive_dirlist(dir, relbase, pattern=None): + '''recursive directory list''' + ret = [] + for f in os.listdir(dir): + f2 = dir + '/' + f + if os.path.isdir(f2): + ret.extend(recursive_dirlist(f2, relbase)) + else: + if pattern and not fnmatch.fnmatch(f, pattern): + continue + ret.append(os_path_relpath(f2, relbase)) + return ret + + +def mkdir_p(dir): + '''like mkdir -p''' + if not dir: + return + if dir.endswith("/"): + mkdir_p(dir[:-1]) + return + if os.path.isdir(dir): + return + mkdir_p(os.path.dirname(dir)) + os.mkdir(dir) + + +def SUBST_VARS_RECURSIVE(string, env): + '''recursively expand variables''' + if string is None: + return string + limit=100 + while (string.find('${') != -1 and limit > 0): + string = subst_vars_error(string, env) + limit -= 1 + return string + + +@conf +def EXPAND_VARIABLES(ctx, varstr, vars=None): + '''expand variables from a user supplied dictionary + + This is most useful when you pass vars=locals() to expand + all your local variables in strings + ''' + + if isinstance(varstr, list): + ret = [] + for s in varstr: + ret.append(EXPAND_VARIABLES(ctx, s, vars=vars)) + return ret + + if not isinstance(varstr, str): + return varstr + + import Environment + env = Environment.Environment() + ret = varstr + # substitute on user supplied dict if avaiilable + if vars is not None: + for v in vars.keys(): + env[v] = vars[v] + ret = SUBST_VARS_RECURSIVE(ret, env) + + # if anything left, subst on the environment as well + if ret.find('${') != -1: + ret = SUBST_VARS_RECURSIVE(ret, ctx.env) + # make sure there is nothing left. Also check for the common + # typo of $( instead of ${ + if ret.find('${') != -1 or ret.find('$(') != -1: + Logs.error('Failed to substitute all variables in varstr=%s' % ret) + sys.exit(1) + return ret +Build.BuildContext.EXPAND_VARIABLES = EXPAND_VARIABLES + + +def RUN_COMMAND(cmd, + env=None, + shell=False): + '''run a external command, return exit code or signal''' + if env: + cmd = SUBST_VARS_RECURSIVE(cmd, env) + + status = os.system(cmd) + if os.WIFEXITED(status): + return os.WEXITSTATUS(status) + if os.WIFSIGNALED(status): + return - os.WTERMSIG(status) + Logs.error("Unknown exit reason %d for command: %s" (status, cmd)) + return -1 + + +# make sure we have md5. some systems don't have it +try: + from hashlib import md5 +except: + try: + import md5 + except: + import Constants + Constants.SIG_NIL = hash('abcd') + class replace_md5(object): + def __init__(self): + self.val = None + def update(self, val): + self.val = hash((self.val, val)) + def digest(self): + return str(self.val) + def hexdigest(self): + return self.digest().encode('hex') + def replace_h_file(filename): + f = open(filename, 'rb') + m = replace_md5() + while (filename): + filename = f.read(100000) + m.update(filename) + f.close() + return m.digest() + Utils.md5 = replace_md5 + Task.md5 = replace_md5 + Utils.h_file = replace_h_file + + +def LOAD_ENVIRONMENT(): + '''load the configuration environment, allowing access to env vars + from new commands''' + import Environment + env = Environment.Environment() + try: + env.load('.lock-wscript') + env.load(env.blddir + '/c4che/default.cache.py') + except: + pass + return env + + +def IS_NEWER(bld, file1, file2): + '''return True if file1 is newer than file2''' + t1 = os.stat(os.path.join(bld.curdir, file1)).st_mtime + t2 = os.stat(os.path.join(bld.curdir, file2)).st_mtime + return t1 > t2 +Build.BuildContext.IS_NEWER = IS_NEWER + + +@conf +def RECURSE(ctx, directory): + '''recurse into a directory, relative to the curdir or top level''' + try: + visited_dirs = ctx.visited_dirs + except: + visited_dirs = ctx.visited_dirs = set() + d = os.path.join(ctx.curdir, directory) + if os.path.exists(d): + abspath = os.path.abspath(d) + else: + abspath = os.path.abspath(os.path.join(Utils.g_module.srcdir, directory)) + ctxclass = ctx.__class__.__name__ + key = ctxclass + ':' + abspath + if key in visited_dirs: + # already done it + return + visited_dirs.add(key) + relpath = os_path_relpath(abspath, ctx.curdir) + if ctxclass == 'Handler': + return ctx.sub_options(relpath) + if ctxclass == 'ConfigurationContext': + return ctx.sub_config(relpath) + if ctxclass == 'BuildContext': + return ctx.add_subdirs(relpath) + Logs.error('Unknown RECURSE context class', ctxclass) + raise +Options.Handler.RECURSE = RECURSE +Build.BuildContext.RECURSE = RECURSE + + +def CHECK_MAKEFLAGS(bld): + '''check for MAKEFLAGS environment variable in case we are being + called from a Makefile try to honor a few make command line flags''' + if not 'WAF_MAKE' in os.environ: + return + makeflags = os.environ.get('MAKEFLAGS') + if makeflags is None: + return + jobs_set = False + # we need to use shlex.split to cope with the escaping of spaces + # in makeflags + for opt in shlex.split(makeflags): + # options can come either as -x or as x + if opt[0:2] == 'V=': + Options.options.verbose = Logs.verbose = int(opt[2:]) + if Logs.verbose > 0: + Logs.zones = ['runner'] + if Logs.verbose > 2: + Logs.zones = ['*'] + elif opt[0].isupper() and opt.find('=') != -1: + loc = opt.find('=') + setattr(Options.options, opt[0:loc], opt[loc+1:]) + elif opt[0] != '-': + for v in opt: + if v == 'j': + jobs_set = True + elif v == 'k': + Options.options.keep = True + elif opt == '-j': + jobs_set = True + elif opt == '-k': + Options.options.keep = True + if not jobs_set: + # default to one job + Options.options.jobs = 1 + +Build.BuildContext.CHECK_MAKEFLAGS = CHECK_MAKEFLAGS + +option_groups = {} + +def option_group(opt, name): + '''find or create an option group''' + global option_groups + if name in option_groups: + return option_groups[name] + gr = opt.add_option_group(name) + option_groups[name] = gr + return gr +Options.Handler.option_group = option_group + + +def save_file(filename, contents, create_dir=False): + '''save data to a file''' + if create_dir: + mkdir_p(os.path.dirname(filename)) + try: + f = open(filename, 'w') + f.write(contents) + f.close() + except: + return False + return True + + +def load_file(filename): + '''return contents of a file''' + try: + f = open(filename, 'r') + r = f.read() + f.close() + except: + return None + return r + + +def reconfigure(ctx): + '''rerun configure if necessary''' + import Configure, samba_wildcard, Scripting + if not os.path.exists(".lock-wscript"): + raise Utils.WafError('configure has not been run') + bld = samba_wildcard.fake_build_environment() + Configure.autoconfig = True + Scripting.check_configured(bld) + + +def map_shlib_extension(ctx, name, python=False): + '''map a filename with a shared library extension of .so to the real shlib name''' + if name is None: + return None + if name[-1:].isdigit(): + # some libraries have specified versions in the wscript rule + return name + (root1, ext1) = os.path.splitext(name) + if python: + (root2, ext2) = os.path.splitext(ctx.env.pyext_PATTERN) + else: + (root2, ext2) = os.path.splitext(ctx.env.shlib_PATTERN) + return root1+ext2 +Build.BuildContext.map_shlib_extension = map_shlib_extension + +def apply_pattern(filename, pattern): + '''apply a filename pattern to a filename that may have a directory component''' + dirname = os.path.dirname(filename) + if not dirname: + return pattern % filename + basename = os.path.basename(filename) + return os.path.join(dirname, pattern % basename) + +def make_libname(ctx, name, nolibprefix=False, version=None, python=False): + """make a library filename + Options: + nolibprefix: don't include the lib prefix + version : add a version number + python : if we should use python module name conventions""" + + if python: + libname = apply_pattern(name, ctx.env.pyext_PATTERN) + else: + libname = apply_pattern(name, ctx.env.shlib_PATTERN) + if nolibprefix and libname[0:3] == 'lib': + libname = libname[3:] + if version: + if version[0] == '.': + version = version[1:] + (root, ext) = os.path.splitext(libname) + if ext == ".dylib": + # special case - version goes before the prefix + libname = "%s.%s%s" % (root, version, ext) + else: + libname = "%s%s.%s" % (root, ext, version) + return libname +Build.BuildContext.make_libname = make_libname + + +def get_tgt_list(bld): + '''return a list of build objects for samba''' + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + # build a list of task generators we are interested in + tgt_list = [] + for tgt in targets: + type = targets[tgt] + if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']: + continue + t = bld.name_to_obj(tgt, bld.env) + if t is None: + Logs.error("Target %s of type %s has no task generator" % (tgt, type)) + sys.exit(1) + tgt_list.append(t) + return tgt_list diff --git a/buildtools/wafsamba/samba_version.py b/buildtools/wafsamba/samba_version.py new file mode 100644 index 0000000000..0b0c159f55 --- /dev/null +++ b/buildtools/wafsamba/samba_version.py @@ -0,0 +1,269 @@ +import os +import Utils +import samba_utils +import sys + +def bzr_version_summary(path): + try: + import bzrlib + except ImportError: + return ("BZR-UNKNOWN", {}) + + import bzrlib.ui + bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal( + sys.stdin, sys.stdout, sys.stderr) + from bzrlib import branch, osutils, workingtree + from bzrlib.plugin import load_plugins + load_plugins() + + b = branch.Branch.open(path) + (revno, revid) = b.last_revision_info() + rev = b.repository.get_revision(revid) + + fields = { + "BZR_REVISION_ID": revid, + "BZR_REVNO": revno, + "COMMIT_DATE": osutils.format_date_with_offset_in_original_timezone(rev.timestamp, + rev.timezone or 0), + "COMMIT_TIME": int(rev.timestamp), + "BZR_BRANCH": rev.properties.get("branch-nick", ""), + } + + # If possible, retrieve the git sha + try: + from bzrlib.plugins.git.object_store import get_object_store + except ImportError: + # No git plugin + ret = "BZR-%d" % revno + else: + store = get_object_store(b.repository) + full_rev = store._lookup_revision_sha1(revid) + fields["GIT_COMMIT_ABBREV"] = full_rev[:7] + fields["GIT_COMMIT_FULLREV"] = full_rev + ret = "GIT-" + fields["GIT_COMMIT_ABBREV"] + + if workingtree.WorkingTree.open(path).has_changes(): + fields["COMMIT_IS_CLEAN"] = 0 + ret += "+" + else: + fields["COMMIT_IS_CLEAN"] = 1 + return (ret, fields) + + +def git_version_summary(path, env=None): + # Get version from GIT + if not 'GIT' in env and os.path.exists("/usr/bin/git"): + # this is useful when doing make dist without configuring + env.GIT = "/usr/bin/git" + + if not 'GIT' in env: + return ("GIT-UNKNOWN", {}) + + environ = dict(os.environ) + environ["GIT_DIR"] = '%s/.git' % path + environ["GIT_WORK_TREE"] = path + git = Utils.cmd_output(env.GIT + ' show --pretty=format:"%h%n%ct%n%H%n%cd" --stat HEAD', silent=True, env=environ) + + lines = git.splitlines() + if not lines or len(lines) < 4: + return ("GIT-UNKNOWN", {}) + + fields = { + "GIT_COMMIT_ABBREV": lines[0], + "GIT_COMMIT_FULLREV": lines[2], + "COMMIT_TIME": int(lines[1]), + "COMMIT_DATE": lines[3], + } + + ret = "GIT-" + fields["GIT_COMMIT_ABBREV"] + + if env.GIT_LOCAL_CHANGES: + clean = Utils.cmd_output('%s diff HEAD | wc -l' % env.GIT, silent=True).strip() + if clean == "0": + fields["COMMIT_IS_CLEAN"] = 1 + else: + fields["COMMIT_IS_CLEAN"] = 0 + ret += "+" + + return (ret, fields) + + +class SambaVersion(object): + + def __init__(self, version_dict, path, env=None): + '''Determine the version number of samba + +See VERSION for the format. Entries on that file are +also accepted as dictionary entries here + ''' + + self.MAJOR=None + self.MINOR=None + self.RELEASE=None + self.REVISION=None + self.TP_RELEASE=None + self.ALPHA_RELEASE=None + self.PRE_RELEASE=None + self.RC_RELEASE=None + self.IS_SNAPSHOT=True + self.RELEASE_NICKNAME=None + self.VENDOR_SUFFIX=None + self.VENDOR_PATCH=None + + for a, b in version_dict.iteritems(): + if a.startswith("SAMBA_VERSION_"): + setattr(self, a[14:], b) + else: + setattr(self, a, b) + + if self.IS_GIT_SNAPSHOT == "yes": + self.IS_SNAPSHOT=True + elif self.IS_GIT_SNAPSHOT == "no": + self.IS_SNAPSHOT=False + else: + raise Exception("Unknown value for IS_GIT_SNAPSHOT: %s" % self.IS_GIT_SNAPSHOT) + + ## + ## start with "3.0.22" + ## + self.MAJOR=int(self.MAJOR) + self.MINOR=int(self.MINOR) + self.RELEASE=int(self.RELEASE) + + SAMBA_VERSION_STRING = ("%u.%u.%u" % (self.MAJOR, self.MINOR, self.RELEASE)) + +## +## maybe add "3.0.22a" or "4.0.0tp11" or "4.0.0alpha1" or "3.0.22pre1" or "3.0.22rc1" +## We do not do pre or rc version on patch/letter releases +## + if self.REVISION is not None: + SAMBA_VERSION_STRING += self.REVISION + if self.TP_RELEASE is not None: + self.TP_RELEASE = int(self.TP_RELEASE) + SAMBA_VERSION_STRING += "tp%u" % self.TP_RELEASE + if self.ALPHA_RELEASE is not None: + self.ALPHA_RELEASE = int(self.ALPHA_RELEASE) + SAMBA_VERSION_STRING += ("alpha%u" % self.ALPHA_RELEASE) + if self.PRE_RELEASE is not None: + self.PRE_RELEASE = int(self.PRE_RELEASE) + SAMBA_VERSION_STRING += ("pre%u" % self.PRE_RELEASE) + if self.RC_RELEASE is not None: + self.RC_RELEASE = int(self.RC_RELEASE) + SAMBA_VERSION_STRING += ("rc%u" % self.RC_RELEASE) + + if self.IS_SNAPSHOT: + if os.path.exists(os.path.join(path, ".git")): + suffix, self.vcs_fields = git_version_summary(path, env=env) + elif os.path.exists(os.path.join(path, ".bzr")): + suffix, self.vcs_fields = bzr_version_summary(path) + else: + suffix = "UNKNOWN" + self.vcs_fields = {} + SAMBA_VERSION_STRING += "-" + suffix + else: + self.vcs_fields = {} + + self.OFFICIAL_STRING = SAMBA_VERSION_STRING + + if self.VENDOR_SUFFIX is not None: + SAMBA_VERSION_STRING += ("-" + self.VENDOR_SUFFIX) + self.VENDOR_SUFFIX = self.VENDOR_SUFFIX + + if self.VENDOR_PATCH is not None: + SAMBA_VERSION_STRING += ("-" + self.VENDOR_PATCH) + self.VENDOR_PATCH = self.VENDOR_PATCH + + self.STRING = SAMBA_VERSION_STRING + + if self.RELEASE_NICKNAME is not None: + self.STRING_WITH_NICKNAME = "%s (%s)" % (self.STRING, self.RELEASE_NICKNAME) + else: + self.STRING_WITH_NICKNAME = self.STRING + + def __str__(self): + string="/* Autogenerated by waf */\n" + string+="#define SAMBA_VERSION_MAJOR %u\n" % self.MAJOR + string+="#define SAMBA_VERSION_MINOR %u\n" % self.MINOR + string+="#define SAMBA_VERSION_RELEASE %u\n" % self.RELEASE + if self.REVISION is not None: + string+="#define SAMBA_VERSION_REVISION %u\n" % self.REVISION + + if self.TP_RELEASE is not None: + string+="#define SAMBA_VERSION_TP_RELEASE %u\n" % self.TP_RELEASE + + if self.ALPHA_RELEASE is not None: + string+="#define SAMBA_VERSION_ALPHA_RELEASE %u\n" % self.ALPHA_RELEASE + + if self.PRE_RELEASE is not None: + string+="#define SAMBA_VERSION_PRE_RELEASE %u\n" % self.PRE_RELEASE + + if self.RC_RELEASE is not None: + string+="#define SAMBA_VERSION_RC_RELEASE %u\n" % self.RC_RELEASE + + for name in sorted(self.vcs_fields.keys()): + string+="#define SAMBA_VERSION_%s " % name + value = self.vcs_fields[name] + if isinstance(value, basestring): + string += "\"%s\"" % value + elif type(value) is int: + string += "%d" % value + else: + raise Exception("Unknown type for %s: %r" % (name, value)) + string += "\n" + + string+="#define SAMBA_VERSION_OFFICIAL_STRING \"" + self.OFFICIAL_STRING + "\"\n" + + if self.VENDOR_SUFFIX is not None: + string+="#define SAMBA_VERSION_VENDOR_SUFFIX " + self.VENDOR_SUFFIX + "\n" + if self.VENDOR_PATCH is not None: + string+="#define SAMBA_VERSION_VENDOR_PATCH " + self.VENDOR_PATCH + "\n" + + if self.RELEASE_NICKNAME is not None: + string+="#define SAMBA_VERSION_RELEASE_NICKNAME " + self.RELEASE_NICKNAME + "\n" + + # We need to put this #ifdef in to the headers so that vendors can override the version with a function + string+=''' +#ifdef SAMBA_VERSION_VENDOR_FUNCTION +# define SAMBA_VERSION_STRING SAMBA_VERSION_VENDOR_FUNCTION +#else /* SAMBA_VERSION_VENDOR_FUNCTION */ +# define SAMBA_VERSION_STRING "''' + self.STRING_WITH_NICKNAME + '''" +#endif +''' + string+="/* Version for mkrelease.sh: \nSAMBA_VERSION_STRING=" + self.STRING_WITH_NICKNAME + "\n */\n" + + return string + + +def samba_version_file(version_file, path, env=None): + '''Parse the version information from a VERSION file''' + + f = open(version_file, 'r') + version_dict = {} + for line in f: + line = line.strip() + if line == '': + continue + if line.startswith("#"): + continue + try: + split_line = line.split("=") + if split_line[1] != "": + value = split_line[1].strip('"') + version_dict[split_line[0]] = value + except: + print("Failed to parse line %s from %s" % (line, version_file)) + raise + + return SambaVersion(version_dict, path, env=env) + + + +def load_version(env=None): + '''load samba versions either from ./VERSION or git + return a version object for detailed breakdown''' + if not env: + env = samba_utils.LOAD_ENVIRONMENT() + + version = samba_version_file("./VERSION", ".", env) + Utils.g_module.VERSION = version.STRING + return version diff --git a/buildtools/wafsamba/samba_wildcard.py b/buildtools/wafsamba/samba_wildcard.py new file mode 100644 index 0000000000..5bf12672a9 --- /dev/null +++ b/buildtools/wafsamba/samba_wildcard.py @@ -0,0 +1,128 @@ +#! /usr/bin/env python + +# based on playground/evil in the waf svn tree + +import os, datetime +import Scripting, Utils, Options, Logs, Environment, fnmatch +from Constants import * +from samba_utils import * + +def run_task(t, k): + '''run a single build task''' + ret = t.run() + if ret: + raise Utils.WafError("Failed to build %s: %u" % (k, ret)) + + +def run_named_build_task(cmd): + '''run a named build task, matching the cmd name using fnmatch + wildcards against inputs and outputs of all build tasks''' + bld = fake_build_environment() + found = False + cwd_node = bld.root.find_dir(os.getcwd()) + top_node = bld.root.find_dir(bld.srcnode.abspath()) + + cmd = os.path.normpath(cmd) + + # cope with builds of bin/*/* + if os.path.islink(cmd): + cmd = os_path_relpath(os.readlink(cmd), os.getcwd()) + + if cmd[0:12] == "bin/default/": + cmd = cmd[12:] + + for g in bld.task_manager.groups: + for attr in ['outputs', 'inputs']: + for t in g.tasks: + s = getattr(t, attr, []) + for k in s: + relpath1 = k.relpath_gen(cwd_node) + relpath2 = k.relpath_gen(top_node) + if (fnmatch.fnmatch(relpath1, cmd) or + fnmatch.fnmatch(relpath2, cmd)): + t.position = [0,0] + print(t.display()) + run_task(t, k) + found = True + + + if not found: + raise Utils.WafError("Unable to find build target matching %s" % cmd) + + + +def wildcard_main(missing_cmd_fn): + '''this replaces main from Scripting, allowing us to override the + behaviour for unknown commands + + If a unknown command is found, then missing_cmd_fn() is called with + the name of the requested command + ''' + Scripting.commands = Options.arg_line[:] + + while Scripting.commands: + x = Scripting.commands.pop(0) + + ini = datetime.datetime.now() + if x == 'configure': + fun = Scripting.configure + elif x == 'build': + fun = Scripting.build + else: + fun = getattr(Utils.g_module, x, None) + + # this is the new addition on top of main from Scripting.py + if not fun: + missing_cmd_fn(x) + break + + ctx = getattr(Utils.g_module, x + '_context', Utils.Context)() + + if x in ['init', 'shutdown', 'dist', 'distclean', 'distcheck']: + try: + fun(ctx) + except TypeError: + fun() + else: + fun(ctx) + + ela = '' + if not Options.options.progress_bar: + ela = ' (%s)' % Utils.get_elapsed_time(ini) + + if x != 'init' and x != 'shutdown': + Logs.info('%r finished successfully%s' % (x, ela)) + + if not Scripting.commands and x != 'shutdown': + Scripting.commands.append('shutdown') + + + + +def fake_build_environment(): + """create all the tasks for the project, but do not run the build + return the build context in use""" + bld = getattr(Utils.g_module, 'build_context', Utils.Context)() + bld = Scripting.check_configured(bld) + + Options.commands['install'] = False + Options.commands['uninstall'] = False + Options.is_install = False + + bld.is_install = 0 # False + + try: + proj = Environment.Environment(Options.lockfile) + except IOError: + raise Utils.WafError("Project not configured (run 'waf configure' first)") + + bld.load_dirs(proj[SRCDIR], proj[BLDDIR]) + bld.load_envs() + + Logs.info("Waf: Entering directory `%s'" % bld.bldnode.abspath()) + bld.add_subdirs([os.path.split(Utils.g_module.root_path)[0]]) + + bld.pre_build() + bld.flush() + return bld + diff --git a/buildtools/wafsamba/stale_files.py b/buildtools/wafsamba/stale_files.py new file mode 100644 index 0000000000..2b94f0823e --- /dev/null +++ b/buildtools/wafsamba/stale_files.py @@ -0,0 +1,98 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Add a pre-build hook to remove all build files +which do not have a corresponding target + +This can be used for example to remove the targets +that have changed name without performing +a full 'waf clean' + +Of course, it will only work if there are no dynamically generated +nodes/tasks, in which case the method will have to be modified +to exclude some folders for example. +""" + +import Logs, Build, os, samba_utils, Options, Utils +from Runner import Parallel + +old_refill_task_list = Parallel.refill_task_list +def replace_refill_task_list(self): + '''replacement for refill_task_list() that deletes stale files''' + + iit = old_refill_task_list(self) + bld = self.bld + + if not getattr(bld, 'new_rules', False): + # we only need to check for stale files if the build rules changed + return iit + + if Options.options.compile_targets: + # not safe when --target is used + return iit + + # execute only once + if getattr(self, 'cleanup_done', False): + return iit + self.cleanup_done = True + + def group_name(g): + tm = self.bld.task_manager + return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0] + + bin_base = bld.bldnode.abspath() + bin_base_len = len(bin_base) + + # paranoia + if bin_base[-4:] != '/bin': + raise Utils.WafError("Invalid bin base: %s" % bin_base) + + # obtain the expected list of files + expected = [] + for i in range(len(bld.task_manager.groups)): + g = bld.task_manager.groups[i] + tasks = g.tasks_gen + for x in tasks: + try: + if getattr(x, 'target'): + tlist = samba_utils.TO_LIST(getattr(x, 'target')) + for t in tlist: + p = os.path.join(x.path.abspath(bld.env), t) + p = os.path.normpath(p) + expected.append(p) + for n in x.allnodes: + p = n.abspath(bld.env) + if p[0:bin_base_len] == bin_base: + expected.append(p) + except: + pass + + for root, dirs, files in os.walk(bin_base): + for f in files: + p = root + '/' + f + if os.path.islink(p): + link = os.readlink(p) + if link[0:bin_base_len] == bin_base: + p = link + if f in ['config.h']: + continue + if f[-2:] not in [ '.c', '.h' ]: + continue + if f[-7:] == '.inst.h': + continue + if p.find("/.conf") != -1: + continue + if not p in expected: + Logs.warn("Removing stale file: %s" % p) + os.unlink(p) + return iit + + +def AUTOCLEANUP_STALE_FILES(bld): + """automatically clean up any files in bin that shouldn't be there""" + old_refill_task_list = Parallel.refill_task_list + Parallel.refill_task_list = replace_refill_task_list + Parallel.bld = bld +Build.BuildContext.AUTOCLEANUP_STALE_FILES = AUTOCLEANUP_STALE_FILES diff --git a/buildtools/wafsamba/symbols.py b/buildtools/wafsamba/symbols.py new file mode 100644 index 0000000000..0d0af79d06 --- /dev/null +++ b/buildtools/wafsamba/symbols.py @@ -0,0 +1,496 @@ +# a waf tool to extract symbols from object files or libraries +# using nm, producing a set of exposed defined/undefined symbols + +import Utils, Build, subprocess, Logs +from samba_wildcard import fake_build_environment +from samba_utils import * + +# these are the data structures used in symbols.py: +# +# bld.env.symbol_map : dictionary mapping public symbol names to list of +# subsystem names where that symbol exists +# +# t.in_library : list of libraries that t is in +# +# bld.env.public_symbols: set of public symbols for each subsystem +# bld.env.used_symbols : set of used symbols for each subsystem +# +# bld.env.syslib_symbols: dictionary mapping system library name to set of symbols +# for that library +# +# LOCAL_CACHE(bld, 'TARGET_TYPE') : dictionary mapping subsystem name to target type + +def symbols_extract(objfiles, dynamic=False): + '''extract symbols from objfile, returning a dictionary containing + the set of undefined and public symbols for each file''' + + ret = {} + + cmd = ["nm"] + if dynamic: + # needed for some .so files + cmd.append("-D") + cmd.extend(objfiles) + + nmpipe = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout + if len(objfiles) == 1: + filename = objfiles[0] + ret[filename] = { "PUBLIC": set(), "UNDEFINED" : set()} + + for line in nmpipe: + line = line.strip() + if line.endswith(':'): + filename = line[:-1] + ret[filename] = { "PUBLIC": set(), "UNDEFINED" : set() } + continue + cols = line.split(" ") + if cols == ['']: + continue + # see if the line starts with an address + if len(cols) == 3: + symbol_type = cols[1] + symbol = cols[2] + else: + symbol_type = cols[0] + symbol = cols[1] + if symbol_type in "BDGTRVWSi": + # its a public symbol + ret[filename]["PUBLIC"].add(symbol) + elif symbol_type in "U": + ret[filename]["UNDEFINED"].add(symbol) + + return ret + + +def real_name(name): + if name.find(".objlist") != -1: + name = name[:-8] + return name + + +def find_syslib_path(bld, libname, deps): + '''find the path to the syslib we will link against''' + # the strategy is to use the targets that depend on the library, and run ldd + # on it to find the real location of the library that is used + + linkpath = deps[0].link_task.outputs[0].abspath(bld.env) + + if libname == "python": + libname += bld.env.PYTHON_VERSION + + ret = None + + lddpipe = subprocess.Popen(['ldd', linkpath], stdout=subprocess.PIPE).stdout + for line in lddpipe: + line = line.strip() + cols = line.split(" ") + if len(cols) < 3 or cols[1] != "=>": + continue + if cols[0].startswith("lib%s." % libname.lower()): + ret = cols[2] + if cols[0].startswith("libc."): + # save this one too + bld.env.libc_path = cols[2] + return ret + + +def build_symbol_sets(bld, tgt_list): + '''build the public_symbols and undefined_symbols attributes for each target''' + + if bld.env.public_symbols: + return + + objlist = [] # list of object file + objmap = {} # map from object filename to target (subsystem) name + + for t in tgt_list: + t.public_symbols = set() + t.undefined_symbols = set() + t.used_symbols = set() + for tsk in getattr(t, 'compiled_tasks', []): + for output in tsk.outputs: + objpath = output.abspath(bld.env) + objlist.append(objpath) + objmap[objpath] = t + + symbols = symbols_extract(objlist) + for obj in objlist: + t = objmap[obj] + t.public_symbols = t.public_symbols.union(symbols[obj]["PUBLIC"]) + t.undefined_symbols = t.undefined_symbols.union(symbols[obj]["UNDEFINED"]) + t.used_symbols = t.used_symbols.union(symbols[obj]["UNDEFINED"]) + + t.undefined_symbols = t.undefined_symbols.difference(t.public_symbols) + + # and the reverse map of public symbols to subsystem name + bld.env.symbol_map = {} + + for t in tgt_list: + for s in t.public_symbols: + if not s in bld.env.symbol_map: + bld.env.symbol_map[s] = [] + bld.env.symbol_map[s].append(real_name(t.sname)) + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + bld.env.public_symbols = {} + for t in tgt_list: + name = real_name(t.sname) + if name in bld.env.public_symbols: + bld.env.public_symbols[name] = bld.env.public_symbols[name].union(t.public_symbols) + else: + bld.env.public_symbols[name] = t.public_symbols + if t.samba_type == 'LIBRARY': + for dep in t.add_objects: + t2 = bld.name_to_obj(dep, bld.env) + bld.ASSERT(t2 is not None, "Library '%s' has unknown dependency '%s'" % (name, dep)) + bld.env.public_symbols[name] = bld.env.public_symbols[name].union(t2.public_symbols) + + bld.env.used_symbols = {} + for t in tgt_list: + name = real_name(t.sname) + if name in bld.env.used_symbols: + bld.env.used_symbols[name] = bld.env.used_symbols[name].union(t.used_symbols) + else: + bld.env.used_symbols[name] = t.used_symbols + if t.samba_type == 'LIBRARY': + for dep in t.add_objects: + t2 = bld.name_to_obj(dep, bld.env) + bld.ASSERT(t2 is not None, "Library '%s' has unknown dependency '%s'" % (name, dep)) + bld.env.used_symbols[name] = bld.env.used_symbols[name].union(t2.used_symbols) + + +def build_syslib_sets(bld, tgt_list): + '''build the public_symbols for all syslibs''' + + if bld.env.syslib_symbols: + return + + # work out what syslibs we depend on, and what targets those are used in + syslibs = {} + objmap = {} + for t in tgt_list: + if getattr(t, 'uselib', []) and t.samba_type in [ 'LIBRARY', 'BINARY', 'PYTHON' ]: + for lib in t.uselib: + if lib in ['PYEMBED', 'PYEXT']: + lib = "python" + if not lib in syslibs: + syslibs[lib] = [] + syslibs[lib].append(t) + + # work out the paths to each syslib + syslib_paths = [] + for lib in syslibs: + path = find_syslib_path(bld, lib, syslibs[lib]) + if path is None: + Logs.warn("Unable to find syslib path for %s" % lib) + if path is not None: + syslib_paths.append(path) + objmap[path] = lib.lower() + + # add in libc + syslib_paths.append(bld.env.libc_path) + objmap[bld.env.libc_path] = 'c' + + symbols = symbols_extract(syslib_paths, dynamic=True) + + # keep a map of syslib names to public symbols + bld.env.syslib_symbols = {} + for lib in symbols: + bld.env.syslib_symbols[lib] = symbols[lib]["PUBLIC"] + + # add to the map of symbols to dependencies + for lib in symbols: + for sym in symbols[lib]["PUBLIC"]: + if not sym in bld.env.symbol_map: + bld.env.symbol_map[sym] = [] + bld.env.symbol_map[sym].append(objmap[lib]) + + # keep the libc symbols as well, as these are useful for some of the + # sanity checks + bld.env.libc_symbols = symbols[bld.env.libc_path]["PUBLIC"] + + # add to the combined map of dependency name to public_symbols + for lib in bld.env.syslib_symbols: + bld.env.public_symbols[objmap[lib]] = bld.env.syslib_symbols[lib] + + +def build_autodeps(bld, t): + '''build the set of dependencies for a target''' + deps = set() + name = real_name(t.sname) + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + for sym in t.undefined_symbols: + if sym in t.public_symbols: + continue + if sym in bld.env.symbol_map: + depname = bld.env.symbol_map[sym] + if depname == [ name ]: + # self dependencies aren't interesting + continue + if t.in_library == depname: + # no need to depend on the library we are part of + continue + if depname[0] in ['c', 'python']: + # these don't go into autodeps + continue + if targets[depname[0]] in [ 'SYSLIB' ]: + deps.add(depname[0]) + continue + t2 = bld.name_to_obj(depname[0], bld.env) + if len(t2.in_library) != 1: + deps.add(depname[0]) + continue + if t2.in_library == t.in_library: + # if we're part of the same library, we don't need to autodep + continue + deps.add(t2.in_library[0]) + t.autodeps = deps + + +def build_library_names(bld, tgt_list): + '''add a in_library attribute to all targets that are part of a library''' + + if bld.env.done_build_library_names: + return + + for t in tgt_list: + t.in_library = [] + + for t in tgt_list: + if t.samba_type in [ 'LIBRARY' ]: + for obj in t.samba_deps_extended: + t2 = bld.name_to_obj(obj, bld.env) + if t2 and t2.samba_type in [ 'SUBSYSTEM', 'ASN1' ]: + if not t.sname in t2.in_library: + t2.in_library.append(t.sname) + bld.env.done_build_library_names = True + + +def check_library_deps(bld, t): + '''check that all the autodeps that have mutual dependency of this + target are in the same library as the target''' + + name = real_name(t.sname) + + if len(t.in_library) > 1: + Logs.warn("WARNING: Target '%s' in multiple libraries: %s" % (t.sname, t.in_library)) + + for dep in t.autodeps: + t2 = bld.name_to_obj(dep, bld.env) + if t2 is None: + continue + for dep2 in t2.autodeps: + if dep2 == name and t.in_library != t2.in_library: + Logs.warn("WARNING: mutual dependency %s <=> %s" % (name, real_name(t2.sname))) + Logs.warn("Libraries should match. %s != %s" % (t.in_library, t2.in_library)) + # raise Utils.WafError("illegal mutual dependency") + + +def check_syslib_collisions(bld, tgt_list): + '''check if a target has any symbol collisions with a syslib + + We do not want any code in Samba to use a symbol name from a + system library. The chance of that causing problems is just too + high. Note that libreplace uses a rep_XX approach of renaming + symbols via macros + ''' + + has_error = False + for t in tgt_list: + for lib in bld.env.syslib_symbols: + common = t.public_symbols.intersection(bld.env.syslib_symbols[lib]) + if common: + Logs.error("ERROR: Target '%s' has symbols '%s' which is also in syslib '%s'" % (t.sname, common, lib)) + has_error = True + if has_error: + raise Utils.WafError("symbols in common with system libraries") + + +def check_dependencies(bld, t): + '''check for depenencies that should be changed''' + + if bld.name_to_obj(t.sname + ".objlist", bld.env): + return + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + remaining = t.undefined_symbols.copy() + remaining = remaining.difference(t.public_symbols) + + sname = real_name(t.sname) + + deps = set(t.samba_deps) + for d in t.samba_deps: + if targets[d] in [ 'EMPTY', 'DISABLED', 'SYSLIB' ]: + continue + bld.ASSERT(d in bld.env.public_symbols, "Failed to find symbol list for dependency '%s'" % d) + diff = remaining.intersection(bld.env.public_symbols[d]) + if not diff and targets[sname] != 'LIBRARY': + Logs.info("Target '%s' has no dependency on %s" % (sname, d)) + else: + remaining = remaining.difference(diff) + + t.unsatisfied_symbols = set() + needed = {} + for sym in remaining: + if sym in bld.env.symbol_map: + dep = bld.env.symbol_map[sym] + if not dep[0] in needed: + needed[dep[0]] = set() + needed[dep[0]].add(sym) + else: + t.unsatisfied_symbols.add(sym) + + for dep in needed: + Logs.info("Target '%s' should add dep '%s' for symbols %s" % (sname, dep, " ".join(needed[dep]))) + + + +def check_syslib_dependencies(bld, t): + '''check for syslib depenencies''' + + if bld.name_to_obj(t.sname + ".objlist", bld.env): + return + + sname = real_name(t.sname) + + remaining = set() + + features = TO_LIST(t.features) + if 'pyembed' in features or 'pyext' in features: + t.unsatisfied_symbols = t.unsatisfied_symbols.difference(bld.env.public_symbols['python']) + + needed = {} + for sym in t.unsatisfied_symbols: + if sym in bld.env.symbol_map: + dep = bld.env.symbol_map[sym][0] + if dep == 'c': + continue + if not dep in needed: + needed[dep] = set() + needed[dep].add(sym) + else: + remaining.add(sym) + + for dep in needed: + Logs.info("Target '%s' should add syslib dep '%s' for symbols %s" % (sname, dep, " ".join(needed[dep]))) + + if remaining: + debug("deps: Target '%s' has unsatisfied symbols: %s" % (sname, " ".join(remaining))) + + + +def symbols_symbolcheck(task): + '''check the internal dependency lists''' + bld = task.env.bld + tgt_list = get_tgt_list(bld) + + build_symbol_sets(bld, tgt_list) + build_library_names(bld, tgt_list) + + for t in tgt_list: + t.autodeps = set() + if getattr(t, 'source', ''): + build_autodeps(bld, t) + + for t in tgt_list: + check_dependencies(bld, t) + + for t in tgt_list: + check_library_deps(bld, t) + +def symbols_syslibcheck(task): + '''check the syslib dependencies''' + bld = task.env.bld + tgt_list = get_tgt_list(bld) + + build_syslib_sets(bld, tgt_list) + check_syslib_collisions(bld, tgt_list) + + for t in tgt_list: + check_syslib_dependencies(bld, t) + + +def symbols_whyneeded(task): + """check why 'target' needs to link to 'subsystem'""" + bld = task.env.bld + tgt_list = get_tgt_list(bld) + + why = Options.options.WHYNEEDED.split(":") + if len(why) != 2: + raise Utils.WafError("usage: WHYNEEDED=TARGET:DEPENDENCY") + target = why[0] + subsystem = why[1] + + build_symbol_sets(bld, tgt_list) + build_library_names(bld, tgt_list) + build_syslib_sets(bld, tgt_list) + + Logs.info("Checking why %s needs to link to %s" % (target, subsystem)) + if not target in bld.env.used_symbols: + Logs.warn("unable to find target '%s' in used_symbols dict" % target) + return + if not subsystem in bld.env.public_symbols: + Logs.warn("unable to find subsystem '%s' in public_symbols dict" % subsystem) + return + overlap = bld.env.used_symbols[target].intersection(bld.env.public_symbols[subsystem]) + if not overlap: + Logs.info("target '%s' doesn't use any public symbols from '%s'" % (target, subsystem)) + else: + Logs.info("target '%s' uses symbols %s from '%s'" % (target, overlap, subsystem)) + + + +def symbols_dupcheck(task): + '''check for symbols defined in two different subsystems''' + bld = task.env.bld + tgt_list = get_tgt_list(bld) + + targets = LOCAL_CACHE(bld, 'TARGET_TYPE') + + Logs.info("Checking for duplicate symbols") + for sym in bld.env.symbol_map: + subsystems = set(bld.env.symbol_map[sym]) + if len(subsystems) == 1: + continue + + if sym in ['main', '_init', '_fini', 'init_samba_module', 'samba_init_module', 'ldb_init_module' ]: + # these are expected to be in many subsystems + continue + + # if all of them are in system libraries, we can ignore them. This copes + # with the duplication between libc, libpthread and libattr + all_syslib = True + for s in subsystems: + if s != 'c' and (not s in targets or targets[s] != 'SYSLIB'): + all_syslib = False + if all_syslib: + continue + Logs.info("symbol %s appears in %s" % (sym, subsystems)) + + +def SYMBOL_CHECK(bld): + '''check our dependency lists''' + if Options.options.SYMBOLCHECK: + bld.SET_BUILD_GROUP('symbolcheck') + task = bld(rule=symbols_symbolcheck, always=True, name='symbol checking') + task.env.bld = bld + + bld.SET_BUILD_GROUP('syslibcheck') + task = bld(rule=symbols_syslibcheck, always=True, name='syslib checking') + task.env.bld = bld + + bld.SET_BUILD_GROUP('syslibcheck') + task = bld(rule=symbols_dupcheck, always=True, name='symbol duplicate checking') + task.env.bld = bld + + if Options.options.WHYNEEDED: + bld.SET_BUILD_GROUP('syslibcheck') + task = bld(rule=symbols_whyneeded, always=True, name='check why a dependency is needed') + task.env.bld = bld + + +Build.BuildContext.SYMBOL_CHECK = SYMBOL_CHECK diff --git a/buildtools/wafsamba/tru64cc.py b/buildtools/wafsamba/tru64cc.py new file mode 100644 index 0000000000..7e85701536 --- /dev/null +++ b/buildtools/wafsamba/tru64cc.py @@ -0,0 +1,77 @@ + +# compiler definition for tru64/OSF1 cc compiler +# based on suncc.py from waf + +import os, optparse +import Utils, Options, Configure +import ccroot, ar +from Configure import conftest + +from compiler_cc import c_compiler + +c_compiler['osf1V'] = ['gcc', 'tru64cc'] + +@conftest +def find_tru64cc(conf): + v = conf.env + cc = None + if v['CC']: cc = v['CC'] + elif 'CC' in conf.environ: cc = conf.environ['CC'] + if not cc: cc = conf.find_program('cc', var='CC') + if not cc: conf.fatal('tru64cc was not found') + cc = conf.cmd_to_list(cc) + + try: + if not Utils.cmd_output(cc + ['-V']): + conf.fatal('tru64cc %r was not found' % cc) + except ValueError: + conf.fatal('tru64cc -V could not be executed') + + v['CC'] = cc + v['CC_NAME'] = 'tru64' + +@conftest +def tru64cc_common_flags(conf): + v = conf.env + + v['CC_SRC_F'] = '' + v['CC_TGT_F'] = ['-c', '-o', ''] + v['CPPPATH_ST'] = '-I%s' # template for adding include paths + + # linker + if not v['LINK_CC']: v['LINK_CC'] = v['CC'] + v['CCLNK_SRC_F'] = '' + v['CCLNK_TGT_F'] = ['-o', ''] + + v['LIB_ST'] = '-l%s' # template for adding libs + v['LIBPATH_ST'] = '-L%s' # template for adding libpaths + v['STATICLIB_ST'] = '-l%s' + v['STATICLIBPATH_ST'] = '-L%s' + v['CCDEFINES_ST'] = '-D%s' + +# v['SONAME_ST'] = '-Wl,-h -Wl,%s' +# v['SHLIB_MARKER'] = '-Bdynamic' +# v['STATICLIB_MARKER'] = '-Bstatic' + + # program + v['program_PATTERN'] = '%s' + + # shared library +# v['shlib_CCFLAGS'] = ['-Kpic', '-DPIC'] + v['shlib_LINKFLAGS'] = ['-shared'] + v['shlib_PATTERN'] = 'lib%s.so' + + # static lib +# v['staticlib_LINKFLAGS'] = ['-Bstatic'] +# v['staticlib_PATTERN'] = 'lib%s.a' + +detect = ''' +find_tru64cc +find_cpp +find_ar +tru64cc_common_flags +cc_load_tools +cc_add_flags +link_add_flags +''' + diff --git a/buildtools/wafsamba/wafsamba.py b/buildtools/wafsamba/wafsamba.py new file mode 100644 index 0000000000..2a1c82a307 --- /dev/null +++ b/buildtools/wafsamba/wafsamba.py @@ -0,0 +1,825 @@ +# a waf tool to add autoconf-like macros to the configure section +# and for SAMBA_ macros for building libraries, binaries etc + +import Build, os, sys, Options, Task, Utils, cc, TaskGen, fnmatch, re, shutil, Logs, Constants +from Configure import conf +from Logs import debug +from samba_utils import SUBST_VARS_RECURSIVE +TaskGen.task_gen.apply_verif = Utils.nada + +# bring in the other samba modules +from samba_optimisation import * +from samba_utils import * +from samba_version import * +from samba_autoconf import * +from samba_patterns import * +from samba_pidl import * +from samba_autoproto import * +from samba_python import * +from samba_deps import * +from samba_bundled import * +import samba_install +import samba_conftests +import samba_abi +import samba_headers +import tru64cc +import irixcc +import hpuxcc +import generic_cc +import samba_dist +import samba_wildcard +import stale_files +import symbols +import pkgconfig + +# some systems have broken threading in python +if os.environ.get('WAF_NOTHREADS') == '1': + import nothreads + +LIB_PATH="shared" + +os.environ['PYTHONUNBUFFERED'] = '1' + + +if Constants.HEXVERSION < 0x105019: + Logs.error(''' +Please use the version of waf that comes with Samba, not +a system installed version. See http://wiki.samba.org/index.php/Waf +for details. + +Alternatively, please run ./configure and make as usual. That will +call the right version of waf.''') + sys.exit(1) + + +@conf +def SAMBA_BUILD_ENV(conf): + '''create the samba build environment''' + conf.env.BUILD_DIRECTORY = conf.blddir + mkdir_p(os.path.join(conf.blddir, LIB_PATH)) + mkdir_p(os.path.join(conf.blddir, LIB_PATH, "private")) + mkdir_p(os.path.join(conf.blddir, "modules")) + mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc')) + # this allows all of the bin/shared and bin/python targets + # to be expressed in terms of build directory paths + mkdir_p(os.path.join(conf.blddir, 'default')) + for p in ['python','shared', 'modules']: + link_target = os.path.join(conf.blddir, 'default/' + p) + if not os.path.lexists(link_target): + os.symlink('../' + p, link_target) + + # get perl to put the blib files in the build directory + blib_bld = os.path.join(conf.blddir, 'default/pidl/blib') + blib_src = os.path.join(conf.srcdir, 'pidl/blib') + mkdir_p(blib_bld + '/man1') + mkdir_p(blib_bld + '/man3') + if os.path.islink(blib_src): + os.unlink(blib_src) + elif os.path.exists(blib_src): + shutil.rmtree(blib_src) + + +def ADD_INIT_FUNCTION(bld, subsystem, target, init_function): + '''add an init_function to the list for a subsystem''' + if init_function is None: + return + bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function) + cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS') + if not subsystem in cache: + cache[subsystem] = [] + cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } ) +Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION + + + +################################################################# +def SAMBA_LIBRARY(bld, libname, source, + deps='', + public_deps='', + includes='', + public_headers=None, + public_headers_install=True, + header_path=None, + pc_files=None, + vnum=None, + soname=None, + cflags='', + ldflags='', + external_library=False, + realname=None, + autoproto=None, + group='libraries', + depends_on='', + local_include=True, + global_include=True, + vars=None, + subdir=None, + install_path=None, + install=True, + pyembed=False, + pyext=False, + target_type='LIBRARY', + bundled_extension=True, + link_name=None, + abi_directory=None, + abi_match=None, + hide_symbols=False, + manpages=None, + private_library=False, + grouping_library=False, + allow_undefined_symbols=False, + enabled=True): + '''define a Samba library''' + + if not enabled: + SET_TARGET_TYPE(bld, libname, 'DISABLED') + return + + source = bld.EXPAND_VARIABLES(source, vars=vars) + if subdir: + source = bld.SUBDIR(subdir, source) + + # remember empty libraries, so we can strip the dependencies + if ((source == '') or (source == [])) and deps == '' and public_deps == '': + SET_TARGET_TYPE(bld, libname, 'EMPTY') + return + + if BUILTIN_LIBRARY(bld, libname): + obj_target = libname + else: + obj_target = libname + '.objlist' + + if group == 'libraries': + subsystem_group = 'main' + else: + subsystem_group = group + + # first create a target for building the object files for this library + # by separating in this way, we avoid recompiling the C files + # separately for the install library and the build library + bld.SAMBA_SUBSYSTEM(obj_target, + source = source, + deps = deps, + public_deps = public_deps, + includes = includes, + public_headers = public_headers, + public_headers_install = public_headers_install, + header_path = header_path, + cflags = cflags, + group = subsystem_group, + autoproto = autoproto, + depends_on = depends_on, + hide_symbols = hide_symbols, + pyext = pyext or (target_type == "PYTHON"), + local_include = local_include, + global_include = global_include) + + if BUILTIN_LIBRARY(bld, libname): + return + + if not SET_TARGET_TYPE(bld, libname, target_type): + return + + # the library itself will depend on that object target + deps += ' ' + public_deps + deps = TO_LIST(deps) + deps.append(obj_target) + + realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON')) + link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON')) + + # we don't want any public libraries without version numbers + if not private_library and vnum is None and soname is None and target_type != 'PYTHON' and not realname: + raise Utils.WafError("public library '%s' must have a vnum" % libname) + + if target_type == 'PYTHON' or realname or not private_library: + bundled_name = libname.replace('_', '-') + else: + bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library) + + ldflags = TO_LIST(ldflags) + + features = 'cc cshlib symlink_lib install_lib' + if target_type == 'PYTHON': + features += ' pyext' + if pyext or pyembed: + # this is quite strange. we should add pyext feature for pyext + # but that breaks the build. This may be a bug in the waf python tool + features += ' pyembed' + + if abi_directory: + features += ' abi_check' + + vscript = None + if bld.env.HAVE_LD_VERSION_SCRIPT: + if private_library: + version = "%s_%s" % (Utils.g_module.APPNAME, Utils.g_module.VERSION) + elif vnum: + version = "%s_%s" % (libname, vnum) + else: + version = None + if version: + vscript = "%s.vscript" % libname + bld.ABI_VSCRIPT(libname, abi_directory, version, vscript, + abi_match) + fullname = apply_pattern(bundled_name, bld.env.shlib_PATTERN) + fullpath = bld.path.find_or_declare(fullname) + vscriptpath = bld.path.find_or_declare(vscript) + if not fullpath: + raise Utils.WafError("unable to find fullpath for %s" % fullname) + if not vscriptpath: + raise Utils.WafError("unable to find vscript path for %s" % vscript) + bld.add_manual_dependency(fullpath, vscriptpath) + if Options.is_install: + # also make the .inst file depend on the vscript + instname = apply_pattern(bundled_name + '.inst', bld.env.shlib_PATTERN) + bld.add_manual_dependency(bld.path.find_or_declare(instname), bld.path.find_or_declare(vscript)) + vscript = os.path.join(bld.path.abspath(bld.env), vscript) + + bld.SET_BUILD_GROUP(group) + t = bld( + features = features, + source = [], + target = bundled_name, + depends_on = depends_on, + samba_ldflags = ldflags, + samba_deps = deps, + samba_includes = includes, + version_script = vscript, + local_include = local_include, + global_include = global_include, + vnum = vnum, + soname = soname, + install_path = None, + samba_inst_path = install_path, + name = libname, + samba_realname = realname, + samba_install = install, + abi_directory = "%s/%s" % (bld.path.abspath(), abi_directory), + abi_match = abi_match, + private_library = private_library, + grouping_library=grouping_library, + allow_undefined_symbols=allow_undefined_symbols + ) + + if realname and not link_name: + link_name = 'shared/%s' % realname + + if link_name: + t.link_name = link_name + + if pc_files is not None: + bld.PKG_CONFIG_FILES(pc_files, vnum=vnum) + + if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']: + bld.MANPAGES(manpages) + + +Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY + + +################################################################# +def SAMBA_BINARY(bld, binname, source, + deps='', + includes='', + public_headers=None, + header_path=None, + modules=None, + ldflags=None, + cflags='', + autoproto=None, + use_hostcc=False, + use_global_deps=True, + compiler=None, + group='binaries', + manpages=None, + local_include=True, + global_include=True, + subsystem_name=None, + pyembed=False, + vars=None, + subdir=None, + install=True, + install_path=None, + enabled=True): + '''define a Samba binary''' + + if not enabled: + SET_TARGET_TYPE(bld, binname, 'DISABLED') + return + + if not SET_TARGET_TYPE(bld, binname, 'BINARY'): + return + + features = 'cc cprogram symlink_bin install_bin' + if pyembed: + features += ' pyembed' + + obj_target = binname + '.objlist' + + source = bld.EXPAND_VARIABLES(source, vars=vars) + if subdir: + source = bld.SUBDIR(subdir, source) + source = unique_list(TO_LIST(source)) + + if group == 'binaries': + subsystem_group = 'main' + else: + subsystem_group = group + + # first create a target for building the object files for this binary + # by separating in this way, we avoid recompiling the C files + # separately for the install binary and the build binary + bld.SAMBA_SUBSYSTEM(obj_target, + source = source, + deps = deps, + includes = includes, + cflags = cflags, + group = subsystem_group, + autoproto = autoproto, + subsystem_name = subsystem_name, + local_include = local_include, + global_include = global_include, + use_hostcc = use_hostcc, + pyext = pyembed, + use_global_deps= use_global_deps) + + bld.SET_BUILD_GROUP(group) + + # the binary itself will depend on that object target + deps = TO_LIST(deps) + deps.append(obj_target) + + t = bld( + features = features, + source = [], + target = binname, + samba_deps = deps, + samba_includes = includes, + local_include = local_include, + global_include = global_include, + samba_modules = modules, + top = True, + samba_subsystem= subsystem_name, + install_path = None, + samba_inst_path= install_path, + samba_install = install, + samba_ldflags = TO_LIST(ldflags) + ) + + if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']: + bld.MANPAGES(manpages) + +Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY + + +################################################################# +def SAMBA_MODULE(bld, modname, source, + deps='', + includes='', + subsystem=None, + init_function=None, + module_init_name='samba_init_module', + autoproto=None, + autoproto_extra_source='', + cflags='', + internal_module=True, + local_include=True, + global_include=True, + vars=None, + subdir=None, + enabled=True, + pyembed=False, + allow_undefined_symbols=False + ): + '''define a Samba module.''' + + source = bld.EXPAND_VARIABLES(source, vars=vars) + if subdir: + source = bld.SUBDIR(subdir, source) + + if internal_module or BUILTIN_LIBRARY(bld, modname): + bld.SAMBA_SUBSYSTEM(modname, source, + deps=deps, + includes=includes, + autoproto=autoproto, + autoproto_extra_source=autoproto_extra_source, + cflags=cflags, + local_include=local_include, + global_include=global_include, + enabled=enabled) + + bld.ADD_INIT_FUNCTION(subsystem, modname, init_function) + return + + if not enabled: + SET_TARGET_TYPE(bld, modname, 'DISABLED') + return + + obj_target = modname + '.objlist' + + realname = modname + if subsystem is not None: + deps += ' ' + subsystem + while realname.startswith("lib"+subsystem+"_"): + realname = realname[len("lib"+subsystem+"_"):] + while realname.startswith(subsystem+"_"): + realname = realname[len(subsystem+"_"):] + + realname = bld.make_libname(realname) + while realname.startswith("lib"): + realname = realname[len("lib"):] + + build_link_name = "modules/%s/%s" % (subsystem, realname) + + if init_function: + cflags += " -D%s=%s" % (init_function, module_init_name) + + bld.SAMBA_LIBRARY(modname, + source, + deps=deps, + includes=includes, + cflags=cflags, + realname = realname, + autoproto = autoproto, + local_include=local_include, + global_include=global_include, + vars=vars, + link_name=build_link_name, + install_path="${MODULESDIR}/%s" % subsystem, + pyembed=pyembed, + allow_undefined_symbols=allow_undefined_symbols + ) + + +Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE + + +################################################################# +def SAMBA_SUBSYSTEM(bld, modname, source, + deps='', + public_deps='', + includes='', + public_headers=None, + public_headers_install=True, + header_path=None, + cflags='', + cflags_end=None, + group='main', + init_function_sentinal=None, + autoproto=None, + autoproto_extra_source='', + depends_on='', + local_include=True, + local_include_first=True, + global_include=True, + subsystem_name=None, + enabled=True, + use_hostcc=False, + use_global_deps=True, + vars=None, + subdir=None, + hide_symbols=False, + pyext=False): + '''define a Samba subsystem''' + + if not enabled: + SET_TARGET_TYPE(bld, modname, 'DISABLED') + return + + # remember empty subsystems, so we can strip the dependencies + if ((source == '') or (source == [])) and deps == '' and public_deps == '': + SET_TARGET_TYPE(bld, modname, 'EMPTY') + return + + if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'): + return + + source = bld.EXPAND_VARIABLES(source, vars=vars) + if subdir: + source = bld.SUBDIR(subdir, source) + source = unique_list(TO_LIST(source)) + + deps += ' ' + public_deps + + bld.SET_BUILD_GROUP(group) + + features = 'cc' + if pyext: + features += ' pyext' + + t = bld( + features = features, + source = source, + target = modname, + samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols), + depends_on = depends_on, + samba_deps = TO_LIST(deps), + samba_includes = includes, + local_include = local_include, + local_include_first = local_include_first, + global_include = global_include, + samba_subsystem= subsystem_name, + samba_use_hostcc = use_hostcc, + samba_use_global_deps = use_global_deps + ) + + if cflags_end is not None: + t.samba_cflags.extend(TO_LIST(cflags_end)) + + if autoproto is not None: + bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source)) + if public_headers is not None: + bld.PUBLIC_HEADERS(public_headers, header_path=header_path, + public_headers_install=public_headers_install) + return t + + +Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM + + +def SAMBA_GENERATOR(bld, name, rule, source='', target='', + group='generators', enabled=True, + public_headers=None, + public_headers_install=True, + header_path=None, + vars=None, + always=False): + '''A generic source generator target''' + + if not SET_TARGET_TYPE(bld, name, 'GENERATOR'): + return + + if not enabled: + return + + bld.SET_BUILD_GROUP(group) + t = bld( + rule=rule, + source=bld.EXPAND_VARIABLES(source, vars=vars), + target=target, + shell=isinstance(rule, str), + on_results=True, + before='cc', + ext_out='.c', + samba_type='GENERATOR', + dep_vars = [rule] + (vars or []), + name=name) + + if always: + t.always = True + + if public_headers is not None: + bld.PUBLIC_HEADERS(public_headers, header_path=header_path, + public_headers_install=public_headers_install) + return t +Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR + + + +@runonce +def SETUP_BUILD_GROUPS(bld): + '''setup build groups used to ensure that the different build + phases happen consecutively''' + bld.p_ln = bld.srcnode # we do want to see all targets! + bld.env['USING_BUILD_GROUPS'] = True + bld.add_group('setup') + bld.add_group('build_compiler_source') + bld.add_group('vscripts') + bld.add_group('base_libraries') + bld.add_group('generators') + bld.add_group('compiler_prototypes') + bld.add_group('compiler_libraries') + bld.add_group('build_compilers') + bld.add_group('build_source') + bld.add_group('prototypes') + bld.add_group('headers') + bld.add_group('main') + bld.add_group('symbolcheck') + bld.add_group('libraries') + bld.add_group('binaries') + bld.add_group('syslibcheck') + bld.add_group('final') +Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS + + +def SET_BUILD_GROUP(bld, group): + '''set the current build group''' + if not 'USING_BUILD_GROUPS' in bld.env: + return + bld.set_group(group) +Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP + + + +@conf +def ENABLE_TIMESTAMP_DEPENDENCIES(conf): + """use timestamps instead of file contents for deps + this currently doesn't work""" + def h_file(filename): + import stat + st = os.stat(filename) + if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file') + m = Utils.md5() + m.update(str(st.st_mtime)) + m.update(str(st.st_size)) + m.update(filename) + return m.digest() + Utils.h_file = h_file + + + +t = Task.simple_task_type('copy_script', 'rm -f "${LINK_TARGET}" && ln -s "${SRC[0].abspath(env)}" ${LINK_TARGET}', + shell=True, color='PINK', ext_in='.bin') +t.quiet = True + +@feature('copy_script') +@before('apply_link') +def copy_script(self): + tsk = self.create_task('copy_script', self.allnodes[0]) + tsk.env.TARGET = self.target + +def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None): + '''used to copy scripts from the source tree into the build directory + for use by selftest''' + + source = bld.path.ant_glob(pattern) + + bld.SET_BUILD_GROUP('build_source') + for s in TO_LIST(source): + iname = s + if installname != None: + iname = installname + target = os.path.join(installdir, iname) + tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target)) + mkdir_p(tgtdir) + t = bld(features='copy_script', + source = s, + target = target, + always = True, + install_path = None) + t.env.LINK_TARGET = target + +Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT + +def copy_and_fix_python_path(task): + pattern='sys.path.insert(0, "bin/python")' + if task.env["PYTHONARCHDIR"] in sys.path and task.env["PYTHONDIR"] in sys.path: + replacement = "" + elif task.env["PYTHONARCHDIR"] == task.env["PYTHONDIR"]: + replacement="""sys.path.insert(0, "%s")""" % task.env["PYTHONDIR"] + else: + replacement="""sys.path.insert(0, "%s") +sys.path.insert(1, "%s")""" % (task.env["PYTHONARCHDIR"], task.env["PYTHONDIR"]) + + installed_location=task.outputs[0].bldpath(task.env) + source_file = open(task.inputs[0].srcpath(task.env)) + installed_file = open(installed_location, 'w') + for line in source_file: + newline = line + if pattern in line: + newline = line.replace(pattern, replacement) + installed_file.write(newline) + installed_file.close() + os.chmod(installed_location, 0755) + return 0 + + +def install_file(bld, destdir, file, chmod=MODE_644, flat=False, + python_fixup=False, destname=None, base_name=None): + '''install a file''' + destdir = bld.EXPAND_VARIABLES(destdir) + if not destname: + destname = file + if flat: + destname = os.path.basename(destname) + dest = os.path.join(destdir, destname) + if python_fixup: + # fixup the python path it will use to find Samba modules + inst_file = file + '.inst' + bld.SAMBA_GENERATOR('python_%s' % destname, + rule=copy_and_fix_python_path, + source=file, + target=inst_file) + file = inst_file + if base_name: + file = os.path.join(base_name, file) + bld.install_as(dest, file, chmod=chmod) + + +def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False, + python_fixup=False, destname=None, base_name=None): + '''install a set of files''' + for f in TO_LIST(files): + install_file(bld, destdir, f, chmod=chmod, flat=flat, + python_fixup=python_fixup, destname=destname, + base_name=base_name) +Build.BuildContext.INSTALL_FILES = INSTALL_FILES + + +def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False, + python_fixup=False, exclude=None, trim_path=None): + '''install a set of files matching a wildcard pattern''' + files=TO_LIST(bld.path.ant_glob(pattern)) + if trim_path: + files2 = [] + for f in files: + files2.append(os_path_relpath(f, trim_path)) + files = files2 + + if exclude: + for f in files[:]: + if fnmatch.fnmatch(f, exclude): + files.remove(f) + INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat, + python_fixup=python_fixup, base_name=trim_path) +Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD + + +def INSTALL_DIRS(bld, destdir, dirs): + '''install a set of directories''' + destdir = bld.EXPAND_VARIABLES(destdir) + dirs = bld.EXPAND_VARIABLES(dirs) + for d in TO_LIST(dirs): + bld.install_dir(os.path.join(destdir, d)) +Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS + + +def MANPAGES(bld, manpages): + '''build and install manual pages''' + bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl' + for m in manpages.split(): + source = m + '.xml' + bld.SAMBA_GENERATOR(m, + source=source, + target=m, + group='final', + rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}' + ) + bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True) +Build.BuildContext.MANPAGES = MANPAGES + + +############################################################# +# give a nicer display when building different types of files +def progress_display(self, msg, fname): + col1 = Logs.colors(self.color) + col2 = Logs.colors.NORMAL + total = self.position[1] + n = len(str(total)) + fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg) + return fs % (self.position[0], self.position[1], col1, fname, col2) + +def link_display(self): + if Options.options.progress_bar != 0: + return Task.Task.old_display(self) + fname = self.outputs[0].bldpath(self.env) + return progress_display(self, 'Linking', fname) +Task.TaskBase.classes['cc_link'].display = link_display + +def samba_display(self): + if Options.options.progress_bar != 0: + return Task.Task.old_display(self) + + targets = LOCAL_CACHE(self, 'TARGET_TYPE') + if self.name in targets: + target_type = targets[self.name] + type_map = { 'GENERATOR' : 'Generating', + 'PROTOTYPE' : 'Generating' + } + if target_type in type_map: + return progress_display(self, type_map[target_type], self.name) + + if len(self.inputs) == 0: + return Task.Task.old_display(self) + + fname = self.inputs[0].bldpath(self.env) + if fname[0:3] == '../': + fname = fname[3:] + ext_loc = fname.rfind('.') + if ext_loc == -1: + return Task.Task.old_display(self) + ext = fname[ext_loc:] + + ext_map = { '.idl' : 'Compiling IDL', + '.et' : 'Compiling ERRTABLE', + '.asn1': 'Compiling ASN1', + '.c' : 'Compiling' } + if ext in ext_map: + return progress_display(self, ext_map[ext], fname) + return Task.Task.old_display(self) + +Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display +Task.TaskBase.classes['Task'].display = samba_display + + +@after('apply_link') +@feature('cshlib') +def apply_bundle_remove_dynamiclib_patch(self): + if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False): + if not getattr(self,'vnum',None): + try: + self.env['LINKFLAGS'].remove('-dynamiclib') + self.env['LINKFLAGS'].remove('-single_module') + except ValueError: + pass diff --git a/buildtools/wafsamba/wscript b/buildtools/wafsamba/wscript new file mode 100755 index 0000000000..ecc2ae51f6 --- /dev/null +++ b/buildtools/wafsamba/wscript @@ -0,0 +1,405 @@ +#!/usr/bin/env python + +# this is a base set of waf rules that everything else pulls in first + +import sys, wafsamba, Configure, Logs +import Options, os, preproc +from samba_utils import * +from optparse import SUPPRESS_HELP + +# this forces configure to be re-run if any of the configure +# sections of the build scripts change. We have to check +# for this in sys.argv as options have not yet been parsed when +# we need to set this. This is off by default until some issues +# are resolved related to WAFCACHE. It will need a lot of testing +# before it is enabled by default. +if '--enable-auto-reconfigure' in sys.argv: + Configure.autoconfig = True + +def set_options(opt): + opt.tool_options('compiler_cc') + + opt.tool_options('gnu_dirs') + + gr = opt.option_group('library handling options') + + gr.add_option('--bundled-libraries', + help=("comma separated list of bundled libraries. May include !LIBNAME to disable bundling a library. Can be 'NONE' or 'ALL' [auto]"), + action="store", dest='BUNDLED_LIBS', default='') + + extension_default = Options.options['PRIVATE_EXTENSION_DEFAULT'] + gr.add_option('--private-library-extension', + help=("name extension for private libraries [%s]" % extension_default), + action="store", dest='PRIVATE_EXTENSION', default=extension_default) + + extension_exception = Options.options['PRIVATE_EXTENSION_EXCEPTION'] + gr.add_option('--private-extension-exception', + help=("comma separated list of libraries to not apply extension to [%s]" % extension_exception), + action="store", dest='PRIVATE_EXTENSION_EXCEPTION', default=extension_exception) + + builtin_defauilt = Options.options['BUILTIN_LIBRARIES_DEFAULT'] + gr.add_option('--builtin-libraries', + help=("command separated list of libraries to build directly into binaries [%s]" % builtin_defauilt), + action="store", dest='BUILTIN_LIBRARIES', default=builtin_defauilt) + + gr.add_option('--minimum-library-version', + help=("list of minimum system library versions (LIBNAME1:version,LIBNAME2:version)"), + action="store", dest='MINIMUM_LIBRARY_VERSION', default='') + + gr.add_option('--disable-shared', + help=("Disable all use of shared libraries"), + action="store_true", dest='disable_shared', default=False) + gr.add_option('--disable-rpath', + help=("Disable use of rpath for build binaries"), + action="store_true", dest='disable_rpath_build', default=False) + gr.add_option('--disable-rpath-install', + help=("Disable use of rpath for library path in installed files"), + action="store_true", dest='disable_rpath_install', default=False) + gr.add_option('--disable-rpath-private-install', + help=("Disable use of rpath for private library path in installed files"), + action="store_true", dest='disable_rpath_private_install', default=False) + gr.add_option('--nonshared-binary', + help=("Disable use of shared libs for the listed binaries"), + action="store", dest='NONSHARED_BINARIES', default='') + gr.add_option('--disable-symbol-versions', + help=("Disable use of the --version-script linker option"), + action="store_true", dest='disable_symbol_versions', default=False) + + opt.add_option('--with-modulesdir', + help=("modules directory [PREFIX/modules]"), + action="store", dest='MODULESDIR', default='${PREFIX}/modules') + + opt.add_option('--with-privatelibdir', + help=("private library directory [PREFIX/lib/%s]" % Utils.g_module.APPNAME), + action="store", dest='PRIVATELIBDIR', default=None) + + gr = opt.option_group('developer options') + + gr.add_option('-C', + help='enable configure cacheing', + action='store_true', dest='enable_configure_cache') + gr.add_option('--enable-auto-reconfigure', + help='enable automatic reconfigure on build', + action='store_true', dest='enable_auto_reconfigure') + gr.add_option('--enable-developer', + help=("Turn on developer warnings and debugging"), + action="store_true", dest='developer', default=False) + gr.add_option('--picky-developer', + help=("Treat all warnings as errors (enable -Werror)"), + action="store_true", dest='picky_developer', default=False) + gr.add_option('--fatal-errors', + help=("Stop compilation on first error (enable -Wfatal-errors)"), + action="store_true", dest='fatal_errors', default=False) + gr.add_option('--enable-gccdeps', + help=("Enable use of gcc -MD dependency module"), + action="store_true", dest='enable_gccdeps', default=True) + gr.add_option('--timestamp-dependencies', + help=("use file timestamps instead of content for build dependencies (BROKEN)"), + action="store_true", dest='timestamp_dependencies', default=False) + gr.add_option('--pedantic', + help=("Enable even more compiler warnings"), + action='store_true', dest='pedantic', default=False) + gr.add_option('--git-local-changes', + help=("mark version with + if local git changes"), + action='store_true', dest='GIT_LOCAL_CHANGES', default=False) + + gr.add_option('--abi-check', + help=("Check ABI signatures for libraries"), + action='store_true', dest='ABI_CHECK', default=False) + gr.add_option('--abi-check-disable', + help=("Disable ABI checking (used with --enable-developer)"), + action='store_true', dest='ABI_CHECK_DISABLE', default=False) + gr.add_option('--abi-update', + help=("Update ABI signature files for libraries"), + action='store_true', dest='ABI_UPDATE', default=False) + + gr.add_option('--show-deps', + help=("Show dependency tree for the given target"), + dest='SHOWDEPS', default='') + + gr.add_option('--symbol-check', + help=("check symbols in object files against project rules"), + action='store_true', dest='SYMBOLCHECK', default=False) + + gr.add_option('--why-needed', + help=("TARGET:DEPENDENCY check why TARGET needs DEPENDENCY"), + action='store', type='str', dest='WHYNEEDED', default=None) + + gr.add_option('--show-duplicates', + help=("Show objects which are included in multiple binaries or libraries"), + action='store_true', dest='SHOW_DUPLICATES', default=False) + + gr = opt.add_option_group('cross compilation options') + + gr.add_option('--cross-compile', + help=("configure for cross-compilation"), + action='store_true', dest='CROSS_COMPILE', default=False) + gr.add_option('--cross-execute', + help=("command prefix to use for cross-execution in configure"), + action='store', dest='CROSS_EXECUTE', default='') + gr.add_option('--cross-answers', + help=("answers to cross-compilation configuration (auto modified)"), + action='store', dest='CROSS_ANSWERS', default='') + gr.add_option('--hostcc', + help=("set host compiler when cross compiling"), + action='store', dest='HOSTCC', default=False) + + # we use SUPPRESS_HELP for these, as they are ignored, and are there only + # to allow existing RPM spec files to work + opt.add_option('--build', + help=SUPPRESS_HELP, + action='store', dest='AUTOCONF_BUILD', default='') + opt.add_option('--host', + help=SUPPRESS_HELP, + action='store', dest='AUTOCONF_HOST', default='') + opt.add_option('--program-prefix', + help=SUPPRESS_HELP, + action='store', dest='AUTOCONF_PROGRAM_PREFIX', default='') + opt.add_option('--disable-dependency-tracking', + help=SUPPRESS_HELP, + action='store_true', dest='AUTOCONF_DISABLE_DEPENDENCY_TRACKING', default=False) + + gr = opt.option_group('dist options') + gr.add_option('--sign-release', + help='sign the release tarball created by waf dist', + action='store_true', dest='SIGN_RELEASE') + gr.add_option('--tag', + help='tag release in git at the same time', + type='string', action='store', dest='TAG_RELEASE') + + +@wafsamba.runonce +def configure(conf): + conf.env.hlist = [] + conf.env.srcdir = conf.srcdir + + if Options.options.timestamp_dependencies: + conf.ENABLE_TIMESTAMP_DEPENDENCIES() + + conf.SETUP_CONFIGURE_CACHE(Options.options.enable_configure_cache) + + # load our local waf extensions + conf.check_tool('gnu_dirs') + conf.check_tool('wafsamba') + + conf.CHECK_CC_ENV() + + conf.check_tool('compiler_cc') + + # we need git for 'waf dist' + conf.find_program('git', var='GIT') + + # older gcc versions (< 4.4) does not work with gccdeps, so we have to see if the .d file is generated + if Options.options.enable_gccdeps: + from TaskGen import feature, after + @feature('testd') + @after('apply_core') + def check_d(self): + tsk = self.compiled_tasks[0] + tsk.outputs.append(tsk.outputs[0].change_ext('.d')) + + import Task + cc = Task.TaskBase.classes['cc'] + oldmeth = cc.run + + cc.run = Task.compile_fun_noshell('cc', '${CC} ${CCFLAGS} ${CPPFLAGS} ${_CCINCFLAGS} ${_CCDEFFLAGS} ${CC_SRC_F}${SRC} ${CC_TGT_F}${TGT[0].abspath(env)}')[0] + try: + try: + conf.check(features='cc testd', fragment='int main() {return 0;}\n', ccflags=['-MD'], mandatory=True, msg='Check for -MD') + except: + pass + else: + conf.check_tool('gccdeps', tooldir=conf.srcdir + "/buildtools/wafsamba") + finally: + cc.run = oldmeth + + # make the install paths available in environment + conf.env.LIBDIR = Options.options.LIBDIR or '${PREFIX}/lib' + conf.env.BINDIR = Options.options.BINDIR or '${PREFIX}/bin' + conf.env.SBINDIR = Options.options.SBINDIR or '${PREFIX}/sbin' + conf.env.MODULESDIR = Options.options.MODULESDIR + conf.env.PRIVATELIBDIR = Options.options.PRIVATELIBDIR + conf.env.BUNDLED_LIBS = Options.options.BUNDLED_LIBS.split(',') + conf.env.BUILTIN_LIBRARIES = Options.options.BUILTIN_LIBRARIES.split(',') + conf.env.DISABLE_SHARED = Options.options.disable_shared + conf.env.NONSHARED_BINARIES = Options.options.NONSHARED_BINARIES.split(',') + + conf.env.PRIVATE_EXTENSION = Options.options.PRIVATE_EXTENSION + conf.env.PRIVATE_EXTENSION_EXCEPTION = Options.options.PRIVATE_EXTENSION_EXCEPTION.split(',') + + conf.env.CROSS_COMPILE = Options.options.CROSS_COMPILE + conf.env.CROSS_EXECUTE = Options.options.CROSS_EXECUTE + conf.env.CROSS_ANSWERS = Options.options.CROSS_ANSWERS + conf.env.HOSTCC = Options.options.HOSTCC + + conf.env.AUTOCONF_BUILD = Options.options.AUTOCONF_BUILD + conf.env.AUTOCONF_HOST = Options.options.AUTOCONF_HOST + conf.env.AUTOCONF_PROGRAM_PREFIX = Options.options.AUTOCONF_PROGRAM_PREFIX + + if (conf.env.AUTOCONF_HOST and + conf.env.AUTOCONF_BUILD and + conf.env.AUTOCONF_BUILD != conf.env.AUTOCONF_HOST): + Logs.error('ERROR: Mismatch between --build and --host. Please use --cross-compile instead') + sys.exit(1) + if conf.env.AUTOCONF_PROGRAM_PREFIX: + Logs.error('ERROR: --program-prefix not supported') + sys.exit(1) + + # enable ABI checking for developers + conf.env.ABI_CHECK = Options.options.ABI_CHECK or Options.options.developer + if Options.options.ABI_CHECK_DISABLE: + conf.env.ABI_CHECK = False + try: + conf.find_program('gdb', mandatory=True) + except: + conf.env.ABI_CHECK = False + + conf.env.GIT_LOCAL_CHANGES = Options.options.GIT_LOCAL_CHANGES + + conf.CHECK_COMMAND(['uname', '-a'], + msg='Checking build system', + define='BUILD_SYSTEM', + on_target=False) + conf.CHECK_UNAME() + + # see if we can compile and run a simple C program + conf.CHECK_CODE('printf("hello world")', + define='HAVE_SIMPLE_C_PROG', + mandatory=True, + execute=True, + headers='stdio.h', + msg='Checking simple C program') + + # see if we can build shared libs + if not conf.CHECK_LIBRARY_SUPPORT(): + conf.env.DISABLE_SHARED = True + + # check for rpath + if not conf.env.DISABLE_SHARED and conf.CHECK_LIBRARY_SUPPORT(rpath=True): + support_rpath = True + conf.env.RPATH_ON_BUILD = not Options.options.disable_rpath_build + conf.env.RPATH_ON_INSTALL = (conf.env.RPATH_ON_BUILD and + not Options.options.disable_rpath_install) + if not conf.env.PRIVATELIBDIR: + conf.env.PRIVATELIBDIR = '%s/%s' % (conf.env.LIBDIR, Utils.g_module.APPNAME) + conf.env.RPATH_ON_INSTALL_PRIVATE = ( + not Options.options.disable_rpath_private_install) + else: + support_rpath = False + conf.env.RPATH_ON_INSTALL = False + conf.env.RPATH_ON_BUILD = False + conf.env.RPATH_ON_INSTALL_PRIVATE = False + if not conf.env.PRIVATELIBDIR: + # rpath is not possible so there is no sense in having a + # private library directory by default. + # the user can of course always override it. + conf.env.PRIVATELIBDIR = conf.env.LIBDIR + + if (not conf.env.DISABLE_SHARED and + not Options.options.disable_symbol_versions and + conf.CHECK_LIBRARY_SUPPORT(rpath=support_rpath, + version_script=True, + msg='-Wl,--version-script support')): + conf.env.HAVE_LD_VERSION_SCRIPT = True + else: + conf.env.HAVE_LD_VERSION_SCRIPT = False + + if sys.platform == "aix5": + conf.DEFINE('_ALL_SOURCE', 1, add_to_cflags=True) + # Might not be needed if ALL_SOURCE is defined + # conf.DEFINE('_XOPEN_SOURCE', 600, add_to_cflags=True) + + # we should use the PIC options in waf instead + # Some compilo didn't support -fPIC but just print a warning + if conf.env['COMPILER_CC'] == "suncc": + conf.ADD_CFLAGS('-KPIC', testflags=True) + # we really want define here as we need to have this + # define even during the tests otherwise detection of + # boolean is broken + conf.DEFINE('_STDC_C99', 1, add_to_cflags=True) + conf.DEFINE('_XPG6', 1, add_to_cflags=True) + else: + conf.ADD_CFLAGS('-fPIC', testflags=True) + + # On Solaris 8 with suncc (at least) the flags for the linker to define the name of the + # library are not always working (if the command line is very very long and with a lot + # files) + + if conf.env['COMPILER_CC'] == "suncc": + save = conf.env['SONAME_ST'] + conf.env['SONAME_ST'] = '-Wl,-h,%s' + if not conf.CHECK_SHLIB_INTRASINC_NAME_FLAGS("Checking if flags %s are ok" % conf.env['SONAME_ST']): + conf.env['SONAME_ST'] = save + + conf.CHECK_INLINE() + + # check for pkgconfig + conf.check_cfg(atleast_pkgconfig_version='0.0.0') + + conf.DEFINE('_GNU_SOURCE', 1, add_to_cflags=True) + conf.DEFINE('_XOPEN_SOURCE_EXTENDED', 1, add_to_cflags=True) + + # get the base headers we'll use for the rest of the tests + conf.CHECK_HEADERS('stdio.h sys/types.h sys/stat.h stdlib.h stddef.h memory.h string.h', + add_headers=True) + conf.CHECK_HEADERS('strings.h inttypes.h stdint.h unistd.h minix/config.h', add_headers=True) + conf.CHECK_HEADERS('ctype.h standards.h stdbool.h stdint.h stdarg.h vararg.h', add_headers=True) + conf.CHECK_HEADERS('limits.h assert.h') + + # see if we need special largefile flags + conf.CHECK_LARGEFILE() + + if 'HAVE_STDDEF_H' in conf.env and 'HAVE_STDLIB_H' in conf.env: + conf.DEFINE('STDC_HEADERS', 1) + + conf.CHECK_HEADERS('sys/time.h time.h', together=True) + + if 'HAVE_SYS_TIME_H' in conf.env and 'HAVE_TIME_H' in conf.env: + conf.DEFINE('TIME_WITH_SYS_TIME', 1) + + # cope with different extensions for libraries + (root, ext) = os.path.splitext(conf.env.shlib_PATTERN) + if ext[0] == '.': + conf.define('SHLIBEXT', ext[1:], quote=True) + else: + conf.define('SHLIBEXT', "so", quote=True) + + conf.CHECK_CODE('long one = 1; return ((char *)(&one))[0]', + execute=True, + define='WORDS_BIGENDIAN') + + # check if signal() takes a void function + if conf.CHECK_CODE('return *(signal (0, 0)) (0) == 1', + define='RETSIGTYPE_INT', + execute=False, + headers='signal.h', + msg='Checking if signal handlers return int'): + conf.DEFINE('RETSIGTYPE', 'int') + else: + conf.DEFINE('RETSIGTYPE', 'void') + + conf.CHECK_VARIABLE('__FUNCTION__', define='HAVE_FUNCTION_MACRO') + + conf.CHECK_CODE('va_list ap1,ap2; va_copy(ap1,ap2)', + define="HAVE_VA_COPY", + msg="Checking for va_copy") + + conf.CHECK_CODE(''' + #define eprintf(...) fprintf(stderr, __VA_ARGS__) + eprintf("bla", "bar") + ''', define='HAVE__VA_ARGS__MACRO') + + conf.SAMBA_BUILD_ENV() + + +def build(bld): + # give a more useful message if the source directory has moved + relpath = os_path_relpath(bld.curdir, bld.srcnode.abspath()) + if relpath.find('../') != -1: + Logs.error('bld.curdir %s is not a child of %s' % (bld.curdir, bld.srcnode.abspath())) + raise Utils.WafError('''The top source directory has moved. Please run distclean and reconfigure''') + + bld.CHECK_MAKEFLAGS() + bld.SETUP_BUILD_GROUPS() + bld.ENFORCE_GROUP_ORDERING() + bld.CHECK_PROJECT_RULES() |