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