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