Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/actions/5a-android-x86/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,4 @@ runs:
"${flags[@]}" \
ANDROID_ABI="$abi" # override the one in CROSS_CMAKE_FLAGS

# TODO: append default -gcc switch (x86_64-linux-android30-clang, i686-linux-android29-clang)
cat install/etc/ldc2.conf/55-target-android-$arch.conf
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
- ldc2.conf can now be a directory. All the files inside it, ordered naturally, will be concatenated and treated like a big config. (#4954)
- Running `ldc-build-runtime --installWithSuffix` now includes installing a target-specific .conf file to that directory. (#4978)
- **Breaking change for ldc2.conf cmake generation**: The `cmake` build process now generates the `ldc2.conf` and `ldc2_install.conf` as directories. `ldc2*.conf.in` and `ADDITIONAL_DEFAULT_LDC_SWITCHES` have been removed, if you need to add switches check out `makeConfSection` in `LdcConfig.cmake`. (#4954)
- When cross-compiling, the fallback value for the (cross) C compiler will be picked based on some heuristics.
The old behavior was to default to `cc`.
As an example, when cross-compiling for `aarch64-linux-gnu` the compilers that are checked are:
- `aarch64-linux-gnu-gcc`
- `aarch64-linux-gnu-clang`
- `clang --target=aarch64-linux-gnu`
- The prebuilt arm64/universal macOS packages additionally bundle the arm64 iOS-*simulator* libraries, for out-of-the-box cross-compilation support via e.g. `-mtriple=arm64-apple-ios12.0-simulator`. (#4974)

#### Platform support
Expand Down
29 changes: 1 addition & 28 deletions driver/cpreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,6 @@ const char *getPathToImportc_h(Loc loc) {
return cached;
}

const std::string &getCC(bool isMSVC,
std::vector<std::string> &additional_args) {
static std::string cached_cc;
static std::vector<std::string> cached_args;
if (cached_cc.empty()) {
std::string fallback = "cc";
if (isMSVC) {
#ifdef _WIN32
// by default, prefer clang-cl.exe (if in PATH) over cl.exe
// (e.g., no echoing of source filename being preprocessed to stderr)
auto found = llvm::sys::findProgramByName("clang-cl.exe");
if (found) {
fallback = found.get();
} else {
fallback = "cl.exe";
}
#else
fallback = "clang-cl";
#endif
}
cached_cc = getGcc(cached_args, fallback.c_str());
}

additional_args.insert(additional_args.end(), cached_args.cbegin(), cached_args.cend());
return cached_cc;
}

FileName getOutputPath(Loc loc, const char *csrcfile) {
llvm::SmallString<64> buffer;

Expand Down Expand Up @@ -96,7 +69,7 @@ FileName runCPreprocessor(FileName csrcfile, Loc loc, OutBuffer &defines) {
FileName ipath = getOutputPath(loc, csrcfile.toChars());

std::vector<std::string> args;
const std::string &cc = getCC(isMSVC, args);
const std::string &cc = getCC(args);

args.push_back(isMSVC ? "/std:c11" : "-std=c11");

Expand Down
2 changes: 1 addition & 1 deletion driver/linker-gcc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath,
tool = getProgram("wasm-ld", &opts::linker);
} else {
argsBuilder = std::make_unique<ArgsBuilder>();
tool = getGcc(argsBuilder->args);
tool = getCC(argsBuilder->args);
}

// build arguments
Expand Down
2 changes: 1 addition & 1 deletion driver/toobj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m,
static void assemble(const std::string &asmpath, const std::string &objpath) {
std::vector<std::string> args;
std::string gcc;
gcc = getGcc(args);
gcc = getCC(args);

args.push_back("-O3");
args.push_back("-c");
Expand Down
102 changes: 94 additions & 8 deletions driver/tool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
#include "llvm/Support/Path.h"
#include "llvm/Target/TargetMachine.h"

#if LDC_LLVM_VER >= 1600
#include "llvm/TargetParser/Host.h"
#else
#include "llvm/Support/Host.h"
#endif

#ifdef _WIN32
#include <Windows.h>
#endif
Expand Down Expand Up @@ -52,6 +58,69 @@ static std::string findProgramByName(llvm::StringRef name) {

//////////////////////////////////////////////////////////////////////////////

namespace {
llvm::SmallVector<std::string, 2> findCCFallback() {
const auto &triple = *global.params.targetTriple;
llvm::Triple nativeTriple{ llvm::sys::getDefaultTargetTriple() };
const auto isNativeBuild =
opts::mTargetTriple.empty() || triple.isCompatibleWith(nativeTriple);

llvm::SmallVector<llvm::SmallVector<std::string, 2>, 3> choices;
if (triple.isWindowsMSVCEnvironment()) {
#ifdef _WIN32
// by default, prefer clang-cl.exe (if in PATH) over cl.exe
// (e.g., no echoing of source filename being preprocessed to stderr)
choices = {
{ "clang-cl.exe" },
{ "cl.exe" },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, this should actually work, because of the preceding windows::MsvcEnvironmentScope in runCPreprocessor() - cl.exe should be in PATH by then, so that the later findProgramByName() succeeds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I wasn't sure if I wanted to move it inside findCCFallback because it looked like that setup included environment needed for compilation & preprocessing and, at the end of the function, they would be rollback.

Copy link
Member

@kinke kinke Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine as-is in that regard. But what's more problematic is that native Windows builds would now have -v output and potential errors mentioning a 'C cross-compiler', while that's not the case in this #ifdef _WIN32 block. So analogous to the Posix-native-build case, we'd need an early-return / common helper function / or simply using choices for the Posix-native-build case too, and then adapting the strings based on isNativeBuild.

};
#else
choices = { { "clang-cl" } };
#endif
} else if (isNativeBuild) {
choices = { { "cc" } };
} else if (triple.isOSDarwin()) {
// Cross-compiling for Apple is most probably done on an Apple machine
choices = { { "cc" } };
} else {
const auto tripleString = triple.getTriple();
choices = {
{ tripleString + "-gcc" },
{ tripleString + "-clang" },
{ "clang", "--target=" + tripleString },
};
}


const auto verbose = global.params.v.verbose;
const auto logCross = verbose && !isNativeBuild;
if (logCross) message("Trying to find a C cross-compiler");

for (auto &choice : choices) {
auto fullPath = findProgramByName(choice[0]);
if (fullPath.empty()) {
if (logCross) message("Did not find C cross-compiler: `%s`", choice[0].c_str());
continue;
}

if (logCross) message("Found C cross-compiler: `%s`", fullPath.c_str());
choice[0] = fullPath;
return choice;
}

if (isNativeBuild) {
error(Loc(), "could not find C compiler `%s`", choices[0][0].c_str());
} else {
error(Loc(), "could not find a C cross-compiler for this cross-compilation");
tip("make sure you have a compiler like `%s` installed and set $CC or pass -gcc accordingly", choices[0][0].c_str());
}

fatal();
}
} // anonymous namespace

////////////////////////////////////////////////////////////////////////////////

std::string getProgram(const char *fallbackName,
const llvm::cl::opt<std::string> *opt,
const char *envVar) {
Expand All @@ -76,19 +145,24 @@ std::string getProgram(const char *fallbackName,

////////////////////////////////////////////////////////////////////////////////

std::string getGcc(std::vector<std::string> &additional_args,
const char *fallback) {
namespace {
std::string getCCImpl(std::vector<std::string> &additional_args) {
if (!gcc.empty())
return getProgram(nullptr, &gcc);

std::string cc = env::get("CC");
if (cc.empty()) {
auto fallback = findCCFallback();
additional_args.insert(additional_args.end(), fallback.begin() + 1, fallback.end());
return fallback[0];
}

#ifdef _WIN32
// spaces in $CC are to be expected on Windows
// (e.g., `C:\Program Files\LLVM\bin\clang-cl.exe`)
return getProgram(fallback, &gcc, "CC");
return getProgram(cc.c_str(), &gcc);
#else
// Posix: in case $CC contains spaces split it into a command and arguments
std::string cc = env::get("CC");
if (cc.empty())
return getProgram(fallback, &gcc);

// $CC is set so fallback doesn't matter anymore.
if (cc.find(' ') == cc.npos)
return getProgram(cc.c_str(), &gcc);

Expand All @@ -103,6 +177,18 @@ std::string getGcc(std::vector<std::string> &additional_args,
return getProgram(args[0].str().c_str(), &gcc);
#endif
}
} // anonymous namespace

std::string getCC(std::vector<std::string> &additional_args) {
static std::string cachedResult;
static std::vector<std::string> cachedArgs;

if (cachedResult.empty())
cachedResult = getCCImpl(cachedArgs);

additional_args.insert(additional_args.end(), cachedArgs.begin(), cachedArgs.end());
return cachedResult;
}

////////////////////////////////////////////////////////////////////////////////

Expand Down
3 changes: 1 addition & 2 deletions driver/tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ namespace opts {
extern llvm::cl::opt<std::string> linker;
}

std::string getGcc(std::vector<std::string> &additional_args,
const char *fallback = "cc");
std::string getCC(std::vector<std::string> &additional_args);
void appendTargetArgsForGcc(std::vector<std::string> &args);

std::string getProgram(const char *fallbackName,
Expand Down
8 changes: 8 additions & 0 deletions tests/driver/cross_cc_fallback.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// RUN: env PATH= CC= not %ldc -mtriple aarch64-unknown-linux-gnut64 -v -defaultlib= %s 2>&1 | FileCheck %s

// CHECK-DAG: aarch64-unknown-linux-gnut64-gcc
// CHECK-DAG: aarch64-unknown-linux-gnut64-clang

// UNSUPPORTED: Windows

extern(C) void main () {}