# $NetBSD: check-files.mk,v 1.47 2024/01/21 03:12:56 rillig Exp $ # # This file checks that the list of installed files matches the PLIST. # For that purpose it records the file list of LOCALBASE before and # after the installation of the package and compares these lists with # the PLIST. # # User-settable variables: # # CHECK_FILES # "yes" to enable the check, "no" to disable it. # # Default value: "yes" # # CHECK_FILES_STRICT # When set to "yes", VARBASE and PKG_SYSCONFDIR are checked in # addition to LOCALBASE. # # CHECK_FILES_ONLY_PREFIX # When set to "yes", the only directory below DESTDIR that may be # modified during installation is PREFIX itself. # # Package-settable variables: # # CHECK_FILES_SKIP # A list of regular expressions (FIXME: all other checks use shell # patterns) that names files to be skipped. This is useful to # avoid getting errors triggered by changes in directories not # really handled by pkgsrc. # _VARGROUPS+= check-files _USER_VARS.check-files= \ CHECK_FILES CHECK_FILES_STRICT CHECK_FILES_ONLY_PREFIX _PKG_VARS.check-files= CHECK_FILES_SUPPORTED CHECK_FILES_SKIP _USE_VARS.check-files= \ DESTDIR PREFIX PKG_SYSCONFDIR VARBASE PKG_DBDIR DISTDIR PACKAGES \ MAKE_DIRS MAKE_DIRS_PERMS OWN_DIRS OWN_DIRS_PERMS \ FONTS_DIRS.x11 FONTS_DIRS.ttf FONTS_DIRS.type1 \ PERL5_INSTALLARCHLIB \ WRKDIR ERROR_DIR \ PLIST INFO_FILES ICON_THEMES _IGN_VARS.check-files= PKGNAME _CHECK_FILES_* _LISTED_VARS.check-files= MAKE_DIRS MAKE_DIRS_PERMS OWN_DIRS OWN_DIRS_PERMS _SORTED_VARS.check-files= CHECK_FILES_SKIP CHECK_FILES?= yes CHECK_FILES_STRICT?= no CHECK_FILES_ONLY_PREFIX?= no # Info index files updated when a new info file is added. .if defined(INFO_FILES) CHECK_FILES_SKIP+= ${PREFIX}/.*/dir .endif # Perl's perllocal.pod index that is regenerated when a local module # is added. # CHECK_FILES_SKIP+= ${PERL5_INSTALLARCHLIB}/perllocal.pod # R's index files that are regenerated when a local module # is added. # CHECK_FILES_SKIP+= ${PREFIX}/lib/R/doc/html/packages.html CHECK_FILES_SKIP+= ${PREFIX}/lib/R/doc/html/search/index.txt CHECK_FILES_SKIP+= ${PKG_DBDIR}/.* # We don't care about what's under linux/proc and linux32/proc in Linux # emulation, which just holds run-time generated data. # CHECK_FILES_SKIP+= ${PREFIX}/emul/linux/proc.* CHECK_FILES_SKIP+= ${PREFIX}/emul/linux32/proc.* # The reference-count meta-data directory used by the {de,}install scripts. CHECK_FILES_SKIP+= ${PKG_DBDIR}.refcount.* # Some people have their distfiles and binary packages below ${LOCALBASE}. CHECK_FILES_SKIP+= ${PACKAGES}/.* CHECK_FILES_SKIP+= ${DISTDIR}/.* # For unprivileged builds, VARBASE is below LOCALBASE. .if ${CHECK_FILES_STRICT:tl} == no CHECK_FILES_SKIP+= ${VARBASE}/.* .endif # Files that are outside of ${PREFIX} in directories we already know we'll # be using for mutable data. # .for d in ${MAKE_DIRS} ${OWN_DIRS} CHECK_FILES_SKIP+= ${d:C|^([^/])|${PREFIX}/\1|}.* .endfor .for dir owner group mode in ${MAKE_DIRS_PERMS} ${OWN_DIRS_PERMS} CHECK_FILES_SKIP+= ${dir:C|^([^/])|${PREFIX}/\1|}.* .endfor # Mutable X11 font database files .if !empty(FONTS_DIRS.x11:M*) CHECK_FILES_SKIP+= ${PREFIX}/.*/encodings.dir CHECK_FILES_SKIP+= ${PREFIX}/.*/fonts.dir .endif .if !empty(FONTS_DIRS.ttf:M*) || \ !empty(FONTS_DIRS.type1:M*) CHECK_FILES_SKIP+= ${PREFIX}/.*/fonts.scale .endif .if !empty(FONTS_DIRS.ttf:M*) || \ !empty(FONTS_DIRS.type1:M*) || \ !empty(FONTS_DIRS.x11:M*) CHECK_FILES_SKIP+= ${PREFIX}/.*/fonts.cache-1 .endif # Mutable icon theme cache files .if ${ICON_THEMES:tl} == yes CHECK_FILES_SKIP+= ${PREFIX}/share/icons/.*/icon-theme.cache .endif # Mutable charset.alias file CHECK_FILES_SKIP+= ${PREFIX}/lib/charset.alias # Mutable locale.alias file CHECK_FILES_SKIP+= ${PREFIX}/share/locale/locale.alias _CHECK_FILES_SKIP_FILTER= ${GREP} -vx ${CHECK_FILES_SKIP:@f@-e ${DESTDIR:Q}${f:Q}@} ########################################################################### # These are the files generated and used by the check-files implementation # used across several check-files targets. # # _CHECK_FILES_ERRMSG.* are the files that contain the error # messages discovered during each stage of file-checking. # # _CHECK_FILES_PRE.* are the file lists generated before any files # from the package are installed. # # _CHECK_FILES_POST.* are the file lists generated after all files # from the package are installed. # # The "pre" and "post" file lists are compared against each other to # determine if the package is installing files where it shouldn't be. # _CHECK_FILES_ERRMSG.prefix= ${ERROR_DIR}/check-files-prefix _CHECK_FILES_PRE.prefix= ${WRKDIR}/.check-files.prefix.pre _CHECK_FILES_POST.prefix= ${WRKDIR}/.check-files.prefix.post _CHECK_FILES_ERRMSG.sysconfdir= ${ERROR_DIR}/check-files-sysconfdir _CHECK_FILES_PRE.sysconfdir= ${WRKDIR}/.check-files.sysconfdir.pre _CHECK_FILES_POST.sysconfdir= ${WRKDIR}/.check-files.sysconfdir.post _CHECK_FILES_ERRMSG.varbase= ${ERROR_DIR}/check-files-varbase _CHECK_FILES_PRE.varbase= ${WRKDIR}/.check-files.varbase.pre _CHECK_FILES_POST.varbase= ${WRKDIR}/.check-files.varbase.post _CHECK_FILES_ERRMSG.only-prefix= ${ERROR_DIR}/check-files-only-prefix _CHECK_FILES_ERRMSGS= # empty _CHECK_FILES_ERRMSGS+= ${_CHECK_FILES_ERRMSG.prefix} .if ${CHECK_FILES_STRICT:tl} != no _CHECK_FILES_ERRMSGS+= ${_CHECK_FILES_ERRMSG.sysconfdir} _CHECK_FILES_ERRMSGS+= ${_CHECK_FILES_ERRMSG.varbase} .endif .if ${CHECK_FILES_ONLY_PREFIX:tl} == yes _CHECK_FILES_ERRMSGS+= ${_CHECK_FILES_ERRMSG.only-prefix} .endif ########################################################################### # _CHECK_FILES_PRE holds the list of targets that are built as part of # building the check-files-pre target. These targets should cause the # "pre" file lists to be generated. # _CHECK_FILES_PRE= # _CHECK_FILES_PRE+= ${_CHECK_FILES_PRE.prefix} .if ${CHECK_FILES_STRICT:tl} != no _CHECK_FILES_PRE+= ${_CHECK_FILES_PRE.sysconfdir} _CHECK_FILES_PRE+= ${_CHECK_FILES_PRE.varbase} .endif ########################################################################### # _CHECK_FILES_POST holds the list of targets that are built as part of # building the check-files-post target. These targets should cause the # "post" file lists to be generated. # _CHECK_FILES_POST= # _CHECK_FILES_POST+= ${_CHECK_FILES_POST.prefix} .if ${CHECK_FILES_STRICT:tl} != no _CHECK_FILES_POST+= ${_CHECK_FILES_POST.sysconfdir} _CHECK_FILES_POST+= ${_CHECK_FILES_POST.varbase} .endif .if ${CHECK_FILES:tl} != no privileged-install-hook: check-files .endif ########################################################################### # check-files-pre and check-files-post targets and subtargets # .PHONY: check-files-pre check-files-post check-files-pre: check-files-pre-message ${_CHECK_FILES_PRE} check-files-post: check-files-post-message ${_CHECK_FILES_POST} .PHONY: check-files-pre-message check-files-post-message check-files-pre-message: @${STEP_MSG} "Generating pre-install file lists" check-files-post-message: @${STEP_MSG} "Generating post-install file lists" ${_CHECK_FILES_PRE.prefix} ${_CHECK_FILES_POST.prefix}: ${RUN} \ ${FIND} ${DESTDIR}${PREFIX}/. \( -type f -o -type l \) -print 2>/dev/null \ | ${SED} -e 's,/\./,/,' \ | ${_CHECK_FILES_SKIP_FILTER} \ | ${SORT} > ${.TARGET} \ || ${TRUE} ${_CHECK_FILES_PRE.sysconfdir} ${_CHECK_FILES_POST.sysconfdir}: ${RUN} \ ${FIND} ${DESTDIR}${PKG_SYSCONFDIR}/. -print 2>/dev/null \ | ${SED} -e 's,/\./,/,' \ | ${_CHECK_FILES_SKIP_FILTER} \ | ${SORT} > ${.TARGET} \ || ${TRUE} ${_CHECK_FILES_PRE.varbase} ${_CHECK_FILES_POST.varbase}: ${RUN} \ ${FIND} ${DESTDIR}${VARBASE}/. -print 2>/dev/null \ | ${SED} -e 's,/\./,/,' \ | ${_CHECK_FILES_SKIP_FILTER} \ | ${SORT} > ${.TARGET} \ || ${TRUE} ########################################################################### # check-files targets and subtargets # .PHONY: check-files-prefix check-files-sysconfdir check-files-varbase check-files-prefix: ${_CHECK_FILES_ERRMSG.prefix} check-files-sysconfdir: ${_CHECK_FILES_ERRMSG.sysconfdir} check-files-varbase: ${_CHECK_FILES_ERRMSG.varbase} # The check-files target looks at the cookie files generated by the # check-files-* subtargets, and if they are non-empty, then they # contain the error messages collected during the build of each # subtarget. # .PHONY: check-files .if ${CHECK_FILES_SUPPORTED:U:tl} == no check-files: @${DO_NADA} .else check-files: check-files-message ${_CHECK_FILES_ERRMSGS} error-check .endif .PHONY: check-files-message check-files-message: @${STEP_MSG} "Checking file-check results for ${PKGNAME}" # Check ${PREFIX} for files which are not listed in the generated ${PLIST} # and vice-versa. # _CHECK_FILES_DIFF= ${WRKDIR}/.check_files_diff _CHECK_FILES_ADDED= ${WRKDIR}/.check_files_added _CHECK_FILES_DELETED= ${WRKDIR}/.check_files_deleted _CHECK_FILES_EXPECTED= ${WRKDIR}/.check_files_expected _CHECK_FILES_MISSING= ${WRKDIR}/.check_files_missing _CHECK_FILES_MISSING_SKIP= ${WRKDIR}/.check_files_missing_skip _CHECK_FILES_MISSING_REAL= ${WRKDIR}/.check_files_missing_real _CHECK_FILES_EXTRA= ${WRKDIR}/.check_files_extra ${_CHECK_FILES_DIFF}: ${_CHECK_FILES_PRE.prefix} ${_CHECK_FILES_POST.prefix} ${RUN} \ ${DIFF} -u ${_CHECK_FILES_PRE.prefix} \ ${_CHECK_FILES_POST.prefix} \ > ${.TARGET} || ${TRUE} ${_CHECK_FILES_ADDED}: ${_CHECK_FILES_DIFF} ${RUN} \ ${GREP} '^+/' ${_CHECK_FILES_DIFF} | ${SED} "s|^+||" | ${SORT} \ > ${.TARGET} ${_CHECK_FILES_DELETED}: ${_CHECK_FILES_DIFF} ${RUN} \ ${GREP} '^-/' ${_CHECK_FILES_DIFF} | ${SED} "s|^-||" | ${SORT} \ > ${.TARGET} ${_CHECK_FILES_EXPECTED}: plist ${RUN} \ ${GREP} '^[^@]' ${PLIST} | ${SED} "s|^|${DESTDIR}${PREFIX}/|" | ${SORT} \ > ${.TARGET} ${_CHECK_FILES_MISSING}: ${_CHECK_FILES_EXPECTED} ${_CHECK_FILES_ADDED} ${RUN} \ ${DIFF} -u ${_CHECK_FILES_EXPECTED} ${_CHECK_FILES_ADDED} | \ ${GREP} '^-[^-]' | ${SED} "s|^-||" | \ while read file; do \ ${TEST} -f "$$file" -o -h "$$file" || ${ECHO} "$$file"; \ done > ${.TARGET} ${_CHECK_FILES_MISSING_REAL}: ${_CHECK_FILES_MISSING} ${RUN} \ ${_CHECK_FILES_SKIP_FILTER} < ${_CHECK_FILES_MISSING} \ > ${.TARGET} || ${TRUE} ${_CHECK_FILES_MISSING_SKIP}: \ ${_CHECK_FILES_MISSING} \ ${_CHECK_FILES_MISSING_REAL} ${RUN} \ ${DIFF} -u ${_CHECK_FILES_MISSING} \ ${_CHECK_FILES_MISSING_REAL} | \ ${GREP} '^-[^-]' | ${SED} "s|^-||" \ > ${.TARGET} ${_CHECK_FILES_EXTRA}: ${_CHECK_FILES_EXPECTED} ${_CHECK_FILES_ADDED} ${RUN} \ ${DIFF} -u ${_CHECK_FILES_EXPECTED} ${_CHECK_FILES_ADDED} | \ ${GREP} '^+[^+]' | ${SED} "s|^+||" | \ while read file; do \ ${TEST} ! -f "$$file" -a ! -h "$$file" || ${ECHO} "$$file"; \ done > ${.TARGET} ${_CHECK_FILES_ERRMSG.prefix}: \ ${_CHECK_FILES_DELETED} \ ${_CHECK_FILES_MISSING} \ ${_CHECK_FILES_MISSING_REAL} \ ${_CHECK_FILES_MISSING_SKIP} \ ${_CHECK_FILES_EXTRA} ${RUN} \ ${RM} -f ${.TARGET}; \ if [ -s ${_CHECK_FILES_DELETED} ]; then \ ${ECHO} "The following files have been deleted" \ "from ${PREFIX}!"; \ ${SED} "s|^| |" ${_CHECK_FILES_DELETED}; \ fi >> ${.TARGET}; \ if [ -s ${_CHECK_FILES_MISSING_REAL} ]; then \ ${ECHO} "************************************************************"; \ ${ECHO} "The following files are in the" \ "PLIST but not in ${DESTDIR}${PREFIX}:"; \ ${SED} "s|^| |" ${_CHECK_FILES_MISSING_REAL}; \ fi >> ${.TARGET}; \ if [ -s ${_CHECK_FILES_EXTRA} ]; then \ ${ECHO} "************************************************************"; \ ${ECHO} "The following files are in" \ "${DESTDIR}${PREFIX} but not in the PLIST:"; \ ${SED} "s|^| |" ${_CHECK_FILES_EXTRA}; \ fi >> ${.TARGET}; \ if [ -s ${_CHECK_FILES_MISSING_SKIP} ]; then \ ${ECHO} "************************************************************"; \ ${ECHO} "The following files are in both the" \ "PLIST and CHECK_FILES_SKIP:"; \ ${SED} "s|^| |" ${_CHECK_FILES_MISSING_SKIP}; \ fi >> ${.TARGET} # Check ${PKG_SYSCONFDIR} for files which are not in the PLIST and are # also not copied into place by the INSTALL scripts. # ${_CHECK_FILES_ERRMSG.sysconfdir}: \ ${_CHECK_FILES_PRE.sysconfdir} \ ${_CHECK_FILES_POST.sysconfdir} ${RUN} \ if ${CMP} -s ${_CHECK_FILES_PRE.sysconfdir} \ ${_CHECK_FILES_POST.sysconfdir}; then \ ${TRUE}; \ else \ ${ECHO} "************************************************************"; \ ${ECHO} "The package has modified ${PKG_SYSCONFDIR}" \ "contents directly!"; \ ${ECHO} " The offending files/directories are:"; \ ${DIFF} -u ${_CHECK_FILES_PRE.sysconfdir} \ ${_CHECK_FILES_POST.sysconfdir} | \ ${GREP} '^+[^+]' | ${SED} "s|^+| |"; \ fi > ${.TARGET} # Check ${VARBASE} for files which are not in the PLIST and are also # not created by the INSTALL scripts. # ${_CHECK_FILES_ERRMSG.varbase}: \ ${_CHECK_FILES_PRE.varbase} \ ${_CHECK_FILES_POST.varbase} ${RUN} \ if ${CMP} -s ${_CHECK_FILES_PRE.varbase} \ ${_CHECK_FILES_POST.varbase}; then \ ${TRUE}; \ else \ ${ECHO} "************************************************************"; \ ${ECHO} "The package has modified ${VARBASE}" \ "contents directly!"; \ ${ECHO} " The offending files/directories are:"; \ ${DIFF} -u ${_CHECK_FILES_PRE.varbase} \ ${_CHECK_FILES_POST.varbase} | \ ${GREP} '^+[^+]' | ${SED} "s|^+| |"; \ fi > ${.TARGET} ${_CHECK_FILES_ERRMSG.only-prefix}: ${RUN} \ files=${WRKDIR}/.check-files-only-prefix; \ ${FIND} ${DESTDIR} \( -type f -o -type l \) -print \ | ${GREP} -v '^${DESTDIR}${PREFIX}/' > "$$files" || :; \ if [ -s "$$files" ]; then \ ${ECHO} "************************************************************"; \ ${ECHO} "The package has installed files outside ${PREFIX}:"; \ ${SED} -e 's,^, ,' "$$files"; \ fi > ${.TARGET} ########################################################################### # check-files-clean removes the state files related to the "check-files" # target so that the check-files-{pre,post} targets may be re-run. # .PHONY: check-files-clean check-clean: check-files-clean check-files-clean: ${RUN} \ ${RM} -f ${_CHECK_FILES_ERRMSGS} \ ${_CHECK_FILES_PRE} ${_CHECK_FILES_POST} \ ${_CHECK_FILES_DIFF} ${_CHECK_FILES_ADDED} \ ${_CHECK_FILES_DELETED} ${_CHECK_FILES_EXPECTED} \ ${_CHECK_FILES_MISSING} ${_CHECK_FILES_MISSING_SKIP} \ ${_CHECK_FILES_MISSING_REAL} ${_CHECK_FILES_EXTRA}