xref: /freebsd/contrib/bmake/mk/gendirdeps.mk (revision 7ef62cebc2f965b0f640263e179276928885e33d)
1# $Id: gendirdeps.mk,v 1.49 2023/04/20 17:45:03 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
106.if !empty(META_FILES)
107
108.if ${.MAKE.LEVEL} > 0 && !empty(GENDIRDEPS_FILTER)
109# so we can compare below
110.-include <${_DEPENDFILE}>
111# yes, I mean :U with no value
112_DIRDEPS := ${DIRDEPS:U:O:u}
113.endif
114
115META_FILES := ${META_FILES:T:O:u}
116
117# pickup customizations
118.-include <local.gendirdeps.mk>
119
120# these are actually prefixes that we'll skip
121# they should all be absolute paths
122SKIP_GENDIRDEPS ?=
123.if !empty(SKIP_GENDIRDEPS)
124_skip_gendirdeps = ${EGREP:Uegrep} -v '^(${SKIP_GENDIRDEPS:O:u:ts|})' |
125.else
126_skip_gendirdeps =
127.endif
128
129# Below we will turn _{VAR} into ${VAR} which keeps this simple
130# GENDIRDEPS_FILTER_DIR_VARS is a list of dirs to be substiuted for.
131# GENDIRDEPS_FILTER_VARS is more general.
132# In each case order matters.
133.if !empty(GENDIRDEPS_FILTER_DIR_VARS)
134GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_DIR_VARS:@v@S,${$v},_{${v}},@}
135.endif
136.if !empty(GENDIRDEPS_FILTER_VARS)
137GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_VARS:@v@S,/${$v}/,/_{${v}}/,@:NS,//,*:u}
138.endif
139
140# this (*should* be set in meta.sys.mk)
141# is the script that extracts what we want.
142META2DEPS ?= ${.PARSEDIR}/meta2deps.sh
143META2DEPS := ${META2DEPS}
144
145.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != "" && ${DEBUG_GENDIRDEPS:Uno:Mmeta2d*} != ""
146_time = time
147_sh_x = sh -x
148_py_d = -ddd
149.else
150_time =
151_sh_x =
152_py_d =
153.endif
154
155.if ${META2DEPS:E} == "py"
156# we can afford to do this all the time.
157DPDEPS ?= no
158META2DEPS_CMD = ${_time} ${PYTHON} ${META2DEPS} ${_py_d}
159.if ${DPDEPS:tl} != "no"
160META2DEPS_CMD += -D ${DPDEPS}
161.endif
162META2DEPS_FILTER = sed 's,^src:,${SRCTOP}/,;s,^\([^/]\),${OBJTOP}/\1,' |
163.elif ${META2DEPS:E} == "sh"
164META2DEPS_CMD = ${_time} ${_sh_x} ${META2DEPS} OBJTOP=${_OBJTOP}
165.else
166META2DEPS_CMD ?= ${META2DEPS}
167.endif
168
169.if ${TARGET_OBJ_SPEC:U${MACHINE}} != ${MACHINE}
170META2DEPS_CMD += -T ${TARGET_OBJ_SPEC}
171.endif
172META2DEPS_CMD += \
173	-R ${RELDIR} -H ${HOST_TARGET} \
174	${M2D_OBJROOTS:O:u:@o@-O $o@} \
175	${M2D_EXCLUDES:O:u:@o@-X $o@} \
176
177
178M2D_OBJROOTS += ${OBJTOP} ${_OBJROOT} ${_objroot}
179.if defined(SB_OBJROOT)
180M2D_OBJROOTS += ${SB_OBJROOT}
181.endif
182.if defined(STAGE_ROOT)
183M2D_OBJROOTS += ${STAGE_ROOT}
184.endif
185.if ${.MAKE.DEPENDFILE_PREFERENCE:U${.MAKE.DEPENDFILE}:M*.${MACHINE}} == ""
186# meta2deps.py only groks objroot
187# so we need to give it what it expects
188# and tell it not to add machine qualifiers
189META2DEPS_ARGS += MACHINE=none
190.endif
191.if defined(SB_BACKING_SB)
192META2DEPS_CMD += -S ${SB_BACKING_SB}/src
193M2D_OBJROOTS += ${SB_BACKING_SB}/${SB_OBJPREFIX}
194.endif
195
196GENDIRDEPS_SEDCMDS += \
197	-e 's,//*$$,,;s,\.${HOST_TARGET:Uhost}$$,.host,' \
198	-e 's,\.${HOST_TARGET32:Uhost32}$$,.host32,' \
199	-e 's,\.${MACHINE}$$,,' \
200	-e 's:\.${TARGET_SPEC:U${MACHINE}}$$::'
201
202# we are only interested in the dirs
203# specifically those we read something from.
204# we canonicalize them to keep things simple
205# if we are using a split-fs sandbox, it gets a little messier.
206_objtop := ${_OBJTOP:tA}
207
208# some people put *.meta in META_XTRAS to make sure we get here
209_meta_files := ${META_FILES:N\*.meta:O:u}
210# assume a big list
211_meta_files_arg= @meta.list
212.if empty(_meta_files) && ${META_FILES:M\*.meta} != ""
213# XXX this should be considered a bad idea,
214# since we cannot ignore stale .meta
215x != cd ${_OBJDIR} && find . -name '*.meta' -print -o \( -type d ! -name . -prune \) | sed 's,^./,,' > meta.list; echo
216.elif ${_meta_files:[#]} > 500
217.export _meta_files
218x != echo; for m in $$_meta_files; do echo $$m; done > meta.list
219# _meta_files is consuming a lot of env space
220# that can impact command line length,
221# and we do not need it any more
222.undef _meta_files
223.unexport _meta_files
224.else
225_meta_files_arg:= ${_meta_files}
226.endif
227
228dir_list != cd ${_OBJDIR} && \
229	${META2DEPS_CMD} MACHINE=${MACHINE} \
230	SRCTOP=${SRCTOP} RELDIR=${RELDIR} CURDIR=${_CURDIR} \
231	${META2DEPS_ARGS} \
232	${_meta_files_arg} | ${META2DEPS_FILTER} ${_skip_gendirdeps} \
233	sed ${GENDIRDEPS_SEDCMDS}
234
235.if ${dir_list:M*ERROR\:*} != ""
236.warning ${dir_list:tW:C,.*(ERROR),\1,}
237.warning Skipping ${_DEPENDFILE:S,${SRCTOP}/,,}
238# we are not going to update anything
239.else
240dpadd_dir_list=
241.if !empty(DPADD)
242_nonlibs := ${DPADD:T:Nlib*:N*include}
243.if !empty(_nonlibs)
244ddep_list =
245.for f in ${_nonlibs:@x@${DPADD:M*/$x}@}
246.if exists($f.dirdep)
247ddep_list += $f.dirdep
248.elif exists(${f:H}.dirdep)
249ddep_list += ${f:H}.dirdep
250.else
251dir_list += ${f:H:tA}
252dpadd_dir_list += ${f:H:tA}
253.endif
254.endfor
255.if !empty(ddep_list)
256ddeps != cat ${ddep_list:O:u} | ${META2DEPS_FILTER} ${_skip_gendirdeps} \
257	sed ${GENDIRDEPS_SEDCMDS}
258
259.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != ""
260.info ${RELDIR}: raw_dir_list='${dir_list}'
261.info ${RELDIR}: ddeps='${ddeps}'
262.endif
263dir_list += ${ddeps}
264.endif
265.endif
266.endif
267
268# DIRDEPS represent things that had to have been built first
269# so they should all be undir OBJTOP.
270# Note that ${_OBJTOP}/bsd/include/machine will get reported
271# to us as $SRCTOP/bsd/sys/$MACHINE_ARCH/include meaning we
272# will want to visit bsd/include
273# so we add
274# ${"${dir_list:M*bsd/sys/${MACHINE_ARCH}/include}":?bsd/include:}
275# to GENDIRDEPS_DIR_LIST_XTRAS
276_objtops = ${OBJTOP} ${_OBJTOP} ${_objtop}
277_objtops := ${_objtops:O:u}
278dirdep_list = \
279	${_objtops:@o@${dir_list:M$o*/*:C,$o[^/]*/,,}@} \
280	${GENDIRDEPS_DIR_LIST_XTRAS}
281
282# sort longest first
283M2D_OBJROOTS := ${M2D_OBJROOTS:O:u:[-1..1]}
284
285# anything we use from an object dir other than ours
286# needs to be qualified with its .<machine> suffix
287# (we used the pseudo machine "host" for the HOST_TARGET).
288skip_ql= ${SRCTOP}* ${_objtops:@o@$o*@}
289.for o in ${M2D_OBJROOTS:${skip_ql:${M_ListToSkip}}}
290# we need := so only skip_ql to this point applies
291ql.$o := ${dir_list:${skip_ql:${M_ListToSkip}}:M$o*/*/*:C,$o([^/]+)/(.*),\2.\1,:S,.${HOST_TARGET},.host,}
292qualdir_list += ${ql.$o}
293.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != ""
294.info ${RELDIR}: o=$o ${ql.$o qualdir_list:L:@v@$v=${$v}@}
295.endif
296skip_ql+= $o*
297.endfor
298
299dirdep_list := ${dirdep_list:O:u}
300qualdir_list := ${qualdir_list:N*.${MACHINE}:O:u}
301
302DIRDEPS = \
303	${dirdep_list:N${RELDIR}:N${RELDIR}/*} \
304	${qualdir_list:N${RELDIR}.*:N${RELDIR}/*}
305
306# We only consider things below $RELDIR/ if they have a makefile.
307# This is the same test that _DIRDEP_USE applies.
308# We have do a double test with dirdep_list as it _may_ contain
309# qualified dirs - if we got anything from a stage dir.
310# qualdir_list we know are all qualified.
311# It would be nice do peform this check for all of DIRDEPS,
312# but we cannot assume that all of the tree is present,
313# in fact we can only assume that RELDIR is.
314DIRDEPS += \
315	${dirdep_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/$d/$m):?$d:${exists(${SRCTOP}/${d:R}/$m):?$d:}}@}@} \
316	${qualdir_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/${d:R}/$m):?$d:}@}@}
317
318# what modifiers do we allow in GENDIRDEPS_FILTER
319GENDIRDEPS_FILTER_MASK += @CMNS
320DIRDEPS := ${DIRDEPS:${GENDIRDEPS_FILTER:UNno:M[${GENDIRDEPS_FILTER_MASK:O:u:ts}]*:ts:}:C,//+,/,g:O:u}
321
322.if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != ""
323.info ${RELDIR}: M2D_OBJROOTS=${M2D_OBJROOTS}
324.info ${RELDIR}: M2D_EXCLUDES=${M2D_EXCLUDES}
325.info ${RELDIR}: dir_list='${dir_list}'
326.info ${RELDIR}: dpadd_dir_list='${dpadd_dir_list}'
327.info ${RELDIR}: dirdep_list='${dirdep_list}'
328.info ${RELDIR}: qualdir_list='${qualdir_list}'
329.info ${RELDIR}: SKIP_GENDIRDEPS='${SKIP_GENDIRDEPS}'
330.info ${RELDIR}: GENDIRDEPS_FILTER='${GENDIRDEPS_FILTER}'
331.info ${RELDIR}: FORCE_DPADD='${DPADD}'
332.info ${RELDIR}: DIRDEPS='${DIRDEPS}'
333.endif
334
335# SRC_DIRDEPS is for checkout logic
336src_dirdep_list = \
337	${dir_list:M${SRCTOP}/*:S,${SRCTOP}/,,}
338
339SRC_DIRDEPS = \
340	${src_dirdep_list:N${RELDIR}:N${RELDIR}/*:C,(/h)/.*,,}
341
342SRC_DIRDEPS := ${SRC_DIRDEPS:${GENDIRDEPS_SRC_FILTER:UN/*:ts:}:C,//+,/,g:O:u}
343
344# if you want to capture SRC_DIRDEPS in .MAKE.DEPENDFILE put
345# SRC_DIRDEPS_FILE = ${_DEPENDFILE}
346# in local.gendirdeps.mk
347.if ${SRC_DIRDEPS_FILE:Uno:tl} != "no"
348ECHO_SRC_DIRDEPS = echo 'SRC_DIRDEPS = \'; echo '${SRC_DIRDEPS:@d@	$d \\${.newline}@}'; echo;
349
350.if ${SRC_DIRDEPS_FILE:T} == ${_DEPENDFILE:T}
351_include_src_dirdeps = ${ECHO_SRC_DIRDEPS}
352.else
353all: ${SRC_DIRDEPS_FILE}
354.if !target(${SRC_DIRDEPS_FILE})
355${SRC_DIRDEPS_FILE}: ${META_FILES} ${_this} ${META2DEPS}
356	@(${ECHO_SRC_DIRDEPS}) > $@
357.endif
358.endif
359.endif
360_include_src_dirdeps ?=
361
362all:	${_DEPENDFILE}
363
364# if this is going to exist it would be there by now
365.if !exists(.depend)
366CAT_DEPEND = /dev/null
367.endif
368CAT_DEPEND ?= .depend
369
370.if !empty(_DIRDEPS) && ${DIRDEPS} != ${_DIRDEPS}
371# we may have changed a filter
372.PHONY: ${_DEPENDFILE}
373.endif
374
375# set this to 'no' and we will not capture any
376# local depends
377LOCAL_DEPENDS_GUARD ?= _{.MAKE.LEVEL} > 0
378
379# 'cat .depend' should suffice, but if we are mixing build modes
380# .depend may contain things we don't want.
381# The sed command at the end of the stream, allows for the filters
382# to output _{VAR} tokens which we will turn into proper ${VAR} references.
383${_DEPENDFILE}: .NOMETA ${CAT_DEPEND:M.depend} ${META_FILES:O:u:@m@${exists($m):?$m:}@} ${_this} ${META2DEPS}
384	@(${GENDIRDEPS_HEADER} echo '# Autogenerated - do NOT edit!'; echo; \
385	echo 'DIRDEPS = \'; \
386	echo '${DIRDEPS:@d@	$d \\${.newline}@}'; echo; \
387	${_include_src_dirdeps} \
388	echo '.include <dirdeps.mk>'; \
389	[ "${LOCAL_DEPENDS_GUARD:[1]:tl}" != no ] || exit 0; \
390	echo; \
391	echo '.if ${LOCAL_DEPENDS_GUARD}'; \
392	echo '# local dependencies - needed for -jN in clean tree'; \
393	[ -s ${CAT_DEPEND} ] && { grep : ${CAT_DEPEND} | grep -v '[/\\]'; }; \
394	echo '.endif' ) | sed 's,_\([{(]\),$$\1,g' > $@.new${.MAKE.PID}
395	@${InstallNew}; InstallNew -s $@.new${.MAKE.PID}
396
397.endif				# meta2deps failed
398.elif !empty(SUBDIR)
399
400DIRDEPS := ${SUBDIR:S,^,${RELDIR}/,:O:u}
401
402all:	${_DEPENDFILE}
403
404${_DEPENDFILE}: .NOMETA ${MAKEFILE} ${_this}
405	@(${GENDIRDEPS_HEADER} echo '# Autogenerated - do NOT edit!'; echo; \
406	echo 'DIRDEPS = \'; \
407	echo '${DIRDEPS:@d@	$d \\${.newline}@}'; echo; \
408	echo '.include <dirdeps.mk>'; \
409	echo ) | sed 's,_\([{(]\),$$\1,g' > $@.new
410	@${InstallNew}; InstallNew $@.new
411
412.else
413
414# nothing to do
415all ${_DEPENDFILE}:
416
417.endif
418${_DEPENDFILE}: .PRECIOUS
419
420# don't waste time looking for ways to make .meta files
421.SUFFIXES:
422