[flang-commits] [flang] [llvm] [Flang][CMake] Use fakeflang as dispatcher (PR #209482)

Michael Kruse via flang-commits flang-commits at lists.llvm.org
Wed Jul 15 01:23:52 PDT 2026


https://github.com/Meinersbur updated https://github.com/llvm/llvm-project/pull/209482

>From 67fc19ed52a03cac504522db768cf626ae300473 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Tue, 14 Jul 2026 11:37:11 +0200
Subject: [PATCH 1/2] Use fakeflang as dispatcher

---
 flang/test/Driver/fakeflang.F                 | 28 +++++-
 flang/tools/CMakeLists.txt                    |  8 +-
 flang/tools/fakeflang/CMakeLists.txt          | 21 +----
 .../tools/fakeflang/ensure_flang_exists.cmake | 13 ---
 flang/tools/fakeflang/fakeflang.cpp           | 88 ++++++++++++++-----
 flang/tools/flang-driver/CMakeLists.txt       | 18 ----
 .../modules/LLVMExternalProjectUtils.cmake    |  8 +-
 7 files changed, 104 insertions(+), 80 deletions(-)
 delete mode 100644 flang/tools/fakeflang/ensure_flang_exists.cmake

diff --git a/flang/test/Driver/fakeflang.F b/flang/test/Driver/fakeflang.F
index 526f535769049..1c064b16c323c 100644
--- a/flang/test/Driver/fakeflang.F
+++ b/flang/test/Driver/fakeflang.F
@@ -2,20 +2,38 @@
 
 ! RUN: rm -rf %t
 ! RUN: mkdir -p %t
+! RUN: cp %s %t/CMakeFortranCompilerId.F
+! RUN: cp %s %t/compiletest.F
 ! RUN: cd %t
 
-! RUN: fakeflang -v -c -cpp -E --target=x86_64-unknown-linux-gnu %s
+! RUN: fakeflang -v -c -cpp -E --target=x86_64-unknown-linux-gnu CMakeFortranCompilerId.F 2>&1 | FileCheck %s --check-prefix=FAKEWARNING
 ! RUN: FileCheck %s --input-file=a.out --check-prefixes=CHECK,GNU
 ! RUN: rm a.out
 
-! RUN: fakeflang -v -c -cpp -E --target=aarch64-linux-gnu %s
+! RUN: fakeflang -v -c -cpp -E --target=aarch64-linux-gnu CMakeFortranCompilerId.F 2>&1 | FileCheck %s --check-prefix=FAKEWARNING
 ! RUN: FileCheck %s --input-file=a.out --check-prefixes=CHECK,GNU
 ! RUN: rm a.out
 
-! RUN: fakeflang -v -c -cpp -E --target=x86_64-pc-windows-msvc %s
+! RUN: fakeflang -v -c -cpp -E --target=x86_64-pc-windows-msvc CMakeFortranCompilerId.F 2>&1 | FileCheck %s --check-prefix=FAKEWARNING
 ! RUN: FileCheck %s --input-file=a.out --check-prefixes=CHECK,MSVC --match-full-lines
 ! RUN: rm a.out
 
+! FLANG_BOOTSTRAP_PROBE forces probe mode
+! RUN: env FLANG_BOOTSTRAP_PROBE=1 fakeflang -v -c --target=x86_64-unknown-linux-gnu compiletest.F 2>&1 | FileCheck %s --check-prefix=FAKEWARNING
+! RUN: FileCheck %s --input-file=a.out --check-prefixes=CHECK,GNU
+! RUN: rm a.out
+
+! When not probing, compile everything normally
+! RUN: fakeflang -c compiletest.F -o compiletest.F.o 2>&1 | FileCheck %s --check-prefix=NOFAKE --allow-empty
+! RUN: test -e compiletest.F.o
+
+
+! FAKEWARNING: fakeflang: warning: Emulating flang for compiler probing
+! NOFAKE-NOT: fakeflang
+
+#ifdef __fakeflang_probe__
+! CHECK: dispatcher=1
+dispatcher=__fakeflang_probe__
 
 ! CHECK: CMAKE_Fortran_COMPILER_ID=1
 CMAKE_Fortran_COMPILER_ID=__flang__
@@ -34,3 +52,7 @@
 ! GNU: CMAKE_Fortran_PLATFORM_ID=_WIN32
 ! MSVC: CMAKE_Fortran_PLATFORM_ID=1
 CMAKE_Fortran_PLATFORM_ID=_WIN32
+#else
+      program helloworld
+      end program helloworld
+#endif
diff --git a/flang/tools/CMakeLists.txt b/flang/tools/CMakeLists.txt
index 235a660221820..975eaa29343fc 100644
--- a/flang/tools/CMakeLists.txt
+++ b/flang/tools/CMakeLists.txt
@@ -8,12 +8,8 @@
 
 add_subdirectory(bbc)
 add_subdirectory(flang-driver)
-if (FLANG_STANDALONE_BUILD)
-  # Mock flang only needed in bootstrapping builds;
-  # fall back to full flang otherwise
-  add_custom_target(flang-lazy)
-  add_dependencies(flang-lazy flang)
-else ()
+if (NOT FLANG_STANDALONE_BUILD)
+  # Mock flang only needed in bootstrapping builds
   add_subdirectory(fakeflang)
 endif ()
 add_subdirectory(tco)
diff --git a/flang/tools/fakeflang/CMakeLists.txt b/flang/tools/fakeflang/CMakeLists.txt
index 3503735729494..b760c6efed65c 100644
--- a/flang/tools/fakeflang/CMakeLists.txt
+++ b/flang/tools/fakeflang/CMakeLists.txt
@@ -5,23 +5,8 @@ set(FLANG_BUILD_TOOLS OFF) # Do not install
 add_flang_tool(fakeflang
   fakeflang.cpp
 )
-target_compile_definitions(fakeflang PRIVATE "CMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}")
 
-# Using $<TARGET_FILE:file> in an add_custom_command would automatically
-# add a dependency.
-set(_flang_exe "${LLVM_RUNTIME_OUTPUT_INTDIR}/flang${CMAKE_EXECUTABLE_SUFFIX}")
-
-# Create ${_flang_exe} using fakeflang if it does not exist yet.
-# Cannot use an OUTPUT/BYPRODUCT as it would make ninja complain about multiple
-# rules to create ${_flang_exe} (the other being being the full flang).
-# Automatically execute it after fakeflang has been built instead.
-add_custom_command(POST_BUILD
-  TARGET fakeflang
-  COMMAND "${CMAKE_COMMAND}" "-DINPUT_FILE=$<TARGET_FILE:fakeflang>" "-DOUTPUT_FILE=${_flang_exe}" "-P" "${CMAKE_CURRENT_SOURCE_DIR}/ensure_flang_exists.cmake"
-)
-
-# Phony target that ensures bin/flang exists; if the full flang has not been
-# compiled yet, use a mock flang driver only for passing CMake's
-# CMake_Fortran_COMPILER tests.
+# Phony target that ensures fakeflang can be called to emulate compiler probes.
+# Clang is required to handle the preprocessing.
 add_custom_target(flang-lazy)
-add_dependencies(flang-lazy fakeflang)
+add_dependencies(flang-lazy fakeflang clang)
diff --git a/flang/tools/fakeflang/ensure_flang_exists.cmake b/flang/tools/fakeflang/ensure_flang_exists.cmake
deleted file mode 100644
index 7fef42de6bb0c..0000000000000
--- a/flang/tools/fakeflang/ensure_flang_exists.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copy INPUT_FILE to OUTPUT_FILE, but only
-# if OUTPUT_FILE does not already exist
-
-if (NOT EXISTS "${OUTPUT_FILE}")
-  cmake_path(GET OUTPUT_FILE PARENT_PATH OUTPUT_DIR)
-  file(MAKE_DIRECTORY "${OUTPUT_DIR}")
-
-  # This could also be the symlink but its small size is not worth the effort
-  # handling platform differences.
-  execute_process(
-    COMMAND ${CMAKE_COMMAND} -E copy "${INPUT_FILE}" "${OUTPUT_FILE}"
-  )
-endif ()
diff --git a/flang/tools/fakeflang/fakeflang.cpp b/flang/tools/fakeflang/fakeflang.cpp
index 563bddde1021d..d23a52296dbb1 100644
--- a/flang/tools/fakeflang/fakeflang.cpp
+++ b/flang/tools/fakeflang/fakeflang.cpp
@@ -25,6 +25,11 @@
 ///
 /// The most relevant preprocessor definition is __flang__ which leads to
 /// CMAKE_Fortran_COMPILER_ID="LLVMFlang".
+///
+/// Whether a particular invocation is CMake's compiler detection is detected
+/// via the filename or when the FLANG_BOOTSTRAP_PROBE environment variable is
+/// set. Any other invocation is forwarded, unmodified, to the real flang
+/// driver that must live right next to this program.
 //
 //===----------------------------------------------------------------------===//
 
@@ -34,14 +39,12 @@
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
 #include "llvm/Support/Program.h"
 #include "llvm/Support/WithColor.h"
 #include <cstdint>
 #include <string>
 
-#define STRINGIFY(X) #X
-#define STRINGIFY_EXPANDED(X) STRINGIFY(X)
-
 static std::string getExecutablePath(const char *argv0) {
   void *anchor = (void *)(intptr_t)getExecutablePath;
   return llvm::sys::fs::getMainExecutable(argv0, anchor);
@@ -52,30 +55,63 @@ static std::string getExecutablePath(const char *argv0) {
   exit(EXIT_FAILURE);
 }
 
-int main(int argc, const char **argv) {
-  llvm::InitLLVM X(argc, argv);
-  std::string SelfExe = getExecutablePath(argv[0]);
+/// A CMake compiler detection probe is assumed if
+///
+/// 1. FLANG_BOOTSTRAP_PROBE=1 has been set during runtimes-configure
+///    (requires CMake 4.2+), or
+///
+/// 2. the file being compiled is CMakeFortranCompilerId.F; all supported
+///    CMake versions below 4.2 use this filename.
+static bool isCompilerIdProbe(llvm::ArrayRef<const char *> OrigArgs) {
+  if (llvm::sys::Process::GetEnv("FLANG_BOOTSTRAP_PROBE"))
+    return true;
 
-  if (llvm::sys::path::stem(SelfExe) == "flang") {
-    llvm::WithColor::remark(llvm::errs())
-        << "This is a mock flang compiler; Use '" STRINGIFY_EXPANDED(
-               CMAKE_MAKE_PROGRAM) " flang' to replace it with the real "
-                                   "compiler\n";
-  }
+  return llvm::any_of(OrigArgs, [](const char *Arg) {
+    return llvm::sys::path::filename(Arg) == "CMakeFortranCompilerId.F";
+  });
+}
+
+/// Forward the invocation directly to Flang. Only reached for invocations
+/// that are not CMake's compiler-id probe, i.e. after the caller's build has
+/// already established (through a real dependency, not this mock) that
+/// flang is up to date.
+static int main_flang(
+    llvm::StringRef SelfExe, llvm::ArrayRef<const char *> OrigArgs) {
+  llvm::SmallString<256> FlangExe{llvm::sys::path::parent_path(SelfExe)};
+  llvm::sys::path::append(FlangExe, "flang");
+
+  llvm::SmallVector<llvm::StringRef> Args;
+  Args.push_back(FlangExe);
+  llvm::append_range(Args, OrigArgs);
+
+  std::string ErrMsg;
+  int RC = llvm::sys::ExecuteAndWait(FlangExe, Args, /*Env=*/std::nullopt,
+      /*Redirects=*/{}, /*SecondsToWait=*/0, /*MemoryLimit=*/0, &ErrMsg);
+  if (RC < 0)
+    fail(ErrMsg);
+  return RC;
+}
+
+/// Use Clang to emulate Flang's preprocessor, answering CMake's
+/// compiler-id probe without needing the real flang to be ready yet.
+static int main_probe(
+    llvm::StringRef SelfExe, llvm::ArrayRef<const char *> OrigArgs) {
+  llvm::WithColor::warning(llvm::errs(), "fakeflang")
+      << "Emulating flang for compiler probing\nFor anything other than "
+         "bootstrapping the Flang toolchain, just use 'flang' instead\n";
 
   llvm::SmallString<256> ClangExe{llvm::sys::path::parent_path(SelfExe)};
   llvm::sys::path::append(ClangExe, "clang");
 
-  llvm::ArrayRef<const char *> AllArgs(argv, static_cast<size_t>(argc));
-  bool hasDashO = AllArgs.size() > 1 &&
-      llvm::any_of(AllArgs.drop_front(), [](const char *Arg) {
-        return llvm::StringRef(Arg).starts_with("-o");
-      });
+  bool hasDashO = llvm::any_of(OrigArgs,
+      [](const char *Arg) { return llvm::StringRef(Arg).starts_with("-o"); });
 
   // Assemble invocation of the preprocessor
   // `-E`: Invoke the preprocessor
   // `-P`: No #line directives
   // `-D..`: Preprocessor definitions that CMake probes
+  // `-D__fakeflang_probe__=1`: To identify fakeflang probe mode
+  //                            (for regression tests)
   // `-fgnuc-version=0`: Prevent clang from implicitly defining __GNUC__;
   //                     CMake's CMakeFortranCompilerId.F.in checks
   //                     __GNUC__before __flang__, so without this flag the
@@ -89,10 +125,8 @@ int main(int argc, const char **argv) {
       "-D__flang_major__=" FLANG_VERSION_MAJOR_STRING,
       "-D__flang_minor__=" FLANG_VERSION_MINOR_STRING,
       "-D__flang_patchlevel__=" FLANG_VERSION_PATCHLEVEL_STRING,
-      "-fgnuc-version=0", "-x", "c"});
-  for (int I = 1; I < argc; ++I) {
-    llvm::StringRef arg = argv[I];
-
+      "-D__fakeflang_probe__=1", "-fgnuc-version=0", "-x", "c"});
+  for (llvm::StringRef arg : OrigArgs) {
     // Sometime CMake tries to invoke the preprocessor
     if (arg == "-cpp" || arg == "-E")
       continue;
@@ -109,3 +143,15 @@ int main(int argc, const char **argv) {
     fail(ErrMsg);
   return RC;
 }
+
+int main(int argc, const char **argv) {
+  llvm::InitLLVM X(argc, argv);
+  std::string SelfExe = getExecutablePath(argv[0]);
+  llvm::ArrayRef<const char *> OrigArgs(argv, static_cast<size_t>(argc));
+  OrigArgs = OrigArgs.drop_front();
+
+  if (isCompilerIdProbe(OrigArgs))
+    return main_probe(SelfExe, OrigArgs);
+
+  return main_flang(SelfExe, OrigArgs);
+}
diff --git a/flang/tools/flang-driver/CMakeLists.txt b/flang/tools/flang-driver/CMakeLists.txt
index d11e7f58a32b8..4dfc0d40cd55d 100644
--- a/flang/tools/flang-driver/CMakeLists.txt
+++ b/flang/tools/flang-driver/CMakeLists.txt
@@ -49,21 +49,3 @@ install(TARGETS flang DESTINATION "${CMAKE_INSTALL_BINDIR}")
 # Keep "flang-new" as a symlink for backwards compatiblity. Remove once "flang"
 # is a widely adopted name.
 add_flang_symlink(flang-new flang)
-
-
-if (NOT FLANG_STANDALONE_BUILD)
-  # This ensures that flang is always built after fakeflang. If fakeflang is
-  # built first, the bin/flang may have a newer timestamp than all of flang's
-  # source files and the make program might consider it 'up-to-date'.
-  # fakedepend.cpp which depends on fakeflang will have to be created after
-  # fakeflang and therefore have a newer timestamp than bin/flang, triggering a
-  # rebuild and therefore overwrite bin/flang with the real version.
-  add_custom_command(
-    OUTPUT fakedepend.cpp
-    DEPENDS flang-lazy
-    COMMAND "${CMAKE_COMMAND}" -E touch fakedepend.cpp
-  )
-  target_sources(flang PRIVATE
-    fakedepend.cpp
-  )
-endif ()
diff --git a/llvm/cmake/modules/LLVMExternalProjectUtils.cmake b/llvm/cmake/modules/LLVMExternalProjectUtils.cmake
index ee270d70a778d..40de0f067736a 100644
--- a/llvm/cmake/modules/LLVMExternalProjectUtils.cmake
+++ b/llvm/cmake/modules/LLVMExternalProjectUtils.cmake
@@ -229,6 +229,7 @@ function(llvm_ExternalProject_Add name source_dir)
     endif()
   endforeach()
 
+  set(maybe_configure_environment_modification "")
   if(ARG_USE_TOOLCHAIN AND NOT CMAKE_CROSSCOMPILING)
     if(CLANG_IN_TOOLCHAIN)
       if(is_msvc_target)
@@ -242,7 +243,11 @@ function(llvm_ExternalProject_Add name source_dir)
       endif()
     endif()
     if(FLANG_IN_TOOLCHAIN)
-      list(APPEND compiler_args -DCMAKE_Fortran_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/flang${CMAKE_EXECUTABLE_SUFFIX})
+      # Use fakeflang to not require having all of flang built for the configure step.
+      list(APPEND compiler_args -DCMAKE_Fortran_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/fakeflang${CMAKE_EXECUTABLE_SUFFIX})
+      if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.2)
+        set(maybe_configure_environment_modification "FLANG_BOOTSTRAP_PROBE=set:1")
+      endif()
     endif()
     if(lld IN_LIST TOOLCHAIN_TOOLS)
       if(is_msvc_target)
@@ -424,6 +429,7 @@ function(llvm_ExternalProject_Add name source_dir)
                ${PASSTHROUGH_VARIABLES}
     CMAKE_CACHE_DEFAULT_ARGS ${CMAKE_CACHE_DEFAULT_ARGS}
     ${build_command_arg}
+    ${maybe_configure_environment_modification}
     INSTALL_COMMAND ""
     STEP_TARGETS configure build
     BUILD_ALWAYS 1

>From 9cdec9a17eb0b9e99c306d1d02d329105681c96a Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Tue, 14 Jul 2026 17:04:06 +0200
Subject: [PATCH 2/2] Actually set CONFIGURE_ENVIRONMENT_MODIFICATION

Confirmed to work with CMake 4.2 without relying on filename.
---
 llvm/cmake/modules/LLVMExternalProjectUtils.cmake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/cmake/modules/LLVMExternalProjectUtils.cmake b/llvm/cmake/modules/LLVMExternalProjectUtils.cmake
index 40de0f067736a..60a880ace5073 100644
--- a/llvm/cmake/modules/LLVMExternalProjectUtils.cmake
+++ b/llvm/cmake/modules/LLVMExternalProjectUtils.cmake
@@ -246,7 +246,7 @@ function(llvm_ExternalProject_Add name source_dir)
       # Use fakeflang to not require having all of flang built for the configure step.
       list(APPEND compiler_args -DCMAKE_Fortran_COMPILER=${LLVM_RUNTIME_OUTPUT_INTDIR}/fakeflang${CMAKE_EXECUTABLE_SUFFIX})
       if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.2)
-        set(maybe_configure_environment_modification "FLANG_BOOTSTRAP_PROBE=set:1")
+        set(maybe_configure_environment_modification CONFIGURE_ENVIRONMENT_MODIFICATION "FLANG_BOOTSTRAP_PROBE=set:1")
       endif()
     endif()
     if(lld IN_LIST TOOLCHAIN_TOOLS)



More information about the flang-commits mailing list