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