xref: /freebsd/contrib/bmake/mk/ldorder.mk (revision c60f6422ffae3ea85e7b10bad950ad27c463af18)
1# $Id: ldorder.mk,v 1.28 2025/08/09 22:42:24 sjg Exp $
2#
3#	@(#) Copyright (c) 2015, Simon J. Gerraty
4#
5#	SPDX-License-Identifier: BSD-2-Clause
6#
7#	Please send copies of changes and bug-fixes to:
8#	sjg@crufty.net
9#
10
11# Try to compute optimal link order.
12# When using only shared libs link order does not much matter,
13# but archive libs are a different matter.
14
15# We can construct a graph of .ldorder-lib${LIB*} dependencies
16# and associate each with _LDORDER_USE to output the relevant
17# ld flags.
18# Due to the nature of make, the result will be in the reverse order
19# that we want to feed to ld.
20# So we need to reverse it before use.
21
22.if !target(_LDORDER_USE)
23# does caller want to use ldorder?
24# yes for prog, normally no for lib
25.if ${.ALLTARGETS:Mldorder} != ""
26_ldorder_use:
27.endif
28
29# define this if we need a barrier between local and external libs
30# see below
31LDORDER_EXTERN_BARRIER ?= .ldorder-extern-barrier
32
33.-include <local.ldorder.mk>
34
35# convert /path/to/libfoo.a into _{LIBFOO}
36LDORDER_INC_FILTER += S,+,PLUS,g S,.so$$,,g
37LDORDER_LIBS_FILTER += O:u
38LDORDER_INC ?= ldorder.inc
39# for meta mode
40REFERENCE_FILE ?= :
41
42_LDORDER_USE: .ldorder-rm .USE .NOTMAIN
43	@echo depends: ${.ALLSRC:M.ldorder-lib*} > /dev/null
44	@echo ${LDADD_${.TARGET:T:S,.ldorder-,,}:U${.TARGET:T:S/.ldorder-lib/-l/}} >> .ldorder
45	@${META_COOKIE_TOUCH}
46
47# we need to truncate our working file
48.ldorder-rm: .NOTMAIN
49	@rm -f .ldorder ldorder-*
50	@${.ALLSRC:O:u:@f@${REFERENCE_FILE} < $f;@}
51	@${META_COOKIE_TOUCH}
52
53# make sure this exists
54.ldorder:	.NOTMAIN
55
56# and finally we need to reverse the order of content
57ldorder: .ldorder .NOTMAIN
58	@{ test ! -s .ldorder || cat -n .ldorder | sort -rn | \
59	sed '/ldorder-/d;s,^[[:space:]0-9]*,,'; } > ${.TARGET}
60
61# Initially we hook contents of DPLIBS and DPADD into our graph
62LDORDER_LIBS ?= ${DPLIBS} ${DPADD:M*/lib*} ${__dpadd_libs}
63# we need to remember this
64_LDORDER_LIBS := ${LDORDER_LIBS:${LDORDER_LIBS_FILTER:ts:}}
65
66.if empty(_LDORDER_LIBS)
67# don't use stale ldorder
68LDADD_LDORDER =
69.else
70# this is how you use it
71LDADD_LDORDER ?= `cat ldorder`
72.endif
73
74# for debug below
75_ldorder = ${RELDIR}.${TARGET_SPEC}
76
77# we make have some libs that exist outside of $SB
78# and want to insert a barrier
79.if target(${LDORDER_EXTERN_BARRIER})
80# eg. in local.ldorder.mk
81# ${LDORDER_EXTERN_BARRIER}:
82#	@test -z "${extern_ldorders}" || \
83#	echo -Wl,-Bdynamic >> .ldorder
84#
85# feel free to put more suitable version in local.ldorder.mk if needed
86# we do *not* count host libs in extern_ldorders
87extern_ldorders ?= ${__dpadd_libs:tA:N/lib*:N/usr/lib*:N${SB}/*:N${SB_OBJROOT:tA}*:T:${LDORDER_LIBS_FILTER:ts:}:R:C/\.so.*//:S,^,.ldorder-,:N.ldorder-}
88sb_ldorders ?= ${.ALLTARGETS:M.ldorder-*:N${LDORDER_EXTERN_BARRIER}:N.ldorder-rm:${extern_ldorders:${M_ListToSkip}}:N.ldorder-}
89
90# finally in Makefile after include of *.mk put
91# .ldorder ${sb_ldorders}: ${LDORDER_EXTERN_BARRIER}
92# ${LDORDER_EXTERN_BARRIER}: ${extern_ldorders}
93.endif
94
95.endif				# !target(_LDORDER_USE)
96
97.if !empty(LDORDER_LIBS) && target(_ldorder_use)
98# canonicalize - these are just tokens anyway
99LDORDER_LIBS := ${LDORDER_LIBS:${LDORDER_LIBS_FILTER:ts:}:R:C/\.so.*//}
100_ldorders := ${LDORDER_LIBS:T:Mlib*:S,^,.ldorder-,}
101
102.for t in ${_ldorders}
103.if !target($t)
104$t: _LDORDER_USE
105.endif
106.endfor
107
108# and this makes it all happen
109.ldorder: ${_ldorders}
110
111# this is how we get the dependencies
112.if ${.INCLUDEDFROMFILE:M*.${LDORDER_INC}} != ""
113_ldorder := .ldorder-${.INCLUDEDFROMFILE:S/.${LDORDER_INC}//}
114${_ldorder}: ${_ldorders}
115.ldorder-rm: ${.INCLUDEDFROMDIR}/${.INCLUDEDFROMFILE}
116.endif
117
118# set DEBUG_LDORDER to pattern[s] that match the dirs of interest
119.if ${DEBUG_LDORDER:Uno:@x@${RELDIR:M$x}@} != ""
120.info ${_ldorder}: ${_ldorders}
121.endif
122
123# now try to find more ...
124# each *.${LDORDER_INC} should set LDORDER_LIBS to what it needs
125# it can also add to CFLAGS etc.
126.for __inc in ${LDORDER_LIBS:S,$,.${LDORDER_INC},}
127.if !target(__${__inc}__)
128__${__inc}__: .NOTMAIN
129# make sure this is reset
130LDORDER_LIBS =
131_ldorders =
132.-include <${__inc}>
133.endif
134.endfor
135
136.endif				# !empty(LDORDER_LIBS)
137
138.ifdef LIB
139# you can make this depend on files (must match *ldorder*)
140# to add extra content - like CFLAGS
141libLDORDER_INC = lib${LIB}.${LDORDER_INC}
142.if !commands(${libLDORDER_INC})
143.if target(ldorder-header)
144${libLDORDER_INC}: ldorder-header
145.endif
146${libLDORDER_INC}:
147	@(cat /dev/null ${.ALLSRC:M*ldorder*}; \
148	echo 'LDORDER_LIBS= ${_LDORDER_LIBS:T:R:${LDORDER_INC_FILTER:ts:}:tu:C,.*,_{&},:N_{}}'; \
149	echo; echo '.include <ldorder.mk>' ) | sed 's,_{,$${,g' > ${.TARGET}
150.endif
151.endif
152