1# $Id: gendirdeps.mk,v 1.50 2023/11/04 16:47:34 sjg Exp $
2
3# SPDX-License-Identifier: BSD-2-Clause
4#
5# Copyright (c) 2011-2020, Simon J. Gerraty
6# Copyright (c) 2010-2018, Juniper Networks, Inc.
7# All rights reserved.
8#
9# Redistribution and use in source and binary forms, with or without
10# modification, are permitted provided that the following conditions
11# are met:
12# 1. Redistributions of source code must retain the above copyright
13#    notice, this list of conditions and the following disclaimer.
14# 2. Redistributions in binary form must reproduce the above copyright
15#    notice, this list of conditions and the following disclaimer in the
16#    documentation and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#
31# This makefile [re]generates ${.MAKE.DEPENDFILE}
32#
33
34.include <install-new.mk>
35
36# Assumptions:
37#	RELDIR is the relative path from ${SRCTOP} to ${_CURDIR}
38#		(SRCTOP is ${SB}/src)
39#	_CURDIR is the absolute version of ${.CURDIR}
40#	_OBJDIR is the absolute version of ${.OBJDIR}
41#	_objroot is realpath of ${_OBJTOP} without ${MACHINE}
42#		this may be different from _OBJROOT if $SB/obj is a
43#		symlink to another filesystem.
44#		_objroot must be a prefix match for _objtop
45
46# If any of GENDIRDEPS_FILTER, GENDIRDEPS_FILTER_DIR_VARS
47# or GENDIRDEPS_FILTER_VARS are set, we use them to filter the
48# output from filemon(4).
49# Any references to variables that dirdeps.mk will set
50# such as DEP_MACHINE, DEP_RELDIR etc, should use that form.
51# Thus we want ${DEP_MACHINE} not ${MACHINE} used in DIRDEPS.
52#
53# If any manually maintained Makefile.depend files will use any
54# DEP_* variables in conditionals, precautions are needed to avoid
55# errors when Makefile.depend is read at level 1+ (ie not via
56# dirdeps.mk)
57# Using MACHINE as an example; such makefiles can do:
58#
59# 	DEP_MACHINE ?= ${MACHINE}
60# 	.if ${DEP_MACHINE} == "xyz"
61#
62# or:
63# 
64# 	.if ${DEP_MACHINE:U${MACHINE}} == "xyz"
65#
66# but it might be safer to set GENDIRDEPS_FILTER_DIR_VARS and
67# GENDIRDEPS_FILTER_VARS via local.meta.sys.mk rather than
68# local.gendirdeps.mk and then:
69#
70# 	.if ${.MAKE.LEVEL} > 0
71# 	.for V in ${GENDIRDEPS_FILTER_DIR_VARS:MDEP_*} \
72# 		${GENDIRDEPS_FILTER_VARS:MDEP_*}
73# 	$V ?= ${${V:S,DEP_,,}}
74# 	.endfor
75# 	.endif
76# 
77.MAIN: all
78
79# keep this simple
80.MAKE.MODE = compat
81
82all:
83
84_CURDIR ?= ${.CURDIR}
85_OBJDIR ?= ${.OBJDIR}
86_OBJTOP ?= ${OBJTOP}
87_OBJROOT ?= ${OBJROOT:U${_OBJTOP:H}}
88.if ${_OBJROOT:M*/}
89_slash=/
90.else
91_slash=
92.endif
93_objroot ?= ${_OBJROOT:tA}${_slash}
94
95_this = ${.PARSEDIR}/${.PARSEFILE}
96
97# remember what to make
98_DEPENDFILE := ${_CURDIR}/${.MAKE.DEPENDFILE:T}
99
100# We do _not_ want to read our own output!
101.MAKE.DEPENDFILE = /dev/null
102
103# caller should have set this
104META_FILES ?= ${.MAKE.META.FILES}
105# this sometimes needs to be passed separately
106.if !empty(META_XTRAS)
107META_FILES += ${META_XTRAS:N\*.meta}
108.endif
109
110.if !empty(META_FILES)
111
112.if ${.MAKE.LEVEL} > 0 && !empty(GENDIRDEPS_FILTER)
113# so we can compare below
114.-include <${_DEPENDFILE}>
115# yes, I mean :U with no value
116_DIRDEPS := ${DIRDEPS:U:O:u}
117.endif
118
119META_FILES := ${META_FILES:T:O:u}
120
121# pickup customizations
122.-include <local.gendirdeps.mk>
123
124# these are actually prefixes that we'll skip
125# they should all be absolute paths
126SKIP_GENDIRDEPS ?=
127.if !empty(SKIP_GENDIRDEPS)
128_skip_gendirdeps = ${EGREP:Uegrep} -v '^(${SKIP_GENDIRDEPS:O:u:ts|})' |
129.else
130_skip_gendirdeps =
131.endif
132
133# Below we will turn _{VAR} into ${VAR} which keeps this simple
134# GENDIRDEPS_FILTER_DIR_VARS is a list of dirs to be substiuted for.
135# GENDIRDEPS_FILTER_VARS is more general.
136# In each case order matters.
137.if !empty(GENDIRDEPS_FILTER_DIR_VARS)
138GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_DIR_VARS:@v@S,${$v},_{${v}},@}
139.endif
140.if !empty(GENDIRDEPS_FILTER_VARS)
141GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_VARS:@v@S,/${$v}/,/_{${v}}/,@:NS,//,*:u}
142.endif
143
144# this (*should* be set in meta.sys.mk)
145# is the script that extracts what we want.
146META2DEPS ?= ${.PARSEDIR}/meta2deps.sh
147META2DEPS := ${META2DEPS}
148
149.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != "" && ${DEBUG_GENDIRDEPS:Uno:Mmeta2d*} != ""
150_time = time
151_sh_x = sh -x
152_py_d = -ddd
153.else
154_time =
155_sh_x =
156_py_d =
157.endif
158
159.if ${META2DEPS:E} == "py"
160# we can afford to do this all the time.
161DPDEPS ?= no
162META2DEPS_CMD = ${_time} ${PYTHON} ${META2DEPS} ${_py_d}
163.if ${DPDEPS:tl} != "no"
164META2DEPS_CMD += -D ${DPDEPS}
165.endif
166META2DEPS_FILTER = sed 's,^src:,${SRCTOP}/,;s,^\([^/]\),${OBJTOP}/\1,' |
167.elif ${META2DEPS:E} == "sh"
168META2DEPS_CMD = ${_time} ${_sh_x} ${META2DEPS} OBJTOP=${_OBJTOP}
169.else
170META2DEPS_CMD ?= ${META2DEPS}
171.endif
172
173.if ${TARGET_OBJ_SPEC:U${MACHINE}} != ${MACHINE}
174META2DEPS_CMD += -T ${TARGET_OBJ_SPEC}
175.endif
176META2DEPS_CMD += \
177	-R ${RELDIR} -H ${HOST_TARGET} \
178	${M2D_OBJROOTS:O:u:@o@-O $o@} \
179	${M2D_EXCLUDES:O:u:@o@-X $o@} \
180
181
182M2D_OBJROOTS += ${OBJTOP} ${_OBJROOT} ${_objroot}
183.if defined(SB_OBJROOT)
184M2D_OBJROOTS += ${SB_OBJROOT}
185.endif
186.if defined(STAGE_ROOT)
187M2D_OBJROOTS += ${STAGE_ROOT}
188.endif
189.if ${.MAKE.DEPENDFILE_PREFERENCE:U${.MAKE.DEPENDFILE}:M*.${MACHINE}} == ""
190# meta2deps.py only groks objroot
191# so we need to give it what it expects
192# and tell it not to add machine qualifiers
193META2DEPS_ARGS += MACHINE=none
194.endif
195.if defined(SB_BACKING_SB)
196META2DEPS_CMD += -S ${SB_BACKING_SB}/src
197M2D_OBJROOTS += ${SB_BACKING_SB}/${SB_OBJPREFIX}
198.endif
199
200GENDIRDEPS_SEDCMDS += \
201	-e 's,//*$$,,;s,\.${HOST_TARGET:Uhost}$$,.host,' \
202	-e 's,\.${HOST_TARGET32:Uhost32}$$,.host32,' \
203	-e 's,\.${MACHINE}$$,,' \
204	-e 's:\.${TARGET_SPEC:U${MACHINE}}$$::'
205
206# we are only interested in the dirs
207# specifically those we read something from.
208# we canonicalize them to keep things simple
209# if we are using a split-fs sandbox, it gets a little messier.
210_objtop := ${_OBJTOP:tA}
211
212# some people put *.meta in META_XTRAS to make sure we get here
213_meta_files := ${META_FILES:N\*.meta:O:u}
214# assume a big list
215_meta_files_arg= @meta.list
216.if empty(_meta_files) && ${META_FILES:M\*.meta} != ""
217# XXX this should be considered a bad idea,
218# since we cannot ignore stale .meta
219x != cd ${_OBJDIR} && find . -name '*.meta' -print -o \( -type d ! -name . -prune \) | sed 's,^./,,' > meta.list; echo
220.elif ${_meta_files:[#]} > 500
221.export _meta_files
222x != echo; for m in $$_meta_files; do echo $$m; done > meta.list
223# _meta_files is consuming a lot of env space
224# that can impact command line length,
225# and we do not need it any more
226.undef _meta_files
227.unexport _meta_files
228.else
229_meta_files_arg:= ${_meta_files}
230.endif
231
232dir_list != cd ${_OBJDIR} && \
233	${META2DEPS_CMD} MACHINE=${MACHINE} \
234	SRCTOP=${SRCTOP} RELDIR=${RELDIR} CURDIR=${_CURDIR} \
235	${META2DEPS_ARGS} \
236	${_meta_files_arg} | ${META2DEPS_FILTER} ${_skip_gendirdeps} \
237	sed ${GENDIRDEPS_SEDCMDS}
238
239.if ${dir_list:M*ERROR\:*} != ""
240.warning ${dir_list:tW:C,.*(ERROR),\1,}
241.warning Skipping ${_DEPENDFILE:S,${SRCTOP}/,,}
242# we are not going to update anything
243.else
244dpadd_dir_list=
245.if !empty(DPADD)
246_nonlibs := ${DPADD:T:Nlib*:N*include}
247.if !empty(_nonlibs)
248ddep_list =
249.for f in ${_nonlibs:@x@${DPADD:M*/$x}@}
250.if exists($f.dirdep)
251ddep_list += $f.dirdep
252.elif exists(${f:H}.dirdep)
253ddep_list += ${f:H}.dirdep
254.else
255dir_list += ${f:H:tA}
256dpadd_dir_list += ${f:H:tA}
257.endif
258.endfor
259.if !empty(ddep_list)
260ddeps != cat ${ddep_list:O:u} | ${META2DEPS_FILTER} ${_skip_gendirdeps} \
261	sed ${GENDIRDEPS_SEDCMDS}
262
263.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != ""
264.info ${RELDIR}: raw_dir_list='${dir_list}'
265.info ${RELDIR}: ddeps='${ddeps}'
266.endif
267dir_list += ${ddeps}
268.endif
269.endif
270.endif
271
272# DIRDEPS represent things that had to have been built first
273# so they should all be undir OBJTOP.
274# Note that ${_OBJTOP}/bsd/include/machine will get reported
275# to us as $SRCTOP/bsd/sys/$MACHINE_ARCH/include meaning we
276# will want to visit bsd/include
277# so we add
278# ${"${dir_list:M*bsd/sys/${MACHINE_ARCH}/include}":?bsd/include:}
279# to GENDIRDEPS_DIR_LIST_XTRAS
280_objtops = ${OBJTOP} ${_OBJTOP} ${_objtop}
281_objtops := ${_objtops:O:u}
282dirdep_list = \
283	${_objtops:@o@${dir_list:M$o*/*:C,$o[^/]*/,,}@} \
284	${GENDIRDEPS_DIR_LIST_XTRAS}
285
286# sort longest first
287M2D_OBJROOTS := ${M2D_OBJROOTS:O:u:[-1..1]}
288
289# anything we use from an object dir other than ours
290# needs to be qualified with its .<machine> suffix
291# (we used the pseudo machine "host" for the HOST_TARGET).
292skip_ql= ${SRCTOP}* ${_objtops:@o@$o*@}
293.for o in ${M2D_OBJROOTS:${skip_ql:${M_ListToSkip}}}
294# we need := so only skip_ql to this point applies
295ql.$o := ${dir_list:${skip_ql:${M_ListToSkip}}:M$o*/*/*:C,$o([^/]+)/(.*),\2.\1,:S,.${HOST_TARGET},.host,}
296qualdir_list += ${ql.$o}
297.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != ""
298.info ${RELDIR}: o=$o ${ql.$o qualdir_list:L:@v@$v=${$v}@}
299.endif
300skip_ql+= $o*
301.endfor
302
303dirdep_list := ${dirdep_list:O:u}
304qualdir_list := ${qualdir_list:N*.${MACHINE}:O:u}
305
306DIRDEPS = \
307	${dirdep_list:N${RELDIR}:N${RELDIR}/*} \
308	${qualdir_list:N${RELDIR}.*:N${RELDIR}/*}
309
310# We only consider things below $RELDIR/ if they have a makefile.
311# This is the same test that _DIRDEP_USE applies.
312# We have do a double test with dirdep_list as it _may_ contain
313# qualified dirs - if we got anything from a stage dir.
314# qualdir_list we know are all qualified.
315# It would be nice do peform this check for all of DIRDEPS,
316# but we cannot assume that all of the tree is present,
317# in fact we can only assume that RELDIR is.
318DIRDEPS += \
319	${dirdep_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/$d/$m):?$d:${exists(${SRCTOP}/${d:R}/$m):?$d:}}@}@} \
320	${qualdir_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/${d:R}/$m):?$d:}@}@}
321
322# what modifiers do we allow in GENDIRDEPS_FILTER
323GENDIRDEPS_FILTER_MASK += @CMNS
324DIRDEPS := ${DIRDEPS:${GENDIRDEPS_FILTER:UNno:M[${GENDIRDEPS_FILTER_MASK:O:u:ts}]*:ts:}:C,//+,/,g:O:u}
325
326.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != ""
327.info ${RELDIR}: M2D_OBJROOTS=${M2D_OBJROOTS}
328.info ${RELDIR}: M2D_EXCLUDES=${M2D_EXCLUDES}
329.info ${RELDIR}: dir_list='${dir_list}'
330.info ${RELDIR}: dpadd_dir_list='${dpadd_dir_list}'
331.info ${RELDIR}: dirdep_list='${dirdep_list}'
332.info ${RELDIR}: qualdir_list='${qualdir_list}'
333.info ${RELDIR}: SKIP_GENDIRDEPS='${SKIP_GENDIRDEPS}'
334.info ${RELDIR}: GENDIRDEPS_FILTER='${GENDIRDEPS_FILTER}'
335.info ${RELDIR}: FORCE_DPADD='${DPADD}'
336.info ${RELDIR}: DIRDEPS='${DIRDEPS}'
337.endif
338
339# SRC_DIRDEPS is for checkout logic
340src_dirdep_list = \
341	${dir_list:M${SRCTOP}/*:S,${SRCTOP}/,,}
342
343SRC_DIRDEPS = \
344	${src_dirdep_list:N${RELDIR}:N${RELDIR}/*:C,(/h)/.*,,}
345
346SRC_DIRDEPS := ${SRC_DIRDEPS:${GENDIRDEPS_SRC_FILTER:UN/*:ts:}:C,//+,/,g:O:u}
347
348# if you want to capture SRC_DIRDEPS in .MAKE.DEPENDFILE put
349# SRC_DIRDEPS_FILE = ${_DEPENDFILE}
350# in local.gendirdeps.mk
351.if ${SRC_DIRDEPS_FILE:Uno:tl} != "no"
352ECHO_SRC_DIRDEPS = echo 'SRC_DIRDEPS = \'; echo '${SRC_DIRDEPS:@d@	$d \\${.newline}@}'; echo;
353
354.if ${SRC_DIRDEPS_FILE:T} == ${_DEPENDFILE:T}
355_include_src_dirdeps = ${ECHO_SRC_DIRDEPS}
356.else
357all: ${SRC_DIRDEPS_FILE}
358.if !target(${SRC_DIRDEPS_FILE})
359${SRC_DIRDEPS_FILE}: ${META_FILES} ${_this} ${META2DEPS}
360	@(${ECHO_SRC_DIRDEPS}) > $@
361.endif
362.endif
363.endif
364_include_src_dirdeps ?=
365
366all:	${_DEPENDFILE}
367
368# if this is going to exist it would be there by now
369.if !exists(.depend)
370CAT_DEPEND = /dev/null
371.endif
372CAT_DEPEND ?= .depend
373
374.if !empty(_DIRDEPS) && ${DIRDEPS} != ${_DIRDEPS}
375# we may have changed a filter
376.PHONY: ${_DEPENDFILE}
377.endif
378
379# set this to 'no' and we will not capture any
380# local depends
381LOCAL_DEPENDS_GUARD ?= _{.MAKE.LEVEL} > 0
382
383# 'cat .depend' should suffice, but if we are mixing build modes
384# .depend may contain things we don't want.
385# The sed command at the end of the stream, allows for the filters
386# to output _{VAR} tokens which we will turn into proper ${VAR} references.
387${_DEPENDFILE}: .NOMETA ${CAT_DEPEND:M.depend} ${META_FILES:O:u:@m@${exists($m):?$m:}@} ${_this} ${META2DEPS}
388	@(${GENDIRDEPS_HEADER} echo '# Autogenerated - do NOT edit!'; echo; \
389	echo 'DIRDEPS = \'; \
390	echo '${DIRDEPS:@d@	$d \\${.newline}@}'; echo; \
391	${_include_src_dirdeps} \
392	echo '.include <dirdeps.mk>'; \
393	[ "${LOCAL_DEPENDS_GUARD:[1]:tl}" != no ] || exit 0; \
394	echo; \
395	echo '.if ${LOCAL_DEPENDS_GUARD}'; \
396	echo '# local dependencies - needed for -jN in clean tree'; \
397	[ -s ${CAT_DEPEND} ] && { grep : ${CAT_DEPEND} | grep -v '[/\\]'; }; \
398	echo '.endif' ) | sed 's,_\([{(]\),$$\1,g' > $@.new${.MAKE.PID}
399	@${InstallNew}; InstallNew -s $@.new${.MAKE.PID}
400
401.endif				# meta2deps failed
402.elif !empty(SUBDIR)
403
404DIRDEPS := ${SUBDIR:S,^,${RELDIR}/,:O:u}
405
406all:	${_DEPENDFILE}
407
408${_DEPENDFILE}: .NOMETA ${MAKEFILE} ${_this}
409	@(${GENDIRDEPS_HEADER} echo '# Autogenerated - do NOT edit!'; echo; \
410	echo 'DIRDEPS = \'; \
411	echo '${DIRDEPS:@d@	$d \\${.newline}@}'; echo; \
412	echo '.include <dirdeps.mk>'; \
413	echo ) | sed 's,_\([{(]\),$$\1,g' > $@.new
414	@${InstallNew}; InstallNew $@.new
415
416.else
417
418# nothing to do
419all ${_DEPENDFILE}:
420
421.endif
422${_DEPENDFILE}: .PRECIOUS
423
424# don't waste time looking for ways to make .meta files
425.SUFFIXES:
426