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