xref: /freebsd/contrib/bmake/mk/meta.stage.mk (revision 1f474190)
1# $Id: meta.stage.mk,v 1.60 2020/08/19 17:51:53 sjg Exp $
2#
3#	@(#) Copyright (c) 2011-2017, Simon J. Gerraty
4#
5#	This file is provided in the hope that it will
6#	be of use.  There is absolutely NO WARRANTY.
7#	Permission to copy, redistribute or otherwise
8#	use this file is hereby granted provided that
9#	the above copyright notice and this notice are
10#	left intact.
11#
12#	Please send copies of changes and bug-fixes to:
13#	sjg@crufty.net
14#
15
16.ifndef NO_STAGING
17
18.if !target(__${.PARSEFILE}__)
19# the guard target is defined later
20
21.-include <local.meta.stage.mk>
22
23.if ${.MAKE.DEPENDFILE_PREFERENCE:U${.MAKE.DEPENDFILE}:M*.${MACHINE}} != ""
24# this is generally safer anyway
25_dirdep ?= ${RELDIR}.${TARGET_SPEC:U${MACHINE}}
26.else
27_dirdep ?= ${RELDIR}
28.endif
29
30CLEANFILES+= .dirdep
31
32# this allows us to trace dependencies back to their src dir
33.dirdep:	.NOPATH
34	@echo '${_dirdep}' > $@
35
36.if defined(NO_POSIX_SHELL) || ${type printf:L:sh:Mbuiltin} == ""
37_stage_file_basename = `basename $$f`
38_stage_target_dirname = `dirname $$t`
39.else
40_stage_file_basename = $${f\#\#*/}
41_stage_target_dirname = $${t%/*}
42.endif
43
44_OBJROOT ?= ${OBJROOT:U${OBJTOP:H}}
45.if ${_OBJROOT:M*/} != ""
46_objroot ?= ${_OBJROOT:tA}/
47.else
48_objroot ?= ${_OBJROOT:tA}
49.endif
50
51# make sure this is global
52_STAGED_DIRS ?=
53.export _STAGED_DIRS
54# add each dir we stage to to _STAGED_DIRS
55# and make sure we have absolute paths so that bmake
56# will match against .MAKE.META.BAILIWICK
57STAGE_DIR_FILTER = tA:@d@$${_STAGED_DIRS::+=$$d}$$d@
58# convert _STAGED_DIRS into suitable filters
59GENDIRDEPS_FILTER += Nnot-empty-is-important \
60	${_STAGED_DIRS:O:u:M${OBJTOP}*:S,${OBJTOP}/,N,} \
61	${_STAGED_DIRS:O:u:M${_objroot}*:N${OBJTOP}*:S,${_objroot},,:C,^([^/]+)/(.*),N\2.\1,:S,${HOST_TARGET},.host,}
62
63LN_CP_SCRIPT = LnCp() { \
64  rm -f $$2 2> /dev/null; \
65  { [ -z "$$mode" ] && ${LN:Uln} $$1 $$2 2> /dev/null; } || \
66  cp -p $$1 $$2; }
67
68# a staging conflict should cause an error
69# a warning is handy when bootstapping different options.
70STAGE_CONFLICT?= ERROR
71.if ${STAGE_CONFLICT:tl} == "error"
72STAGE_CONFLICT_ACTION= exit 1
73.else
74STAGE_CONFLICT_ACTION=
75.endif
76
77# it is an error for more than one src dir to try and stage
78# the same file
79STAGE_DIRDEP_SCRIPT = ${LN_CP_SCRIPT}; StageDirdep() { \
80  t=$$1; \
81  if [ -s $$t.dirdep ]; then \
82	cmp -s .dirdep $$t.dirdep && return; \
83	x=`cat $$t.dirdep`; \
84	case "${RELDIR}:${_dirdep}" in $${x%.*}:$${x}*) ;; \
85	*) echo "${STAGE_CONFLICT}: $$t installed by $$x not ${_dirdep}" >&2; \
86	${STAGE_CONFLICT_ACTION} ;; esac; \
87  fi; \
88  LnCp .dirdep $$t.dirdep || exit 1; }
89
90# common logic for staging files
91# this all relies on RELDIR being set to a subdir of SRCTOP
92# we use ln(1) if we can, else cp(1)
93STAGE_FILE_SCRIPT = ${STAGE_DIRDEP_SCRIPT}; StageFiles() { \
94  case "$$1" in "") return;; -m) mode=$$2; shift 2;; *) mode=;; esac; \
95  dest=$$1; shift; \
96  mkdir -p $$dest; \
97  [ -s .dirdep ] || echo '${_dirdep}' > .dirdep; \
98  for f in "$$@"; do \
99	case "$$f" in */*) t=$$dest/${_stage_file_basename};; *) t=$$dest/$$f;; esac; \
100	StageDirdep $$t; \
101	LnCp $$f $$t || exit 1; \
102	[ -z "$$mode" ] || chmod $$mode $$t; \
103  done; :; }
104
105STAGE_LINKS_SCRIPT = ${STAGE_DIRDEP_SCRIPT}; StageLinks() { \
106  case "$$1" in "") return;; --) shift;; -*) ldest= lnf=$$1; shift;; /*) ldest=$$1/;; esac; \
107  dest=$$1; shift; \
108  mkdir -p $$dest; \
109  [ -s .dirdep ] || echo '${_dirdep}' > .dirdep; \
110  while test $$\# -ge 2; do \
111	l=$$ldest$$1; shift; \
112	t=$$dest/$$1; \
113	case "$$1" in */*) mkdir -p ${_stage_target_dirname};; esac; \
114	shift; \
115	StageDirdep $$t; \
116	rm -f $$t 2>/dev/null; \
117	ln $$lnf $$l $$t || exit 1; \
118  done; :; }
119
120STAGE_AS_SCRIPT = ${STAGE_DIRDEP_SCRIPT}; StageAs() { \
121  case "$$1" in "") return;; -m) mode=$$2; shift 2;; *) mode=;; esac; \
122  dest=$$1; shift; \
123  mkdir -p $$dest; \
124  [ -s .dirdep ] || echo '${_dirdep}' > .dirdep; \
125  while test $$\# -ge 2; do \
126	s=$$1; shift; \
127	t=$$dest/$$1; \
128	case "$$1" in */*) mkdir -p ${_stage_target_dirname};; esac; \
129	shift; \
130	StageDirdep $$t; \
131	LnCp $$s $$t || exit 1; \
132	[ -z "$$mode" ] || chmod $$mode $$t; \
133  done; :; }
134
135# this is simple, a list of the "staged" files depends on this,
136_STAGE_BASENAME_USE:	.USE .dirdep ${.TARGET:T}
137	@${STAGE_FILE_SCRIPT}; StageFiles ${.TARGET:H:${STAGE_DIR_FILTER}} ${.TARGET:T}
138
139_STAGE_AS_BASENAME_USE:        .USE .dirdep ${.TARGET:T}
140	@${STAGE_AS_SCRIPT}; StageAs ${.TARGET:H:${STAGE_DIR_FILTER}} ${.TARGET:T} ${STAGE_AS_${.TARGET:T}:U${.TARGET:T}}
141
142
143.endif				# first time
144
145
146.if !empty(STAGE_INCSDIR)
147.if !empty(STAGE_INCS)
148stage_incs: ${STAGE_INCS:N*\**}
149.endif
150.if target(stage_incs) || !empty(.ALLTARGETS:Mstage_includes)
151STAGE_TARGETS += stage_incs
152STAGE_INCS ?= ${.ALLSRC:N.dirdep:Nstage_*}
153stage_includes: stage_incs
154stage_incs:	.dirdep
155	@${STAGE_FILE_SCRIPT}; StageFiles ${STAGE_INCSDIR:${STAGE_DIR_FILTER}} ${STAGE_INCS}
156	@touch $@
157
158.endif
159.endif
160
161.if !empty(STAGE_LIBDIR)
162.if !empty(STAGE_LIBS)
163stage_libs: ${STAGE_LIBS:N*\**}
164.endif
165.if target(stage_libs)
166STAGE_TARGETS += stage_libs
167STAGE_LIBS ?= ${.ALLSRC:N.dirdep:Nstage_*}
168stage_libs:	.dirdep
169	@${STAGE_FILE_SCRIPT}; StageFiles ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} ${STAGE_LIBS}
170.if !defined(NO_SHLIB_LINKS)
171.if !empty(SHLIB_LINKS)
172	@${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} \
173	${SHLIB_LINKS:@t@${STAGE_LIBS:T:M$t.*} $t@}
174.elif !empty(SHLIB_LINK) && !empty(SHLIB_NAME)
175	@${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} ${SHLIB_NAME} ${SHLIB_LINK}
176.endif
177.endif
178	@touch $@
179.endif
180.endif
181
182.if !empty(STAGE_DIR)
183STAGE_SETS += _default
184STAGE_DIR._default = ${STAGE_DIR}
185STAGE_LINKS_DIR._default = ${STAGE_LINKS_DIR:U${STAGE_OBJTOP}}
186STAGE_SYMLINKS_DIR._default = ${STAGE_SYMLINKS_DIR:U${STAGE_OBJTOP}}
187STAGE_FILES._default = ${STAGE_FILES}
188STAGE_LINKS._default = ${STAGE_LINKS}
189STAGE_SYMLINKS._default = ${STAGE_SYMLINKS}
190.endif
191
192.if !empty(STAGE_SETS)
193CLEANFILES += ${STAGE_SETS:@s@stage*$s@}
194
195# some makefiles need to populate multiple directories
196.for s in ${STAGE_SETS:O:u}
197.if !empty(STAGE_FILES.$s)
198stage_files.$s: ${STAGE_FILES.$s:N*\**}
199.endif
200.if target(stage_files.$s) || target(stage_files${s:S,^,.,:N._default})
201STAGE_TARGETS += stage_files
202STAGE_FILES.$s ?= ${.ALLSRC:N.dirdep:Nstage_*}
203.if !target(.stage_files.$s)
204.stage_files.$s:
205.if $s != "_default"
206stage_files:	stage_files.$s
207stage_files.$s:	.dirdep
208.else
209STAGE_FILES ?= ${.ALLSRC:N.dirdep:Nstage_*}
210stage_files:	.dirdep
211.endif
212	@${STAGE_FILE_SCRIPT}; StageFiles ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_FILES.$s:O}
213	@touch $@
214.endif
215.endif
216
217.if !empty(STAGE_LINKS.$s)
218stage_links.$s:
219.endif
220.if target(stage_links.$s) || target(stage_links${s:S,^,.,:N._default})
221STAGE_LINKS_DIR.$s ?= ${STAGE_OBJTOP}
222STAGE_TARGETS += stage_links
223.if !target(.stage_links.$s)
224.stage_links.$s:
225.if $s != "_default"
226stage_links:	stage_links.$s
227stage_links.$s:	.dirdep
228.else
229stage_links:	.dirdep
230.endif
231	@${STAGE_LINKS_SCRIPT}; StageLinks ${STAGE_LINKS_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_LINKS.$s}
232	@touch $@
233.endif
234.endif
235
236.if !empty(STAGE_SYMLINKS.$s)
237stage_symlinks.$s:
238.endif
239.if target(stage_symlinks.$s) || target(stage_symlinks${s:S,^,.,:N._default})
240STAGE_SYMLINKS_DIR.$s ?= ${STAGE_OBJTOP}
241STAGE_TARGETS += stage_symlinks
242.if !target(.stage_symlinks.$s)
243.stage_symlinks.$s:
244.if $s != "_default"
245stage_symlinks:	stage_symlinks.$s
246stage_symlinks.$s:	.dirdep
247.else
248stage_symlinks:	.dirdep
249.endif
250	@${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_SYMLINKS_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_SYMLINKS.$s}
251	@touch $@
252.endif
253.endif
254
255.endfor
256.endif
257
258.if !empty(STAGE_AS_SETS)
259CLEANFILES += ${STAGE_AS_SETS:@s@stage*$s@}
260
261# sometimes things need to be renamed as they are staged
262# each ${file} will be staged as ${STAGE_AS_${file:T}}
263# one could achieve the same with SYMLINKS
264# stage_as_and_symlink makes the original name a symlink to the new name
265# it is the same as using stage_as and stage_symlinks but ensures
266# both operations happen together
267.for s in ${STAGE_AS_SETS:O:u}
268.if !empty(STAGE_AS.$s)
269stage_as.$s: ${STAGE_AS.$s:N*\**}
270.endif
271.if target(stage_as.$s)
272STAGE_TARGETS += stage_as
273STAGE_AS.$s ?= ${.ALLSRC:N.dirdep:Nstage_*}
274.if !target(.stage_as.$s)
275.stage_as.$s:
276stage_as:	stage_as.$s
277stage_as.$s:	.dirdep
278	@${STAGE_AS_SCRIPT}; StageAs ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS.$s:O:@f@$f ${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}}@}
279	@touch $@
280.endif
281.endif
282
283.if !empty(STAGE_AS_AND_SYMLINK.$s)
284stage_as_and_symlink.$s: ${STAGE_AS_AND_SYMLINK.$s:N*\**}
285.endif
286.if target(stage_as_and_symlink.$s)
287STAGE_TARGETS += stage_as_and_symlink
288STAGE_AS_AND_SYMLINK.$s ?= ${.ALLSRC:N.dirdep:Nstage_*}
289.if !target(.stage_as_and_symlink.$s)
290.stage_as_and_symlink.$s:
291stage_as_and_symlink:	stage_as_and_symlink.$s
292stage_as_and_symlink.$s:	.dirdep
293	@${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}}}@}
294	@${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@}
295	@touch $@
296.endif
297.endif
298
299.endfor
300.endif
301
302CLEANFILES += ${STAGE_TARGETS} stage_incs stage_includes
303
304# this lot also only makes sense the first time...
305.if !target(__${.PARSEFILE}__)
306__${.PARSEFILE}__:
307
308# stage_*links usually needs to follow any others.
309# for non-jobs mode the order here matters
310staging: ${STAGE_TARGETS:N*_links} ${STAGE_TARGETS:M*_links}
311
312.if ${.MAKE.JOBS:U0} > 0 && ${STAGE_TARGETS:U:M*_links} != ""
313# the above isn't sufficient
314.for t in ${STAGE_TARGETS:N*links:O:u}
315.ORDER: $t stage_links
316.endfor
317.endif
318
319# generally we want staging to wait until everything else is done
320STAGING_WAIT ?= .WAIT
321
322.if ${.MAKE.LEVEL} > 0
323all: ${STAGING_WAIT} staging
324.endif
325
326.if exists(${.PARSEDIR}/stage-install.sh) && !defined(STAGE_INSTALL)
327# this will run install(1) and then followup with .dirdep files.
328STAGE_INSTALL := sh ${.PARSEDIR:tA}/stage-install.sh INSTALL="${INSTALL}" OBJDIR=${.OBJDIR:tA}
329.endif
330
331# if ${INSTALL} gets run during 'all' assume it is for staging?
332.if ${.TARGETS:Nall} == "" && defined(STAGE_INSTALL)
333INSTALL := ${STAGE_INSTALL}
334.if target(beforeinstall)
335beforeinstall: .dirdep
336.endif
337.endif
338.NOPATH: ${STAGE_FILES}
339
340.if !empty(STAGE_TARGETS)
341# for backwards compat make sure they exist
342${STAGE_TARGETS}:
343
344.NOPATH: ${CLEANFILES}
345
346MK_STALE_STAGED?= no
347.if ${MK_STALE_STAGED} == "yes"
348all: stale_staged
349# get a list of paths that we have just staged
350# get a list of paths that we have previously staged to those same dirs
351# anything in the 2nd list but not the first is stale - remove it.
352stale_staged: staging .NOMETA
353	@egrep '^[WL] .*${STAGE_OBJTOP}' /dev/null ${.MAKE.META.FILES:M*stage_*} | \
354	sed "/\.dirdep/d;s,.* '*\(${STAGE_OBJTOP}/[^ '][^ ']*\).*,\1," | \
355	sort > ${.TARGET}.staged1
356	@grep -l '${_dirdep}' /dev/null ${_STAGED_DIRS:M${STAGE_OBJTOP}*:O:u:@d@$d/*.dirdep@} | \
357	sed 's,\.dirdep,,' | sort > ${.TARGET}.staged2
358	@comm -13 ${.TARGET}.staged1 ${.TARGET}.staged2 > ${.TARGET}.stale
359	@test ! -s ${.TARGET}.stale || { \
360		echo "Removing stale staged files..."; \
361		sed 's,.*,& &.dirdep,' ${.TARGET}.stale | xargs rm -f; }
362
363.endif
364.endif
365.endif
366.endif
367