freebsd-dev/share/mk/gendirdeps.mk
Simon J. Gerraty f957a32dac Update meta.sys.mk and related local*mk
Move the setting of TARGET_SPEC_VARS to local.sys.env.mk
so meta.sys.mk can do the processing, and include local.meta.sys.mk
later.

Move the setting of GENDIRDEPS_FILTER*_VARS from local.gendirdeps.mk
to local.meta.sys.mk so we can automatically set DEP_* at level 1+
to avoid syntax errors when DEP_* variables are used in conditionals
in Makefile.depend files.

Update gendirdeps.mk just to get the documentation about the above.
No functional change.

local.dirdeps.mk be more careful about adding to DIRDEPS to avoid
unnecessary overhead, and introducing cycles in the graph.
Also set DEP_MACHINE_CPUARCH.

Reviewed by:	stevek
Sponsored by:	Juniper Networks, Inc.
Differential Revision:	https://reviews.freebsd.org/D39737
2023-04-20 20:38:53 -07:00

422 lines
13 KiB
Makefile

# $Id: gendirdeps.mk,v 1.49 2023/04/20 17:45:03 sjg Exp $
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2011-2020, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# This makefile [re]generates ${.MAKE.DEPENDFILE}
#
.include <install-new.mk>
# Assumptions:
# RELDIR is the relative path from ${SRCTOP} to ${_CURDIR}
# (SRCTOP is ${SB}/src)
# _CURDIR is the absolute version of ${.CURDIR}
# _OBJDIR is the absolute version of ${.OBJDIR}
# _objroot is realpath of ${_OBJTOP} without ${MACHINE}
# this may be different from _OBJROOT if $SB/obj is a
# symlink to another filesystem.
# _objroot must be a prefix match for _objtop
# If any of GENDIRDEPS_FILTER, GENDIRDEPS_FILTER_DIR_VARS
# or GENDIRDEPS_FILTER_VARS are set, we use them to filter the
# output from filemon(4).
# Any references to variables that dirdeps.mk will set
# such as DEP_MACHINE, DEP_RELDIR etc, should use that form.
# Thus we want ${DEP_MACHINE} not ${MACHINE} used in DIRDEPS.
#
# If any manually maintained Makefile.depend files will use any
# DEP_* variables in conditionals, precautions are needed to avoid
# errors when Makefile.depend is read at level 1+ (ie not via
# dirdeps.mk)
# Using MACHINE as an example; such makefiles can do:
#
# DEP_MACHINE ?= ${MACHINE}
# .if ${DEP_MACHINE} == "xyz"
#
# or:
#
# .if ${DEP_MACHINE:U${MACHINE}} == "xyz"
#
# but it might be safer to set GENDIRDEPS_FILTER_DIR_VARS and
# GENDIRDEPS_FILTER_VARS via local.meta.sys.mk rather than
# local.gendirdeps.mk and then:
#
# .if ${.MAKE.LEVEL} > 0
# .for V in ${GENDIRDEPS_FILTER_DIR_VARS:MDEP_*} \
# ${GENDIRDEPS_FILTER_VARS:MDEP_*}
# $V ?= ${${V:S,DEP_,,}}
# .endfor
# .endif
#
.MAIN: all
# keep this simple
.MAKE.MODE = compat
all:
_CURDIR ?= ${.CURDIR}
_OBJDIR ?= ${.OBJDIR}
_OBJTOP ?= ${OBJTOP}
_OBJROOT ?= ${OBJROOT:U${_OBJTOP:H}}
.if ${_OBJROOT:M*/}
_slash=/
.else
_slash=
.endif
_objroot ?= ${_OBJROOT:tA}${_slash}
_this = ${.PARSEDIR}/${.PARSEFILE}
# remember what to make
_DEPENDFILE := ${_CURDIR}/${.MAKE.DEPENDFILE:T}
# We do _not_ want to read our own output!
.MAKE.DEPENDFILE = /dev/null
# caller should have set this
META_FILES ?= ${.MAKE.META.FILES}
.if !empty(META_FILES)
.if ${.MAKE.LEVEL} > 0 && !empty(GENDIRDEPS_FILTER)
# so we can compare below
.-include <${_DEPENDFILE}>
# yes, I mean :U with no value
_DIRDEPS := ${DIRDEPS:U:O:u}
.endif
META_FILES := ${META_FILES:T:O:u}
# pickup customizations
.-include <local.gendirdeps.mk>
# these are actually prefixes that we'll skip
# they should all be absolute paths
SKIP_GENDIRDEPS ?=
.if !empty(SKIP_GENDIRDEPS)
_skip_gendirdeps = ${EGREP:Uegrep} -v '^(${SKIP_GENDIRDEPS:O:u:ts|})' |
.else
_skip_gendirdeps =
.endif
# Below we will turn _{VAR} into ${VAR} which keeps this simple
# GENDIRDEPS_FILTER_DIR_VARS is a list of dirs to be substiuted for.
# GENDIRDEPS_FILTER_VARS is more general.
# In each case order matters.
.if !empty(GENDIRDEPS_FILTER_DIR_VARS)
GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_DIR_VARS:@v@S,${$v},_{${v}},@}
.endif
.if !empty(GENDIRDEPS_FILTER_VARS)
GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_VARS:@v@S,/${$v}/,/_{${v}}/,@:NS,//,*:u}
.endif
# this (*should* be set in meta.sys.mk)
# is the script that extracts what we want.
META2DEPS ?= ${.PARSEDIR}/meta2deps.sh
META2DEPS := ${META2DEPS}
.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != "" && ${DEBUG_GENDIRDEPS:Uno:Mmeta2d*} != ""
_time = time
_sh_x = sh -x
_py_d = -ddd
.else
_time =
_sh_x =
_py_d =
.endif
.if ${META2DEPS:E} == "py"
# we can afford to do this all the time.
DPDEPS ?= no
META2DEPS_CMD = ${_time} ${PYTHON} ${META2DEPS} ${_py_d}
.if ${DPDEPS:tl} != "no"
META2DEPS_CMD += -D ${DPDEPS}
.endif
META2DEPS_FILTER = sed 's,^src:,${SRCTOP}/,;s,^\([^/]\),${OBJTOP}/\1,' |
.elif ${META2DEPS:E} == "sh"
META2DEPS_CMD = ${_time} ${_sh_x} ${META2DEPS} OBJTOP=${_OBJTOP}
.else
META2DEPS_CMD ?= ${META2DEPS}
.endif
.if ${TARGET_OBJ_SPEC:U${MACHINE}} != ${MACHINE}
META2DEPS_CMD += -T ${TARGET_OBJ_SPEC}
.endif
META2DEPS_CMD += \
-R ${RELDIR} -H ${HOST_TARGET} \
${M2D_OBJROOTS:O:u:@o@-O $o@} \
${M2D_EXCLUDES:O:u:@o@-X $o@} \
M2D_OBJROOTS += ${OBJTOP} ${_OBJROOT} ${_objroot}
.if defined(SB_OBJROOT)
M2D_OBJROOTS += ${SB_OBJROOT}
.endif
.if defined(STAGE_ROOT)
M2D_OBJROOTS += ${STAGE_ROOT}
.endif
.if ${.MAKE.DEPENDFILE_PREFERENCE:U${.MAKE.DEPENDFILE}:M*.${MACHINE}} == ""
# meta2deps.py only groks objroot
# so we need to give it what it expects
# and tell it not to add machine qualifiers
META2DEPS_ARGS += MACHINE=none
.endif
.if defined(SB_BACKING_SB)
META2DEPS_CMD += -S ${SB_BACKING_SB}/src
M2D_OBJROOTS += ${SB_BACKING_SB}/${SB_OBJPREFIX}
.endif
GENDIRDEPS_SEDCMDS += \
-e 's,//*$$,,;s,\.${HOST_TARGET:Uhost}$$,.host,' \
-e 's,\.${HOST_TARGET32:Uhost32}$$,.host32,' \
-e 's,\.${MACHINE}$$,,' \
-e 's:\.${TARGET_SPEC:U${MACHINE}}$$::'
# we are only interested in the dirs
# specifically those we read something from.
# we canonicalize them to keep things simple
# if we are using a split-fs sandbox, it gets a little messier.
_objtop := ${_OBJTOP:tA}
# some people put *.meta in META_XTRAS to make sure we get here
_meta_files := ${META_FILES:N\*.meta:O:u}
# assume a big list
_meta_files_arg= @meta.list
.if empty(_meta_files) && ${META_FILES:M\*.meta} != ""
# XXX this should be considered a bad idea,
# since we cannot ignore stale .meta
x != cd ${_OBJDIR} && find . -name '*.meta' -print -o \( -type d ! -name . -prune \) | sed 's,^./,,' > meta.list; echo
.elif ${_meta_files:[#]} > 500
.export _meta_files
x != echo; for m in $$_meta_files; do echo $$m; done > meta.list
# _meta_files is consuming a lot of env space
# that can impact command line length,
# and we do not need it any more
.undef _meta_files
.unexport _meta_files
.else
_meta_files_arg:= ${_meta_files}
.endif
dir_list != cd ${_OBJDIR} && \
${META2DEPS_CMD} MACHINE=${MACHINE} \
SRCTOP=${SRCTOP} RELDIR=${RELDIR} CURDIR=${_CURDIR} \
${META2DEPS_ARGS} \
${_meta_files_arg} | ${META2DEPS_FILTER} ${_skip_gendirdeps} \
sed ${GENDIRDEPS_SEDCMDS}
.if ${dir_list:M*ERROR\:*} != ""
.warning ${dir_list:tW:C,.*(ERROR),\1,}
.warning Skipping ${_DEPENDFILE:S,${SRCTOP}/,,}
# we are not going to update anything
.else
dpadd_dir_list=
.if !empty(DPADD)
_nonlibs := ${DPADD:T:Nlib*:N*include}
.if !empty(_nonlibs)
ddep_list =
.for f in ${_nonlibs:@x@${DPADD:M*/$x}@}
.if exists($f.dirdep)
ddep_list += $f.dirdep
.elif exists(${f:H}.dirdep)
ddep_list += ${f:H}.dirdep
.else
dir_list += ${f:H:tA}
dpadd_dir_list += ${f:H:tA}
.endif
.endfor
.if !empty(ddep_list)
ddeps != cat ${ddep_list:O:u} | ${META2DEPS_FILTER} ${_skip_gendirdeps} \
sed ${GENDIRDEPS_SEDCMDS}
.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != ""
.info ${RELDIR}: raw_dir_list='${dir_list}'
.info ${RELDIR}: ddeps='${ddeps}'
.endif
dir_list += ${ddeps}
.endif
.endif
.endif
# DIRDEPS represent things that had to have been built first
# so they should all be undir OBJTOP.
# Note that ${_OBJTOP}/bsd/include/machine will get reported
# to us as $SRCTOP/bsd/sys/$MACHINE_ARCH/include meaning we
# will want to visit bsd/include
# so we add
# ${"${dir_list:M*bsd/sys/${MACHINE_ARCH}/include}":?bsd/include:}
# to GENDIRDEPS_DIR_LIST_XTRAS
_objtops = ${OBJTOP} ${_OBJTOP} ${_objtop}
_objtops := ${_objtops:O:u}
dirdep_list = \
${_objtops:@o@${dir_list:M$o*/*:C,$o[^/]*/,,}@} \
${GENDIRDEPS_DIR_LIST_XTRAS}
# sort longest first
M2D_OBJROOTS := ${M2D_OBJROOTS:O:u:[-1..1]}
# anything we use from an object dir other than ours
# needs to be qualified with its .<machine> suffix
# (we used the pseudo machine "host" for the HOST_TARGET).
skip_ql= ${SRCTOP}* ${_objtops:@o@$o*@}
.for o in ${M2D_OBJROOTS:${skip_ql:${M_ListToSkip}}}
# we need := so only skip_ql to this point applies
ql.$o := ${dir_list:${skip_ql:${M_ListToSkip}}:M$o*/*/*:C,$o([^/]+)/(.*),\2.\1,:S,.${HOST_TARGET},.host,}
qualdir_list += ${ql.$o}
.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != ""
.info ${RELDIR}: o=$o ${ql.$o qualdir_list:L:@v@$v=${$v}@}
.endif
skip_ql+= $o*
.endfor
dirdep_list := ${dirdep_list:O:u}
qualdir_list := ${qualdir_list:N*.${MACHINE}:O:u}
DIRDEPS = \
${dirdep_list:N${RELDIR}:N${RELDIR}/*} \
${qualdir_list:N${RELDIR}.*:N${RELDIR}/*}
# We only consider things below $RELDIR/ if they have a makefile.
# This is the same test that _DIRDEP_USE applies.
# We have do a double test with dirdep_list as it _may_ contain
# qualified dirs - if we got anything from a stage dir.
# qualdir_list we know are all qualified.
# It would be nice do peform this check for all of DIRDEPS,
# but we cannot assume that all of the tree is present,
# in fact we can only assume that RELDIR is.
DIRDEPS += \
${dirdep_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/$d/$m):?$d:${exists(${SRCTOP}/${d:R}/$m):?$d:}}@}@} \
${qualdir_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/${d:R}/$m):?$d:}@}@}
# what modifiers do we allow in GENDIRDEPS_FILTER
GENDIRDEPS_FILTER_MASK += @CMNS
DIRDEPS := ${DIRDEPS:${GENDIRDEPS_FILTER:UNno:M[${GENDIRDEPS_FILTER_MASK:O:u:ts}]*:ts:}:C,//+,/,g:O:u}
.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != ""
.info ${RELDIR}: M2D_OBJROOTS=${M2D_OBJROOTS}
.info ${RELDIR}: M2D_EXCLUDES=${M2D_EXCLUDES}
.info ${RELDIR}: dir_list='${dir_list}'
.info ${RELDIR}: dpadd_dir_list='${dpadd_dir_list}'
.info ${RELDIR}: dirdep_list='${dirdep_list}'
.info ${RELDIR}: qualdir_list='${qualdir_list}'
.info ${RELDIR}: SKIP_GENDIRDEPS='${SKIP_GENDIRDEPS}'
.info ${RELDIR}: GENDIRDEPS_FILTER='${GENDIRDEPS_FILTER}'
.info ${RELDIR}: FORCE_DPADD='${DPADD}'
.info ${RELDIR}: DIRDEPS='${DIRDEPS}'
.endif
# SRC_DIRDEPS is for checkout logic
src_dirdep_list = \
${dir_list:M${SRCTOP}/*:S,${SRCTOP}/,,}
SRC_DIRDEPS = \
${src_dirdep_list:N${RELDIR}:N${RELDIR}/*:C,(/h)/.*,,}
SRC_DIRDEPS := ${SRC_DIRDEPS:${GENDIRDEPS_SRC_FILTER:UN/*:ts:}:C,//+,/,g:O:u}
# if you want to capture SRC_DIRDEPS in .MAKE.DEPENDFILE put
# SRC_DIRDEPS_FILE = ${_DEPENDFILE}
# in local.gendirdeps.mk
.if ${SRC_DIRDEPS_FILE:Uno:tl} != "no"
ECHO_SRC_DIRDEPS = echo 'SRC_DIRDEPS = \'; echo '${SRC_DIRDEPS:@d@ $d \\${.newline}@}'; echo;
.if ${SRC_DIRDEPS_FILE:T} == ${_DEPENDFILE:T}
_include_src_dirdeps = ${ECHO_SRC_DIRDEPS}
.else
all: ${SRC_DIRDEPS_FILE}
.if !target(${SRC_DIRDEPS_FILE})
${SRC_DIRDEPS_FILE}: ${META_FILES} ${_this} ${META2DEPS}
@(${ECHO_SRC_DIRDEPS}) > $@
.endif
.endif
.endif
_include_src_dirdeps ?=
all: ${_DEPENDFILE}
# if this is going to exist it would be there by now
.if !exists(.depend)
CAT_DEPEND = /dev/null
.endif
CAT_DEPEND ?= .depend
.if !empty(_DIRDEPS) && ${DIRDEPS} != ${_DIRDEPS}
# we may have changed a filter
.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
# .depend may contain things we don't want.
# The sed command at the end of the stream, allows for the filters
# to output _{VAR} tokens which we will turn into proper ${VAR} references.
${_DEPENDFILE}: .NOMETA ${CAT_DEPEND:M.depend} ${META_FILES:O:u:@m@${exists($m):?$m:}@} ${_this} ${META2DEPS}
@(${GENDIRDEPS_HEADER} echo '# Autogenerated - do NOT edit!'; echo; \
echo 'DIRDEPS = \'; \
echo '${DIRDEPS:@d@ $d \\${.newline}@}'; echo; \
${_include_src_dirdeps} \
echo '.include <dirdeps.mk>'; \
[ "${LOCAL_DEPENDS_GUARD:[1]:tl}" != no ] || exit 0; \
echo; \
echo '.if ${LOCAL_DEPENDS_GUARD}'; \
echo '# local dependencies - needed for -jN in clean tree'; \
[ -s ${CAT_DEPEND} ] && { grep : ${CAT_DEPEND} | grep -v '[/\\]'; }; \
echo '.endif' ) | sed 's,_\([{(]\),$$\1,g' > $@.new${.MAKE.PID}
@${InstallNew}; InstallNew -s $@.new${.MAKE.PID}
.endif # meta2deps failed
.elif !empty(SUBDIR)
DIRDEPS := ${SUBDIR:S,^,${RELDIR}/,:O:u}
all: ${_DEPENDFILE}
${_DEPENDFILE}: .NOMETA ${MAKEFILE} ${_this}
@(${GENDIRDEPS_HEADER} echo '# Autogenerated - do NOT edit!'; echo; \
echo 'DIRDEPS = \'; \
echo '${DIRDEPS:@d@ $d \\${.newline}@}'; echo; \
echo '.include <dirdeps.mk>'; \
echo ) | sed 's,_\([{(]\),$$\1,g' > $@.new
@${InstallNew}; InstallNew $@.new
.else
# nothing to do
all ${_DEPENDFILE}:
.endif
${_DEPENDFILE}: .PRECIOUS
# don't waste time looking for ways to make .meta files
.SUFFIXES: