#!/usr/bin/env ksh # $Id: msync.ksh,v 4.52 2012/08/12 18:57:25 ksb Exp $ # Make sure this master source (or meta source) structure is stable: # modes are distribution quality, # co'd files are specified version, # RCS or CVS dirs are lint clean (SVN and git in future), # the make macro SOURCE catches all the files in the top-level directories # (this is not from msrc, it is totally local policy), # honor the GEN macro to suppess files that are not SOURCE: # (once again this is local policy only), # recurse into SUBDIRs under -R: # subdirs match this version (again, local policy for rcsvg), # check that every SEND file not in GEN exists (is readable really). # Bugs: # We know about level2s stuff more than we should, but I got sick of # getting all the way to building a package to have the versions bug # make me go around the loop again. # # N.B. import VersionFunc, MSYNC_GROUP from the environment. # # We guess at the "ksb version" for this product, if not given. # ksb always uses the major revision number of the mkcmd template or # the msrc recipe (Makefile) as the key {1.x -> One, 2.x -> Two and the # like}. Under -N we'll disclose the computed symbolic name: change that # logic to your local policy (if you have one) and rename this program. # typeset PROGNAME PfSPAT MYTEMP PROGNAME=`basename $0 .ksh` PfSPAT=${TMPDIR:-/tmp}/msYncXXXXXX MYTEMP=`mktemp -d "$PfSPAT" ` || exit 73 trap 'rm -rf $MYTEMP' EXIT # The site policy to find the local symbolic version from a revision (ksb) # number. It might consult the date, or some other local data source. : ${VersionFunc:="VersionDefault"} function VersionDefault { sed -e 's/^0\.[0-9.]*/Zero/' -e 's/^1\.[0-9.]*/One/' \ -e 's/^2\.[0-9.]*/Two/' -e 's/^3\.[0-9.]*/Three/' \ -e 's/^4\.[0-9.]*/Four/' -e 's/^5\.[0-9.]*/Five/' \ -e 's/^6\.[0-9.]*/Six/' -e 's/^7\.[0-9.]*/Seven/' \ -e 's/^8\.[0-9.]*/Eight/' -e 's/^9\.[0-9.]*/Nine/' \ -e 's/^10\.[0-9.]*/Ten/' -e 's/^11\.[0-9.]*/Eleven/' \ -e 's/^12\.[0-9.]*/Twelve/' -e 's/^13\.[0-9.]*/Thirteen/' \ -e 's/^14\.[0-9.]*/Fourteen/' -e 's/^15\.[0-9.]*/Fifteen/' \ -e 's/^16\.[0-9.]*/Sixteen/' -e 's/^17\.[0-9.]*/Seventeen/' \ -e 's/^18\.[0-9.]*/Eighteen/' -e 's/^19\.[0-9.]*/Nineteen/' \ -e 's/^20\.[0-9.]*/Twenty/' -e 's/^[0-9].*/Max/' } typeset slide GRPOF VERSION RECUR_SUBDIR PreList VEROF LEVEL3 slide='P=$1; shift; set _ -`expr _"$P" : _'\''-.\(.*\)'\''` ${1+"$@"}; shift' case $PROGNAME in *verof*) VEROF=true ;; *) VEROF=false ;; esac GRPOF=false VERSION=false RECUR_SUBDIR=false LEVEL3="" PreList="" : ${DIFF_OPTS:=""} ${SPEC_FILE:=ITO.spec} while [ $# -gt 0 ] ; do case "$1" in --) shift break ;; -G) GRPOF=true shift ;; -G*) GRPOF=true eval "$slide" ;; -N) VEROF=true shift ;; -N*) VEROF=true eval "$slide" ;; -V) VERSION=true shift ;; -V*) VERSION=true eval "$slide" ;; -m) # note that -m could have embedded spaces, but not quotes PreList="$PreList -m \"$2\"" shift; shift ;; -m*) PreList="$PreList \"$1\"" shift ;; -R) RECUR_SUBDIR=true shift ;; -R*) RECUR_SUBDIR=true eval "$slide" ;; -3) LEVEL3=true shift ;; -3*) LEVEL3=true eval "$slide" ;; -h*) echo "$PROGNAME: [-3R] [-m prereq] [diff-opts] [version] [directory]" echo "$PROGNAME: -G [directory|file]" echo "$PROGNAME: -N [directory|file]" echo "$PROGNAME: -h" echo "$PROGNAME: -V" echo "3 directory is a level3 package source" echo "h output only this help message" echo "m prereq specify __msrc replacement (optionany :cleanup)" echo "G output the expected group ownership for source files" echo "N output the default symbolic name for directory" echo "R recurse into SUBDIRs" echo "V output only our version information" echo "diff-opts [-bciuw] [-C lines] [-u lines] [-U lines]" echo "version compare to this desired symbolic revision name" echo "directory target source directory, or directory's makefile" exit 0 ;; -[bciw]) DIFF_OPTS="$DIFF_OPTS $1" shift ;; -[bciw]*) DIFF_OPTS="$DIFF_OPTS `expr _\"$1\" : '_\(..\).*'`" eval "$slide" ;; -[uUC]) DIFF_OPTS="$DIFF_OPTS $1$2" shift;shift ;; -[uUC]*) DIFF_OPTS="$DIFF_OPTS $1" shift ;; -*) echo "$PROGNAME: usage [diff-opts] [version] [directory]" echo "$PROGNAME: usage -G [version] [directory|file]" echo "$PROGNAME: usage -N [version] [directory|file]" echo "$PROGNAME: usage -h" echo "$PROGNAME: usage -V" exit 64 ;; *) break ;; esac done typeset FileWhich PreReq DIR DIRNAME VFROM VER KeyFile Target MkGroup FileWhich=/dev/null PreReq=`eval msrc $PreList -V | tr -s '\t ' ' ' | sed -n -e 's/.*make hooks*: *//p'` VFROM="" VER="" if $VEROF || $GRPOF ; then : nada elif [ _-- = _"$1" ] ; then shift elif [ $# -gt 0 ] ; then VFROM="command line specification" VER=$1 shift fi # Locate this process over the correct directory, name the key file for # -N (or -G, but we don't use it) DIR=. DIRNAME="" if [ _-- = _"$1" -o \( $# -gt 0 -a _'' = _"$1" \) ] ; then shift elif [ $# -gt 0 ] ; then DIR=$1 DIRNAME=`echo $DIR/ | tr -s '/' '/' ` shift # Specified file is the control recipe or target file for version name case _"$DIRNAME" in _./) DIRNAME="" ;; *) if [ -f $DIR ] ; then FileWhich=`basename $DIR` cd `dirname $DIR` else cd $DIR fi ;; esac fi # After the directory, then what to do with $* # If we are in a old-style (1986 version) directory default to the # version of the Distfile if [ -f Distfile -a -f Make.host ] ; then if $VEROF || $GRPOF ; then : ${FileWhich:=Distfile} else echo "$PROGNAME: master source version prior to 2008" exit 65 # DATAERROR fi fi # When we have a Makefile.meta we are a level3 by default if [ -f Makefile.meta ] ; then : ${LEVEL3:=true} else : ${LEVEL3:=false} fi : `pwd` for $FileWhich # Find the current head of the recipe file (should do svn/svk better) for CNTL in $FileWhich Msrc.mk msrc.mk Makefile makefile ; do [ _/dev/null = _$CNTL ] && continue if [ -d RCS -a -f $CNTL ]; then co -q -p $CNTL >$MYTEMP/makefile && break elif [ -d CVS -a -f $CNTL ]; then CVSROOT=`cat CVS/Root` REPO=`cat CVS/Repository` export MYTEMP CVSROOT REPO (cd $MYTEMP && cvs -Q -d $CVSROOT co -d co $REPO/$CNTL) cp $TMPDIR/co/$CNTL $MYTEMP/makefile && break elif [ -f $CNTL ]; then cp $CNTL $MYTEMP/makefile && break fi done if [ -s $MYTEMP/makefile ] ; then : 'ok' elif $VERSION ; then echo "$PROGNAME:" '$Id: msync.ksh,v 4.52 2012/08/12 18:57:25 ksb Exp $' echo "$PROGNAME: template for private file space: $PfSPAT" exit 0 else echo 1>&2 "$PROGNAME: $DIR: not a master source directory" exit 65 fi # The meta information control policy I use: SITE_POLICY # Look for marker Msync(target) and if found runs # make -sf $recipe $target # rather than this command, set an env variable so we don't loop. : ${MSYNC_CHECK:=true} if $MSYNC_CHECK && Target=`MK= mk -smMsync -dtarget $MYTEMP/makefile 2>/dev/null` ; then # Don't recurse for -N, -G, or -V, and do not exec here, because # we use the recipe in $MYTEMP, which we need to removed on exit. if ! $VEROF && ! $GRPOF && ! $VERSION ; then MSYNC_CHECK=false make -sf $MYTEMP/makefile $Target # XXX we ignore the rest of the dir params :-( --ksb exit $? fi fi # The control group policy I use: SITE_POLICY # Use a marked line Msync(group) which should output the group name # else let $MSYNC_GROUP be the group, if set (default mk rule) # else default to "source" (in that rule). # Publish the group found for any -R dirs (or additional dirs) echo '$'"Msync(group): echo \${MSYNC_GROUP:-source}" >$MYTEMP/defrule MkGroup=`MK= mk -smMsync -dgroup -t$MYTEMP/defrule $MYTEMP/makefile` || { echo "$PROGNAME: control group markup failed in $DIRNAME$CNTL" exit 69 } : ${MSYNC_GROUP:=$MkGroup} export MSYNC_GROUP # This is a bit of site policy, and really should not be hardcoded here, # but is is so small and I think it is always correct. SITE_POLICY # ZZZ Msync(RCS|CVS|SCCS|SVN|git) might output the correct file? cat <$MYTEMP/RCS.cf . drwxr-xr-x/2020 * ${MkGroup} - *,v -r-?r-??-? " " - -No_rm_star -r?-r?-?-- " " - * !-r??r??r-? " " - ! cat <$MYTEMP/CVS.cf . drwxr?x?-?/2000 * ${MkGroup} - Entries* -rw-r?-?-- " " - Root " " " " Repository " " " " Tag " " " " Checkin.prog " " " " Update.prog " " " " -No_rm_star -r?-r?-?-- " " - * !-r??r???-? " " " ! # SVN LLL I have no idea what is in a SVN or .svn dir or how to use svk cat <$MYTEMP/SVN.cf . drwxr?x?-?/2020 * ${MkGroup} - * -r??????-? " " - ! # Later add git, maybe. SCCS support would be nice too, but # does anyone still use that? I'm never sure how far back to go. # When we didn't get a version (saw "--" or nothing) compute it # This is site policy, look for your own revision control markup. # Note that we [b]racket a character here to prevent meta matches on this file. if [ -z "$VER" ] ; then VFROM="version from $CNTL" VER=`ident <$MYTEMP/makefile 2>$MYTEMP/ident.err| sed -n -e 's/.*\$[R]evision: \(.*\)\$.*/\1/p' \ -e 's!.*\$[H]eader: .*,v \([0-9.]*\) \([0-9/]* [0-9:]*\) \([^ :]*\).*\$.*!\1!'p \ -e 's!.*\$[I]d: .*,v \([0-9.]*\) \([0-9/]* [0-9:]*\) \([^ :]*\).*\$.*!\1!'p \ -e 's!.*\$Fed[E]x: .*,v \([0-9.]*\) \([0-9/]* [0-9:]*\) \([^ :]*\).*\$.*!\1!'p | tr -d ' \t' | $VersionFunc | oue` || exit 65 if [ -s $MYTEMP/ident.err ] ; then : ${VER:=One} echo "$PROGNAME: $CNTL must have revision markup, default to \"$VER\"" fi fi # If they like we'll just tell then the local policy symbolic name or # the source group's name. if $VEROF ; then echo "$VER" xapply "Tv=\`$0 -N \"%q1\"\` && [ _$VER != _\$Tv ] && echo \"$PROGNAME: %q1: \$Tv does not match $VER\"" "$@" 1>&2 exit 0 fi if $GRPOF ; then echo "$MkGroup" xapply "Tg=\`$0 -G \"%q1\"\` && [ _$MkGroup != _\$Tg ] && echo \"$PROGNAME: %q1: \$Tg does not match $MkGroup\"" "$@" 1>&2 exit 0 fi # Read the msrc macros from make recipe file we found. Because msrc # doesn't have a policy for SOURCE/GEN, we mmsrc to fetch those. # ZZZ we don't catch the source of the macro (viz. make|generated|empty). msrc -d P `$LEVEL3 && echo " -y INTO=$MYTEMP/dest"` -C/dev/null -m "${PreReq%:*}" -f$MYTEMP/makefile 2>$MYTEMP/bugs | (cd $MYTEMP; perl -w -e 'my($V) = undef; open(my($fh), ">", "/dev/null") or die $!; while (<>) { chomp; if (m/^(\w+): \[.*\]\s*$/o) { $V = $1; close $fh; open($fh, ">", "$V") or die $!; next; } $_ =~ s/^\t+//; print $fh "$_\n"; }' ) if grep "make: " $MYTEMP/bugs 1>&2 ; then echo "$PROGNAME: prerequisites failed" 1>&2 exit 70 fi if ! $VERSION && grep -v "^msrc: cat " $MYTEMP/bugs | grep "msrc: " 1>&2 ; then echo "$PROGNAME: msrc doesn't love this directory" 1>&2 exit 65 fi if MP=`whence mmsrc` ; then $MP -f $MYTEMP/makefile -b SOURCE >$MYTEMP/SOURCE else MP="make" $MP -f $MYTEMP/makefile -V SOURCE |tr -s ' \t\n' '\n\n\n' |sed -e '/^$/d' fi >$MYTEMP/SOURCE if [ ! -s $MYTEMP/SOURCE ] ; then $VERSION || echo "$PROGNAME: ${MP##*/}: recipe doesn't render a SOURCE macro (guessed at value)" 1>&2 find . -type f -print -o -type d -prune >$MYTEMP/SOURCE fi oue -D $MYTEMP/source.db -R '' $MYTEMP/SOURCE if [ _"make" = _"$MP" ] ; then $MP -f $MYTEMP/makefile -V GEN else $MP -f $MYTEMP/makefile -b GEN |tr -s ' \t\n' '\n\n\n' |sed -e '/^$/d' fi | sed -e '/^$/d' >$MYTEMP/GEN oue -D $MYTEMP/gen.db -R '' $MYTEMP/GEN # We know enough to report a useful -V output if $VERSION ; then echo "$PROGNAME:" '$Id: msync.ksh,v 4.52 2012/08/12 18:57:25 ksb Exp $' echo "$PROGNAME: template for private file space: $PfSPAT" if [ -s $MYTEMP/makefile ] ; then echo "$PROGNAME: control recipe from \"$CNTL\"" fi echo "$PROGNAME: $VFROM: $VER" echo "$PROGNAME: make prereq hook: $PreReq" echo "$PROGNAME: MSYNC_GROUP control group: $MkGroup" echo "$PROGNAME: MSYNC_CHECK recipe indirection: $MSYNC_CHECK" if [ ! -z "$Target" ] ; then echo "$PROGNAME: control Msync(target): $Target" fi exit 0 fi # Called for each directory listed to check for compliance ScanSrc () { if [ -f $1/.cvsignore ] ; then echo ".cvsignore" |oue -D $MYTEMP/source.db -R '' fi for file in `/bin/ls -1 $1` do if [ ! -d "$1/$file" ] ; then [ -n "`echo "$file" |oue -vI $MYTEMP/gen.db`" ] && continue [ -L "$1/$file" ] && continue echo "$file" |oue -I $MYTEMP/source.db -D $MYTEMP/elim.db >>$MYTEMP/missing continue fi # the RCS or CVS may be a symbolic link to a directory case "$file" in CVS|RCS|SVN) echo "$file drwxr-xr-x/2020 * ${MkGroup} -" >>$MYTEMP/ck.cf if [ _. = _$1 ] ; then instck -C $MYTEMP/${file##*/}.cf ${file##*/} 2>&1 else instck -C $MYTEMP/${file##*/}.cf $1/${file##*/} 2>&1 fi if [ 1 -ne $(ls -dl . ${file##*/} | oue -k '%[1 1]' | wc -l) ] ; then echo "$PROGNAME: ${file##*/} directory is not the same mode as parent" fi ;; *) echo "$file dr-xr-xr-x/2220 * ${MkGroup} -" >>$MYTEMP/ck.cf echo "$file" >>$MYTEMP/founddir ;; esac done oue -I $MYTEMP/gen.db -iR '%k r??r????? * * -' /dev/null >>$MYTEMP/ck.cf cat <>$MYTEMP/ck.cf # plain files a.out !00755/07022 * ${MkGroup} - core !0 * " - errs !0 * " - lint.out !0 * " - FRC !0 * " - ! if [ -d $1/RCS ] ; then cat <>$MYTEMP/ck.cf # We could do better here. --ksb if [ -d $1/CVS ] ; then cat <>$MYTEMP/ck.cf # hg, SVN, git checks? ZZZ instck -C$MYTEMP/ck.cf $1 2>&1 # XXX We can exceed the command-line argument max on the rcsdiff, # we need glob and xapply to avoid that, but it doesn't happen much. # XXX We could ski -No_rm_star here if [ _"$1/RCS/*" != _"`echo $1/RCS/*`" ] ; then rlog -R -L $1/RCS/* 2>&1 |sed -e "s,^,Locked file: ," ( cd $1 && rcsdiff $DIFF_OPTS -q -r"$VER" RCS/* ) 2>&1 | sed -e "s!^rcsdiff: !rcsdiff: ${DIRNAME}!" elif [ _"$1/CVS/*" != _"`echo $1/CVS/*`" ] ; then cvs -Q status -l |grep ^File: |grep -v "Status: Up-to-date" ( cd $1 && cvs -q diff $DIFF_OPTS -l -r"$VER" . ) 2>&1 | sed -e "s!^Index: !Index: ${DIRNAME}!" fi # SVN or .svn, (for above) I don't use that enough to know LLL # Look for missing GEN files or source files or Cache dirs oue -I $MYTEMP/gen.db -iR '%k' /dev/null | xapply -f '[ -f "%q1" -o -e "%q1.host" ] || echo "'"$PROGNAME"': GEN: %q1: missing file" 1>&2' - } ScanSrc . >$MYTEMP/bugs # Check for missing SEND files not in GEN (these may not be instanced in # a clean structure when the wrong prereq target is specified). if [ ! -s $MYTEMP/bugs ] ; then if [ -s $MYTEMP/SEND -o 1 -eq `wc -c < $MYTEMP/SEND` ] ; then cat $MYTEMP/SEND else oue -I $MYTEMP/source.db -i /dev/null fi | oue -I $MYTEMP/gen.db | xapply -f '[ -r "%q1" ] || echo "'"$PROGNAME: $DIRNAME$CNTL"'": SEND file \"%1\" not readable' - >>$MYTEMP/bugs fi # Check SUBDIRs too, as set in the recipe or is none are set check dirs # we found. We also consult IGNORE here, for ones we found. -- ksb if $RECUR_SUBDIR ; then ( # subshell because we change argv if [ -s $MYTEMP/SUBDIR ] ; then set _ "$@" `< $MYTEMP/SUBDIR` elif [ -s $MYTEMP/founddir ] ; then set _ "$@" `oue -vd $MYTEMP/founddir $MYTEMP/IGNORE` else set _ fi shift xapply 'echo "In directory %q1" '"$0 -R -- '$VER' \"%q1\"" "$@" )>>$MYTEMP/bugs fi if [ -s "$MYTEMP/bugs" ] ; then cat $MYTEMP/bugs elif [ -s "$MYTEMP/missing" ] ; then COUNT=`wc -l < $MYTEMP/missing` PL="" [ 1 -eq $COUNT ] || PL="s" echo "$PROGNAME: $DIRNAME$CNTL: might be missing" $COUNT "file$PL from the SOURCE macro:" fmt $MYTEMP/missing |sed -e 's/^/ /' else echo "$PROGNAME: $DIR in sync at $VER" fi oue -I $MYTEMP/source.db -i /dev/null | sed -e p -e 's/\(.*[/]\)/\1RCS\//' -e '/^[^/]*$/s!^!RCS/!' | xapply -2 -f '[ -d "%Q1" ] && exit 0 if [ ! -d %[2/-$] ] ; then [ -f "%q2,v" ] || echo "'"$PROGNAME"': '"$DIR"': missing RCS cache" elif [ -f %q1,v ] ; then echo "'"$PROGNAME"': RCS cache file '"${DIRNAME}"'%q1,v should be in an RCS subdirectory" else [ -f "%q2,v" ] || echo "'"$PROGNAME"': '"$DIR"': missing RCS cache for %q1" fi' - - |oue if [ ! -f "$SPEC_FILE" ] ; then : nada elif KEYFILE=`MK= mk -smKeyFile ${SPEC_FILE}` ; then SPEC_VER=`sed -n -e 's/Version[ : ]*\([.0-9]*\)$/\1/p' $SPEC_FILE | head -1` if [ ! -e "$KEYFILE" ] ; then echo "$PROGNAME: $SPEC_FILE: $KEYFILE: key file missing" elif ! KEY_VER=`ident $KEYFILE | sed -n -e 's/.*\$Id: [^ ]* \([.0-9]*\) .*/\1/p' | head -1` ; then echo "$PROGNAME: $KEYFILE has no version information" elif [ _"$SPEC_VER" != _"$KEY_VER" ] ; then echo "$PROGNAME: RPM control $SPEC_FILE claims version \"$SPEC_VER\", but key file $KEYFILE claims \"$KEY_VER\"" fi fi if [ $# -ge 1 ] ; then set _ `for i ; do case "$i" in /*) echo $i ;; *) echo $OLDPWD/$i ;; esac done` shift xapply "$0`$RECUR_SUBDIR && echo " -R"` -- '$VER' \"%q1\"" "$@" fi # Update a clean target, if one was requested under -m (e.g. "-m :"). if expr "$PreReq" : '.*:.*' >/dev/null ; then make -sf $MYTEMP/makefile ${PreReq##*:} || { echo "$PROGNAME: make: ${PreReq##*:}: failed" 1>&2 exit 70 } oue -I $MYTEMP/gen.db -iR '%k' /dev/null | xapply -e "TARGET=${PreReq##*:}" -f '[ -f "%q1" ] && echo "'"$PROGNAME"': GEN: %q1: not removed by update of cleanup target $TARGET" 1>&2' - fi exit 0