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