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