From bf7aa99a55a7692da6e55864098fd085392542b0 Mon Sep 17 00:00:00 2001 From: "Simon J. Gerraty" Date: Sun, 16 Apr 2023 18:40:53 -0700 Subject: [PATCH] Update meta mode makefiles meta2deps - add checks to detect truncated/corrupted filemon data (only known to happen on Linux hosts), to ensure we do not auto update dependencies based on incomplete data. meta.stage.mk adds STAGE_SHLIB_LINKS_FILTER and STAGE_LINK_AS_* We also allow for hosts where egrep is deprecated for grep -E Reviewed by: stevek --- share/mk/dirdeps-options.mk | 9 ++++++- share/mk/gendirdeps.mk | 8 +++--- share/mk/meta.autodep.mk | 5 ++-- share/mk/meta.stage.mk | 21 ++++++++------- share/mk/meta2deps.py | 52 ++++++++++++++++++++++++++++++----- share/mk/meta2deps.sh | 54 +++++++++++++++++++++++++++++++------ 6 files changed, 119 insertions(+), 30 deletions(-) diff --git a/share/mk/dirdeps-options.mk b/share/mk/dirdeps-options.mk index b31d2033ae98..9a97615bbeb8 100644 --- a/share/mk/dirdeps-options.mk +++ b/share/mk/dirdeps-options.mk @@ -1,4 +1,4 @@ -# $Id: dirdeps-options.mk,v 1.20 2022/03/17 20:11:36 sjg Exp $ +# $Id: dirdeps-options.mk,v 1.21 2022/09/06 22:18:45 sjg Exp $ # # @(#) Copyright (c) 2018-2022, Simon J. Gerraty # @@ -42,6 +42,13 @@ # so we qualify MK_FOO with .${TARGET_SPEC} and each component # TARGET_SPEC_VAR (in reverse order) before using MK_FOO. # +# Because Makefile.depend.options are processed at both level 0 (when +# computing DIRDEPS to build) and higher (when updating +# Makefile.depend* after successful build), it is important that +# all references to TARGET_SPEC_VARs should use the syntax +# ${DEP_${TARGET_SPEC_VAR}:U${TARGET_SPEC_VAR}} to ensure correct +# behavior. +# # This should have been set by Makefile.depend.options # before including us diff --git a/share/mk/gendirdeps.mk b/share/mk/gendirdeps.mk index 6d26b3d308b1..1ff2036237ed 100644 --- a/share/mk/gendirdeps.mk +++ b/share/mk/gendirdeps.mk @@ -1,5 +1,4 @@ -# $FreeBSD$ -# $Id: gendirdeps.mk,v 1.46 2020/08/19 17:51:53 sjg Exp $ +# $Id: gendirdeps.mk,v 1.48 2022/09/09 17:44:29 sjg Exp $ # Copyright (c) 2011-2020, Simon J. Gerraty # Copyright (c) 2010-2018, Juniper Networks, Inc. @@ -89,7 +88,7 @@ META_FILES := ${META_FILES:T:O:u} # they should all be absolute paths SKIP_GENDIRDEPS ?= .if !empty(SKIP_GENDIRDEPS) -_skip_gendirdeps = egrep -v '^(${SKIP_GENDIRDEPS:O:u:ts|})' | +_skip_gendirdeps = ${EGREP:Uegrep} -v '^(${SKIP_GENDIRDEPS:O:u:ts|})' | .else _skip_gendirdeps = .endif @@ -340,6 +339,8 @@ CAT_DEPEND ?= .depend .PHONY: ${_DEPENDFILE} .endif +# set this to 'no' and we will not capture any +# local depends LOCAL_DEPENDS_GUARD ?= _{.MAKE.LEVEL} > 0 # 'cat .depend' should suffice, but if we are mixing build modes @@ -352,6 +353,7 @@ ${_DEPENDFILE}: .NOMETA ${CAT_DEPEND:M.depend} ${META_FILES:O:u:@m@${exists($m): echo '${DIRDEPS:@d@ $d \\${.newline}@}'; echo; \ ${_include_src_dirdeps} \ echo '.include '; \ + [ "${LOCAL_DEPENDS_GUARD:[1]:tl}" != no ] || exit 0; \ echo; \ echo '.if ${LOCAL_DEPENDS_GUARD}'; \ echo '# local dependencies - needed for -jN in clean tree'; \ diff --git a/share/mk/meta.autodep.mk b/share/mk/meta.autodep.mk index 842554beea45..cd08ac3b3520 100644 --- a/share/mk/meta.autodep.mk +++ b/share/mk/meta.autodep.mk @@ -1,5 +1,4 @@ -# $FreeBSD$ -# $Id: meta.autodep.mk,v 1.55 2021/12/13 08:12:01 sjg Exp $ +# $Id: meta.autodep.mk,v 1.56 2022/09/09 17:44:29 sjg Exp $ # # @(#) Copyright (c) 2010, Simon J. Gerraty @@ -175,7 +174,7 @@ DEPEND_SUFFIXES += .c .h .cpp .hpp .cxx .hxx .cc .hh .endif .depend: .NOMETA $${.MAKE.META.CREATED} ${_this} @echo "Updating $@: ${.OODATE:T:[1..8]}" - @egrep -i '^R .*\.(${DEPEND_SUFFIXES:tl:O:u:S,^.,,:ts|})$$' /dev/null ${.MAKE.META.FILES:T:O:u:${META_FILE_FILTER:ts:}:M*o.meta} | \ + @${EGREP:Uegrep} -i '^R .*\.(${DEPEND_SUFFIXES:tl:O:u:S,^.,,:ts|})$$' /dev/null ${.MAKE.META.FILES:T:O:u:${META_FILE_FILTER:ts:}:M*o.meta} | \ sed -e 's, \./, ,${OBJDIR_REFS:O:u:@d@;s, $d/, ,@};/\//d' \ -e 's,^\([^/][^/]*\).meta...[0-9]* ,\1: ,' | \ sort -u | \ diff --git a/share/mk/meta.stage.mk b/share/mk/meta.stage.mk index 9f54f3b1f9c5..168e46d22a82 100644 --- a/share/mk/meta.stage.mk +++ b/share/mk/meta.stage.mk @@ -1,5 +1,4 @@ -# $FreeBSD$ -# $Id: meta.stage.mk,v 1.60 2020/08/19 17:51:53 sjg Exp $ +# $Id: meta.stage.mk,v 1.67 2023/04/17 01:22:10 sjg Exp $ # # @(#) Copyright (c) 2011-2017, Simon J. Gerraty # @@ -31,8 +30,11 @@ _dirdep ?= ${RELDIR} CLEANFILES+= .dirdep # this allows us to trace dependencies back to their src dir -.dirdep: .NOPATH +.dirdep: .NOPATH +.if !commands(.dirdep) +.dirdep: @echo '${_dirdep}' > $@ +.endif .if defined(NO_POSIX_SHELL) || ${type printf:L:sh:Mbuiltin} == "" _stage_file_basename = `basename $$f` @@ -64,7 +66,7 @@ GENDIRDEPS_FILTER += Nnot-empty-is-important \ LN_CP_SCRIPT = LnCp() { \ rm -f $$2 2> /dev/null; \ { [ -z "$$mode" ] && ${LN:Uln} $$1 $$2 2> /dev/null; } || \ - cp -p $$1 $$2; } + cp -p $$1 $$2 2> /dev/null || cp $$1 $$2; } # a staging conflict should cause an error # a warning is handy when bootstapping different options. @@ -171,7 +173,7 @@ stage_libs: .dirdep .if !defined(NO_SHLIB_LINKS) .if !empty(SHLIB_LINKS) @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} \ - ${SHLIB_LINKS:@t@${STAGE_LIBS:T:M$t.*} $t@} + ${SHLIB_LINKS:@t@${STAGE_LIBS:T:M$t.*:${STAGE_SHLIB_LINKS_FILTER:U}} $t@} .elif !empty(SHLIB_LINK) && !empty(SHLIB_NAME) @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} ${SHLIB_NAME} ${SHLIB_LINK} .endif @@ -262,7 +264,8 @@ CLEANFILES += ${STAGE_AS_SETS:@s@stage*$s@} # sometimes things need to be renamed as they are staged # each ${file} will be staged as ${STAGE_AS_${file:T}} # one could achieve the same with SYMLINKS -# stage_as_and_symlink makes the original name a symlink to the new name +# stage_as_and_symlink makes the original name (or ${STAGE_LINK_AS_${name}}) +# a symlink to the new name # it is the same as using stage_as and stage_symlinks but ensures # both operations happen together .for s in ${STAGE_AS_SETS:O:u} @@ -292,7 +295,7 @@ STAGE_AS_AND_SYMLINK.$s ?= ${.ALLSRC:N.dirdep:Nstage_*} stage_as_and_symlink: stage_as_and_symlink.$s stage_as_and_symlink.$s: .dirdep @${STAGE_AS_SCRIPT}; StageAs ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@$f ${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}}@} - @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}} $f@} + @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}} ${STAGE_LINK_AS_${f}:U$f}@} @touch $@ .endif .endif @@ -304,7 +307,7 @@ CLEANFILES += ${STAGE_TARGETS} stage_incs stage_includes # this lot also only makes sense the first time... .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN # stage_*links usually needs to follow any others. # for non-jobs mode the order here matters @@ -351,7 +354,7 @@ all: stale_staged # get a list of paths that we have previously staged to those same dirs # anything in the 2nd list but not the first is stale - remove it. stale_staged: staging .NOMETA - @egrep '^[WL] .*${STAGE_OBJTOP}' /dev/null ${.MAKE.META.FILES:M*stage_*} | \ + @${EGREP:Uegrep} '^[WL] .*${STAGE_OBJTOP}' /dev/null ${.MAKE.META.FILES:M*stage_*} | \ sed "/\.dirdep/d;s,.* '*\(${STAGE_OBJTOP}/[^ '][^ ']*\).*,\1," | \ sort > ${.TARGET}.staged1 @grep -l '${_dirdep}' /dev/null ${_STAGED_DIRS:M${STAGE_OBJTOP}*:O:u:@d@$d/*.dirdep@} | \ diff --git a/share/mk/meta2deps.py b/share/mk/meta2deps.py index 1bcf1baedaaa..d7820ec71b5b 100755 --- a/share/mk/meta2deps.py +++ b/share/mk/meta2deps.py @@ -37,8 +37,7 @@ """ RCSid: - $FreeBSD$ - $Id: meta2deps.py,v 1.40 2021/12/13 19:32:46 sjg Exp $ + $Id: meta2deps.py,v 1.45 2023/01/18 01:35:24 sjg Exp $ Copyright (c) 2011-2020, Simon J. Gerraty Copyright (c) 2011-2017, Juniper Networks, Inc. @@ -67,7 +66,10 @@ """ -import os, re, sys +import os +import re +import sys +import stat def resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr): """ @@ -245,6 +247,7 @@ def __init__(self, name, conf={}): self.curdir = conf.get('CURDIR') self.reldir = conf.get('RELDIR') self.dpdeps = conf.get('DPDEPS') + self.pids = {} self.line = 0 if not self.conf: @@ -445,12 +448,13 @@ def parse(self, name=None, file=None): pid_cwd = {} pid_last_dir = {} last_pid = 0 + eof_token = False self.line = 0 if self.curdir: self.seenit(self.curdir) # we ignore this - interesting = 'CEFLRV' + interesting = '#CEFLRVX' for line in f: self.line += 1 # ignore anything we don't care about @@ -477,6 +481,12 @@ def parse(self, name=None, file=None): print("%s: CWD=%s" % (self.name, cwd), file=self.debug_out) continue + if w[0] == '#': + # check the file has not been truncated + if line.find('Bye') > 0: + eof_token = True + continue + pid = int(w[1]) if pid != last_pid: if last_pid: @@ -506,6 +516,13 @@ def parse(self, name=None, file=None): print("cwd=", cwd, file=self.debug_out) continue + if w[0] == 'X': + try: + del self.pids[pid] + except KeyError: + pass + continue + if w[2] in self.seen: if self.debug > 2: print("seen:", w[2], file=self.debug_out) @@ -519,11 +536,34 @@ def parse(self, name=None, file=None): continue elif w[0] in 'ERWS': path = w[2] - if path == '.': + if w[0] == 'E': + self.pids[pid] = path + elif path == '.': continue self.parse_path(path, cwd, w[0], w) - assert(version > 0) + if version == 0: + raise AssertionError('missing filemon data') + if not eof_token: + raise AssertionError('truncated filemon data') + + setid_pids = [] + # self.pids should be empty! + for pid,path in self.pids.items(): + try: + # no guarantee that path is still valid + if os.stat(path).st_mode & (stat.S_ISUID|stat.S_ISGID): + # we do not expect anything after Exec + setid_pids.append(pid) + continue + except: + # we do not care why the above fails, + # we do not want to miss the ERROR below. + pass + print("ERROR: missing eXit for {} pid {}".format(path, pid)) + for pid in setid_pids: + del self.pids[pid] + assert(len(self.pids) == 0) if not file: f.close() diff --git a/share/mk/meta2deps.sh b/share/mk/meta2deps.sh index 4767b83f4343..56367e0105f4 100755 --- a/share/mk/meta2deps.sh +++ b/share/mk/meta2deps.sh @@ -49,8 +49,10 @@ # The output, is a set of absolute paths with "SB" like: #.nf # +# $SB/obj-i386/bsd/gnu/lib/csu +# $SB/obj-i386/bsd/gnu/lib/libgcc # $SB/obj-i386/bsd/include -# $SB/obj-i386/bsd/lib/csu/i386 +# $SB/obj-i386/bsd/lib/csu/i386-elf # $SB/obj-i386/bsd/lib/libc # $SB/src/bsd/include # $SB/src/bsd/sys/i386/include @@ -75,8 +77,7 @@ # RCSid: -# $FreeBSD$ -# $Id: meta2deps.sh,v 1.14 2020/10/02 03:11:17 sjg Exp $ +# $Id: meta2deps.sh,v 1.20 2023/01/18 01:35:24 sjg Exp $ # Copyright (c) 2010-2013, Juniper Networks, Inc. # All rights reserved. @@ -138,6 +139,13 @@ add_list() { eval "$name=\"$list\"" } +# some Linux systems have deprecated egrep in favor of grep -E +# but not everyone supports that +case "`echo bmake | egrep 'a|b' 2>&1`" in +bmake) ;; +*) egrep() { grep -E "$@"; } +esac + _excludes_f() { egrep -v "$EXCLUDES" } @@ -240,8 +248,8 @@ meta2deps() { ;; *) cat /dev/null "$@";; esac 2> /dev/null | - sed -e 's,^CWD,C C,;/^[CREFLMV] /!d' -e "s,',,g" | - $_excludes | ( version=no + sed -e 's,^CWD,C C,;/^[#CREFLMVX] /!d' -e "s,',,g" | + $_excludes | ( version=no epids= xpids= eof_token=no while read op pid path junk do : op=$op pid=$pid path=$path @@ -259,10 +267,15 @@ meta2deps() { *) ;; esac version=0 + case "$eof_token" in + no) ;; # ignore + 0) error "truncated filemon data";; + esac + eof_token=0 continue ;; $pid,$pid) ;; - *) + [1-9]*) case "$lpid" in "") ;; *) eval ldir_$lpid=$ldir;; @@ -272,8 +285,9 @@ meta2deps() { ;; esac + : op=$op path=$path case "$op,$path" in - V,*) version=$path; continue;; + V,*) version=$pid; continue;; W,*srcrel|*.dirdep) continue;; C,*) case "$path" in @@ -289,7 +303,18 @@ meta2deps() { eval cwd_$path=$cwd ldir_$path=$ldir continue ;; + \#,bye) eof_token=1; continue;; + \#*) continue;; *) dir=${path%/*} + case "$op" in + E) # setid apps get no tracing so we won't see eXit + case `'ls' -l $path 2> /dev/null | sed 's, .*,,'` in + *s*) ;; + *) epids="$epids $pid";; + esac + ;; + X) xpids="$xpids $pid"; continue;; + esac case "$path" in $src_re|$obj_re) ;; /*/stage/*) ;; @@ -379,9 +404,22 @@ meta2deps() { echo $dir;; esac done > $tf.dirdep + : version=$version case "$version" in 0) error "no filemon data";; - esac ) || exit 1 + esac + : eof_token=$eof_token + case "$eof_token" in + 0) error "truncated filemon data";; + esac + for p in $epids + do + : p=$p + case " $xpids " in + *" $p "*) ;; + *) error "missing eXit for pid $p";; + esac + done ) || exit 1 _nl=echo for f in $tf.dirdep $tf.qual $tf.srcdep do