Lots of build system cleanup and documentation

This splits mk/stageN.mk into host.mk and target.mk and makes
the build rules somewhat simpler - there's no more building from stageN
into stageN+1; instead we always build from stageN(host) to
stageN(target) then promote from stageN(target) to stageN+1(host).

Add a big honkin explaination right at the top of Makefile.in
This commit is contained in:
Brian Anderson 2011-10-01 20:12:08 -07:00
parent 9563c17d78
commit 6e654564db
10 changed files with 235 additions and 167 deletions

View File

@ -1,3 +1,47 @@
# An explanation of how the build is structured:
#
# There are multiple build stages (0-3) needed to verify that the
# compiler is properly self-hosting. Each stage is divided between
# 'host' artifacts and 'target' artifacts, where the stageN host
# compiler builds artifacts for 1 or more stageN target architectures.
# Once the stageN target compiler has been built for the host
# architecture it is promoted (copied) to a stageN+1 host artifact.
#
# The stage3 host compiler is a compiler that successfully builds
# itself and should (in theory) be bitwise identical to the stage2
# host compiler. The process is bootstrapped using a stage0 host
# compiler downloaded from a previous snapshot.
#
# At no time should stageN artifacts be interacting with artifacts
# from other stages. For consistency, we use the 'promotion' logic
# for all artifacts, even those that don't make sense on non-host
# architectures.
#
# The directory layout for a stage is intended to match the layout
# of the installed compiler, and looks like the following:
#
# stageN - this is the system root, corresponding to, e.g. /usr
# bin - binaries compiled for the host
# lib - libraries used by the host compiler
# rustc - rustc's own place to organize libraries
# $(target) - target-specific artifacts
# bin - binaries for target architectures
# lib - libraries for target architectures
#
# A note about host libraries:
#
# The only libraries that get promoted to stageN/lib are those needed
# by rustc. In general, rustc programs, even those compiled for the
# host architecture will use libraries from the target
# directories. This gives rust some freedom to experiment with how
# libraries are managed and versioned without polluting the common
# areas of the filesystem.
#
# General rust binaries may stil live in the host bin directory; they
# will just link against the libraries in the target lib directory.
#
# Admittedly this is a little convoluted.
######################################################################
# Residual auto-configuration
######################################################################
@ -142,12 +186,16 @@ COMPILER_INPUTS := $(wildcard $(addprefix $(S)src/comp/, \
# Exports for sub-utilities
######################################################################
# Note that any variable that re-configure should pick up needs to be
# exported
export CFG_SRC_DIR
export CFG_BUILD_DIR
export CFG_VERSION
export CFG_HOST_TRIPLE
export CFG_LLVM_ROOT
export CFG_ENABLE_MINGW_CROSS
export CFG_PREFIX
######################################################################
# Subprograms
@ -179,33 +227,34 @@ TARGET_HOST_ROOT$(1) = $$(TARGET_ROOT$(1)$$(CFG_HOST_TRIPLE))
TARGET_HOST_BIN$(1) = $$(TARGET_BIN$(1)$$(CFG_HOST_TRIPLE))
TARGET_HOST_LIB$(1) = $$(TARGET_LIB$(1)$$(CFG_HOST_TRIPLE))
# The name of the standard library used by rustc
ifdef CFG_DISABLE_SHAREDSTD
HOST_STDLIB_DEFAULT$(1) = $$(HOST_LIB$(1))/libstd.rlib
TARGET_STDLIB_DEFAULT$(1)$(2) = $$(TARGET_LIB$(1)$(2))/libstd.rlib
else
HOST_STDLIB_DEFAULT$(1) = $$(HOST_LIB$(1))/$(CFG_STDLIB)
TARGET_STDLIB_DEFAULT$(1)$(2) = $$(TARGET_LIB$(1)$(2))/$(CFG_STDLIB)
endif
ifdef CFG_DISABLE_SHAREDSTD
SREQ$(1)$(2) = $$(HOST_BIN$(1))/rustc$(X) \
$$(HOST_LIB$(1))/$$(CFG_RUNTIME) \
$$(HOST_STDLIB_DEFAULT$(1)) \
$$(HOST_LIB$(1))/$$(CFG_RUSTLLVM) \
$$(TARGET_LIB$(1)$(2))/$$(CFG_RUNTIME) \
$$(TARGET_LIB$(1)$(2))/$$(CFG_STDLIB) \
$$(TARGET_LIB$(1)$(2))/intrinsics.bc \
$$(TARGET_LIB$(1)$(2))/main.o \
$$(MKFILES)
else
SREQ$(1)$(2) = $$(HOST_BIN$(1))/rustc$(X) \
$$(HOST_LIB$(1))/$$(CFG_RUNTIME) \
$$(HOST_STDLIB_DEFAULT$(1)) \
$$(HOST_LIB$(1))/$$(CFG_RUSTLLVM) \
$$(TARGET_LIB$(1)$(2))/$$(CFG_RUNTIME) \
$$(TARGET_LIB$(1)$(2))/$$(CFG_STDLIB) \
$$(TARGET_LIB$(1)$(2))/intrinsics.bc \
$$(TARGET_LIB$(1)$(2))/main.o \
$$(MKFILES)
endif
# Preqrequisites for using the stageN compiler
HOST_SREQ$(1) = \
$$(HOST_BIN$(1))/rustc$$(X) \
$$(HOST_LIB$(1))/$$(CFG_RUNTIME) \
$$(HOST_LIB$(1))/$$(CFG_RUSTLLVM) \
$$(HOST_STDLIB_DEFAULT$(1)) \
$$(MKFILES)
# Prerequisites for using the stageN compiler to build target artifacts
TARGET_SREQ$(1)$(2) = \
$$(HOST_SREQ$(1)) \
$$(TARGET_LIB$(1)$(2))/$$(CFG_RUNTIME) \
$$(TARGET_LIB$(1)$(2))/intrinsics.bc \
$$(TARGET_LIB$(1)$(2))/main.o
# Prerequisites for complete stageN targets
SREQ$(1)$(2) = \
$$(TARGET_SREQ$(1)$(2)) \
$$(TARGET_LIB$(1)$(2))/$$(CFG_STDLIB)
ifeq ($(1),0)
# Don't run the the stage0 compiler under valgrind - that ship has sailed
@ -241,16 +290,14 @@ CFG_INFO := $(info cfg: *** compiler is in snapshot transition ***)
CFG_INFO := $(info cfg: *** stage2 and later will not be built ***)
CFG_INFO := $(info cfg:)
FUZZ := $(HOST_BIN1)/fuzzer$(X)
all: $(SREQ1$(CFG_HOST_TRIPLE)) $(GENERATED) $(DOCS)
all: $(SREQ0$(CFG_HOST_TRIPLE)) $(SREQ1$(CFG_HOST_TRIPLE)) \
$(GENERATED) $(DOCS) $(FUZZ)
else
ALL_SREQS = $(foreach target,$(CFG_TARGET_TRIPLES), \
$(SREQ0$(target)) $(SREQ1$(target)) $(SREQ2$(target)) $(SREQ3$(target)))
FUZZ := $(HOST_BIN3)/fuzzer$(X)
all: $(SREQ3$(CFG_HOST_TRIPLE)) $(GENERATED) $(DOCS) $(FUZZ)
all: $(ALL_SREQS) $(GENERATED) $(DOCS) $(FUZZ)
endif
@ -268,7 +315,8 @@ config.mk: $(S)configure $(S)Makefile.in $(S)src/snapshots.txt
######################################################################
include $(CFG_SRC_DIR)/mk/intrinsics.mk
include $(CFG_SRC_DIR)/mk/stageN.mk
include $(CFG_SRC_DIR)/mk/target.mk
include $(CFG_SRC_DIR)/mk/host.mk
include $(CFG_SRC_DIR)/mk/stage0.mk
include $(CFG_SRC_DIR)/mk/rt.mk
include $(CFG_SRC_DIR)/mk/rustllvm.mk

5
configure vendored
View File

@ -223,6 +223,11 @@ opt mingw-cross 0 "cross-compile for win32 using mingw"
if [ $HELP -eq 1 ]
then
echo ""
echo "Useful environment variables:"
echo ""
printf " %-32s %s\n" "CFG_LLVM_ROOT" "The host LLVM install"
printf " %-32s %s\n" "CFG_PREFIX" "The installation prefix"
echo ""
exit 0
fi

View File

@ -3,7 +3,8 @@
######################################################################
CLEAN_STAGE_RULES = $(foreach target,$(CFG_TARGET_TRIPLES), \
clean0$(target) clean1$(target) clean2$(target) clean3$(target))
clean0$(target) clean1$(target) clean2$(target) clean3$(target)) \
clean0 clean1 clean2 clean3
.PHONY: clean
@ -43,14 +44,20 @@ clean-misc:
define CLEAN_STAGE_N
clean$(1)$(2):
clean$(1):
$(Q)rm -f $$(HOST_BIN$(1))/rustc
$(Q)rm -f $$(HOST_BIN$(1))/fuzzer
$(Q)rm -f $$(HOST_LIB$(1))/$(CFG_RUNTIME)
$(Q)rm -f $$(HOST_LIB$(1))/$(CFG_STDLIB)
$(Q)rm -f $$(HOST_LIB$(1))/$(CFG_RUSTLLVM)
$(Q)rm -f $$(HOST_LIB$(1))/libstd.rlib
clean$(1)$(2):
$(Q)rm -f $$(TARGET_BIN$(1)$(2))/rustc
$(Q)rm -f $$(TARGET_BIN$(1)$(2))/fuzzer
$(Q)rm -f $$(TARGET_LIB$(1)$(2))/$(CFG_RUNTIME)
$(Q)rm -f $$(TARGET_LIB$(1)$(2))/$(CFG_STDLIB)
$(Q)rm -f $$(TARGET_LIB$(1)$(2))/$(CFG_RUSTLLVM)
$(Q)rm -f $$(TARGET_LIB$(1)$(2))/libstd.rlib
$(Q)rm -f $$(TARGET_LIB$(1)$(2))/intrinsics.bc
$(Q)rm -f $$(TARGET_LIB$(1)$(2))/main.o

View File

@ -3,14 +3,23 @@ FUZZER_INPUTS := $(wildcard $(addprefix $(S)src/fuzzer/, *.rs))
define FUZZ_STAGE_N
stage$(2)/bin/fuzzer$$(X): $$(FUZZER_CRATE) $$(FUZZER_INPUTS) \
$$(SREQ$(2)$$(CFG_HOST_TRIPLE)) \
$$(HOST_LIB$(2))/$$(CFG_RUNTIME) \
$$(HOST_LIB$(2))/$$(CFG_RUSTLLVM) \
$$(HOST_LIB$(2))/$$(CFG_STDLIB) \
$$(HOST_LIB$(2))/$$(CFG_LIBRUSTC)
# We only really care about fuzzing on the host arch
$$(TARGET_BIN$(1)$(CFG_HOST_TRIPLE))/fuzzer$$(X): \
$$(FUZZER_CRATE) $$(FUZZER_INPUTS) \
$$(TARGET_SREQ$(1)$(CFG_HOST_TRIPLE)) \
$$(TARGET_LIB$(1)$(CFG_HOST_TRIPLE))/$$(CFG_STDLIB) \
$$(TARGET_LIB$(1)$(CFG_HOST_TRIPLE))/$$(CFG_LIBRUSTC)
@$$(call E, compile_and_link: $$@)
$$(STAGE$(1)) -L $$(HOST_LIB$(2)) -o $$@ $$<
$$(STAGE$(1)) -o $$@ $$<
# Promote the stageN target to stageN+1 host
# FIXME: Shouldn't need to depend on host/librustc.so once
# rpath is working
$$(HOST_BIN$(2))/fuzzer$$(X): \
$$(TARGET_BIN$(1)$(CFG_HOST_TRIPLE))/fuzzer$$(X) \
$$(HOST_LIB$(2))/$$(CFG_LIBRUSTC)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
endef

56
mk/host.mk Normal file
View File

@ -0,0 +1,56 @@
# HOST_STAGE_N template: arg 1 is the N we're promoting *from*, arg 2
# is N+1. Must be invoked to promote target artifacts to host artifacts
# for stage 1-3 (stage0 host artifacts come from the snapshot).
#
# The easiest way to read this template is to assume we're promoting
# stage1 to stage2 and mentally gloss $(1) as 1, $(2) as 2.
define HOST_STAGE_N
# Host libraries and executables (stage$(2)/bin/rustc and its runtime needs)
$$(HOST_BIN$(2))/rustc$$(X): \
$$(TARGET_HOST_BIN$(1))/rustc$$(X) \
$$(HOST_LIB$(2))/$$(CFG_RUNTIME) \
$$(HOST_LIB$(2))/$$(CFG_RUSTLLVM) \
$$(HOST_STDLIB_DEFAULT$(2))
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
# FIXME: The fuzzer depends on this. Remove once it's rpathed to correctly
# find it in the appropriate target directory
$$(HOST_LIB$(2))/$$(CFG_LIBRUSTC): \
$$(TARGET_HOST_LIB$(1))/$$(CFG_LIBRUSTC) \
$$(HOST_LIB$(2))/$$(CFG_RUNTIME) \
$$(HOST_LIB$(2))/$$(CFG_RUSTLLVM) \
$$(HOST_STDLIB_DEFAULT$(2))
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(HOST_LIB$(2))/$$(CFG_RUNTIME): \
$$(TARGET_HOST_LIB$(1))/$$(CFG_RUNTIME)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(HOST_LIB$(2))/$$(CFG_STDLIB): \
$$(TARGET_HOST_LIB$(1))/$$(CFG_STDLIB) \
$$(HOST_LIB$(2))/$$(CFG_RUNTIME)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(HOST_LIB$(2))/libstd.rlib: \
$$(TARGET_HOST_LIB$(1))/libstd.rlib \
$$(HOST_LIB$(2))/$$(CFG_RUNTIME)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(HOST_LIB$(2))/$$(CFG_RUSTLLVM): \
$$(TARGET_HOST_LIB$(1))/$$(CFG_RUSTLLVM)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
endef
$(eval $(call HOST_STAGE_N,0,1))
$(eval $(call HOST_STAGE_N,1,2))
$(eval $(call HOST_STAGE_N,2,3))

View File

@ -1,10 +1,11 @@
ifdef VERBOSE
INSTALL = cp $(1)/$(3) $(2)/$(3)
else
INSTALL = @$(call E, install $(2)/$(3)) && cp $(1)/$(3) $(2)/$(3)
INSTALL = @$(call E, install: $(2)/$(3)) && cp $(1)/$(3) $(2)/$(3)
endif
ISTAGE = 1
# The stage we install from
ISTAGE = 3
PREFIX_ROOT = $(CFG_PREFIX)
PREFIX_BIN = $(PREFIX_ROOT)/bin

View File

@ -1,12 +1,9 @@
snap-stage1: $(HOST_BIN1)/rustc$(X) $(HOST_LIB1)/$(CFG_RUNTIME) \
$(HOST_LIB1)/$(CFG_RUSTLLVM) $(HOST_LIB1)/$(CFG_STDLIB)
snap-stage1: $(HOST_SREQ1)
$(S)src/etc/make-snapshot.py stage1
snap-stage2: $(HOST_BIN2)/rustc$(X) $(HOST_LIB2)/$(CFG_RUNTIME) \
$(HOST_LIB2)/$(CFG_RUSTLLVM) $(HOST_LIB2)/$(CFG_STDLIB)
snap-stage2: $(HOST_SREQ2)
$(S)src/etc/make-snapshot.py stage2
snap-stage3: $(HOST_BIN3)/rustc$(X) $(HOST_LIB3)/$(CFG_RUNTIME) \
$(HOST_LIB3)/$(CFG_RUSTLLVM) $(HOST_LIB3)/$(CFG_STDLIB)
snap-stage3: $(HOST_SREQ3)
$(S)src/etc/make-snapshot.py stage3

View File

@ -1,9 +1,13 @@
$(HOST_BIN0)/rustc$(X): $(S)src/snapshots.txt $(S)src/etc/get-snapshot.py $(MKFILES)
# Extract the snapshot host compiler
$(HOST_BIN0)/rustc$(X): \
$(S)src/snapshots.txt \
$(S)src/etc/get-snapshot.py $(MKFILES)
@$(call E, fetch: $@)
$(Q)$(S)src/etc/get-snapshot.py
$(Q)touch $@
# Host libs will be made in the process of making rustc above.
# Host libs will be extracted by the above rule
$(HOST_LIB0)/$(CFG_RUNTIME): $(HOST_BIN0)/rustc$(X)
$(Q)touch $@
@ -13,10 +17,3 @@ $(HOST_LIB0)/$(CFG_STDLIB): $(HOST_BIN0)/rustc$(X)
$(HOST_LIB0)/$(CFG_RUSTLLVM): $(HOST_BIN0)/rustc$(X)
$(Q)touch $@
# Instantiate template (in stageN.mk) for building
# target libraries.
SREQpre = $(MKFILES)
$(eval $(call TARGET_LIBS,pre,0,$(CFG_HOST_TRIPLE)))

View File

@ -1,113 +0,0 @@
# STAGE_N template: arg 1 is the N we're building *from*, arg 2 is N+1, arg 3
# is the target triple we're building for. You have to invoke this for each
# target triple.
#
# The easiest way to read this template is to assume we're building stage2
# using stage1, and mentally gloss $(1) as 1, $(2) as 2.
#
# TARGET_LIBS is pulled out seperately because we need to specially invoke
# it to build stage0/lib/libstd using stage0/rustc and to use the
# new rustrt in stage0/lib/.
define STAGE_N
# Host libraries and executables (stage$(2)/rustc and its runtime needs)
#
# NB: Due to make not wanting to run the same implicit rules twice on the same
# rule tree (implicit-rule recursion prevention, see "Chains of Implicit
# Rules" in GNU Make manual) we have to re-state the %.o and %.s patterns here
# for different directories, to handle cases where (say) a test relies on a
# compiler that relies on a .o file.
$$(HOST_BIN$(2))/%.o: $$(HOST_BIN$(2))/%.s
@$$(call E, assemble [gcc]: $$@)
$$(Q)gcc $$(CFG_GCCISH_CFLAGS) -o $$@ -c $$<
$$(HOST_LIB$(2))/%.o: $$(HOST_LIB$(2))/%.s
@$$(call E, assemble [gcc]: $$@)
$$(Q)gcc $$(CFG_GCCISH_CFLAGS) -o $$@ -c $$<
$$(HOST_BIN$(2))/rustc$$(X): $$(COMPILER_CRATE) $$(COMPILER_INPUTS) \
$$(HOST_LIB$(2))/$$(CFG_RUNTIME) \
$$(HOST_STDLIB_DEFAULT$(2)) \
$$(HOST_LIB$(2))/$$(CFG_RUSTLLVM) \
$$(SREQ$(1)$(3))
@$$(call E, compile_and_link: $$@)
$$(STAGE$(1)) -L $$(HOST_LIB$(2)) -o $$@ $$<
$$(HOST_LIB$(2))/$$(CFG_LIBRUSTC): \
$$(COMPILER_CRATE) $$(COMPILER_INPUTS) \
$$(SREQ$(2)$(3))
@$$(call E, compile_and_link: $$@)
$$(STAGE$(1)) -L $$(HOST_LIB$(2)) --lib -o $$@ $$<
$$(HOST_LIB$(2))/$$(CFG_RUNTIME): rt/$$(CFG_RUNTIME)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(HOST_LIB$(2))/$$(CFG_STDLIB): $$(TARGET_HOST_LIB$(1))/$$(CFG_STDLIB)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(HOST_LIB$(2))/libstd.rlib: $$(TARGET_HOST_LIB$(1))/libstd.rlib
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(HOST_LIB$(2))/$$(CFG_RUSTLLVM): rustllvm/$$(CFG_RUSTLLVM)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
# Expand out target libraries
$(eval $(call TARGET_LIBS,$(1),$(2),$(3)))
endef
define TARGET_LIBS
# New per-target-arch target libraries; when we've transitioned to
# using these exclusively, you should delete the non-arch-prefixed
# rules above. They're duplicates, redundant.
$$(TARGET_LIB$(2)$(3))/intrinsics.bc: $$(INTRINSICS_BC)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(TARGET_LIB$(2)$(3))/main.o: rt/main.o
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(TARGET_LIB$(2)$(3))/$$(CFG_STDLIB): \
$$(STDLIB_CRATE) $$(STDLIB_INPUTS) \
$$(HOST_BIN$(2))/rustc$$(X) \
$$(HOST_LIB$(2))/$$(CFG_RUNTIME) \
$$(HOST_LIB$(2))/$$(CFG_RUSTLLVM) \
$$(TARGET_LIB$(2)$(3))/intrinsics.bc \
$$(SREQ$(1)$(3))
@$$(call E, compile_and_link: $$@)
$$(STAGE$(2)) --lib -o $$@ $$<
$$(TARGET_LIB$(2)$(3))/libstd.rlib: \
$$(STDLIB_CRATE) $$(STDLIB_INPUTS) \
$$(HOST_BIN$(2))/rustc$$(X) \
$$(HOST_LIB$(2))/$$(CFG_RUNTIME) \
$$(HOST_LIB$(2))/$$(CFG_RUSTLLVM) \
$$(TARGET_LIB$(2)$(3))/intrinsics.bc \
$$(SREQ$(1)$(3))
@$$(call E, compile_and_link: $$@)
$$(STAGE$(2)) --lib --static -o $$@ $$<
$$(TARGET_LIB$(2)$(3))/$$(CFG_RUNTIME): rt/$$(CFG_RUNTIME)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
endef
# Instantiate template for 0->1, 1->2, 2->3 build dirs
$(foreach target,$(CFG_TARGET_TRIPLES), \
$(eval $(call STAGE_N,0,1,$(target))) \
$(eval $(call STAGE_N,1,2,$(target))) \
$(eval $(call STAGE_N,2,3,$(target))))

61
mk/target.mk Normal file
View File

@ -0,0 +1,61 @@
# TARGET_STAGE_N template: This defines how target artifacts are built
# for all stage/target architecture combinations. Argument 1 is the
# stage and arg 2 is the target triple
# FIXME: We don't actually know how to build many of these when host
# and target architectures are not the same
define TARGET_STAGE_N
$$(TARGET_LIB$(1)$(2))/intrinsics.bc: $$(INTRINSICS_BC)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(TARGET_LIB$(1)$(2))/main.o: rt/main.o
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(TARGET_LIB$(1)$(2))/$$(CFG_STDLIB): \
$$(STDLIB_CRATE) $$(STDLIB_INPUTS) \
$$(TARGET_SREQ$(1)$(2))
@$$(call E, compile_and_link: $$@)
$$(STAGE$(1)) --lib -o $$@ $$<
$$(TARGET_LIB$(1)$(2))/libstd.rlib: \
$$(STDLIB_CRATE) $$(STDLIB_INPUTS) \
$$(TARGET_SREQ$(1)$(2))
@$$(call E, compile_and_link: $$@)
$$(STAGE$(1)) --lib --static -o $$@ $$<
$$(TARGET_LIB$(1)$(2))/$$(CFG_RUNTIME): rt/$$(CFG_RUNTIME)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(TARGET_LIB$(1)$(2))/$$(CFG_RUSTLLVM): rustllvm/$$(CFG_RUSTLLVM)
@$$(call E, cp: $$@)
$$(Q)cp $$< $$@
$$(TARGET_BIN$(1)$(2))/rustc$$(X): \
$$(COMPILER_CRATE) $$(COMPILER_INPUTS) \
$$(TARGET_SREQ$(1)$(2)) \
$$(TARGET_LIB$(1)$(2))/$$(CFG_RUSTLLVM) \
$$(TARGET_STDLIB_DEFAULT$(1)$(2))
@$$(call E, compile_and_link: $$@)
$$(STAGE$(1)) -o $$@ $$<
$$(TARGET_LIB$(1)$(2))/$$(CFG_LIBRUSTC): \
$$(COMPILER_CRATE) $$(COMPILER_INPUTS) \
$$(TARGET_SREQ$(1)$(2)) \
$$(TARGET_LIB$(1)$(2))/$$(CFG_RUSTLLVM) \
$$(TARGET_STDLIB_DEFAULT$(1)$(2))
@$$(call E, compile_and_link: $$@)
$$(STAGE$(1)) --lib -o $$@ $$<
endef
# Instantiate template for all stages
$(foreach target,$(CFG_TARGET_TRIPLES), \
$(eval $(call TARGET_STAGE_N,0,$(target))) \
$(eval $(call TARGET_STAGE_N,1,$(target))) \
$(eval $(call TARGET_STAGE_N,2,$(target))) \
$(eval $(call TARGET_STAGE_N,3,$(target))))