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