/// \param File Either an absolute or relative path.
std::string getAbsolutePath(StringRef File);
+/// \brief Changes CommandLine to contain implicit flags that would have been
+/// defined had the compiler driver been invoked through the path InvokedAs.
+///
+/// For example, when called with \c InvokedAs set to `i686-linux-android-g++`,
+/// the arguments '-target', 'i686-linux-android`, `--driver-mode=g++` will
+/// be inserted after the first argument in \c CommandLine.
+///
+/// This function will not add new `-target` or `--driver-mode` flags if they
+/// are already present in `CommandLine` (even if they have different settings
+/// than would have been inserted).
+///
+/// \pre `llvm::InitializeAllTargets()` has been called.
+///
+/// \param CommandLine the command line used to invoke the compiler driver or
+/// Clang tool, including the path to the executable as \c CommandLine[0].
+/// \param InvokedAs the path to the driver used to infer implicit flags.
+///
+/// \note This will not set \c CommandLine[0] to \c InvokedAs. The tooling
+/// infrastructure expects that CommandLine[0] is a tool path relative to which
+/// the builtin headers can be found.
+void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
+ StringRef InvokedAs);
+
/// \brief Creates a \c CompilerInvocation.
clang::CompilerInvocation *newInvocation(
clang::DiagnosticsEngine *Diagnostics,
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Tool.h"
+#include "clang/Driver/ToolChain.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendDiagnostic.h"
return AbsolutePath.str();
}
+void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
+ StringRef InvokedAs) {
+ if (!CommandLine.empty() && !InvokedAs.empty()) {
+ bool AlreadyHasTarget = false;
+ bool AlreadyHasMode = false;
+ // Skip CommandLine[0].
+ for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();
+ ++Token) {
+ StringRef TokenRef(*Token);
+ AlreadyHasTarget |=
+ (TokenRef == "-target" || TokenRef.startswith("-target="));
+ AlreadyHasMode |= (TokenRef == "--driver-mode" ||
+ TokenRef.startswith("--driver-mode="));
+ }
+ auto TargetMode =
+ clang::driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs);
+ if (!AlreadyHasMode && !TargetMode.second.empty()) {
+ CommandLine.insert(++CommandLine.begin(), TargetMode.second);
+ }
+ if (!AlreadyHasTarget && !TargetMode.first.empty()) {
+ CommandLine.insert(++CommandLine.begin(), {"-target", TargetMode.first});
+ }
+ }
+}
+
namespace {
class SingleFrontendActionFactory : public FrontendActionFactory {
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/TargetRegistry.h"
#include "gtest/gtest.h"
#include <algorithm>
#include <string>
EXPECT_FALSE(Found);
}
+namespace {
+/// Find a target name such that looking for it in TargetRegistry by that name
+/// returns the same target. We expect that there is at least one target
+/// configured with this property.
+std::string getAnyTarget() {
+ llvm::InitializeAllTargets();
+ for (const auto &Target : llvm::TargetRegistry::targets()) {
+ std::string Error;
+ if (llvm::TargetRegistry::lookupTarget(Target.getName(), Error) ==
+ &Target) {
+ return Target.getName();
+ }
+ }
+ return "";
+}
+}
+
+TEST(addTargetAndModeForProgramName, AddsTargetAndMode) {
+ std::string Target = getAnyTarget();
+ ASSERT_FALSE(Target.empty());
+
+ std::vector<std::string> Args = {"clang", "-foo"};
+ addTargetAndModeForProgramName(Args, "");
+ EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args);
+ addTargetAndModeForProgramName(Args, Target + "-g++");
+ EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target,
+ "--driver-mode=g++", "-foo"}),
+ Args);
+}
+
+TEST(addTargetAndModeForProgramName, PathIgnored) {
+ std::string Target = getAnyTarget();
+ ASSERT_FALSE(Target.empty());
+
+ SmallString<32> ToolPath;
+ llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++");
+
+ std::vector<std::string> Args = {"clang", "-foo"};
+ addTargetAndModeForProgramName(Args, ToolPath);
+ EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target,
+ "--driver-mode=g++", "-foo"}),
+ Args);
+}
+
+TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) {
+ std::string Target = getAnyTarget();
+ ASSERT_FALSE(Target.empty());
+
+ std::vector<std::string> Args = {"clang", "-foo", "-target", "something"};
+ addTargetAndModeForProgramName(Args, Target + "-g++");
+ EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
+ "-target", "something"}),
+ Args);
+
+ std::vector<std::string> ArgsAlt = {"clang", "-foo", "-target=something"};
+ addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
+ EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
+ "-target=something"}),
+ ArgsAlt);
+}
+
+TEST(addTargetAndModeForProgramName, IgnoresExistingMode) {
+ std::string Target = getAnyTarget();
+ ASSERT_FALSE(Target.empty());
+
+ std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"};
+ addTargetAndModeForProgramName(Args, Target + "-g++");
+ EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo",
+ "--driver-mode=abc"}),
+ Args);
+
+ std::vector<std::string> ArgsAlt = {"clang", "-foo", "--driver-mode", "abc"};
+ addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
+ EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo",
+ "--driver-mode", "abc"}),
+ ArgsAlt);
+}
+
#ifndef LLVM_ON_WIN32
TEST(ClangToolTest, BuildASTs) {
FixedCompilationDatabase Compilations("/", std::vector<std::string>());