xref: /freebsd/contrib/bmake/mk/rust.mk (revision c60f6422ffae3ea85e7b10bad950ad27c463af18)
1# $Id: rust.mk,v 1.38 2025/08/09 22:42:24 sjg Exp $
2#
3#	@(#) Copyright (c) 2024, 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##
12# This makefile is used when a build includes one or more Rust projects.
13#
14# We first include local.rust.mk to allow for customization.
15# You can get very fancy - the logic/functionality here is minimal but
16# can be extended via local.rust.mk
17#
18# If RUST_PROJECT_DIR (where we find Cargo.toml) is not set, we will
19# make it ${.CURDIR:C,/src.*,,} actually we use
20# ${SRCTOP}/${RELDIR:C,/src.*,,} to ensure we don't confuse ${SRCTOP}
21# with ${RUST_PROJECT_DIR}/src.
22#
23# If ${.OBJDIR} is not ${.CURDIR} we will default CARGO_TARGET_DIR
24# to ${.OBJDIR}.
25#
26# First, if ${.CURDIR} is a subdir of ${RUST_PROJECT_DIR} (will happen
27# if an Emacs user does 'M-x compile' while visiting a src file) we
28# will need to adjust ${.OBJDIR} (and hence CARGO_TARGET_DIR).
29#
30# We assume that RUST_CARGO will be used to build Rust projects,
31# so we default RUST_CARGO_PROJECT_DIR to ${RUST_PROJECT_DIR} and
32# provide a _CARGO_USE that we automatically associate with
33# targets named 'cargo.*' the default is 'cargo.build'.
34#
35# _CARGO_USE will chdir to ${RUST_CARGO_PROJECT_DIR} and run
36# ${RUST_CARGO} with ENV, FLAGS and ARGS variables derived from
37# ${.TARGET:E:tu} so in the case of 'cargo.build' we get:
38# RUST_CARGO_BUILD_ENV, RUST_CARGO_BUILD_FLAGS and RUST_CARGO_BUILD_ARGS
39#
40# _CARGO_USE will "just work" for additional targets like
41# 'cargo.test', 'cargo.clippy', ... which will run '${RUST_CARGO} test',
42# '${RUST_CARGO} clippy' etc.
43#
44# If MK_META_MODE is "yes" 'cargo.build' will touch ${.TARGET}
45# so the default make rules will not consider it always out-of-date.
46# In META MODE, 'bmake' will know if anything changed that should
47# cause the target to be re-built.
48#
49# If MK_STAGING_RUST is "yes" we will stage the binary we
50# built to a suitable location under ${STAGE_OBJTOP}.
51#
52
53all:
54.MAIN: all
55
56# allow for customization
57.-include <local.rust.mk>
58
59RUST_CARGO ?= cargo
60RUSTC ?= rustc
61.if ${.CURDIR} == ${SRCTOP}
62RELDIR ?= .
63.else
64RELDIR ?= ${.CURDIR:S,${SRCTOP}/,,}
65.endif
66.if empty(RUST_PROJECT_DIR)
67# we want this set correctly from anywhere within
68# using RELDIR avoids confusing ${SRCTOP} with ${RUST_PROJECT_DIR}/src
69RUST_PROJECT_DIR := ${SRCTOP}/${RELDIR:C,/src.*,,}
70.if ${RUST_PROJECT_DIR:T:Nsrc:N.} == ""
71RUST_PROJECT_DIR := ${RUST_PROJECT_DIR:H}
72.endif
73.endif
74
75.if ${.OBJDIR} != ${.CURDIR}
76.if ${.CURDIR:M${RUST_PROJECT_DIR}/*} != ""
77# Our .CURDIR is below RUST_PROJECT_DIR and thus our
78# .OBJDIR is likely not what we want either.
79# This can easily happen if in Emacs we do 'M-x compile' while
80# visiting a src file.
81# It is easily fixed.
82__objdir := ${.OBJDIR:S,${.CURDIR:S,${RUST_PROJECT_DIR},,},,}
83.OBJDIR: ${__objdir}
84.endif
85# tell cargo where to drop build artifacts
86CARGO_TARGET_DIR ?= ${.OBJDIR}
87.if !empty(OBJROOT) && exists(${OBJROOT})
88CARGO_HOME_RELDIR ?= rust/cargo_home
89CARGO_HOME ?= ${OBJROOT}/common/${RUST_CARGO_HOME_RELDIR}
90.endif
91.elif ${.CURDIR} != ${RUST_PROJECT_DIR}
92.OBJDIR: ${RUST_PROJECT_DIR}
93.endif
94CARGO_TARGET_DIR ?= target
95
96.if ${MK_DIRDEPS_BUILD:Uno} == "no" || ${.MAKE.LEVEL} > 0
97.export CARGO_HOME CARGO_TARGET_DIR RUST_PROJECT_DIR RUSTC
98
99all: cargo.build
100
101.if empty(RUST_PROJECT_FILES)
102RUST_PROJECT_FILES != find ${RUST_PROJECT_DIR} -type f \( \
103	-name '*.rs' -o \
104	-name Cargo.lock -o \
105	-name Cargo.toml \) | sort
106.endif
107RUST_CARGO_BUILD_DEPS += ${RUST_PROJECT_FILES:U}
108.endif
109
110RUST_CARGO_PROJECT_DIR ?= ${RUST_PROJECT_DIR}
111
112.if ${RUSTC:M/*}
113# make sure we find all the other toolchain bits in the same place
114RUST_CARGO_ENV += PATH=${RUSTC:H}:${PATH}
115
116# cargo clippy needs extra help finding the sysroot
117# https://github.com/rust-lang/rust-clippy/issues/3523
118RUST_CARGO_CLIPPY_ENV += RUSTC_SYSROOT=${${RUSTC} --print sysroot:L:sh}
119.endif
120
121.if ${LDFLAGS:U:M-[BL]*} != ""
122# we may need to tell rustc where to find the native libs needed
123# rustc documents a space after -L so put it back
124RUST_LDFLAGS := ${LDFLAGS:C/(-[BL]) /\1/gW:M-[BL]*:S/-L/& /:S/-B/-C link-arg=&/}
125.endif
126.if !empty(RUST_LDFLAGS)
127RUSTFLAGS += ${RUST_LDFLAGS}
128.endif
129.if !empty(RUSTFLAGS)
130RUST_CARGO_BUILD_ENV += RUSTFLAGS="${RUSTFLAGS}"
131.endif
132
133_CARGO_USE:	.USEBEFORE
134	@(cd ${RUST_CARGO_PROJECT_DIR} && ${RUST_CARGO_ENV} \
135	${RUST_CARGO_${.TARGET:E:tu}_ENV} \
136	${RUST_CARGO} ${RUST_CARGO_${.TARGET:E:tu}_FLAGS:U${RUST_CARGO_FLAGS}} \
137	${.TARGET:E} ${RUST_CARGO_${.TARGET:E:tu}_ARGS})
138
139RUST_CARGO_TARGETS += cargo.build
140cargo.build: ${RUST_CARGO_BUILD_DEPS}
141.if ${.OBJDIR} != ${RUST_PROJECT_DIR}
142	test ! -s Cargo.lock || cp -p Cargo.lock ${RUST_CARGO_PROJECT_DIR}
143.endif
144	@${META_COOKIE_TOUCH}
145
146# handle cargo.{run,test,...}
147RUST_CARGO_TARGETS += ${.TARGETS:Mcargo.*}
148${RUST_CARGO_TARGETS:O:u}: _CARGO_USE
149
150.if ${MK_DEBUG_RUST:Uno} == "no" && \
151	${DEBUG_RUST_DIRS:Unone:@x@${RELDIR:M$x}@} == ""
152RUST_CARGO_BUILD_ARGS += --release
153.endif
154
155.if ${RUST_CARGO_BUILD_ARGS:U:M--release} != ""
156RUST_CARGO_TARGET = release
157.else
158RUST_CARGO_TARGET = debug
159.endif
160
161# do we want cargo.build to depend on cargo.fmt --check ?
162# if user did make cargo.fmt the target would exist by now
163.if ${MK_RUST_CARGO_FMT_CHECK:Uno} == "yes" && !target(cargo.fmt)
164RUST_CARGO_FMT_CHECK_ARGS ?= --check
165RUST_CARGO_FMT_ARGS += ${RUST_CARGO_FMT_CHECK_ARGS}
166cargo.fmt: _CARGO_USE
167cargo.build: cargo.fmt
168.endif
169
170# useful? defaults
171RUST_CARGO_CLIPPY_ARGS ?= -- -D warnings --no-deps
172
173# do we want cargo.clippy to be run after cargo.build?
174.if ${MK_RUST_CARGO_CLIPPY:Uno} == "yes" && !target(cargo.clippy)
175cargo.clippy: _CARGO_USE
176cargo.clippy: cargo.build
177all: cargo.clippy
178.endif
179
180.if !defined(RUST_LIBS)
181RUST_PROGS ?= ${RUST_PROJECT_DIR:T}
182.endif
183.if !empty(RUST_PROGS)
184BINDIR ?= ${prefix}/bin
185# there could be a target triple involved
186RUST_CARGO_TARGET_DIR ?= ${CARGO_TARGET_DIR}
187RUST_CARGO_OUTPUT_DIR ?= ${RUST_CARGO_TARGET_DIR}/${RUST_CARGO_TARGET}
188
189RUST_CARGO_BUILD_OUTPUT_LIST := ${RUST_PROGS:S,^,${RUST_CARGO_OUTPUT_DIR}/,}
190
191${RUST_CARGO_BUILD_OUTPUT_LIST}: cargo.build
192.endif
193
194# for late customizations
195.-include <local.rust.build.mk>
196