diff --git a/contrib/src/README b/contrib/src/README
index 581a3921954324841a710cb5a10a56ad0e4a4b43..a17d82e47f284a8ed74479f66d03e4fec9acf4b8 100644
--- a/contrib/src/README
+++ b/contrib/src/README
@@ -92,18 +92,12 @@ Dependencies
 If package bar depends on package foo, the special DEPS_bar variable
 should be defined as follow:
 
-	DEPS_bar = foo $(DEPS_foo)
-
-Note that dependency resolution is unfortunately _not_ recursive.
-Therefore $(DEPS_foo) really should be specified explicitly as shown
-above. (In practice, this will not make any difference insofar as there
-are no pure second-level nested dependencies. For instance, libass
-depends on FontConfig, which depends on FreeType, but libass depends
-directly on FreeType anyway.)
-
-Also note that DEPS_bar is set "recursively" with =, rather than
-"immediately" with :=. This is so that $(DEPS_foo) is expanded
-correctly, even if DEPS_foo it is defined after DEPS_bar.
+	DEPS_bar = foo
+
+Dependency resolution is recursive (unlike in previous versions); the
+above will cause bar to also depend on the dependencies of foo.  It is
+illegal to have a dependency cycle, and an error will be emitted if
+one is created.
 
 Implementation note:
 
diff --git a/contrib/src/ffmpeg/rules.mak b/contrib/src/ffmpeg/rules.mak
index b1db5d1311e86fb683dc294882be165ddb832f6a..e86403e2567f1d9bbf5697c81cb52d88ae2807a4 100644
--- a/contrib/src/ffmpeg/rules.mak
+++ b/contrib/src/ffmpeg/rules.mak
@@ -11,7 +11,7 @@ ifeq ($(call need_pkg,"libavutil >= 55.75.100 libavcodec >= 57.106.101 libavform
 PKGS_FOUND += ffmpeg
 endif
 
-DEPS_ffmpeg = iconv zlib x264 vpx opus speex $(DEPS_vpx)
+DEPS_ffmpeg = iconv zlib x264 vpx opus speex
 
 FFMPEGCONF = \
 	--cc="$(CC)" \
diff --git a/contrib/src/flac/rules.mak b/contrib/src/flac/rules.mak
index 7abc69938cddb0a41905e794148c5bb38c40d958..857593aad79323ae8c2986aaa25bd6b184eb050c 100644
--- a/contrib/src/flac/rules.mak
+++ b/contrib/src/flac/rules.mak
@@ -47,7 +47,7 @@ FLACCONF += --disable-asm-optimizations
 endif
 endif
 
-DEPS_flac = ogg $(DEPS_ogg)
+DEPS_flac = ogg
 
 .flac: flac
 	cd $< && $(HOSTVARS) ./configure $(FLACCONF)
diff --git a/contrib/src/gnutls/rules.mak b/contrib/src/gnutls/rules.mak
index 843135814f17836f819058e3b951913594ee57bc..4441fcf9101e18336f78e79402b5317129692b21 100644
--- a/contrib/src/gnutls/rules.mak
+++ b/contrib/src/gnutls/rules.mak
@@ -60,7 +60,7 @@ ifdef HAVE_IOS
 	GNUTLS_CONF += --disable-hardware-acceleration
 endif
 
-DEPS_gnutls = nettle $(DEPS_nettle) iconv $(DEPS_iconv)
+DEPS_gnutls = nettle iconv
 
 
 #Workaround for localtime_r function
diff --git a/contrib/src/main.mak b/contrib/src/main.mak
index 51cbbfbcddb04661ea8300e94d7a0f6ec850d483..2cc639a464379b35608ef3db3bf01d783b94c6cc 100644
--- a/contrib/src/main.mak
+++ b/contrib/src/main.mak
@@ -391,7 +391,10 @@ PKGS_AUTOMATIC := $(filter-out $(PKGS_FOUND),$(PKGS))
 # Apply manual selection (from bootstrap):
 PKGS_MANUAL := $(sort $(PKGS_ENABLE) $(filter-out $(PKGS_DISABLE),$(PKGS_AUTOMATIC)))
 # Resolve dependencies:
-PKGS_DEPS := $(filter-out $(PKGS_FOUND) $(PKGS_MANUAL),$(sort $(foreach p,$(PKGS_MANUAL),$(DEPS_$(p)))))
+dep_on = $(if $(filter $1,$2),\
+  $(error Dependency cycle detected: $(patsubst %,% ->,$2) $(filter $1,$2)),\
+  $(sort $(foreach p,$(filter-out $(PKGS_FOUND),$(1)),$(p) $(call dep_on,$(DEPS_$(p)),$2 $(p)))))
+PKGS_DEPS := $(call dep_on,$(PKGS_MANUAL))
 PKGS := $(sort $(PKGS_MANUAL) $(PKGS_DEPS))
 
 convert-static:
diff --git a/contrib/src/nettle/rules.mak b/contrib/src/nettle/rules.mak
index f811297a66b2879fc3c78f0baf0b6ec6f14fe70b..b924c7f87b4660acf94d8b66dc9a6284837e41c6 100644
--- a/contrib/src/nettle/rules.mak
+++ b/contrib/src/nettle/rules.mak
@@ -19,7 +19,7 @@ nettle: nettle-$(NETTLE_VERSION).tar.gz .sum-nettle
 	$(UPDATE_AUTOCONFIG)
 	$(MOVE)
 
-DEPS_nettle = gmp $(DEPS_gmp)
+DEPS_nettle = gmp
 
 .nettle: nettle
 ifdef HAVE_IOS
diff --git a/contrib/src/opendht/rules.mak b/contrib/src/opendht/rules.mak
index 4443a6fc03c47353aa4a5adeab539fbe6f19acf7..b7fbe8ec832d1f1e7cc5377bddc4bec3ef2b33a3 100644
--- a/contrib/src/opendht/rules.mak
+++ b/contrib/src/opendht/rules.mak
@@ -15,7 +15,7 @@ ifneq ($(call need_pkg,"libargon2"),)
 DEPS_opendht += argon2
 endif
 ifneq ($(call need_pkg,"gnutls >= 3.3.0"),)
-DEPS_opendht += gnutls $(DEPS_gnutls)
+DEPS_opendht += gnutls
 endif
 
 $(TARBALLS)/opendht-$(OPENDHT_VERSION).tar.gz:
diff --git a/contrib/src/secp256k1/rules.mak b/contrib/src/secp256k1/rules.mak
index 7f20f103aa61b030e8a91cc040cd9d226f38e75c..b28f2f412363a97c45b1d1b20a618c5ab01355e0 100644
--- a/contrib/src/secp256k1/rules.mak
+++ b/contrib/src/secp256k1/rules.mak
@@ -7,7 +7,7 @@ PKGS += secp256k1
 ifeq ($(call need_pkg,"libsecp256k1"),)
 PKGS_FOUND += secp256k1
 endif
-DEPS_secp256k1 = gmp $(DEPS_gmp)
+DEPS_secp256k1 = gmp
 
 $(TARBALLS)/secp256k1-$(SECP256K1_VERSION).tar.gz:
 	$(call download,$(SECP256K1_URL))
diff --git a/contrib/src/vorbis/rules.mak b/contrib/src/vorbis/rules.mak
index 1fb2685145adf3d5b62d6dca7608df58d65e7db2..e20cc74939bb51d9bd29e92209afed9c105b7c59 100644
--- a/contrib/src/vorbis/rules.mak
+++ b/contrib/src/vorbis/rules.mak
@@ -36,7 +36,7 @@ endif
 	$(UPDATE_AUTOCONFIG)
 	$(MOVE)
 
-DEPS_vorbis = ogg $(DEPS_ogg)
+DEPS_vorbis = ogg
 
 .vorbis: vorbis
 	$(RECONF) -Im4
@@ -47,7 +47,7 @@ DEPS_vorbis = ogg $(DEPS_ogg)
 .sum-vorbisenc: .sum-vorbis
 	touch $@
 
-DEPS_vorbisenc = vorbis $(DEPS_vorbis)
+DEPS_vorbisenc = vorbis
 
 .vorbisenc:
 	touch $@