[llvm] [runtimes] Make later runtimes wait for earlier header targets (PR #192814)

Yaxun Liu via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 18 16:20:27 PDT 2026


https://github.com/yxsamliu created https://github.com/llvm/llvm-project/pull/192814

`LLVM_ENABLE_RUNTIMES` is already ordered so that dependencies come first
(e.g. `libc;compiler-rt;openmp`). The problem is that this order only
controls CMake configuration. Once ninja starts, the targets from sibling
runtimes are independent unless we add explicit dependencies.

A real example is the amdgcn runtimes build:

  - `libc` generates GPU headers such as `<string.h>` and `<limits.h>`
  - `compiler-rt` includes those headers when building
    `libclang_rt.profile-amdgcn`

Today, even though `libc` appears before `compiler-rt`, ninja can still
start compiling `compiler-rt/lib/profile/InstrProfiling.c` before
`libc` finishes generating its headers. That makes the build fail.

Fix this in `runtimes/CMakeLists.txt`, where the runtime order is known.
After each `add_subdirectory`, recursively collect the targets created by
that runtime and make them depend on prerequisite targets exported by
earlier runtimes. For now we seed the list with LLVM-libc's
`libc-headers` target, so all later runtimes wait for generated libc
headers before they build.

This is generic: later runtimes do not need to know the internal target
names inside libc, and future runtimes can export similar prerequisite
targets the same way.

No functional change when `libc` is not part of the runtimes build.

Related: PR #177665.



>From 84d68502bef4c189bc5b051eea51ecfe4f520d86 Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Fri, 17 Apr 2026 13:31:08 -0400
Subject: [PATCH] [runtimes] Make later runtimes wait for earlier header
 targets

`LLVM_ENABLE_RUNTIMES` is already ordered so that dependencies come first
(e.g. `libc;compiler-rt;openmp`). The problem is that this order only
controls CMake configuration. Once ninja starts, the targets from sibling
runtimes are independent unless we add explicit dependencies.

A real example is the amdgcn runtimes build:

  - `libc` generates GPU headers such as `<string.h>` and `<limits.h>`
  - `compiler-rt` includes those headers when building
    `libclang_rt.profile-amdgcn`

Today, even though `libc` appears before `compiler-rt`, ninja can still
start compiling `compiler-rt/lib/profile/InstrProfiling.c` before
`libc` finishes generating its headers. That makes the build fail.

Fix this in `runtimes/CMakeLists.txt`, where the runtime order is known.
After each `add_subdirectory`, recursively collect the targets created by
that runtime and make them depend on prerequisite targets exported by
earlier runtimes. For now we seed the list with LLVM-libc's
`libc-headers` target, so all later runtimes wait for generated libc
headers before they build.

This is generic: later runtimes do not need to know the internal target
names inside libc, and future runtimes can export similar prerequisite
targets the same way.

No functional change when `libc` is not part of the runtimes build.

Related: PR #177665.
---
 runtimes/CMakeLists.txt | 44 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/runtimes/CMakeLists.txt b/runtimes/CMakeLists.txt
index eba41b8b10ac4..fb14f4f0da8cd 100644
--- a/runtimes/CMakeLists.txt
+++ b/runtimes/CMakeLists.txt
@@ -364,12 +364,56 @@ if(LLVM_INCLUDE_TESTS)
   umbrella_lit_testsuite_begin(check-runtimes)
 endif()
 
+# Return every target created under <dir>, including subdirectories.
+# We use this to say "all compiler-rt targets depend on libc-headers"
+# instead of naming every compiler-rt target by hand.
+function(_llvm_runtimes_collect_targets dir out_var)
+  set(_collected "")
+  get_directory_property(_targets DIRECTORY "${dir}" BUILDSYSTEM_TARGETS)
+  list(APPEND _collected ${_targets})
+  get_directory_property(_subs DIRECTORY "${dir}" SUBDIRECTORIES)
+  foreach(_sub ${_subs})
+    _llvm_runtimes_collect_targets("${_sub}" _sub_targets)
+    list(APPEND _collected ${_sub_targets})
+  endforeach()
+  set(${out_var} ${_collected} PARENT_SCOPE)
+endfunction()
+
+# "Things later runtimes should wait for".
+# Example: if the runtime list is `libc;compiler-rt;openmp`, then after we
+# add libc we put `libc-headers` in this list. When we later add compiler-rt
+# and openmp, we make all of their targets depend on `libc-headers`.
+# This turns the runtime list order into a real ninja dependency, not just
+# a configuration-time order.
+set(_llvm_runtimes_cross_deps "")
+
 # We do this in two loops so that HAVE_* is set for each runtime before the
 # other runtimes are added.
 foreach(entry ${runtimes})
   get_filename_component(projName ${entry} NAME)
 
   add_subdirectory(${entry} ${projName})
+
+  # Make every target from this runtime wait for the prerequisites gathered
+  # from earlier runtimes.
+  # Example: after adding `libc`, the list contains `libc-headers`, so when
+  # we add `compiler-rt` we attach `add_dependencies(<each compiler-rt target>
+  # libc-headers)`.
+  # Skip the walk when the list is empty.
+  if(_llvm_runtimes_cross_deps)
+    _llvm_runtimes_collect_targets("${entry}" _new_targets)
+    foreach(_tgt ${_new_targets})
+      if(TARGET ${_tgt})
+        add_dependencies(${_tgt} ${_llvm_runtimes_cross_deps})
+      endif()
+    endforeach()
+  endif()
+
+  # Seed the list for later runtimes.
+  # Today only LLVM-libc publishes such a target: `libc-headers`.
+  if(projName STREQUAL "libc" AND TARGET libc-headers)
+    list(APPEND _llvm_runtimes_cross_deps libc-headers)
+  endif()
 endforeach()
 
 # Define runtimes-test-depends so the parent build can use it unconditionally.



More information about the llvm-commits mailing list