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