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