[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