diff options
author | ed <ed@FreeBSD.org> | 2009-06-02 17:58:47 +0000 |
---|---|---|
committer | ed <ed@FreeBSD.org> | 2009-06-02 17:58:47 +0000 |
commit | f27e5a09a0d815b8a4814152954ff87dadfdefc0 (patch) | |
tree | ce7d964cbb5e39695b71481698f10cb099c23d4a /lib/Driver | |
download | FreeBSD-src-f27e5a09a0d815b8a4814152954ff87dadfdefc0.zip FreeBSD-src-f27e5a09a0d815b8a4814152954ff87dadfdefc0.tar.gz |
Import Clang, at r72732.
Diffstat (limited to 'lib/Driver')
-rw-r--r-- | lib/Driver/Action.cpp | 79 | ||||
-rw-r--r-- | lib/Driver/Arg.cpp | 192 | ||||
-rw-r--r-- | lib/Driver/ArgList.cpp | 232 | ||||
-rw-r--r-- | lib/Driver/CMakeLists.txt | 19 | ||||
-rw-r--r-- | lib/Driver/Compilation.cpp | 174 | ||||
-rw-r--r-- | lib/Driver/Driver.cpp | 1254 | ||||
-rw-r--r-- | lib/Driver/HostInfo.cpp | 408 | ||||
-rw-r--r-- | lib/Driver/InputInfo.h | 101 | ||||
-rw-r--r-- | lib/Driver/Job.cpp | 31 | ||||
-rw-r--r-- | lib/Driver/Makefile | 28 | ||||
-rw-r--r-- | lib/Driver/OptTable.cpp | 265 | ||||
-rw-r--r-- | lib/Driver/Option.cpp | 250 | ||||
-rw-r--r-- | lib/Driver/Phases.cpp | 27 | ||||
-rw-r--r-- | lib/Driver/Tool.cpp | 19 | ||||
-rw-r--r-- | lib/Driver/ToolChain.cpp | 35 | ||||
-rw-r--r-- | lib/Driver/ToolChains.cpp | 475 | ||||
-rw-r--r-- | lib/Driver/ToolChains.h | 134 | ||||
-rw-r--r-- | lib/Driver/Tools.cpp | 2033 | ||||
-rw-r--r-- | lib/Driver/Tools.h | 316 | ||||
-rw-r--r-- | lib/Driver/Types.cpp | 205 |
20 files changed, 6277 insertions, 0 deletions
diff --git a/lib/Driver/Action.cpp b/lib/Driver/Action.cpp new file mode 100644 index 0000000..cabc33e --- /dev/null +++ b/lib/Driver/Action.cpp @@ -0,0 +1,79 @@ +//===--- Action.cpp - Abstract compilation steps ------------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Action.h" + +#include <cassert> +using namespace clang::driver; + +Action::~Action() { + // FIXME: Free the inputs. The problem is that BindArchAction shares + // inputs; so we can't just walk the inputs. +} + +const char *Action::getClassName(ActionClass AC) { + switch (AC) { + case InputClass: return "input"; + case BindArchClass: return "bind-arch"; + case PreprocessJobClass: return "preprocessor"; + case PrecompileJobClass: return "precompiler"; + case AnalyzeJobClass: return "analyzer"; + case CompileJobClass: return "compiler"; + case AssembleJobClass: return "assembler"; + case LinkJobClass: return "linker"; + case LipoJobClass: return "lipo"; + } + + assert(0 && "invalid class"); + return 0; +} + +InputAction::InputAction(const Arg &_Input, types::ID _Type) + : Action(InputClass, _Type), Input(_Input) { +} + +BindArchAction::BindArchAction(Action *Input, const char *_ArchName) + : Action(BindArchClass, Input, Input->getType()), ArchName(_ArchName) { +} + +JobAction::JobAction(ActionClass Kind, Action *Input, types::ID Type) + : Action(Kind, Input, Type) { +} + +JobAction::JobAction(ActionClass Kind, const ActionList &Inputs, types::ID Type) + : Action(Kind, Inputs, Type) { +} + +PreprocessJobAction::PreprocessJobAction(Action *Input, types::ID OutputType) + : JobAction(PreprocessJobClass, Input, OutputType) { +} + +PrecompileJobAction::PrecompileJobAction(Action *Input, types::ID OutputType) + : JobAction(PrecompileJobClass, Input, OutputType) { +} + +AnalyzeJobAction::AnalyzeJobAction(Action *Input, types::ID OutputType) + : JobAction(AnalyzeJobClass, Input, OutputType) { +} + +CompileJobAction::CompileJobAction(Action *Input, types::ID OutputType) + : JobAction(CompileJobClass, Input, OutputType) { +} + +AssembleJobAction::AssembleJobAction(Action *Input, types::ID OutputType) + : JobAction(AssembleJobClass, Input, OutputType) { +} + +LinkJobAction::LinkJobAction(ActionList &Inputs, types::ID Type) + : JobAction(LinkJobClass, Inputs, Type) { +} + +LipoJobAction::LipoJobAction(ActionList &Inputs, types::ID Type) + : JobAction(LipoJobClass, Inputs, Type) { +} diff --git a/lib/Driver/Arg.cpp b/lib/Driver/Arg.cpp new file mode 100644 index 0000000..e227d7e --- /dev/null +++ b/lib/Driver/Arg.cpp @@ -0,0 +1,192 @@ +//===--- Arg.cpp - Argument Implementations -----------------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/Option.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang::driver; + +Arg::Arg(ArgClass _Kind, const Option *_Opt, unsigned _Index, + const Arg *_BaseArg) + : Kind(_Kind), Opt(_Opt), BaseArg(_BaseArg), Index(_Index), Claimed(false) +{ +} + +Arg::~Arg() { } + +void Arg::dump() const { + llvm::errs() << "<"; + switch (Kind) { + default: + assert(0 && "Invalid kind"); +#define P(N) case N: llvm::errs() << #N; break + P(FlagClass); + P(PositionalClass); + P(JoinedClass); + P(SeparateClass); + P(CommaJoinedClass); + P(JoinedAndSeparateClass); +#undef P + } + + llvm::errs() << " Opt:"; + Opt->dump(); + + llvm::errs() << " Index:" << Index; + + if (isa<CommaJoinedArg>(this) || isa<SeparateArg>(this)) + llvm::errs() << " NumValues:" << getNumValues(); + + llvm::errs() << ">\n"; +} + +std::string Arg::getAsString(const ArgList &Args) const { + std::string Res; + llvm::raw_string_ostream OS(Res); + + ArgStringList ASL; + render(Args, ASL); + for (ArgStringList::iterator + it = ASL.begin(), ie = ASL.end(); it != ie; ++it) { + if (it != ASL.begin()) + OS << ' '; + OS << *it; + } + + return OS.str(); +} + +void Arg::renderAsInput(const ArgList &Args, ArgStringList &Output) const { + if (!getOption().hasNoOptAsInput()) { + render(Args, Output); + return; + } + + for (unsigned i = 0, e = getNumValues(); i != e; ++i) + Output.push_back(getValue(Args, i)); +} + +FlagArg::FlagArg(const Option *Opt, unsigned Index, const Arg *BaseArg) + : Arg(FlagClass, Opt, Index, BaseArg) { +} + +void FlagArg::render(const ArgList &Args, ArgStringList &Output) const { + Output.push_back(Args.getArgString(getIndex())); +} + +const char *FlagArg::getValue(const ArgList &Args, unsigned N) const { + assert(0 && "Invalid index."); + return 0; +} + +PositionalArg::PositionalArg(const Option *Opt, unsigned Index, + const Arg *BaseArg) + : Arg(PositionalClass, Opt, Index, BaseArg) { +} + +void PositionalArg::render(const ArgList &Args, ArgStringList &Output) const { + Output.push_back(Args.getArgString(getIndex())); +} + +const char *PositionalArg::getValue(const ArgList &Args, unsigned N) const { + assert(N < getNumValues() && "Invalid index."); + return Args.getArgString(getIndex()); +} + +JoinedArg::JoinedArg(const Option *Opt, unsigned Index, const Arg *BaseArg) + : Arg(JoinedClass, Opt, Index, BaseArg) { +} + +void JoinedArg::render(const ArgList &Args, ArgStringList &Output) const { + if (getOption().hasForceSeparateRender()) { + Output.push_back(getOption().getName()); + Output.push_back(getValue(Args, 0)); + } else { + Output.push_back(Args.getArgString(getIndex())); + } +} + +const char *JoinedArg::getValue(const ArgList &Args, unsigned N) const { + assert(N < getNumValues() && "Invalid index."); + // FIXME: Avoid strlen. + return Args.getArgString(getIndex()) + strlen(getOption().getName()); +} + +CommaJoinedArg::CommaJoinedArg(const Option *Opt, unsigned Index, + const char *Str, const Arg *BaseArg) + : Arg(CommaJoinedClass, Opt, Index, BaseArg) { + const char *Prev = Str; + for (;; ++Str) { + char c = *Str; + + if (!c) { + if (Prev != Str) + Values.push_back(std::string(Prev, Str)); + break; + } else if (c == ',') { + if (Prev != Str) + Values.push_back(std::string(Prev, Str)); + Prev = Str + 1; + } + } +} + +void CommaJoinedArg::render(const ArgList &Args, ArgStringList &Output) const { + Output.push_back(Args.getArgString(getIndex())); +} + +const char *CommaJoinedArg::getValue(const ArgList &Args, unsigned N) const { + assert(N < getNumValues() && "Invalid index."); + return Values[N].c_str(); +} + +SeparateArg::SeparateArg(const Option *Opt, unsigned Index, unsigned _NumValues, + const Arg *BaseArg) + : Arg(SeparateClass, Opt, Index, BaseArg), NumValues(_NumValues) { +} + +void SeparateArg::render(const ArgList &Args, ArgStringList &Output) const { + if (getOption().hasForceJoinedRender()) { + assert(getNumValues() == 1 && "Cannot force joined render with > 1 args."); + // FIXME: Avoid std::string. + std::string Joined(getOption().getName()); + Joined += Args.getArgString(getIndex()); + Output.push_back(Args.MakeArgString(Joined.c_str())); + } else { + Output.push_back(Args.getArgString(getIndex())); + for (unsigned i = 0; i < NumValues; ++i) + Output.push_back(Args.getArgString(getIndex() + 1 + i)); + } +} + +const char *SeparateArg::getValue(const ArgList &Args, unsigned N) const { + assert(N < getNumValues() && "Invalid index."); + return Args.getArgString(getIndex() + 1 + N); +} + +JoinedAndSeparateArg::JoinedAndSeparateArg(const Option *Opt, unsigned Index, + const Arg *BaseArg) + : Arg(JoinedAndSeparateClass, Opt, Index, BaseArg) { +} + +void JoinedAndSeparateArg::render(const ArgList &Args, + ArgStringList &Output) const { + Output.push_back(Args.getArgString(getIndex())); + Output.push_back(Args.getArgString(getIndex() + 1)); +} + +const char *JoinedAndSeparateArg::getValue(const ArgList &Args, + unsigned N) const { + assert(N < getNumValues() && "Invalid index."); + if (N == 0) + return Args.getArgString(getIndex()) + strlen(getOption().getName()); + return Args.getArgString(getIndex() + 1); +} diff --git a/lib/Driver/ArgList.cpp b/lib/Driver/ArgList.cpp new file mode 100644 index 0000000..593694c --- /dev/null +++ b/lib/Driver/ArgList.cpp @@ -0,0 +1,232 @@ +//===--- ArgList.cpp - Argument List Management -------------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/ArgList.h" +#include "clang/Driver/Arg.h" +#include "clang/Driver/Option.h" + +using namespace clang::driver; + +ArgList::ArgList(arglist_type &_Args) : Args(_Args) { +} + +ArgList::~ArgList() { +} + +void ArgList::append(Arg *A) { + Args.push_back(A); +} + +Arg *ArgList::getLastArg(options::ID Id, bool Claim) const { + // FIXME: Make search efficient? + for (const_reverse_iterator it = rbegin(), ie = rend(); it != ie; ++it) { + if ((*it)->getOption().matches(Id)) { + if (Claim) (*it)->claim(); + return *it; + } + } + + return 0; +} + +Arg *ArgList::getLastArg(options::ID Id0, options::ID Id1, bool Claim) const { + Arg *Res, *A0 = getLastArg(Id0, false), *A1 = getLastArg(Id1, false); + + if (A0 && A1) + Res = A0->getIndex() > A1->getIndex() ? A0 : A1; + else + Res = A0 ? A0 : A1; + + if (Claim && Res) + Res->claim(); + + return Res; +} + +bool ArgList::hasFlag(options::ID Pos, options::ID Neg, bool Default) const { + if (Arg *A = getLastArg(Pos, Neg)) + return A->getOption().matches(Pos); + return Default; +} + +void ArgList::AddLastArg(ArgStringList &Output, options::ID Id) const { + if (Arg *A = getLastArg(Id)) { + A->claim(); + A->render(*this, Output); + } +} + +void ArgList::AddAllArgs(ArgStringList &Output, options::ID Id0) const { + // FIXME: Make fast. + for (const_iterator it = begin(), ie = end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(Id0)) { + A->claim(); + A->render(*this, Output); + } + } +} + +void ArgList::AddAllArgs(ArgStringList &Output, options::ID Id0, + options::ID Id1) const { + // FIXME: Make fast. + for (const_iterator it = begin(), ie = end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(Id0) || A->getOption().matches(Id1)) { + A->claim(); + A->render(*this, Output); + } + } +} + +void ArgList::AddAllArgs(ArgStringList &Output, options::ID Id0, + options::ID Id1, options::ID Id2) const { + // FIXME: Make fast. + for (const_iterator it = begin(), ie = end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(Id0) || A->getOption().matches(Id1) || + A->getOption().matches(Id2)) { + A->claim(); + A->render(*this, Output); + } + } +} + +void ArgList::AddAllArgValues(ArgStringList &Output, options::ID Id0) const { + // FIXME: Make fast. + for (const_iterator it = begin(), ie = end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(Id0)) { + A->claim(); + for (unsigned i = 0, e = A->getNumValues(); i != e; ++i) + Output.push_back(A->getValue(*this, i)); + } + } +} + +void ArgList::AddAllArgValues(ArgStringList &Output, options::ID Id0, + options::ID Id1) const { + // FIXME: Make fast. + for (const_iterator it = begin(), ie = end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(Id0) || A->getOption().matches(Id1)) { + A->claim(); + for (unsigned i = 0, e = A->getNumValues(); i != e; ++i) + Output.push_back(A->getValue(*this, i)); + } + } +} + +void ArgList::AddAllArgsTranslated(ArgStringList &Output, options::ID Id0, + const char *Translation, + bool Joined) const { + // FIXME: Make fast. + for (const_iterator it = begin(), ie = end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(Id0)) { + A->claim(); + + if (Joined) { + std::string Value = Translation; + Value += A->getValue(*this, 0); + Output.push_back(MakeArgString(Value.c_str())); + } else { + Output.push_back(Translation); + Output.push_back(A->getValue(*this, 0)); + } + } + } +} + +void ArgList::ClaimAllArgs(options::ID Id0) const { + // FIXME: Make fast. + for (const_iterator it = begin(), ie = end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(Id0)) + A->claim(); + } +} + +// + +InputArgList::InputArgList(const char **ArgBegin, const char **ArgEnd) + : ArgList(ActualArgs), NumInputArgStrings(ArgEnd - ArgBegin) +{ + ArgStrings.append(ArgBegin, ArgEnd); +} + +InputArgList::~InputArgList() { + // An InputArgList always owns its arguments. + for (iterator it = begin(), ie = end(); it != ie; ++it) + delete *it; +} + +unsigned InputArgList::MakeIndex(const char *String0) const { + unsigned Index = ArgStrings.size(); + + // Tuck away so we have a reliable const char *. + SynthesizedStrings.push_back(String0); + ArgStrings.push_back(SynthesizedStrings.back().c_str()); + + return Index; +} + +unsigned InputArgList::MakeIndex(const char *String0, + const char *String1) const { + unsigned Index0 = MakeIndex(String0); + unsigned Index1 = MakeIndex(String1); + assert(Index0 + 1 == Index1 && "Unexpected non-consecutive indices!"); + (void) Index1; + return Index0; +} + +const char *InputArgList::MakeArgString(const char *Str) const { + return getArgString(MakeIndex(Str)); +} + +// + +DerivedArgList::DerivedArgList(InputArgList &_BaseArgs, bool _OnlyProxy) + : ArgList(_OnlyProxy ? _BaseArgs.getArgs() : ActualArgs), + BaseArgs(_BaseArgs), OnlyProxy(_OnlyProxy) +{ +} + +DerivedArgList::~DerivedArgList() { + // We only own the arguments we explicitly synthesized. + for (iterator it = SynthesizedArgs.begin(), ie = SynthesizedArgs.end(); + it != ie; ++it) + delete *it; +} + +const char *DerivedArgList::MakeArgString(const char *Str) const { + return BaseArgs.MakeArgString(Str); +} + +Arg *DerivedArgList::MakeFlagArg(const Arg *BaseArg, const Option *Opt) const { + return new FlagArg(Opt, BaseArgs.MakeIndex(Opt->getName()), BaseArg); +} + +Arg *DerivedArgList::MakePositionalArg(const Arg *BaseArg, const Option *Opt, + const char *Value) const { + return new PositionalArg(Opt, BaseArgs.MakeIndex(Value), BaseArg); +} + +Arg *DerivedArgList::MakeSeparateArg(const Arg *BaseArg, const Option *Opt, + const char *Value) const { + return new SeparateArg(Opt, BaseArgs.MakeIndex(Opt->getName(), Value), 1, + BaseArg); +} + +Arg *DerivedArgList::MakeJoinedArg(const Arg *BaseArg, const Option *Opt, + const char *Value) const { + std::string Joined(Opt->getName()); + Joined += Value; + return new JoinedArg(Opt, BaseArgs.MakeIndex(Joined.c_str()), BaseArg); +} diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt new file mode 100644 index 0000000..7147d8f --- /dev/null +++ b/lib/Driver/CMakeLists.txt @@ -0,0 +1,19 @@ +set(LLVM_NO_RTTI 1) + +add_clang_library(clangDriver + Action.cpp + Arg.cpp + ArgList.cpp + Compilation.cpp + Driver.cpp + HostInfo.cpp + Job.cpp + OptTable.cpp + Option.cpp + Phases.cpp + Tool.cpp + ToolChain.cpp + ToolChains.cpp + Tools.cpp + Types.cpp + ) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp new file mode 100644 index 0000000..1e044c6b --- /dev/null +++ b/lib/Driver/Compilation.cpp @@ -0,0 +1,174 @@ +//===--- Compilation.cpp - Compilation Task Implementation --------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Compilation.h" + +#include "clang/Driver/Action.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/ToolChain.h" + +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Program.h" +#include <sys/stat.h> +#include <errno.h> +using namespace clang::driver; + +Compilation::Compilation(Driver &D, + ToolChain &_DefaultToolChain, + InputArgList *_Args) + : TheDriver(D), DefaultToolChain(_DefaultToolChain), Args(_Args) { +} + +Compilation::~Compilation() { + delete Args; + + // Free any derived arg lists. + for (llvm::DenseMap<const ToolChain*, DerivedArgList*>::iterator + it = TCArgs.begin(), ie = TCArgs.end(); it != ie; ++it) + delete it->second; + + // Free the actions, if built. + for (ActionList::iterator it = Actions.begin(), ie = Actions.end(); + it != ie; ++it) + delete *it; +} + +const DerivedArgList &Compilation::getArgsForToolChain(const ToolChain *TC) { + if (!TC) + TC = &DefaultToolChain; + + DerivedArgList *&Entry = TCArgs[TC]; + if (!Entry) + Entry = TC->TranslateArgs(*Args); + + return *Entry; +} + +void Compilation::PrintJob(llvm::raw_ostream &OS, const Job &J, + const char *Terminator, bool Quote) const { + if (const Command *C = dyn_cast<Command>(&J)) { + OS << " \"" << C->getExecutable() << '"'; + for (ArgStringList::const_iterator it = C->getArguments().begin(), + ie = C->getArguments().end(); it != ie; ++it) { + if (Quote) + OS << " \"" << *it << '"'; + else + OS << ' ' << *it; + } + OS << Terminator; + } else if (const PipedJob *PJ = dyn_cast<PipedJob>(&J)) { + for (PipedJob::const_iterator + it = PJ->begin(), ie = PJ->end(); it != ie; ++it) + PrintJob(OS, **it, (it + 1 != PJ->end()) ? " |\n" : "\n", Quote); + } else { + const JobList *Jobs = cast<JobList>(&J); + for (JobList::const_iterator + it = Jobs->begin(), ie = Jobs->end(); it != ie; ++it) + PrintJob(OS, **it, Terminator, Quote); + } +} + +bool Compilation::CleanupFileList(const ArgStringList &Files, + bool IssueErrors) const { + bool Success = true; + + for (ArgStringList::const_iterator + it = Files.begin(), ie = Files.end(); it != ie; ++it) { + llvm::sys::Path P(*it); + std::string Error; + + if (P.eraseFromDisk(false, &Error)) { + // Failure is only failure if the file doesn't exist. There is a + // race condition here due to the limited interface of + // llvm::sys::Path, we want to know if the removal gave E_NOENT. + + // FIXME: Grumble, P.exists() is broken. PR3837. + struct stat buf; + if (::stat(P.c_str(), &buf) == 0 + || errno != ENOENT) { + if (IssueErrors) + getDriver().Diag(clang::diag::err_drv_unable_to_remove_file) + << Error; + Success = false; + } + } + } + + return Success; +} + +int Compilation::ExecuteCommand(const Command &C) const { + llvm::sys::Path Prog(C.getExecutable()); + const char **Argv = new const char*[C.getArguments().size() + 2]; + Argv[0] = C.getExecutable(); + std::copy(C.getArguments().begin(), C.getArguments().end(), Argv+1); + Argv[C.getArguments().size() + 1] = 0; + + if (getDriver().CCCEcho || getArgs().hasArg(options::OPT_v)) + PrintJob(llvm::errs(), C, "\n", false); + + std::string Error; + int Res = + llvm::sys::Program::ExecuteAndWait(Prog, Argv, + /*env*/0, /*redirects*/0, + /*secondsToWait*/0, /*memoryLimit*/0, + &Error); + if (!Error.empty()) { + assert(Res && "Error string set with 0 result code!"); + getDriver().Diag(clang::diag::err_drv_command_failure) << Error; + } + + delete[] Argv; + return Res; +} + +int Compilation::ExecuteJob(const Job &J) const { + if (const Command *C = dyn_cast<Command>(&J)) { + return ExecuteCommand(*C); + } else if (const PipedJob *PJ = dyn_cast<PipedJob>(&J)) { + // Piped commands with a single job are easy. + if (PJ->size() == 1) + return ExecuteCommand(**PJ->begin()); + + getDriver().Diag(clang::diag::err_drv_unsupported_opt) << "-pipe"; + return 1; + } else { + const JobList *Jobs = cast<JobList>(&J); + for (JobList::const_iterator + it = Jobs->begin(), ie = Jobs->end(); it != ie; ++it) + if (int Res = ExecuteJob(**it)) + return Res; + return 0; + } +} + +int Compilation::Execute() const { + // Just print if -### was present. + if (getArgs().hasArg(options::OPT__HASH_HASH_HASH)) { + PrintJob(llvm::errs(), Jobs, "\n", true); + return 0; + } + + // If there were errors building the compilation, quit now. + if (getDriver().getDiags().getNumErrors()) + return 1; + + int Res = ExecuteJob(Jobs); + + // Remove temp files. + CleanupFileList(TempFiles); + + // If the compilation failed, remove result files as well. + if (Res != 0 && !getArgs().hasArg(options::OPT_save_temps)) + CleanupFileList(ResultFiles, true); + + return Res; +} diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp new file mode 100644 index 0000000..d8c6a0a --- /dev/null +++ b/lib/Driver/Driver.cpp @@ -0,0 +1,1254 @@ +//===--- Driver.cpp - Clang GCC Compatible Driver -----------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Driver.h" + +#include "clang/Driver/Action.h" +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/HostInfo.h" +#include "clang/Driver/Job.h" +#include "clang/Driver/Option.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/Tool.h" +#include "clang/Driver/ToolChain.h" +#include "clang/Driver/Types.h" + +#include "clang/Basic/Version.h" + +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" +#include "llvm/System/Program.h" + +#include "InputInfo.h" + +#include <map> + +using namespace clang::driver; +using namespace clang; + +Driver::Driver(const char *_Name, const char *_Dir, + const char *_DefaultHostTriple, + const char *_DefaultImageName, + Diagnostic &_Diags) + : Opts(new OptTable()), Diags(_Diags), + Name(_Name), Dir(_Dir), DefaultHostTriple(_DefaultHostTriple), + DefaultImageName(_DefaultImageName), + Host(0), + CCCIsCXX(false), CCCEcho(false), CCCPrintBindings(false), + CCCGenericGCCName("gcc"), CCCUseClang(true), CCCUseClangCXX(false), + CCCUseClangCPP(true), CCCUsePCH(true), + SuppressMissingInputWarning(false) +{ + // Only use clang on i386 and x86_64 by default. + CCCClangArchs.insert("i386"); + CCCClangArchs.insert("x86_64"); +} + +Driver::~Driver() { + delete Opts; + delete Host; +} + +InputArgList *Driver::ParseArgStrings(const char **ArgBegin, + const char **ArgEnd) { + llvm::PrettyStackTraceString CrashInfo("Command line argument parsing"); + InputArgList *Args = new InputArgList(ArgBegin, ArgEnd); + + // FIXME: Handle '@' args (or at least error on them). + + unsigned Index = 0, End = ArgEnd - ArgBegin; + while (Index < End) { + // gcc's handling of empty arguments doesn't make + // sense, but this is not a common use case. :) + // + // We just ignore them here (note that other things may + // still take them as arguments). + if (Args->getArgString(Index)[0] == '\0') { + ++Index; + continue; + } + + unsigned Prev = Index; + Arg *A = getOpts().ParseOneArg(*Args, Index); + assert(Index > Prev && "Parser failed to consume argument."); + + // Check for missing argument error. + if (!A) { + assert(Index >= End && "Unexpected parser error."); + Diag(clang::diag::err_drv_missing_argument) + << Args->getArgString(Prev) + << (Index - Prev - 1); + break; + } + + if (A->getOption().isUnsupported()) { + Diag(clang::diag::err_drv_unsupported_opt) << A->getAsString(*Args); + continue; + } + Args->append(A); + } + + return Args; +} + +Compilation *Driver::BuildCompilation(int argc, const char **argv) { + llvm::PrettyStackTraceString CrashInfo("Compilation construction"); + + // FIXME: Handle environment options which effect driver behavior, + // somewhere (client?). GCC_EXEC_PREFIX, COMPILER_PATH, + // LIBRARY_PATH, LPATH, CC_PRINT_OPTIONS, QA_OVERRIDE_GCC3_OPTIONS. + + // FIXME: What are we going to do with -V and -b? + + // FIXME: This stuff needs to go into the Compilation, not the + // driver. + bool CCCPrintOptions = false, CCCPrintActions = false; + + const char **Start = argv + 1, **End = argv + argc; + const char *HostTriple = DefaultHostTriple.c_str(); + + // Read -ccc args. + // + // FIXME: We need to figure out where this behavior should + // live. Most of it should be outside in the client; the parts that + // aren't should have proper options, either by introducing new ones + // or by overloading gcc ones like -V or -b. + for (; Start != End && memcmp(*Start, "-ccc-", 5) == 0; ++Start) { + const char *Opt = *Start + 5; + + if (!strcmp(Opt, "print-options")) { + CCCPrintOptions = true; + } else if (!strcmp(Opt, "print-phases")) { + CCCPrintActions = true; + } else if (!strcmp(Opt, "print-bindings")) { + CCCPrintBindings = true; + } else if (!strcmp(Opt, "cxx")) { + CCCIsCXX = true; + } else if (!strcmp(Opt, "echo")) { + CCCEcho = true; + + } else if (!strcmp(Opt, "gcc-name")) { + assert(Start+1 < End && "FIXME: -ccc- argument handling."); + CCCGenericGCCName = *++Start; + + } else if (!strcmp(Opt, "clang-cxx")) { + CCCUseClangCXX = true; + } else if (!strcmp(Opt, "pch-is-pch")) { + CCCUsePCH = true; + } else if (!strcmp(Opt, "pch-is-pth")) { + CCCUsePCH = false; + } else if (!strcmp(Opt, "no-clang")) { + CCCUseClang = false; + } else if (!strcmp(Opt, "no-clang-cpp")) { + CCCUseClangCPP = false; + } else if (!strcmp(Opt, "clang-archs")) { + assert(Start+1 < End && "FIXME: -ccc- argument handling."); + const char *Cur = *++Start; + + CCCClangArchs.clear(); + for (;;) { + const char *Next = strchr(Cur, ','); + + if (Next) { + if (Cur != Next) + CCCClangArchs.insert(std::string(Cur, Next)); + Cur = Next + 1; + } else { + if (*Cur != '\0') + CCCClangArchs.insert(std::string(Cur)); + break; + } + } + + } else if (!strcmp(Opt, "host-triple")) { + assert(Start+1 < End && "FIXME: -ccc- argument handling."); + HostTriple = *++Start; + + } else { + // FIXME: Error handling. + llvm::errs() << "invalid option: " << *Start << "\n"; + exit(1); + } + } + + InputArgList *Args = ParseArgStrings(Start, End); + + Host = GetHostInfo(HostTriple); + + // The compilation takes ownership of Args. + Compilation *C = new Compilation(*this, *Host->getToolChain(*Args), Args); + + // FIXME: This behavior shouldn't be here. + if (CCCPrintOptions) { + PrintOptions(C->getArgs()); + return C; + } + + if (!HandleImmediateArgs(*C)) + return C; + + // Construct the list of abstract actions to perform for this + // compilation. We avoid passing a Compilation here simply to + // enforce the abstraction that pipelining is not host or toolchain + // dependent (other than the driver driver test). + if (Host->useDriverDriver()) + BuildUniversalActions(C->getArgs(), C->getActions()); + else + BuildActions(C->getArgs(), C->getActions()); + + if (CCCPrintActions) { + PrintActions(*C); + return C; + } + + BuildJobs(*C); + + return C; +} + +void Driver::PrintOptions(const ArgList &Args) const { + unsigned i = 0; + for (ArgList::const_iterator it = Args.begin(), ie = Args.end(); + it != ie; ++it, ++i) { + Arg *A = *it; + llvm::errs() << "Option " << i << " - " + << "Name: \"" << A->getOption().getName() << "\", " + << "Values: {"; + for (unsigned j = 0; j < A->getNumValues(); ++j) { + if (j) + llvm::errs() << ", "; + llvm::errs() << '"' << A->getValue(Args, j) << '"'; + } + llvm::errs() << "}\n"; + } +} + +static std::string getOptionHelpName(const OptTable &Opts, options::ID Id) { + std::string Name = Opts.getOptionName(Id); + + // Add metavar, if used. + switch (Opts.getOptionKind(Id)) { + case Option::GroupClass: case Option::InputClass: case Option::UnknownClass: + assert(0 && "Invalid option with help text."); + + case Option::MultiArgClass: case Option::JoinedAndSeparateClass: + assert(0 && "Cannot print metavar for this kind of option."); + + case Option::FlagClass: + break; + + case Option::SeparateClass: case Option::JoinedOrSeparateClass: + Name += ' '; + // FALLTHROUGH + case Option::JoinedClass: case Option::CommaJoinedClass: + Name += Opts.getOptionMetaVar(Id); + break; + } + + return Name; +} + +void Driver::PrintHelp(bool ShowHidden) const { + llvm::raw_ostream &OS = llvm::outs(); + + OS << "OVERVIEW: clang \"gcc-compatible\" driver\n"; + OS << '\n'; + OS << "USAGE: " << Name << " [options] <input files>\n"; + OS << '\n'; + OS << "OPTIONS:\n"; + + // Render help text into (option, help) pairs. + std::vector< std::pair<std::string, const char*> > OptionHelp; + + for (unsigned i = options::OPT_INPUT, e = options::LastOption; i != e; ++i) { + options::ID Id = (options::ID) i; + if (const char *Text = getOpts().getOptionHelpText(Id)) + OptionHelp.push_back(std::make_pair(getOptionHelpName(getOpts(), Id), + Text)); + } + + if (ShowHidden) { + OptionHelp.push_back(std::make_pair("\nDRIVER OPTIONS:","")); + OptionHelp.push_back(std::make_pair("-ccc-cxx", + "Act as a C++ driver")); + OptionHelp.push_back(std::make_pair("-ccc-gcc-name", + "Name for native GCC compiler")); + OptionHelp.push_back(std::make_pair("-ccc-clang-cxx", + "Use the clang compiler for C++")); + OptionHelp.push_back(std::make_pair("-ccc-no-clang", + "Never use the clang compiler")); + OptionHelp.push_back(std::make_pair("-ccc-no-clang-cpp", + "Never use the clang preprocessor")); + OptionHelp.push_back(std::make_pair("-ccc-clang-archs", + "Comma separate list of architectures " + "to use the clang compiler for")); + OptionHelp.push_back(std::make_pair("-ccc-pch-is-pch", + "Use lazy PCH for precompiled headers")); + OptionHelp.push_back(std::make_pair("-ccc-pch-is-pth", + "Use pretokenized headers for precompiled headers")); + + OptionHelp.push_back(std::make_pair("\nDEBUG/DEVELOPMENT OPTIONS:","")); + OptionHelp.push_back(std::make_pair("-ccc-host-triple", + "Simulate running on the given target")); + OptionHelp.push_back(std::make_pair("-ccc-print-options", + "Dump parsed command line arguments")); + OptionHelp.push_back(std::make_pair("-ccc-print-phases", + "Dump list of actions to perform")); + OptionHelp.push_back(std::make_pair("-ccc-print-bindings", + "Show bindings of tools to actions")); + OptionHelp.push_back(std::make_pair("CCC_ADD_ARGS", + "(ENVIRONMENT VARIABLE) Comma separated list of " + "arguments to prepend to the command line")); + } + + // Find the maximum option length. + unsigned OptionFieldWidth = 0; + for (unsigned i = 0, e = OptionHelp.size(); i != e; ++i) { + // Skip titles. + if (!OptionHelp[i].second) + continue; + + // Limit the amount of padding we are willing to give up for + // alignment. + unsigned Length = OptionHelp[i].first.size(); + if (Length <= 23) + OptionFieldWidth = std::max(OptionFieldWidth, Length); + } + + for (unsigned i = 0, e = OptionHelp.size(); i != e; ++i) { + const std::string &Option = OptionHelp[i].first; + OS << " " << Option; + for (int j = Option.length(), e = OptionFieldWidth; j < e; ++j) + OS << ' '; + OS << ' ' << OptionHelp[i].second << '\n'; + } + + OS.flush(); +} + +void Driver::PrintVersion(const Compilation &C) const { + static char buf[] = "$URL: https://ed@llvm.org/svn/llvm-project/cfe/trunk/lib/Driver/Driver.cpp $"; + char *zap = strstr(buf, "/lib/Driver"); + if (zap) + *zap = 0; + zap = strstr(buf, "/clang/tools/clang"); + if (zap) + *zap = 0; + const char *vers = buf+6; + // FIXME: Add cmake support and remove #ifdef +#ifdef SVN_REVISION + const char *revision = SVN_REVISION; +#else + const char *revision = ""; +#endif + // FIXME: The following handlers should use a callback mechanism, we + // don't know what the client would like to do. + + llvm::errs() << "clang version " CLANG_VERSION_STRING " (" + << vers << " " << revision << ")" << "\n"; + + const ToolChain &TC = C.getDefaultToolChain(); + llvm::errs() << "Target: " << TC.getTripleString() << '\n'; +} + +bool Driver::HandleImmediateArgs(const Compilation &C) { + // The order these options are handled in in gcc is all over the + // place, but we don't expect inconsistencies w.r.t. that to matter + // in practice. + + if (C.getArgs().hasArg(options::OPT_dumpversion)) { + llvm::outs() << CLANG_VERSION_STRING "\n"; + return false; + } + + if (C.getArgs().hasArg(options::OPT__help) || + C.getArgs().hasArg(options::OPT__help_hidden)) { + PrintHelp(C.getArgs().hasArg(options::OPT__help_hidden)); + return false; + } + + if (C.getArgs().hasArg(options::OPT__version)) { + PrintVersion(C); + return false; + } + + if (C.getArgs().hasArg(options::OPT_v) || + C.getArgs().hasArg(options::OPT__HASH_HASH_HASH)) { + PrintVersion(C); + SuppressMissingInputWarning = true; + } + + const ToolChain &TC = C.getDefaultToolChain(); + if (C.getArgs().hasArg(options::OPT_print_search_dirs)) { + llvm::outs() << "programs: ="; + for (ToolChain::path_list::const_iterator it = TC.getProgramPaths().begin(), + ie = TC.getProgramPaths().end(); it != ie; ++it) { + if (it != TC.getProgramPaths().begin()) + llvm::outs() << ':'; + llvm::outs() << *it; + } + llvm::outs() << "\n"; + llvm::outs() << "libraries: ="; + for (ToolChain::path_list::const_iterator it = TC.getFilePaths().begin(), + ie = TC.getFilePaths().end(); it != ie; ++it) { + if (it != TC.getFilePaths().begin()) + llvm::outs() << ':'; + llvm::outs() << *it; + } + llvm::outs() << "\n"; + return false; + } + + // FIXME: The following handlers should use a callback mechanism, we + // don't know what the client would like to do. + if (Arg *A = C.getArgs().getLastArg(options::OPT_print_file_name_EQ)) { + llvm::outs() << GetFilePath(A->getValue(C.getArgs()), TC).toString() + << "\n"; + return false; + } + + if (Arg *A = C.getArgs().getLastArg(options::OPT_print_prog_name_EQ)) { + llvm::outs() << GetProgramPath(A->getValue(C.getArgs()), TC).toString() + << "\n"; + return false; + } + + if (C.getArgs().hasArg(options::OPT_print_libgcc_file_name)) { + llvm::outs() << GetFilePath("libgcc.a", TC).toString() << "\n"; + return false; + } + + return true; +} + +static unsigned PrintActions1(const Compilation &C, + Action *A, + std::map<Action*, unsigned> &Ids) { + if (Ids.count(A)) + return Ids[A]; + + std::string str; + llvm::raw_string_ostream os(str); + + os << Action::getClassName(A->getKind()) << ", "; + if (InputAction *IA = dyn_cast<InputAction>(A)) { + os << "\"" << IA->getInputArg().getValue(C.getArgs()) << "\""; + } else if (BindArchAction *BIA = dyn_cast<BindArchAction>(A)) { + os << '"' << (BIA->getArchName() ? BIA->getArchName() : + C.getDefaultToolChain().getArchName()) << '"' + << ", {" << PrintActions1(C, *BIA->begin(), Ids) << "}"; + } else { + os << "{"; + for (Action::iterator it = A->begin(), ie = A->end(); it != ie;) { + os << PrintActions1(C, *it, Ids); + ++it; + if (it != ie) + os << ", "; + } + os << "}"; + } + + unsigned Id = Ids.size(); + Ids[A] = Id; + llvm::errs() << Id << ": " << os.str() << ", " + << types::getTypeName(A->getType()) << "\n"; + + return Id; +} + +void Driver::PrintActions(const Compilation &C) const { + std::map<Action*, unsigned> Ids; + for (ActionList::const_iterator it = C.getActions().begin(), + ie = C.getActions().end(); it != ie; ++it) + PrintActions1(C, *it, Ids); +} + +void Driver::BuildUniversalActions(const ArgList &Args, + ActionList &Actions) const { + llvm::PrettyStackTraceString CrashInfo("Building actions for universal build"); + // Collect the list of architectures. Duplicates are allowed, but + // should only be handled once (in the order seen). + llvm::StringSet<> ArchNames; + llvm::SmallVector<const char *, 4> Archs; + for (ArgList::const_iterator it = Args.begin(), ie = Args.end(); + it != ie; ++it) { + Arg *A = *it; + + if (A->getOption().getId() == options::OPT_arch) { + const char *Name = A->getValue(Args); + + // FIXME: We need to handle canonicalization of the specified + // arch? + + A->claim(); + if (ArchNames.insert(Name)) + Archs.push_back(Name); + } + } + + // When there is no explicit arch for this platform, make sure we + // still bind the architecture (to the default) so that -Xarch_ is + // handled correctly. + if (!Archs.size()) + Archs.push_back(0); + + // FIXME: We killed off some others but these aren't yet detected in + // a functional manner. If we added information to jobs about which + // "auxiliary" files they wrote then we could detect the conflict + // these cause downstream. + if (Archs.size() > 1) { + // No recovery needed, the point of this is just to prevent + // overwriting the same files. + if (const Arg *A = Args.getLastArg(options::OPT_save_temps)) + Diag(clang::diag::err_drv_invalid_opt_with_multiple_archs) + << A->getAsString(Args); + } + + ActionList SingleActions; + BuildActions(Args, SingleActions); + + // Add in arch binding and lipo (if necessary) for every top level + // action. + for (unsigned i = 0, e = SingleActions.size(); i != e; ++i) { + Action *Act = SingleActions[i]; + + // Make sure we can lipo this kind of output. If not (and it is an + // actual output) then we disallow, since we can't create an + // output file with the right name without overwriting it. We + // could remove this oddity by just changing the output names to + // include the arch, which would also fix + // -save-temps. Compatibility wins for now. + + if (Archs.size() > 1 && !types::canLipoType(Act->getType())) + Diag(clang::diag::err_drv_invalid_output_with_multiple_archs) + << types::getTypeName(Act->getType()); + + ActionList Inputs; + for (unsigned i = 0, e = Archs.size(); i != e; ++i) + Inputs.push_back(new BindArchAction(Act, Archs[i])); + + // Lipo if necessary, We do it this way because we need to set the + // arch flag so that -Xarch_ gets overwritten. + if (Inputs.size() == 1 || Act->getType() == types::TY_Nothing) + Actions.append(Inputs.begin(), Inputs.end()); + else + Actions.push_back(new LipoJobAction(Inputs, Act->getType())); + } +} + +void Driver::BuildActions(const ArgList &Args, ActionList &Actions) const { + llvm::PrettyStackTraceString CrashInfo("Building compilation actions"); + // Start by constructing the list of inputs and their types. + + // Track the current user specified (-x) input. We also explicitly + // track the argument used to set the type; we only want to claim + // the type when we actually use it, so we warn about unused -x + // arguments. + types::ID InputType = types::TY_Nothing; + Arg *InputTypeArg = 0; + + llvm::SmallVector<std::pair<types::ID, const Arg*>, 16> Inputs; + for (ArgList::const_iterator it = Args.begin(), ie = Args.end(); + it != ie; ++it) { + Arg *A = *it; + + if (isa<InputOption>(A->getOption())) { + const char *Value = A->getValue(Args); + types::ID Ty = types::TY_INVALID; + + // Infer the input type if necessary. + if (InputType == types::TY_Nothing) { + // If there was an explicit arg for this, claim it. + if (InputTypeArg) + InputTypeArg->claim(); + + // stdin must be handled specially. + if (memcmp(Value, "-", 2) == 0) { + // If running with -E, treat as a C input (this changes the + // builtin macros, for example). This may be overridden by + // -ObjC below. + // + // Otherwise emit an error but still use a valid type to + // avoid spurious errors (e.g., no inputs). + if (!Args.hasArg(options::OPT_E, false)) + Diag(clang::diag::err_drv_unknown_stdin_type); + Ty = types::TY_C; + } else { + // Otherwise lookup by extension, and fallback to ObjectType + // if not found. We use a host hook here because Darwin at + // least has its own idea of what .s is. + if (const char *Ext = strrchr(Value, '.')) + Ty = Host->lookupTypeForExtension(Ext + 1); + + if (Ty == types::TY_INVALID) + Ty = types::TY_Object; + } + + // -ObjC and -ObjC++ override the default language, but only for "source + // files". We just treat everything that isn't a linker input as a + // source file. + // + // FIXME: Clean this up if we move the phase sequence into the type. + if (Ty != types::TY_Object) { + if (Args.hasArg(options::OPT_ObjC)) + Ty = types::TY_ObjC; + else if (Args.hasArg(options::OPT_ObjCXX)) + Ty = types::TY_ObjCXX; + } + } else { + assert(InputTypeArg && "InputType set w/o InputTypeArg"); + InputTypeArg->claim(); + Ty = InputType; + } + + // Check that the file exists. It isn't clear this is worth + // doing, since the tool presumably does this anyway, and this + // just adds an extra stat to the equation, but this is gcc + // compatible. + if (memcmp(Value, "-", 2) != 0 && !llvm::sys::Path(Value).exists()) + Diag(clang::diag::err_drv_no_such_file) << A->getValue(Args); + else + Inputs.push_back(std::make_pair(Ty, A)); + + } else if (A->getOption().isLinkerInput()) { + // Just treat as object type, we could make a special type for + // this if necessary. + Inputs.push_back(std::make_pair(types::TY_Object, A)); + + } else if (A->getOption().getId() == options::OPT_x) { + InputTypeArg = A; + InputType = types::lookupTypeForTypeSpecifier(A->getValue(Args)); + + // Follow gcc behavior and treat as linker input for invalid -x + // options. Its not clear why we shouldn't just revert to + // unknown; but this isn't very important, we might as well be + // bug comatible. + if (!InputType) { + Diag(clang::diag::err_drv_unknown_language) << A->getValue(Args); + InputType = types::TY_Object; + } + } + } + + if (!SuppressMissingInputWarning && Inputs.empty()) { + Diag(clang::diag::err_drv_no_input_files); + return; + } + + // Determine which compilation mode we are in. We look for options + // which affect the phase, starting with the earliest phases, and + // record which option we used to determine the final phase. + Arg *FinalPhaseArg = 0; + phases::ID FinalPhase; + + // -{E,M,MM} only run the preprocessor. + if ((FinalPhaseArg = Args.getLastArg(options::OPT_E)) || + (FinalPhaseArg = Args.getLastArg(options::OPT_M)) || + (FinalPhaseArg = Args.getLastArg(options::OPT_MM))) { + FinalPhase = phases::Preprocess; + + // -{fsyntax-only,-analyze,emit-llvm,S} only run up to the compiler. + } else if ((FinalPhaseArg = Args.getLastArg(options::OPT_fsyntax_only)) || + (FinalPhaseArg = Args.getLastArg(options::OPT__analyze, + options::OPT__analyze_auto)) || + (FinalPhaseArg = Args.getLastArg(options::OPT_S))) { + FinalPhase = phases::Compile; + + // -c only runs up to the assembler. + } else if ((FinalPhaseArg = Args.getLastArg(options::OPT_c))) { + FinalPhase = phases::Assemble; + + // Otherwise do everything. + } else + FinalPhase = phases::Link; + + // Reject -Z* at the top level, these options should never have been + // exposed by gcc. + if (Arg *A = Args.getLastArg(options::OPT_Z_Joined)) + Diag(clang::diag::err_drv_use_of_Z_option) << A->getAsString(Args); + + // Construct the actions to perform. + ActionList LinkerInputs; + for (unsigned i = 0, e = Inputs.size(); i != e; ++i) { + types::ID InputType = Inputs[i].first; + const Arg *InputArg = Inputs[i].second; + + unsigned NumSteps = types::getNumCompilationPhases(InputType); + assert(NumSteps && "Invalid number of steps!"); + + // If the first step comes after the final phase we are doing as + // part of this compilation, warn the user about it. + phases::ID InitialPhase = types::getCompilationPhase(InputType, 0); + if (InitialPhase > FinalPhase) { + // Claim here to avoid the more general unused warning. + InputArg->claim(); + Diag(clang::diag::warn_drv_input_file_unused) + << InputArg->getAsString(Args) + << getPhaseName(InitialPhase) + << FinalPhaseArg->getOption().getName(); + continue; + } + + // Build the pipeline for this file. + Action *Current = new InputAction(*InputArg, InputType); + for (unsigned i = 0; i != NumSteps; ++i) { + phases::ID Phase = types::getCompilationPhase(InputType, i); + + // We are done if this step is past what the user requested. + if (Phase > FinalPhase) + break; + + // Queue linker inputs. + if (Phase == phases::Link) { + assert(i + 1 == NumSteps && "linking must be final compilation step."); + LinkerInputs.push_back(Current); + Current = 0; + break; + } + + // Some types skip the assembler phase (e.g., llvm-bc), but we + // can't encode this in the steps because the intermediate type + // depends on arguments. Just special case here. + if (Phase == phases::Assemble && Current->getType() != types::TY_PP_Asm) + continue; + + // Otherwise construct the appropriate action. + Current = ConstructPhaseAction(Args, Phase, Current); + if (Current->getType() == types::TY_Nothing) + break; + } + + // If we ended with something, add to the output list. + if (Current) + Actions.push_back(Current); + } + + // Add a link action if necessary. + if (!LinkerInputs.empty()) + Actions.push_back(new LinkJobAction(LinkerInputs, types::TY_Image)); +} + +Action *Driver::ConstructPhaseAction(const ArgList &Args, phases::ID Phase, + Action *Input) const { + llvm::PrettyStackTraceString CrashInfo("Constructing phase actions"); + // Build the appropriate action. + switch (Phase) { + case phases::Link: assert(0 && "link action invalid here."); + case phases::Preprocess: { + types::ID OutputTy; + // -{M, MM} alter the output type. + if (Args.hasArg(options::OPT_M) || Args.hasArg(options::OPT_MM)) { + OutputTy = types::TY_Dependencies; + } else { + OutputTy = types::getPreprocessedType(Input->getType()); + assert(OutputTy != types::TY_INVALID && + "Cannot preprocess this input type!"); + } + return new PreprocessJobAction(Input, OutputTy); + } + case phases::Precompile: + return new PrecompileJobAction(Input, types::TY_PCH); + case phases::Compile: { + if (Args.hasArg(options::OPT_fsyntax_only)) { + return new CompileJobAction(Input, types::TY_Nothing); + } else if (Args.hasArg(options::OPT__analyze, options::OPT__analyze_auto)) { + return new AnalyzeJobAction(Input, types::TY_Plist); + } else if (Args.hasArg(options::OPT_emit_llvm) || + Args.hasArg(options::OPT_flto) || + Args.hasArg(options::OPT_O4)) { + types::ID Output = + Args.hasArg(options::OPT_S) ? types::TY_LLVMAsm : types::TY_LLVMBC; + return new CompileJobAction(Input, Output); + } else { + return new CompileJobAction(Input, types::TY_PP_Asm); + } + } + case phases::Assemble: + return new AssembleJobAction(Input, types::TY_Object); + } + + assert(0 && "invalid phase in ConstructPhaseAction"); + return 0; +} + +void Driver::BuildJobs(Compilation &C) const { + llvm::PrettyStackTraceString CrashInfo("Building compilation jobs"); + bool SaveTemps = C.getArgs().hasArg(options::OPT_save_temps); + bool UsePipes = C.getArgs().hasArg(options::OPT_pipe); + + // FIXME: Pipes are forcibly disabled until we support executing + // them. + if (!CCCPrintBindings) + UsePipes = false; + + // -save-temps inhibits pipes. + if (SaveTemps && UsePipes) { + Diag(clang::diag::warn_drv_pipe_ignored_with_save_temps); + UsePipes = true; + } + + Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o); + + // It is an error to provide a -o option if we are making multiple + // output files. + if (FinalOutput) { + unsigned NumOutputs = 0; + for (ActionList::const_iterator it = C.getActions().begin(), + ie = C.getActions().end(); it != ie; ++it) + if ((*it)->getType() != types::TY_Nothing) + ++NumOutputs; + + if (NumOutputs > 1) { + Diag(clang::diag::err_drv_output_argument_with_multiple_files); + FinalOutput = 0; + } + } + + for (ActionList::const_iterator it = C.getActions().begin(), + ie = C.getActions().end(); it != ie; ++it) { + Action *A = *it; + + // If we are linking an image for multiple archs then the linker + // wants -arch_multiple and -final_output <final image + // name>. Unfortunately, this doesn't fit in cleanly because we + // have to pass this information down. + // + // FIXME: This is a hack; find a cleaner way to integrate this + // into the process. + const char *LinkingOutput = 0; + if (isa<LipoJobAction>(A)) { + if (FinalOutput) + LinkingOutput = FinalOutput->getValue(C.getArgs()); + else + LinkingOutput = DefaultImageName.c_str(); + } + + InputInfo II; + BuildJobsForAction(C, A, &C.getDefaultToolChain(), + /*CanAcceptPipe*/ true, + /*AtTopLevel*/ true, + /*LinkingOutput*/ LinkingOutput, + II); + } + + // If the user passed -Qunused-arguments or there were errors, don't + // warn about any unused arguments. + if (Diags.getNumErrors() || + C.getArgs().hasArg(options::OPT_Qunused_arguments)) + return; + + // Claim -### here. + (void) C.getArgs().hasArg(options::OPT__HASH_HASH_HASH); + + for (ArgList::const_iterator it = C.getArgs().begin(), ie = C.getArgs().end(); + it != ie; ++it) { + Arg *A = *it; + + // FIXME: It would be nice to be able to send the argument to the + // Diagnostic, so that extra values, position, and so on could be + // printed. + if (!A->isClaimed()) { + if (A->getOption().hasNoArgumentUnused()) + continue; + + // Suppress the warning automatically if this is just a flag, + // and it is an instance of an argument we already claimed. + const Option &Opt = A->getOption(); + if (isa<FlagOption>(Opt)) { + bool DuplicateClaimed = false; + + // FIXME: Use iterator. + for (ArgList::const_iterator it = C.getArgs().begin(), + ie = C.getArgs().end(); it != ie; ++it) { + if ((*it)->isClaimed() && (*it)->getOption().matches(Opt.getId())) { + DuplicateClaimed = true; + break; + } + } + + if (DuplicateClaimed) + continue; + } + + Diag(clang::diag::warn_drv_unused_argument) + << A->getAsString(C.getArgs()); + } + } +} + +void Driver::BuildJobsForAction(Compilation &C, + const Action *A, + const ToolChain *TC, + bool CanAcceptPipe, + bool AtTopLevel, + const char *LinkingOutput, + InputInfo &Result) const { + llvm::PrettyStackTraceString CrashInfo("Building compilation jobs for action"); + + bool UsePipes = C.getArgs().hasArg(options::OPT_pipe); + // FIXME: Pipes are forcibly disabled until we support executing + // them. + if (!CCCPrintBindings) + UsePipes = false; + + if (const InputAction *IA = dyn_cast<InputAction>(A)) { + // FIXME: It would be nice to not claim this here; maybe the old + // scheme of just using Args was better? + const Arg &Input = IA->getInputArg(); + Input.claim(); + if (isa<PositionalArg>(Input)) { + const char *Name = Input.getValue(C.getArgs()); + Result = InputInfo(Name, A->getType(), Name); + } else + Result = InputInfo(&Input, A->getType(), ""); + return; + } + + if (const BindArchAction *BAA = dyn_cast<BindArchAction>(A)) { + const char *ArchName = BAA->getArchName(); + std::string Arch; + if (!ArchName) { + Arch = C.getDefaultToolChain().getArchName(); + ArchName = Arch.c_str(); + } + BuildJobsForAction(C, + *BAA->begin(), + Host->getToolChain(C.getArgs(), ArchName), + CanAcceptPipe, + AtTopLevel, + LinkingOutput, + Result); + return; + } + + const JobAction *JA = cast<JobAction>(A); + const Tool &T = TC->SelectTool(C, *JA); + + // See if we should use an integrated preprocessor. We do so when we + // have exactly one input, since this is the only use case we care + // about (irrelevant since we don't support combine yet). + bool UseIntegratedCPP = false; + const ActionList *Inputs = &A->getInputs(); + if (Inputs->size() == 1 && isa<PreprocessJobAction>(*Inputs->begin())) { + if (!C.getArgs().hasArg(options::OPT_no_integrated_cpp) && + !C.getArgs().hasArg(options::OPT_traditional_cpp) && + !C.getArgs().hasArg(options::OPT_save_temps) && + T.hasIntegratedCPP()) { + UseIntegratedCPP = true; + Inputs = &(*Inputs)[0]->getInputs(); + } + } + + // Only use pipes when there is exactly one input. + bool TryToUsePipeInput = Inputs->size() == 1 && T.acceptsPipedInput(); + InputInfoList InputInfos; + for (ActionList::const_iterator it = Inputs->begin(), ie = Inputs->end(); + it != ie; ++it) { + InputInfo II; + BuildJobsForAction(C, *it, TC, TryToUsePipeInput, + /*AtTopLevel*/false, + LinkingOutput, + II); + InputInfos.push_back(II); + } + + // Determine if we should output to a pipe. + bool OutputToPipe = false; + if (CanAcceptPipe && T.canPipeOutput()) { + // Some actions default to writing to a pipe if they are the top + // level phase and there was no user override. + // + // FIXME: Is there a better way to handle this? + if (AtTopLevel) { + if (isa<PreprocessJobAction>(A) && !C.getArgs().hasArg(options::OPT_o)) + OutputToPipe = true; + } else if (UsePipes) + OutputToPipe = true; + } + + // Figure out where to put the job (pipes). + Job *Dest = &C.getJobs(); + if (InputInfos[0].isPipe()) { + assert(TryToUsePipeInput && "Unrequested pipe!"); + assert(InputInfos.size() == 1 && "Unexpected pipe with multiple inputs."); + Dest = &InputInfos[0].getPipe(); + } + + // Always use the first input as the base input. + const char *BaseInput = InputInfos[0].getBaseInput(); + + // Determine the place to write output to (nothing, pipe, or + // filename) and where to put the new job. + if (JA->getType() == types::TY_Nothing) { + Result = InputInfo(A->getType(), BaseInput); + } else if (OutputToPipe) { + // Append to current piped job or create a new one as appropriate. + PipedJob *PJ = dyn_cast<PipedJob>(Dest); + if (!PJ) { + PJ = new PipedJob(); + // FIXME: Temporary hack so that -ccc-print-bindings work until + // we have pipe support. Please remove later. + if (!CCCPrintBindings) + cast<JobList>(Dest)->addJob(PJ); + Dest = PJ; + } + Result = InputInfo(PJ, A->getType(), BaseInput); + } else { + Result = InputInfo(GetNamedOutputPath(C, *JA, BaseInput, AtTopLevel), + A->getType(), BaseInput); + } + + if (CCCPrintBindings) { + llvm::errs() << "# \"" << T.getToolChain().getTripleString() << '"' + << " - \"" << T.getName() << "\", inputs: ["; + for (unsigned i = 0, e = InputInfos.size(); i != e; ++i) { + llvm::errs() << InputInfos[i].getAsString(); + if (i + 1 != e) + llvm::errs() << ", "; + } + llvm::errs() << "], output: " << Result.getAsString() << "\n"; + } else { + T.ConstructJob(C, *JA, *Dest, Result, InputInfos, + C.getArgsForToolChain(TC), LinkingOutput); + } +} + +const char *Driver::GetNamedOutputPath(Compilation &C, + const JobAction &JA, + const char *BaseInput, + bool AtTopLevel) const { + llvm::PrettyStackTraceString CrashInfo("Computing output path"); + // Output to a user requested destination? + if (AtTopLevel) { + if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o)) + return C.addResultFile(FinalOutput->getValue(C.getArgs())); + } + + // Output to a temporary file? + if (!AtTopLevel && !C.getArgs().hasArg(options::OPT_save_temps)) { + std::string TmpName = + GetTemporaryPath(types::getTypeTempSuffix(JA.getType())); + return C.addTempFile(C.getArgs().MakeArgString(TmpName.c_str())); + } + + llvm::sys::Path BasePath(BaseInput); + std::string BaseName(BasePath.getLast()); + + // Determine what the derived output name should be. + const char *NamedOutput; + if (JA.getType() == types::TY_Image) { + NamedOutput = DefaultImageName.c_str(); + } else { + const char *Suffix = types::getTypeTempSuffix(JA.getType()); + assert(Suffix && "All types used for output should have a suffix."); + + std::string::size_type End = std::string::npos; + if (!types::appendSuffixForType(JA.getType())) + End = BaseName.rfind('.'); + std::string Suffixed(BaseName.substr(0, End)); + Suffixed += '.'; + Suffixed += Suffix; + NamedOutput = C.getArgs().MakeArgString(Suffixed.c_str()); + } + + // As an annoying special case, PCH generation doesn't strip the + // pathname. + if (JA.getType() == types::TY_PCH) { + BasePath.eraseComponent(); + if (BasePath.isEmpty()) + BasePath = NamedOutput; + else + BasePath.appendComponent(NamedOutput); + return C.addResultFile(C.getArgs().MakeArgString(BasePath.c_str())); + } else { + return C.addResultFile(NamedOutput); + } +} + +llvm::sys::Path Driver::GetFilePath(const char *Name, + const ToolChain &TC) const { + const ToolChain::path_list &List = TC.getFilePaths(); + for (ToolChain::path_list::const_iterator + it = List.begin(), ie = List.end(); it != ie; ++it) { + llvm::sys::Path P(*it); + P.appendComponent(Name); + if (P.exists()) + return P; + } + + return llvm::sys::Path(Name); +} + +llvm::sys::Path Driver::GetProgramPath(const char *Name, + const ToolChain &TC, + bool WantFile) const { + const ToolChain::path_list &List = TC.getProgramPaths(); + for (ToolChain::path_list::const_iterator + it = List.begin(), ie = List.end(); it != ie; ++it) { + llvm::sys::Path P(*it); + P.appendComponent(Name); + if (WantFile ? P.exists() : P.canExecute()) + return P; + } + + // If all else failed, search the path. + llvm::sys::Path P(llvm::sys::Program::FindProgramByName(Name)); + if (!P.empty()) + return P; + + return llvm::sys::Path(Name); +} + +std::string Driver::GetTemporaryPath(const char *Suffix) const { + // FIXME: This is lame; sys::Path should provide this function (in + // particular, it should know how to find the temporary files dir). + std::string Error; + const char *TmpDir = ::getenv("TMPDIR"); + if (!TmpDir) + TmpDir = ::getenv("TEMP"); + if (!TmpDir) + TmpDir = ::getenv("TMP"); + if (!TmpDir) + TmpDir = "/tmp"; + llvm::sys::Path P(TmpDir); + P.appendComponent("cc"); + if (P.makeUnique(false, &Error)) { + Diag(clang::diag::err_drv_unable_to_make_temp) << Error; + return ""; + } + + // FIXME: Grumble, makeUnique sometimes leaves the file around!? + // PR3837. + P.eraseFromDisk(false, 0); + + P.appendSuffix(Suffix); + return P.toString(); +} + +const HostInfo *Driver::GetHostInfo(const char *TripleStr) const { + llvm::PrettyStackTraceString CrashInfo("Constructing host"); + llvm::Triple Triple(TripleStr); + + // Normalize Arch a bit. + // + // FIXME: We shouldn't need to do this once everything goes through the triple + // interface. + if (Triple.getArchName() == "i686") + Triple.setArchName("i386"); + else if (Triple.getArchName() == "amd64") + Triple.setArchName("x86_64"); + else if (Triple.getArchName() == "ppc" || + Triple.getArchName() == "Power Macintosh") + Triple.setArchName("powerpc"); + else if (Triple.getArchName() == "ppc64") + Triple.setArchName("powerpc64"); + + switch (Triple.getOS()) { + case llvm::Triple::Darwin: + return createDarwinHostInfo(*this, Triple); + case llvm::Triple::DragonFly: + return createDragonFlyHostInfo(*this, Triple); + case llvm::Triple::FreeBSD: + return createFreeBSDHostInfo(*this, Triple); + case llvm::Triple::Linux: + return createLinuxHostInfo(*this, Triple); + default: + return createUnknownHostInfo(*this, Triple); + } +} + +bool Driver::ShouldUseClangCompiler(const Compilation &C, const JobAction &JA, + const std::string &ArchNameStr) const { + // FIXME: Remove this hack. + const char *ArchName = ArchNameStr.c_str(); + if (ArchNameStr == "powerpc") + ArchName = "ppc"; + else if (ArchNameStr == "powerpc64") + ArchName = "ppc64"; + + // Check if user requested no clang, or clang doesn't understand + // this type (we only handle single inputs for now). + if (!CCCUseClang || JA.size() != 1 || + !types::isAcceptedByClang((*JA.begin())->getType())) + return false; + + // Otherwise make sure this is an action clang understands. + if (isa<PreprocessJobAction>(JA)) { + if (!CCCUseClangCPP) { + Diag(clang::diag::warn_drv_not_using_clang_cpp); + return false; + } + } else if (!isa<PrecompileJobAction>(JA) && !isa<CompileJobAction>(JA)) + return false; + + // Use clang for C++? + if (!CCCUseClangCXX && types::isCXX((*JA.begin())->getType())) { + Diag(clang::diag::warn_drv_not_using_clang_cxx); + return false; + } + + // Always use clang for precompiling, regardless of archs. PTH is + // platform independent, and this allows the use of the static + // analyzer on platforms we don't have full IRgen support for. + if (isa<PrecompileJobAction>(JA)) + return true; + + // Finally, don't use clang if this isn't one of the user specified + // archs to build. + if (!CCCClangArchs.empty() && !CCCClangArchs.count(ArchName)) { + Diag(clang::diag::warn_drv_not_using_clang_arch) << ArchName; + return false; + } + + return true; +} + +/// GetReleaseVersion - Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and +/// return the grouped values as integers. Numbers which are not +/// provided are set to 0. +/// +/// \return True if the entire string was parsed (9.2), or all groups +/// were parsed (10.3.5extrastuff). +bool Driver::GetReleaseVersion(const char *Str, unsigned &Major, + unsigned &Minor, unsigned &Micro, + bool &HadExtra) { + HadExtra = false; + + Major = Minor = Micro = 0; + if (*Str == '\0') + return true; + + char *End; + Major = (unsigned) strtol(Str, &End, 10); + if (*Str != '\0' && *End == '\0') + return true; + if (*End != '.') + return false; + + Str = End+1; + Minor = (unsigned) strtol(Str, &End, 10); + if (*Str != '\0' && *End == '\0') + return true; + if (*End != '.') + return false; + + Str = End+1; + Micro = (unsigned) strtol(Str, &End, 10); + if (*Str != '\0' && *End == '\0') + return true; + if (Str == End) + return false; + HadExtra = true; + return true; +} diff --git a/lib/Driver/HostInfo.cpp b/lib/Driver/HostInfo.cpp new file mode 100644 index 0000000..603b3ab --- /dev/null +++ b/lib/Driver/HostInfo.cpp @@ -0,0 +1,408 @@ +//===--- HostInfo.cpp - Host specific information -----------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/HostInfo.h" + +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Option.h" +#include "clang/Driver/Options.h" + +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Compiler.h" + +#include "ToolChains.h" + +#include <cassert> + +using namespace clang::driver; + +HostInfo::HostInfo(const Driver &D, const llvm::Triple &_Triple) + : TheDriver(D), Triple(_Triple) +{ + +} + +HostInfo::~HostInfo() { +} + +namespace { + +// Darwin Host Info + +/// DarwinHostInfo - Darwin host information implementation. +class DarwinHostInfo : public HostInfo { + /// Darwin version of host. + unsigned DarwinVersion[3]; + + /// GCC version to use on this host. + unsigned GCCVersion[3]; + + /// Cache of tool chains we have created. + mutable llvm::StringMap<ToolChain *> ToolChains; + +public: + DarwinHostInfo(const Driver &D, const llvm::Triple &Triple); + ~DarwinHostInfo(); + + virtual bool useDriverDriver() const; + + virtual types::ID lookupTypeForExtension(const char *Ext) const { + types::ID Ty = types::lookupTypeForExtension(Ext); + + // Darwin always preprocesses assembly files (unless -x is used + // explicitly). + if (Ty == types::TY_PP_Asm) + return types::TY_Asm; + + return Ty; + } + + virtual ToolChain *getToolChain(const ArgList &Args, + const char *ArchName) const; +}; + +DarwinHostInfo::DarwinHostInfo(const Driver &D, const llvm::Triple& Triple) + : HostInfo(D, Triple) { + + assert((getArchName() == "i386" || getArchName() == "x86_64" || + getArchName() == "powerpc" || getArchName() == "powerpc64" || + getArchName() == "arm") && + "Unknown Darwin arch."); + + assert(memcmp(&getOSName()[0], "darwin", 6) == 0 && + "Unknown Darwin platform."); + bool HadExtra; + if (!Driver::GetReleaseVersion(&getOSName()[6], + DarwinVersion[0], DarwinVersion[1], + DarwinVersion[2], HadExtra)) { + D.Diag(clang::diag::err_drv_invalid_darwin_version) + << getOSName(); + } + + // We can only call 4.2.1 for now. + GCCVersion[0] = 4; + GCCVersion[1] = 2; + GCCVersion[2] = 1; +} + +DarwinHostInfo::~DarwinHostInfo() { + for (llvm::StringMap<ToolChain*>::iterator + it = ToolChains.begin(), ie = ToolChains.end(); it != ie; ++it) + delete it->second; +} + +bool DarwinHostInfo::useDriverDriver() const { + return true; +} + +ToolChain *DarwinHostInfo::getToolChain(const ArgList &Args, + const char *ArchName) const { + std::string Arch; + if (!ArchName) { + Arch = getArchName(); + ArchName = Arch.c_str(); + + // If no arch name is specified, infer it from the host and + // -m32/-m64. + if (Arg *A = Args.getLastArg(options::OPT_m32, options::OPT_m64)) { + if (getArchName() == "i386" || getArchName() == "x86_64") { + ArchName = + (A->getOption().getId() == options::OPT_m32) ? "i386" : "x86_64"; + } else if (getArchName() == "powerpc" || getArchName() == "powerpc64") { + ArchName = (A->getOption().getId() == options::OPT_m32) ? "powerpc" : + "powerpc64"; + } + } + } else { + // Normalize arch name; we shouldn't be doing this here. + // + // FIXME: This should be unnecessary once everything moves over to using the + // ID based Triple interface. + if (strcmp(ArchName, "ppc") == 0) + ArchName = "powerpc"; + else if (strcmp(ArchName, "ppc64") == 0) + ArchName = "powerpc64"; + } + + ToolChain *&TC = ToolChains[ArchName]; + if (!TC) { + llvm::Triple TCTriple(getTriple()); + TCTriple.setArchName(ArchName); + + if (strcmp(ArchName, "i386") == 0 || strcmp(ArchName, "x86_64") == 0) + TC = new toolchains::Darwin_X86(*this, TCTriple, + DarwinVersion, + GCCVersion); + else + TC = new toolchains::Darwin_GCC(*this, TCTriple); + } + + return TC; +} + +// Unknown Host Info + +/// UnknownHostInfo - Generic host information to use for unknown +/// hosts. +class UnknownHostInfo : public HostInfo { + /// Cache of tool chains we have created. + mutable llvm::StringMap<ToolChain*> ToolChains; + +public: + UnknownHostInfo(const Driver &D, const llvm::Triple& Triple); + ~UnknownHostInfo(); + + virtual bool useDriverDriver() const; + + virtual types::ID lookupTypeForExtension(const char *Ext) const { + return types::lookupTypeForExtension(Ext); + } + + virtual ToolChain *getToolChain(const ArgList &Args, + const char *ArchName) const; +}; + +UnknownHostInfo::UnknownHostInfo(const Driver &D, const llvm::Triple& Triple) + : HostInfo(D, Triple) { +} + +UnknownHostInfo::~UnknownHostInfo() { + for (llvm::StringMap<ToolChain*>::iterator + it = ToolChains.begin(), ie = ToolChains.end(); it != ie; ++it) + delete it->second; +} + +bool UnknownHostInfo::useDriverDriver() const { + return false; +} + +ToolChain *UnknownHostInfo::getToolChain(const ArgList &Args, + const char *ArchName) const { + assert(!ArchName && + "Unexpected arch name on platform without driver driver support."); + + // Automatically handle some instances of -m32/-m64 we know about. + std::string Arch = getArchName(); + ArchName = Arch.c_str(); + if (Arg *A = Args.getLastArg(options::OPT_m32, options::OPT_m64)) { + if (getArchName() == "i386" || getArchName() == "x86_64") { + ArchName = + (A->getOption().getId() == options::OPT_m32) ? "i386" : "x86_64"; + } else if (getArchName() == "powerpc" || getArchName() == "powerpc64") { + ArchName = + (A->getOption().getId() == options::OPT_m32) ? "powerpc" : "powerpc64"; + } + } + + ToolChain *&TC = ToolChains[ArchName]; + if (!TC) { + llvm::Triple TCTriple(getTriple()); + TCTriple.setArchName(ArchName); + + TC = new toolchains::Generic_GCC(*this, TCTriple); + } + + return TC; +} + +// FreeBSD Host Info + +/// FreeBSDHostInfo - FreeBSD host information implementation. +class FreeBSDHostInfo : public HostInfo { + /// Cache of tool chains we have created. + mutable llvm::StringMap<ToolChain*> ToolChains; + +public: + FreeBSDHostInfo(const Driver &D, const llvm::Triple& Triple) + : HostInfo(D, Triple) {} + ~FreeBSDHostInfo(); + + virtual bool useDriverDriver() const; + + virtual types::ID lookupTypeForExtension(const char *Ext) const { + return types::lookupTypeForExtension(Ext); + } + + virtual ToolChain *getToolChain(const ArgList &Args, + const char *ArchName) const; +}; + +FreeBSDHostInfo::~FreeBSDHostInfo() { + for (llvm::StringMap<ToolChain*>::iterator + it = ToolChains.begin(), ie = ToolChains.end(); it != ie; ++it) + delete it->second; +} + +bool FreeBSDHostInfo::useDriverDriver() const { + return false; +} + +ToolChain *FreeBSDHostInfo::getToolChain(const ArgList &Args, + const char *ArchName) const { + bool Lib32 = false; + + assert(!ArchName && + "Unexpected arch name on platform without driver driver support."); + + // On x86_64 we need to be able to compile 32-bits binaries as well. + // Compiling 64-bit binaries on i386 is not supported. We don't have a + // lib64. + std::string Arch = getArchName(); + ArchName = Arch.c_str(); + if (Args.hasArg(options::OPT_m32) && getArchName() == "x86_64") { + ArchName = "i386"; + Lib32 = true; + } + + ToolChain *&TC = ToolChains[ArchName]; + if (!TC) { + llvm::Triple TCTriple(getTriple()); + TCTriple.setArchName(ArchName); + + TC = new toolchains::FreeBSD(*this, TCTriple, Lib32); + } + + return TC; +} + +// DragonFly Host Info + +/// DragonFlyHostInfo - DragonFly host information implementation. +class DragonFlyHostInfo : public HostInfo { + /// Cache of tool chains we have created. + mutable llvm::StringMap<ToolChain*> ToolChains; + +public: + DragonFlyHostInfo(const Driver &D, const llvm::Triple& Triple) + : HostInfo(D, Triple) {} + ~DragonFlyHostInfo(); + + virtual bool useDriverDriver() const; + + virtual types::ID lookupTypeForExtension(const char *Ext) const { + return types::lookupTypeForExtension(Ext); + } + + virtual ToolChain *getToolChain(const ArgList &Args, + const char *ArchName) const; +}; + +DragonFlyHostInfo::~DragonFlyHostInfo() { + for (llvm::StringMap<ToolChain*>::iterator + it = ToolChains.begin(), ie = ToolChains.end(); it != ie; ++it) + delete it->second; +} + +bool DragonFlyHostInfo::useDriverDriver() const { + return false; +} + +ToolChain *DragonFlyHostInfo::getToolChain(const ArgList &Args, + const char *ArchName) const { + assert(!ArchName && + "Unexpected arch name on platform without driver driver support."); + + ToolChain *&TC = ToolChains[getArchName()]; + + if (!TC) { + llvm::Triple TCTriple(getTriple()); + TCTriple.setArchName(getArchName()); + + TC = new toolchains::DragonFly(*this, TCTriple); + } + + return TC; +} + +// Linux Host Info + +/// LinuxHostInfo - Linux host information implementation. +class LinuxHostInfo : public HostInfo { + /// Cache of tool chains we have created. + mutable llvm::StringMap<ToolChain*> ToolChains; + +public: + LinuxHostInfo(const Driver &D, const llvm::Triple& Triple) + : HostInfo(D, Triple) {} + ~LinuxHostInfo(); + + virtual bool useDriverDriver() const; + + virtual types::ID lookupTypeForExtension(const char *Ext) const { + return types::lookupTypeForExtension(Ext); + } + + virtual ToolChain *getToolChain(const ArgList &Args, + const char *ArchName) const; +}; + +LinuxHostInfo::~LinuxHostInfo() { + for (llvm::StringMap<ToolChain*>::iterator + it = ToolChains.begin(), ie = ToolChains.end(); it != ie; ++it) + delete it->second; +} + +bool LinuxHostInfo::useDriverDriver() const { + return false; +} + +ToolChain *LinuxHostInfo::getToolChain(const ArgList &Args, + const char *ArchName) const { + + assert(!ArchName && + "Unexpected arch name on platform without driver driver support."); + + ArchName = getArchName().c_str(); + + ToolChain *&TC = ToolChains[ArchName]; + + if (!TC) { + llvm::Triple TCTriple(getTriple()); + TCTriple.setArchName(getArchName()); + + TC = new toolchains::Linux(*this, TCTriple); + } + + return TC; +} + +} + +const HostInfo * +clang::driver::createDarwinHostInfo(const Driver &D, + const llvm::Triple& Triple){ + return new DarwinHostInfo(D, Triple); +} + +const HostInfo * +clang::driver::createFreeBSDHostInfo(const Driver &D, + const llvm::Triple& Triple) { + return new FreeBSDHostInfo(D, Triple); +} + +const HostInfo * +clang::driver::createDragonFlyHostInfo(const Driver &D, + const llvm::Triple& Triple) { + return new DragonFlyHostInfo(D, Triple); +} + +const HostInfo * +clang::driver::createLinuxHostInfo(const Driver &D, + const llvm::Triple& Triple) { + return new LinuxHostInfo(D, Triple); +} + +const HostInfo * +clang::driver::createUnknownHostInfo(const Driver &D, + const llvm::Triple& Triple) { + return new UnknownHostInfo(D, Triple); +} diff --git a/lib/Driver/InputInfo.h b/lib/Driver/InputInfo.h new file mode 100644 index 0000000..c657bef --- /dev/null +++ b/lib/Driver/InputInfo.h @@ -0,0 +1,101 @@ +//===--- InputInfo.h - Input Source & Type Information ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_LIB_DRIVER_INPUTINFO_H_ +#define CLANG_LIB_DRIVER_INPUTINFO_H_ + +#include "clang/Driver/Types.h" + +#include <cassert> +#include <string> + +namespace clang { +namespace driver { + class PipedJob; + +/// InputInfo - Wrapper for information about an input source. +class InputInfo { + // FIXME: The distinction between filenames and inputarg here is + // gross; we should probably drop the idea of a "linker + // input". Doing so means tweaking pipelining to still create link + // steps when it sees linker inputs (but not treat them as + // arguments), and making sure that arguments get rendered + // correctly. + enum Class { + Nothing, + Filename, + InputArg, + Pipe + }; + + union { + const char *Filename; + const Arg *InputArg; + PipedJob *Pipe; + } Data; + Class Kind; + types::ID Type; + const char *BaseInput; + +public: + InputInfo() {} + InputInfo(types::ID _Type, const char *_BaseInput) + : Kind(Nothing), Type(_Type), BaseInput(_BaseInput) { + } + InputInfo(const char *_Filename, types::ID _Type, const char *_BaseInput) + : Kind(Filename), Type(_Type), BaseInput(_BaseInput) { + Data.Filename = _Filename; + } + InputInfo(const Arg *_InputArg, types::ID _Type, const char *_BaseInput) + : Kind(InputArg), Type(_Type), BaseInput(_BaseInput) { + Data.InputArg = _InputArg; + } + InputInfo(PipedJob *_Pipe, types::ID _Type, const char *_BaseInput) + : Kind(Pipe), Type(_Type), BaseInput(_BaseInput) { + Data.Pipe = _Pipe; + } + + bool isNothing() const { return Kind == Nothing; } + bool isFilename() const { return Kind == Filename; } + bool isInputArg() const { return Kind == InputArg; } + bool isPipe() const { return Kind == Pipe; } + types::ID getType() const { return Type; } + const char *getBaseInput() const { return BaseInput; } + + const char *getFilename() const { + assert(isFilename() && "Invalid accessor."); + return Data.Filename; + } + const Arg &getInputArg() const { + assert(isInputArg() && "Invalid accessor."); + return *Data.InputArg; + } + PipedJob &getPipe() const { + assert(isPipe() && "Invalid accessor."); + return *Data.Pipe; + } + + /// getAsString - Return a string name for this input, for + /// debugging. + std::string getAsString() const { + if (isPipe()) + return "(pipe)"; + else if (isFilename()) + return std::string("\"") + getFilename() + '"'; + else if (isInputArg()) + return "(input arg)"; + else + return "(nothing)"; + } +}; + +} // end namespace driver +} // end namespace clang + +#endif diff --git a/lib/Driver/Job.cpp b/lib/Driver/Job.cpp new file mode 100644 index 0000000..222cf15 --- /dev/null +++ b/lib/Driver/Job.cpp @@ -0,0 +1,31 @@ +//===--- Job.cpp - Command to Execute -----------------------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Job.h" + +#include <cassert> +using namespace clang::driver; + +Job::~Job() {} + +Command::Command(const char *_Executable, const ArgStringList &_Arguments) + : Job(CommandClass), Executable(_Executable), Arguments(_Arguments) { +} + +PipedJob::PipedJob() : Job(PipedJobClass) {} + +JobList::JobList() : Job(JobListClass) {} + +void Job::addCommand(Command *C) { + if (PipedJob *PJ = dyn_cast<PipedJob>(this)) + PJ->addCommand(C); + else + cast<JobList>(this)->addJob(C); +} + diff --git a/lib/Driver/Makefile b/lib/Driver/Makefile new file mode 100644 index 0000000..d163f0f --- /dev/null +++ b/lib/Driver/Makefile @@ -0,0 +1,28 @@ +##===- clang/lib/Driver/Makefile ---------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../../.. +LIBRARYNAME := clangDriver +BUILD_ARCHIVE = 1 +CXXFLAGS = -fno-rtti + + +include $(LEVEL)/Makefile.common + +SVN_REVISION := $(shell cd $(PROJ_SRC_DIR)/../.. && svnversion) + +CPP.Defines += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include \ + -DSVN_REVISION='"$(SVN_REVISION)"' + +$(ObjDir)/.ver-svn .ver: $(ObjDir)/.dir + @if [ '$(SVN_REVISION)' != '$(shell cat $(ObjDir)/.ver-svn 2>/dev/null)' ]; then\ + echo '$(SVN_REVISION)' > $(ObjDir)/.ver-svn; \ + fi +$(ObjDir)/.ver-svn: .ver +$(ObjDir)/Driver.o: $(ObjDir)/.ver-svn diff --git a/lib/Driver/OptTable.cpp b/lib/Driver/OptTable.cpp new file mode 100644 index 0000000..7ea6a8b --- /dev/null +++ b/lib/Driver/OptTable.cpp @@ -0,0 +1,265 @@ +//===--- Options.cpp - Option info table --------------------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Options.h" + +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/Option.h" +#include <algorithm> +#include <cassert> + +using namespace clang::driver; +using namespace clang::driver::options; + +struct Info { + const char *Name; + const char *Flags; + const char *HelpText; + const char *MetaVar; + + Option::OptionClass Kind; + unsigned GroupID; + unsigned AliasID; + unsigned Param; +}; + +// Ordering on Info. The ordering is *almost* lexicographic, with two +// exceptions. First, '\0' comes at the end of the alphabet instead of +// the beginning (thus options preceed any other options which prefix +// them). Second, for options with the same name, the less permissive +// version should come first; a Flag option should preceed a Joined +// option, for example. + +static int StrCmpOptionName(const char *A, const char *B) { + char a = *A, b = *B; + while (a == b) { + if (a == '\0') + return 0; + + a = *++A; + b = *++B; + } + + if (a == '\0') // A is a prefix of B. + return 1; + if (b == '\0') // B is a prefix of A. + return -1; + + // Otherwise lexicographic. + return (a < b) ? -1 : 1; +} + +static inline bool operator<(const Info &A, const Info &B) { + if (&A == &B) + return false; + + if (int N = StrCmpOptionName(A.Name, B.Name)) + return N == -1; + + // Names are the same, check that classes are in order; exactly one + // should be joined, and it should succeed the other. + assert(((A.Kind == Option::JoinedClass) ^ (B.Kind == Option::JoinedClass)) && + "Unexpected classes for options with same name."); + return B.Kind == Option::JoinedClass; +} + +// + +static Info OptionInfos[] = { + // The InputOption info + { "<input>", "d", 0, 0, Option::InputClass, OPT_INVALID, OPT_INVALID, 0 }, + // The UnknownOption info + { "<unknown>", "", 0, 0, Option::UnknownClass, OPT_INVALID, OPT_INVALID, 0 }, + +#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ + HELPTEXT, METAVAR) \ + { NAME, FLAGS, HELPTEXT, METAVAR, \ + Option::KIND##Class, OPT_##GROUP, OPT_##ALIAS, PARAM }, +#include "clang/Driver/Options.def" +}; +static const unsigned numOptions = sizeof(OptionInfos) / sizeof(OptionInfos[0]); + +static Info &getInfo(unsigned id) { + assert(id > 0 && id - 1 < numOptions && "Invalid Option ID."); + return OptionInfos[id - 1]; +} + +OptTable::OptTable() : Options(new Option*[numOptions]()) { + // Find start of normal options. + FirstSearchableOption = 0; + for (unsigned i = OPT_UNKNOWN + 1; i < LastOption; ++i) { + if (getInfo(i).Kind != Option::GroupClass) { + FirstSearchableOption = i; + break; + } + } + assert(FirstSearchableOption != 0 && "No searchable options?"); + +#ifndef NDEBUG + // Check that everything after the first searchable option is a + // regular option class. + for (unsigned i = FirstSearchableOption; i < LastOption; ++i) { + Option::OptionClass Kind = getInfo(i).Kind; + assert((Kind != Option::InputClass && Kind != Option::UnknownClass && + Kind != Option::GroupClass) && + "Special options should be defined first!"); + } + + // Check that options are in order. + for (unsigned i = FirstSearchableOption + 1; i < LastOption; ++i) { + if (!(getInfo(i - 1) < getInfo(i))) { + getOption((options::ID) (i - 1))->dump(); + getOption((options::ID) i)->dump(); + assert(0 && "Options are not in order!"); + } + } +#endif +} + +OptTable::~OptTable() { + for (unsigned i = 0; i < numOptions; ++i) + delete Options[i]; + delete[] Options; +} + +unsigned OptTable::getNumOptions() const { + return numOptions; +} + +const char *OptTable::getOptionName(options::ID id) const { + return getInfo(id).Name; +} + +unsigned OptTable::getOptionKind(options::ID id) const { + return getInfo(id).Kind; +} + +const char *OptTable::getOptionHelpText(options::ID id) const { + return getInfo(id).HelpText; +} + +const char *OptTable::getOptionMetaVar(options::ID id) const { + return getInfo(id).MetaVar; +} + +const Option *OptTable::getOption(options::ID id) const { + if (id == OPT_INVALID) + return 0; + + assert((unsigned) (id - 1) < numOptions && "Invalid ID."); + + Option *&Entry = Options[id - 1]; + if (!Entry) + Entry = constructOption(id); + + return Entry; +} + +Option *OptTable::constructOption(options::ID id) const { + Info &info = getInfo(id); + const OptionGroup *Group = + cast_or_null<OptionGroup>(getOption((options::ID) info.GroupID)); + const Option *Alias = getOption((options::ID) info.AliasID); + + Option *Opt = 0; + switch (info.Kind) { + case Option::InputClass: + Opt = new InputOption(); break; + case Option::UnknownClass: + Opt = new UnknownOption(); break; + case Option::GroupClass: + Opt = new OptionGroup(id, info.Name, Group); break; + case Option::FlagClass: + Opt = new FlagOption(id, info.Name, Group, Alias); break; + case Option::JoinedClass: + Opt = new JoinedOption(id, info.Name, Group, Alias); break; + case Option::SeparateClass: + Opt = new SeparateOption(id, info.Name, Group, Alias); break; + case Option::CommaJoinedClass: + Opt = new CommaJoinedOption(id, info.Name, Group, Alias); break; + case Option::MultiArgClass: + Opt = new MultiArgOption(id, info.Name, Group, Alias, info.Param); break; + case Option::JoinedOrSeparateClass: + Opt = new JoinedOrSeparateOption(id, info.Name, Group, Alias); break; + case Option::JoinedAndSeparateClass: + Opt = new JoinedAndSeparateOption(id, info.Name, Group, Alias); break; + } + + for (const char *s = info.Flags; *s; ++s) { + switch (*s) { + default: assert(0 && "Invalid option flag."); + case 'J': + assert(info.Kind == Option::SeparateClass && "Invalid option."); + Opt->setForceJoinedRender(true); break; + case 'S': + assert(info.Kind == Option::JoinedClass && "Invalid option."); + Opt->setForceSeparateRender(true); break; + case 'd': Opt->setDriverOption(true); break; + case 'i': Opt->setNoOptAsInput(true); break; + case 'l': Opt->setLinkerInput(true); break; + case 'q': Opt->setNoArgumentUnused(true); break; + case 'u': Opt->setUnsupported(true); break; + } + } + + return Opt; +} + +// Support lower_bound between info and an option name. +static inline bool operator<(struct Info &I, const char *Name) { + return StrCmpOptionName(I.Name, Name) == -1; +} +static inline bool operator<(const char *Name, struct Info &I) { + return StrCmpOptionName(Name, I.Name) == -1; +} + +Arg *OptTable::ParseOneArg(const InputArgList &Args, unsigned &Index) const { + unsigned Prev = Index; + const char *Str = Args.getArgString(Index); + + // Anything that doesn't start with '-' is an input, as is '-' itself. + if (Str[0] != '-' || Str[1] == '\0') + return new PositionalArg(getOption(OPT_INPUT), Index++); + + struct Info *Start = OptionInfos + FirstSearchableOption - 1; + struct Info *End = OptionInfos + LastOption - 1; + + // Search for the first next option which could be a prefix. + Start = std::lower_bound(Start, End, Str); + + // Options are stored in sorted order, with '\0' at the end of the + // alphabet. Since the only options which can accept a string must + // prefix it, we iteratively search for the next option which could + // be a prefix. + // + // FIXME: This is searching much more than necessary, but I am + // blanking on the simplest way to make it fast. We can solve this + // problem when we move to TableGen. + for (; Start != End; ++Start) { + // Scan for first option which is a proper prefix. + for (; Start != End; ++Start) + if (memcmp(Str, Start->Name, strlen(Start->Name)) == 0) + break; + if (Start == End) + break; + + // See if this option matches. + options::ID id = (options::ID) (Start - OptionInfos + 1); + if (Arg *A = getOption(id)->accept(Args, Index)) + return A; + + // Otherwise, see if this argument was missing values. + if (Prev != Index) + return 0; + } + + return new PositionalArg(getOption(OPT_UNKNOWN), Index++); +} + diff --git a/lib/Driver/Option.cpp b/lib/Driver/Option.cpp new file mode 100644 index 0000000..cad2bbf --- /dev/null +++ b/lib/Driver/Option.cpp @@ -0,0 +1,250 @@ +//===--- Option.cpp - Abstract Driver Options ---------------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Option.h" + +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <algorithm> +using namespace clang::driver; + +Option::Option(OptionClass _Kind, options::ID _ID, const char *_Name, + const OptionGroup *_Group, const Option *_Alias) + : Kind(_Kind), ID(_ID), Name(_Name), Group(_Group), Alias(_Alias), + Unsupported(false), LinkerInput(false), NoOptAsInput(false), + ForceSeparateRender(false), ForceJoinedRender(false), + DriverOption(false), NoArgumentUnused(false) +{ + + // Multi-level aliases are not supported, and alias options cannot + // have groups. This just simplifies option tracking, it is not an + // inherent limitation. + assert((!Alias || (!Alias->Alias && !Group)) && + "Multi-level aliases and aliases with groups are unsupported."); +} + +Option::~Option() { +} + +void Option::dump() const { + llvm::errs() << "<"; + switch (Kind) { + default: + assert(0 && "Invalid kind"); +#define P(N) case N: llvm::errs() << #N; break + P(GroupClass); + P(InputClass); + P(UnknownClass); + P(FlagClass); + P(JoinedClass); + P(SeparateClass); + P(CommaJoinedClass); + P(MultiArgClass); + P(JoinedOrSeparateClass); + P(JoinedAndSeparateClass); +#undef P + } + + llvm::errs() << " Name:\"" << Name << '"'; + + if (Group) { + llvm::errs() << " Group:"; + Group->dump(); + } + + if (Alias) { + llvm::errs() << " Alias:"; + Alias->dump(); + } + + if (const MultiArgOption *MOA = dyn_cast<MultiArgOption>(this)) + llvm::errs() << " NumArgs:" << MOA->getNumArgs(); + + llvm::errs() << ">\n"; +} + +bool Option::matches(const Option *Opt) const { + // Aliases are never considered in matching. + if (Opt->getAlias()) + return matches(Opt->getAlias()); + if (Alias) + return Alias->matches(Opt); + + if (this == Opt) + return true; + + if (Group) + return Group->matches(Opt); + return false; +} + +bool Option::matches(options::ID Id) const { + // FIXME: Decide what to do here; we should either pull out the + // handling of alias on the option for Id from the other matches, or + // find some other solution (which hopefully doesn't require using + // the option table). + if (Alias) + return Alias->matches(Id); + + if (ID == Id) + return true; + + if (Group) + return Group->matches(Id); + return false; +} + +OptionGroup::OptionGroup(options::ID ID, const char *Name, + const OptionGroup *Group) + : Option(Option::GroupClass, ID, Name, Group, 0) { +} + +Arg *OptionGroup::accept(const InputArgList &Args, unsigned &Index) const { + assert(0 && "accept() should never be called on an OptionGroup"); + return 0; +} + +InputOption::InputOption() + : Option(Option::InputClass, options::OPT_INPUT, "<input>", 0, 0) { +} + +Arg *InputOption::accept(const InputArgList &Args, unsigned &Index) const { + assert(0 && "accept() should never be called on an InputOption"); + return 0; +} + +UnknownOption::UnknownOption() + : Option(Option::UnknownClass, options::OPT_UNKNOWN, "<unknown>", 0, 0) { +} + +Arg *UnknownOption::accept(const InputArgList &Args, unsigned &Index) const { + assert(0 && "accept() should never be called on an UnknownOption"); + return 0; +} + +FlagOption::FlagOption(options::ID ID, const char *Name, + const OptionGroup *Group, const Option *Alias) + : Option(Option::FlagClass, ID, Name, Group, Alias) { +} + +Arg *FlagOption::accept(const InputArgList &Args, unsigned &Index) const { + // Matches iff this is an exact match. + // FIXME: Avoid strlen. + if (strlen(getName()) != strlen(Args.getArgString(Index))) + return 0; + + return new FlagArg(this, Index++); +} + +JoinedOption::JoinedOption(options::ID ID, const char *Name, + const OptionGroup *Group, const Option *Alias) + : Option(Option::JoinedClass, ID, Name, Group, Alias) { +} + +Arg *JoinedOption::accept(const InputArgList &Args, unsigned &Index) const { + // Always matches. + return new JoinedArg(this, Index++); +} + +CommaJoinedOption::CommaJoinedOption(options::ID ID, const char *Name, + const OptionGroup *Group, + const Option *Alias) + : Option(Option::CommaJoinedClass, ID, Name, Group, Alias) { +} + +Arg *CommaJoinedOption::accept(const InputArgList &Args, + unsigned &Index) const { + // Always matches. We count the commas now so we can answer + // getNumValues easily. + + // Get the suffix string. + // FIXME: Avoid strlen, and move to helper method? + const char *Suffix = Args.getArgString(Index) + strlen(getName()); + return new CommaJoinedArg(this, Index++, Suffix); +} + +SeparateOption::SeparateOption(options::ID ID, const char *Name, + const OptionGroup *Group, const Option *Alias) + : Option(Option::SeparateClass, ID, Name, Group, Alias) { +} + +Arg *SeparateOption::accept(const InputArgList &Args, unsigned &Index) const { + // Matches iff this is an exact match. + // FIXME: Avoid strlen. + if (strlen(getName()) != strlen(Args.getArgString(Index))) + return 0; + + Index += 2; + if (Index > Args.getNumInputArgStrings()) + return 0; + + return new SeparateArg(this, Index - 2, 1); +} + +MultiArgOption::MultiArgOption(options::ID ID, const char *Name, + const OptionGroup *Group, const Option *Alias, + unsigned _NumArgs) + : Option(Option::MultiArgClass, ID, Name, Group, Alias), NumArgs(_NumArgs) { + assert(NumArgs > 1 && "Invalid MultiArgOption!"); +} + +Arg *MultiArgOption::accept(const InputArgList &Args, unsigned &Index) const { + // Matches iff this is an exact match. + // FIXME: Avoid strlen. + if (strlen(getName()) != strlen(Args.getArgString(Index))) + return 0; + + Index += 1 + NumArgs; + if (Index > Args.getNumInputArgStrings()) + return 0; + + return new SeparateArg(this, Index - 1 - NumArgs, NumArgs); +} + +JoinedOrSeparateOption::JoinedOrSeparateOption(options::ID ID, const char *Name, + const OptionGroup *Group, + const Option *Alias) + : Option(Option::JoinedOrSeparateClass, ID, Name, Group, Alias) { +} + +Arg *JoinedOrSeparateOption::accept(const InputArgList &Args, + unsigned &Index) const { + // If this is not an exact match, it is a joined arg. + // FIXME: Avoid strlen. + if (strlen(getName()) != strlen(Args.getArgString(Index))) + return new JoinedArg(this, Index++); + + // Otherwise it must be separate. + Index += 2; + if (Index > Args.getNumInputArgStrings()) + return 0; + + return new SeparateArg(this, Index - 2, 1); +} + +JoinedAndSeparateOption::JoinedAndSeparateOption(options::ID ID, + const char *Name, + const OptionGroup *Group, + const Option *Alias) + : Option(Option::JoinedAndSeparateClass, ID, Name, Group, Alias) { +} + +Arg *JoinedAndSeparateOption::accept(const InputArgList &Args, + unsigned &Index) const { + // Always matches. + + Index += 2; + if (Index > Args.getNumInputArgStrings()) + return 0; + + return new JoinedAndSeparateArg(this, Index - 2); +} + diff --git a/lib/Driver/Phases.cpp b/lib/Driver/Phases.cpp new file mode 100644 index 0000000..df4cdee --- /dev/null +++ b/lib/Driver/Phases.cpp @@ -0,0 +1,27 @@ +//===--- Phases.cpp - Transformations on Driver Types -------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Phases.h" + +#include <cassert> + +using namespace clang::driver; + +const char *phases::getPhaseName(ID Id) { + switch (Id) { + case Preprocess: return "preprocessor"; + case Precompile: return "precompiler"; + case Compile: return "compiler"; + case Assemble: return "assembler"; + case Link: return "linker"; + } + + assert(0 && "Invalid phase id."); + return 0; +} diff --git a/lib/Driver/Tool.cpp b/lib/Driver/Tool.cpp new file mode 100644 index 0000000..6f6589a --- /dev/null +++ b/lib/Driver/Tool.cpp @@ -0,0 +1,19 @@ +//===--- Tool.cpp - Compilation Tools -----------------------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Tool.h" + +using namespace clang::driver; + +Tool::Tool(const char *_Name, const ToolChain &TC) : Name(_Name), + TheToolChain(TC) { +} + +Tool::~Tool() { +} diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp new file mode 100644 index 0000000..20ed31b --- /dev/null +++ b/lib/Driver/ToolChain.cpp @@ -0,0 +1,35 @@ +//===--- ToolChain.cpp - Collections of tools for one platform ----------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/ToolChain.h" + +#include "clang/Driver/Action.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/HostInfo.h" + +using namespace clang::driver; + +ToolChain::ToolChain(const HostInfo &_Host, const llvm::Triple &_Triple) + : Host(_Host), Triple(_Triple) { +} + +ToolChain::~ToolChain() { +} + +llvm::sys::Path ToolChain::GetFilePath(const Compilation &C, + const char *Name) const { + return Host.getDriver().GetFilePath(Name, *this); + +} + +llvm::sys::Path ToolChain::GetProgramPath(const Compilation &C, + const char *Name, + bool WantFile) const { + return Host.getDriver().GetProgramPath(Name, *this, WantFile); +} diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp new file mode 100644 index 0000000..436d343 --- /dev/null +++ b/lib/Driver/ToolChains.cpp @@ -0,0 +1,475 @@ +//===--- ToolChains.cpp - ToolChain Implementations ---------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ToolChains.h" + +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/HostInfo.h" +#include "clang/Driver/Option.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" + +#include <cstdlib> // ::getenv + +using namespace clang::driver; +using namespace clang::driver::toolchains; + +/// Darwin_X86 - Darwin tool chain for i386 and x86_64. + +Darwin_X86::Darwin_X86(const HostInfo &Host, const llvm::Triple& Triple, + const unsigned (&_DarwinVersion)[3], + const unsigned (&_GCCVersion)[3]) + : ToolChain(Host, Triple) { + DarwinVersion[0] = _DarwinVersion[0]; + DarwinVersion[1] = _DarwinVersion[1]; + DarwinVersion[2] = _DarwinVersion[2]; + GCCVersion[0] = _GCCVersion[0]; + GCCVersion[1] = _GCCVersion[1]; + GCCVersion[2] = _GCCVersion[2]; + + llvm::raw_string_ostream(MacosxVersionMin) + << "10." << DarwinVersion[0] - 4 << '.' << DarwinVersion[1]; + + ToolChainDir = "i686-apple-darwin"; + ToolChainDir += llvm::utostr(DarwinVersion[0]); + ToolChainDir += "/"; + ToolChainDir += llvm::utostr(GCCVersion[0]); + ToolChainDir += '.'; + ToolChainDir += llvm::utostr(GCCVersion[1]); + ToolChainDir += '.'; + ToolChainDir += llvm::utostr(GCCVersion[2]); + + std::string Path; + if (getArchName() == "x86_64") { + Path = getHost().getDriver().Dir; + Path += "/../lib/gcc/"; + Path += getToolChainDir(); + Path += "/x86_64"; + getFilePaths().push_back(Path); + + Path = "/usr/lib/gcc/"; + Path += getToolChainDir(); + Path += "/x86_64"; + getFilePaths().push_back(Path); + } + + Path = getHost().getDriver().Dir; + Path += "/../lib/gcc/"; + Path += getToolChainDir(); + getFilePaths().push_back(Path); + + Path = "/usr/lib/gcc/"; + Path += getToolChainDir(); + getFilePaths().push_back(Path); + + Path = getHost().getDriver().Dir; + Path += "/../libexec/gcc/"; + Path += getToolChainDir(); + getProgramPaths().push_back(Path); + + Path = "/usr/libexec/gcc/"; + Path += getToolChainDir(); + getProgramPaths().push_back(Path); + + Path = getHost().getDriver().Dir; + Path += "/../libexec"; + getProgramPaths().push_back(Path); + + getProgramPaths().push_back(getHost().getDriver().Dir); +} + +Darwin_X86::~Darwin_X86() { + // Free tool implementations. + for (llvm::DenseMap<unsigned, Tool*>::iterator + it = Tools.begin(), ie = Tools.end(); it != ie; ++it) + delete it->second; +} + +Tool &Darwin_X86::SelectTool(const Compilation &C, + const JobAction &JA) const { + Action::ActionClass Key; + if (getHost().getDriver().ShouldUseClangCompiler(C, JA, getArchName())) + Key = Action::AnalyzeJobClass; + else + Key = JA.getKind(); + + Tool *&T = Tools[Key]; + if (!T) { + switch (Key) { + case Action::InputClass: + case Action::BindArchClass: + assert(0 && "Invalid tool kind."); + case Action::PreprocessJobClass: + T = new tools::darwin::Preprocess(*this); break; + case Action::AnalyzeJobClass: + T = new tools::Clang(*this); break; + case Action::PrecompileJobClass: + case Action::CompileJobClass: + T = new tools::darwin::Compile(*this); break; + case Action::AssembleJobClass: + T = new tools::darwin::Assemble(*this); break; + case Action::LinkJobClass: + T = new tools::darwin::Link(*this, MacosxVersionMin.c_str()); break; + case Action::LipoJobClass: + T = new tools::darwin::Lipo(*this); break; + } + } + + return *T; +} + +DerivedArgList *Darwin_X86::TranslateArgs(InputArgList &Args) const { + DerivedArgList *DAL = new DerivedArgList(Args, false); + const OptTable &Opts = getHost().getDriver().getOpts(); + + // FIXME: We really want to get out of the tool chain level argument + // translation business, as it makes the driver functionality much + // more opaque. For now, we follow gcc closely solely for the + // purpose of easily achieving feature parity & testability. Once we + // have something that works, we should reevaluate each translation + // and try to push it down into tool specific logic. + + Arg *OSXVersion = + Args.getLastArg(options::OPT_mmacosx_version_min_EQ, false); + Arg *iPhoneVersion = + Args.getLastArg(options::OPT_miphoneos_version_min_EQ, false); + if (OSXVersion && iPhoneVersion) { + getHost().getDriver().Diag(clang::diag::err_drv_argument_not_allowed_with) + << OSXVersion->getAsString(Args) + << iPhoneVersion->getAsString(Args); + } else if (!OSXVersion && !iPhoneVersion) { + // Chose the default version based on the arch. + // + // FIXME: This will need to be fixed when we merge in arm support. + + // Look for MACOSX_DEPLOYMENT_TARGET, otherwise use the version + // from the host. + const char *Version = ::getenv("MACOSX_DEPLOYMENT_TARGET"); + if (!Version) + Version = MacosxVersionMin.c_str(); + const Option *O = Opts.getOption(options::OPT_mmacosx_version_min_EQ); + DAL->append(DAL->MakeJoinedArg(0, O, Version)); + } + + for (ArgList::iterator it = Args.begin(), ie = Args.end(); it != ie; ++it) { + Arg *A = *it; + + if (A->getOption().matches(options::OPT_Xarch__)) { + // FIXME: Canonicalize name. + if (getArchName() != A->getValue(Args, 0)) + continue; + + // FIXME: The arg is leaked here, and we should have a nicer + // interface for this. + unsigned Prev, Index = Prev = A->getIndex() + 1; + Arg *XarchArg = Opts.ParseOneArg(Args, Index); + + // If the argument parsing failed or more than one argument was + // consumed, the -Xarch_ argument's parameter tried to consume + // extra arguments. Emit an error and ignore. + // + // We also want to disallow any options which would alter the + // driver behavior; that isn't going to work in our model. We + // use isDriverOption() as an approximation, although things + // like -O4 are going to slip through. + if (!XarchArg || Index > Prev + 1 || + XarchArg->getOption().isDriverOption()) { + getHost().getDriver().Diag(clang::diag::err_drv_invalid_Xarch_argument) + << A->getAsString(Args); + continue; + } + + XarchArg->setBaseArg(A); + A = XarchArg; + } + + // Sob. These is strictly gcc compatible for the time being. Apple + // gcc translates options twice, which means that self-expanding + // options add duplicates. + options::ID id = A->getOption().getId(); + switch (id) { + default: + DAL->append(A); + break; + + case options::OPT_mkernel: + case options::OPT_fapple_kext: + DAL->append(A); + DAL->append(DAL->MakeFlagArg(A, Opts.getOption(options::OPT_static))); + DAL->append(DAL->MakeFlagArg(A, Opts.getOption(options::OPT_static))); + break; + + case options::OPT_dependency_file: + DAL->append(DAL->MakeSeparateArg(A, Opts.getOption(options::OPT_MF), + A->getValue(Args))); + break; + + case options::OPT_gfull: + DAL->append(DAL->MakeFlagArg(A, Opts.getOption(options::OPT_g_Flag))); + DAL->append(DAL->MakeFlagArg(A, + Opts.getOption(options::OPT_fno_eliminate_unused_debug_symbols))); + break; + + case options::OPT_gused: + DAL->append(DAL->MakeFlagArg(A, Opts.getOption(options::OPT_g_Flag))); + DAL->append(DAL->MakeFlagArg(A, + Opts.getOption(options::OPT_feliminate_unused_debug_symbols))); + break; + + case options::OPT_fterminated_vtables: + case options::OPT_findirect_virtual_calls: + DAL->append(DAL->MakeFlagArg(A, + Opts.getOption(options::OPT_fapple_kext))); + DAL->append(DAL->MakeFlagArg(A, Opts.getOption(options::OPT_static))); + break; + + case options::OPT_shared: + DAL->append(DAL->MakeFlagArg(A, Opts.getOption(options::OPT_dynamiclib))); + break; + + case options::OPT_fconstant_cfstrings: + DAL->append(DAL->MakeFlagArg(A, + Opts.getOption(options::OPT_mconstant_cfstrings))); + break; + + case options::OPT_fno_constant_cfstrings: + DAL->append(DAL->MakeFlagArg(A, + Opts.getOption(options::OPT_mno_constant_cfstrings))); + break; + + case options::OPT_Wnonportable_cfstrings: + DAL->append(DAL->MakeFlagArg(A, + Opts.getOption(options::OPT_mwarn_nonportable_cfstrings))); + break; + + case options::OPT_Wno_nonportable_cfstrings: + DAL->append(DAL->MakeFlagArg(A, + Opts.getOption(options::OPT_mno_warn_nonportable_cfstrings))); + break; + + case options::OPT_fpascal_strings: + DAL->append(DAL->MakeFlagArg(A, + Opts.getOption(options::OPT_mpascal_strings))); + break; + + case options::OPT_fno_pascal_strings: + DAL->append(DAL->MakeFlagArg(A, + Opts.getOption(options::OPT_mno_pascal_strings))); + break; + } + } + + // FIXME: Actually, gcc always adds this, but it is filtered for + // duplicates somewhere. This also changes the order of things, so + // look it up. + if (getArchName() == "x86_64") + if (!Args.hasArg(options::OPT_m64, false)) + DAL->append(DAL->MakeFlagArg(0, Opts.getOption(options::OPT_m64))); + + if (!Args.hasArg(options::OPT_mtune_EQ, false)) + DAL->append(DAL->MakeJoinedArg(0, Opts.getOption(options::OPT_mtune_EQ), + "core2")); + + return DAL; +} + +bool Darwin_X86::IsMathErrnoDefault() const { + return false; +} + +bool Darwin_X86::IsUnwindTablesDefault() const { + // FIXME: Gross; we should probably have some separate target + // definition, possibly even reusing the one in clang. + return getArchName() == "x86_64"; +} + +const char *Darwin_X86::GetDefaultRelocationModel() const { + return "pic"; +} + +const char *Darwin_X86::GetForcedPicModel() const { + if (getArchName() == "x86_64") + return "pic"; + return 0; +} + +/// Generic_GCC - A tool chain using the 'gcc' command to perform +/// all subcommands; this relies on gcc translating the majority of +/// command line options. + +Generic_GCC::Generic_GCC(const HostInfo &Host, const llvm::Triple& Triple) + : ToolChain(Host, Triple) +{ + std::string Path(getHost().getDriver().Dir); + Path += "/../libexec"; + getProgramPaths().push_back(Path); + + getProgramPaths().push_back(getHost().getDriver().Dir); +} + +Generic_GCC::~Generic_GCC() { + // Free tool implementations. + for (llvm::DenseMap<unsigned, Tool*>::iterator + it = Tools.begin(), ie = Tools.end(); it != ie; ++it) + delete it->second; +} + +Tool &Generic_GCC::SelectTool(const Compilation &C, + const JobAction &JA) const { + Action::ActionClass Key; + if (getHost().getDriver().ShouldUseClangCompiler(C, JA, getArchName())) + Key = Action::AnalyzeJobClass; + else + Key = JA.getKind(); + + Tool *&T = Tools[Key]; + if (!T) { + switch (Key) { + case Action::InputClass: + case Action::BindArchClass: + assert(0 && "Invalid tool kind."); + case Action::PreprocessJobClass: + T = new tools::gcc::Preprocess(*this); break; + case Action::PrecompileJobClass: + T = new tools::gcc::Precompile(*this); break; + case Action::AnalyzeJobClass: + T = new tools::Clang(*this); break; + case Action::CompileJobClass: + T = new tools::gcc::Compile(*this); break; + case Action::AssembleJobClass: + T = new tools::gcc::Assemble(*this); break; + case Action::LinkJobClass: + T = new tools::gcc::Link(*this); break; + + // This is a bit ungeneric, but the only platform using a driver + // driver is Darwin. + case Action::LipoJobClass: + T = new tools::darwin::Lipo(*this); break; + } + } + + return *T; +} + +bool Generic_GCC::IsMathErrnoDefault() const { + return true; +} + +bool Generic_GCC::IsUnwindTablesDefault() const { + // FIXME: Gross; we should probably have some separate target + // definition, possibly even reusing the one in clang. + return getArchName() == "x86_64"; +} + +const char *Generic_GCC::GetDefaultRelocationModel() const { + return "static"; +} + +const char *Generic_GCC::GetForcedPicModel() const { + return 0; +} + +DerivedArgList *Generic_GCC::TranslateArgs(InputArgList &Args) const { + return new DerivedArgList(Args, true); +} + +/// FreeBSD - FreeBSD tool chain which can call as(1) and ld(1) directly. + +FreeBSD::FreeBSD(const HostInfo &Host, const llvm::Triple& Triple, bool Lib32) + : Generic_GCC(Host, Triple) { + if (Lib32) { + getFilePaths().push_back(getHost().getDriver().Dir + "/../lib32"); + getFilePaths().push_back("/usr/lib32"); + } else { + getFilePaths().push_back(getHost().getDriver().Dir + "/../lib"); + getFilePaths().push_back("/usr/lib"); + } +} + +Tool &FreeBSD::SelectTool(const Compilation &C, const JobAction &JA) const { + Action::ActionClass Key; + if (getHost().getDriver().ShouldUseClangCompiler(C, JA, getArchName())) + Key = Action::AnalyzeJobClass; + else + Key = JA.getKind(); + + Tool *&T = Tools[Key]; + if (!T) { + switch (Key) { + case Action::AssembleJobClass: + T = new tools::freebsd::Assemble(*this); break; + case Action::LinkJobClass: + T = new tools::freebsd::Link(*this); break; + default: + T = &Generic_GCC::SelectTool(C, JA); + } + } + + return *T; +} + +/// Linux toolchain (very bare-bones at the moment). + +Linux::Linux(const HostInfo &Host, const llvm::Triple& Triple) + : Generic_GCC(Host, Triple) { + getFilePaths().push_back(getHost().getDriver().Dir + "/../lib/clang/1.0/"); + getFilePaths().push_back("/lib/"); + getFilePaths().push_back("/usr/lib/"); + // FIXME: Figure out some way to get gcc's libdir + // (e.g. /usr/lib/gcc/i486-linux-gnu/4.3/ for Ubuntu 32-bit); we need + // crtbegin.o/crtend.o/etc., and want static versions of various + // libraries. If we had our own crtbegin.o/crtend.o/etc, we could probably + // get away with using shared versions in /usr/lib, though. + // We could fall back to the approach we used for includes (a massive + // list), but that's messy at best. +} + +/// DragonFly - DragonFly tool chain which can call as(1) and ld(1) directly. + +DragonFly::DragonFly(const HostInfo &Host, const llvm::Triple& Triple) + : Generic_GCC(Host, Triple) { + + // Path mangling to find libexec + std::string Path(getHost().getDriver().Dir); + + Path += "/../libexec"; + getProgramPaths().push_back(Path); + getProgramPaths().push_back(getHost().getDriver().Dir); + + getFilePaths().push_back(getHost().getDriver().Dir + "/../lib"); + getFilePaths().push_back("/usr/lib"); + getFilePaths().push_back("/usr/lib/gcc41"); +} + +Tool &DragonFly::SelectTool(const Compilation &C, const JobAction &JA) const { + Action::ActionClass Key; + if (getHost().getDriver().ShouldUseClangCompiler(C, JA, getArchName())) + Key = Action::AnalyzeJobClass; + else + Key = JA.getKind(); + + Tool *&T = Tools[Key]; + if (!T) { + switch (Key) { + case Action::AssembleJobClass: + T = new tools::dragonfly::Assemble(*this); break; + case Action::LinkJobClass: + T = new tools::dragonfly::Link(*this); break; + default: + T = &Generic_GCC::SelectTool(C, JA); + } + } + + return *T; +} diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h new file mode 100644 index 0000000..3ecd171 --- /dev/null +++ b/lib/Driver/ToolChains.h @@ -0,0 +1,134 @@ +//===--- ToolChains.h - ToolChain Implementations ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_LIB_DRIVER_TOOLCHAINS_H_ +#define CLANG_LIB_DRIVER_TOOLCHAINS_H_ + +#include "clang/Driver/Action.h" +#include "clang/Driver/ToolChain.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/Compiler.h" + +#include "Tools.h" + +namespace clang { +namespace driver { +namespace toolchains { + + /// Generic_GCC - A tool chain using the 'gcc' command to perform + /// all subcommands; this relies on gcc translating the majority of + /// command line options. +class VISIBILITY_HIDDEN Generic_GCC : public ToolChain { +protected: + mutable llvm::DenseMap<unsigned, Tool*> Tools; + +public: + Generic_GCC(const HostInfo &Host, const llvm::Triple& Triple); + ~Generic_GCC(); + + virtual DerivedArgList *TranslateArgs(InputArgList &Args) const; + + virtual Tool &SelectTool(const Compilation &C, const JobAction &JA) const; + + virtual bool IsMathErrnoDefault() const; + virtual bool IsUnwindTablesDefault() const; + virtual const char *GetDefaultRelocationModel() const; + virtual const char *GetForcedPicModel() const; +}; + + /// Darwin_X86 - Darwin tool chain for i386 an x86_64. +class VISIBILITY_HIDDEN Darwin_X86 : public ToolChain { + mutable llvm::DenseMap<unsigned, Tool*> Tools; + + /// Darwin version of tool chain. + unsigned DarwinVersion[3]; + + /// GCC version to use. + unsigned GCCVersion[3]; + + /// The directory suffix for this tool chain. + std::string ToolChainDir; + + /// The default macosx-version-min of this tool chain; empty until + /// initialized. + mutable std::string MacosxVersionMin; + + const char *getMacosxVersionMin() const; + +public: + Darwin_X86(const HostInfo &Host, const llvm::Triple& Triple, + const unsigned (&DarwinVersion)[3], + const unsigned (&GCCVersion)[3]); + ~Darwin_X86(); + + void getDarwinVersion(unsigned (&Res)[3]) const { + Res[0] = DarwinVersion[0]; + Res[1] = DarwinVersion[1]; + Res[2] = DarwinVersion[2]; + } + + void getMacosxVersion(unsigned (&Res)[3]) const { + Res[0] = 10; + Res[1] = DarwinVersion[0] - 4; + Res[2] = DarwinVersion[1]; + } + + const char *getMacosxVersionStr() const { + return MacosxVersionMin.c_str(); + } + + const std::string &getToolChainDir() const { + return ToolChainDir; + } + + virtual DerivedArgList *TranslateArgs(InputArgList &Args) const; + + virtual Tool &SelectTool(const Compilation &C, const JobAction &JA) const; + + virtual bool IsMathErrnoDefault() const; + virtual bool IsUnwindTablesDefault() const; + virtual const char *GetDefaultRelocationModel() const; + virtual const char *GetForcedPicModel() const; +}; + + /// Darwin_GCC - Generic Darwin tool chain using gcc. +class VISIBILITY_HIDDEN Darwin_GCC : public Generic_GCC { +public: + Darwin_GCC(const HostInfo &Host, const llvm::Triple& Triple) + : Generic_GCC(Host, Triple) {} + + virtual const char *GetDefaultRelocationModel() const { return "pic"; } +}; + +class VISIBILITY_HIDDEN FreeBSD : public Generic_GCC { +public: + FreeBSD(const HostInfo &Host, const llvm::Triple& Triple, bool Lib32); + + virtual Tool &SelectTool(const Compilation &C, const JobAction &JA) const; +}; + +class VISIBILITY_HIDDEN DragonFly : public Generic_GCC { +public: + DragonFly(const HostInfo &Host, const llvm::Triple& Triple); + + virtual Tool &SelectTool(const Compilation &C, const JobAction &JA) const; +}; + +class VISIBILITY_HIDDEN Linux : public Generic_GCC { +public: + Linux(const HostInfo &Host, const llvm::Triple& Triple); +}; + + +} // end namespace toolchains +} // end namespace driver +} // end namespace clang + +#endif diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp new file mode 100644 index 0000000..abfabbb --- /dev/null +++ b/lib/Driver/Tools.cpp @@ -0,0 +1,2033 @@ +//===--- Tools.cpp - Tools Implementations ------------------------------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Tools.h" + +#include "clang/Driver/Action.h" +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/Driver.h" // FIXME: Remove? +#include "clang/Driver/DriverDiagnostic.h" // FIXME: Remove? +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Job.h" +#include "clang/Driver/HostInfo.h" +#include "clang/Driver/Option.h" +#include "clang/Driver/ToolChain.h" +#include "clang/Driver/Util.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +#include "InputInfo.h" +#include "ToolChains.h" + +using namespace clang::driver; +using namespace clang::driver::tools; + +static const char *MakeFormattedString(const ArgList &Args, + const llvm::format_object_base &Fmt) { + std::string Str; + llvm::raw_string_ostream(Str) << Fmt; + return Args.MakeArgString(Str.c_str()); +} + +void Clang::AddPreprocessingOptions(const Driver &D, + const ArgList &Args, + ArgStringList &CmdArgs, + const InputInfo &Output, + const InputInfoList &Inputs) const { + // Handle dependency file generation. + Arg *A; + if ((A = Args.getLastArg(options::OPT_M)) || + (A = Args.getLastArg(options::OPT_MM)) || + (A = Args.getLastArg(options::OPT_MD)) || + (A = Args.getLastArg(options::OPT_MMD))) { + // Determine the output location. + const char *DepFile; + if (Output.getType() == types::TY_Dependencies) { + if (Output.isPipe()) + DepFile = "-"; + else + DepFile = Output.getFilename(); + } else if (Arg *MF = Args.getLastArg(options::OPT_MF)) { + DepFile = MF->getValue(Args); + } else if (A->getOption().getId() == options::OPT_M || + A->getOption().getId() == options::OPT_MM) { + DepFile = "-"; + } else { + DepFile = darwin::CC1::getDependencyFileName(Args, Inputs); + } + CmdArgs.push_back("-dependency-file"); + CmdArgs.push_back(DepFile); + + // Add an -MT option if the user didn't specify their own. + // FIXME: This should use -MQ, when we support it. + if (!Args.hasArg(options::OPT_MT) && !Args.hasArg(options::OPT_MQ)) { + const char *DepTarget; + + // If user provided -o, that is the dependency target, except + // when we are only generating a dependency file. + Arg *OutputOpt = Args.getLastArg(options::OPT_o); + if (OutputOpt && Output.getType() != types::TY_Dependencies) { + DepTarget = OutputOpt->getValue(Args); + } else { + // Otherwise derive from the base input. + // + // FIXME: This should use the computed output file location. + llvm::sys::Path P(Inputs[0].getBaseInput()); + + P.eraseSuffix(); + P.appendSuffix("o"); + DepTarget = Args.MakeArgString(P.getLast().c_str()); + } + + CmdArgs.push_back("-MT"); + CmdArgs.push_back(DepTarget); + } + + if (A->getOption().getId() == options::OPT_M || + A->getOption().getId() == options::OPT_MD) + CmdArgs.push_back("-sys-header-deps"); + } + + Args.AddLastArg(CmdArgs, options::OPT_MP); + Args.AddAllArgs(CmdArgs, options::OPT_MT); + + // FIXME: Use iterator. + + // Add -i* options, and automatically translate to + // -include-pch/-include-pth for transparent PCH support. It's + // wonky, but we include looking for .gch so we can support seamless + // replacement into a build system already set up to be generating + // .gch files. + for (ArgList::const_iterator + it = Args.begin(), ie = Args.end(); it != ie; ++it) { + const Arg *A = *it; + if (!A->getOption().matches(options::OPT_clang_i_Group)) + continue; + + if (A->getOption().matches(options::OPT_include)) { + bool FoundPTH = false; + bool FoundPCH = false; + llvm::sys::Path P(A->getValue(Args)); + if (D.CCCUsePCH) { + P.appendSuffix("pch"); + if (P.exists()) + FoundPCH = true; + else + P.eraseSuffix(); + } + + if (!FoundPCH) { + P.appendSuffix("pth"); + if (P.exists()) + FoundPTH = true; + else + P.eraseSuffix(); + } + + if (!FoundPCH && !FoundPTH) { + P.appendSuffix("gch"); + if (P.exists()) { + FoundPCH = D.CCCUsePCH; + FoundPTH = !D.CCCUsePCH; + } + else + P.eraseSuffix(); + } + + if (FoundPCH || FoundPTH) { + A->claim(); + if (D.CCCUsePCH) + CmdArgs.push_back("-include-pch"); + else + CmdArgs.push_back("-include-pth"); + CmdArgs.push_back(Args.MakeArgString(P.c_str())); + continue; + } + } + + // Not translated, render as usual. + A->claim(); + A->render(Args, CmdArgs); + } + + Args.AddAllArgs(CmdArgs, options::OPT_D, options::OPT_U); + Args.AddAllArgs(CmdArgs, options::OPT_I_Group, options::OPT_F); + + // Add -Wp, and -Xassembler if using the preprocessor. + + // FIXME: There is a very unfortunate problem here, some troubled + // souls abuse -Wp, to pass preprocessor options in gcc syntax. To + // really support that we would have to parse and then translate + // those options. :( + Args.AddAllArgValues(CmdArgs, options::OPT_Wp_COMMA, + options::OPT_Xpreprocessor); +} + +void Clang::ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + const Driver &D = getToolChain().getHost().getDriver(); + ArgStringList CmdArgs; + + assert(Inputs.size() == 1 && "Unable to handle multiple inputs."); + + CmdArgs.push_back("-triple"); + const char *TripleStr = + Args.MakeArgString(getToolChain().getTripleString().c_str()); + CmdArgs.push_back(TripleStr); + + if (isa<AnalyzeJobAction>(JA)) { + assert(JA.getType() == types::TY_Plist && "Invalid output type."); + CmdArgs.push_back("-analyze"); + } else if (isa<PreprocessJobAction>(JA)) { + if (Output.getType() == types::TY_Dependencies) + CmdArgs.push_back("-Eonly"); + else + CmdArgs.push_back("-E"); + } else if (isa<PrecompileJobAction>(JA)) { + if (D.CCCUsePCH) + CmdArgs.push_back("-emit-pch"); + else + CmdArgs.push_back("-emit-pth"); + } else { + assert(isa<CompileJobAction>(JA) && "Invalid action for clang tool."); + + if (JA.getType() == types::TY_Nothing) { + CmdArgs.push_back("-fsyntax-only"); + } else if (JA.getType() == types::TY_LLVMAsm) { + CmdArgs.push_back("-emit-llvm"); + } else if (JA.getType() == types::TY_LLVMBC) { + CmdArgs.push_back("-emit-llvm-bc"); + } else if (JA.getType() == types::TY_PP_Asm) { + CmdArgs.push_back("-S"); + } + } + + // The make clang go fast button. + CmdArgs.push_back("-disable-free"); + + // Set the main file name, so that debug info works even with + // -save-temps. + CmdArgs.push_back("-main-file-name"); + CmdArgs.push_back(darwin::CC1::getBaseInputName(Args, Inputs)); + + // Some flags which affect the language (via preprocessor + // defines). See darwin::CC1::AddCPPArgs. + if (Args.hasArg(options::OPT_static)) + CmdArgs.push_back("-static-define"); + + if (isa<AnalyzeJobAction>(JA)) { + // Add default argument set. + if (!Args.hasArg(options::OPT__analyzer_no_default_checks)) { + CmdArgs.push_back("-warn-dead-stores"); + CmdArgs.push_back("-checker-cfref"); + CmdArgs.push_back("-analyzer-eagerly-assume"); + CmdArgs.push_back("-warn-objc-methodsigs"); + // Do not enable the missing -dealloc check. + // '-warn-objc-missing-dealloc', + CmdArgs.push_back("-warn-objc-unused-ivars"); + } + + // Set the output format. The default is plist, for (lame) historical + // reasons. + CmdArgs.push_back("-analyzer-output"); + if (Arg *A = Args.getLastArg(options::OPT__analyzer_output)) + CmdArgs.push_back(A->getValue(Args)); + else + CmdArgs.push_back("plist"); + + // Add -Xanalyzer arguments when running as analyzer. + Args.AddAllArgValues(CmdArgs, options::OPT_Xanalyzer); + } + + // Perform argument translation for LLVM backend. This + // takes some care in reconciling with llvm-gcc. The + // issue is that llvm-gcc translates these options based on + // the values in cc1, whereas we are processing based on + // the driver arguments. + // + // FIXME: This is currently broken for -f flags when -fno + // variants are present. + + // This comes from the default translation the driver + cc1 + // would do to enable flag_pic. + // + // FIXME: Centralize this code. + bool PICEnabled = (Args.hasArg(options::OPT_fPIC) || + Args.hasArg(options::OPT_fpic) || + Args.hasArg(options::OPT_fPIE) || + Args.hasArg(options::OPT_fpie)); + bool PICDisabled = (Args.hasArg(options::OPT_mkernel) || + Args.hasArg(options::OPT_static)); + const char *Model = getToolChain().GetForcedPicModel(); + if (!Model) { + if (Args.hasArg(options::OPT_mdynamic_no_pic)) + Model = "dynamic-no-pic"; + else if (PICDisabled) + Model = "static"; + else if (PICEnabled) + Model = "pic"; + else + Model = getToolChain().GetDefaultRelocationModel(); + } + CmdArgs.push_back("--relocation-model"); + CmdArgs.push_back(Model); + + // Infer the __PIC__ value. + // + // FIXME: This isn't quite right on Darwin, which always sets + // __PIC__=2. + if (strcmp(Model, "pic") == 0 || strcmp(Model, "dynamic-no-pic") == 0) { + if (Args.hasArg(options::OPT_fPIC)) + CmdArgs.push_back("-pic-level=2"); + else + CmdArgs.push_back("-pic-level=1"); + } + + if (Args.hasArg(options::OPT_ftime_report)) + CmdArgs.push_back("--time-passes"); + // FIXME: Set --enable-unsafe-fp-math. + if (!Args.hasArg(options::OPT_fomit_frame_pointer)) + CmdArgs.push_back("--disable-fp-elim"); + if (!Args.hasFlag(options::OPT_fzero_initialized_in_bss, + options::OPT_fno_zero_initialized_in_bss, + true)) + CmdArgs.push_back("--nozero-initialized-in-bss"); + if (Args.hasArg(options::OPT_dA) || Args.hasArg(options::OPT_fverbose_asm)) + CmdArgs.push_back("--asm-verbose"); + if (Args.hasArg(options::OPT_fdebug_pass_structure)) + CmdArgs.push_back("--debug-pass=Structure"); + if (Args.hasArg(options::OPT_fdebug_pass_arguments)) + CmdArgs.push_back("--debug-pass=Arguments"); + // FIXME: set --inline-threshhold=50 if (optimize_size || optimize + // < 3) + if (Args.hasFlag(options::OPT_funwind_tables, + options::OPT_fno_unwind_tables, + (getToolChain().IsUnwindTablesDefault() && + !Args.hasArg(options::OPT_mkernel)))) + CmdArgs.push_back("--unwind-tables=1"); + else + CmdArgs.push_back("--unwind-tables=0"); + if (!Args.hasFlag(options::OPT_mred_zone, + options::OPT_mno_red_zone, + true) || + Args.hasArg(options::OPT_mkernel) || + Args.hasArg(options::OPT_fapple_kext)) + CmdArgs.push_back("--disable-red-zone"); + if (Args.hasFlag(options::OPT_msoft_float, + options::OPT_mno_soft_float, + false)) + CmdArgs.push_back("--soft-float"); + + // FIXME: Handle -mtune=. + (void) Args.hasArg(options::OPT_mtune_EQ); + + if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) { + // FIXME: We may need some translation here from the options gcc takes to + // names the LLVM backend understand? + CmdArgs.push_back("-mcpu"); + CmdArgs.push_back(A->getValue(Args)); + } else { + // Select default CPU. + + // FIXME: Need target hooks. + if (memcmp(getToolChain().getOS().c_str(), "darwin", 6) == 0) { + if (getToolChain().getArchName() == "x86_64") + CmdArgs.push_back("--mcpu=core2"); + else if (getToolChain().getArchName() == "i386") + CmdArgs.push_back("--mcpu=yonah"); + } else { + if (getToolChain().getArchName() == "x86_64") + CmdArgs.push_back("--mcpu=x86-64"); + else if (getToolChain().getArchName() == "i386") + CmdArgs.push_back("--mcpu=pentium4"); + } + } + + // FIXME: Use iterator. + for (ArgList::const_iterator + it = Args.begin(), ie = Args.end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(options::OPT_m_x86_Features_Group)) { + const char *Name = A->getOption().getName(); + + // Skip over "-m". + assert(Name[0] == '-' && Name[1] == 'm' && "Invalid feature name."); + Name += 2; + + bool IsNegative = memcmp(Name, "no-", 3) == 0; + if (IsNegative) + Name += 3; + + A->claim(); + CmdArgs.push_back("-target-feature"); + CmdArgs.push_back(MakeFormattedString(Args, + llvm::format("%c%s", + IsNegative ? '-' : '+', + Name))); + } + } + + if (Args.hasFlag(options::OPT_fmath_errno, + options::OPT_fno_math_errno, + getToolChain().IsMathErrnoDefault())) + CmdArgs.push_back("--fmath-errno=1"); + else + CmdArgs.push_back("--fmath-errno=0"); + + if (Arg *A = Args.getLastArg(options::OPT_flimited_precision_EQ)) { + CmdArgs.push_back("--limit-float-precision"); + CmdArgs.push_back(A->getValue(Args)); + } + + // FIXME: Add --stack-protector-buffer-size=<xxx> on + // -fstack-protect. + + Arg *Unsupported; + if ((Unsupported = Args.getLastArg(options::OPT_MG)) || + (Unsupported = Args.getLastArg(options::OPT_MQ)) || + (Unsupported = Args.getLastArg(options::OPT_iframework))) + D.Diag(clang::diag::err_drv_clang_unsupported) + << Unsupported->getOption().getName(); + + Args.AddAllArgs(CmdArgs, options::OPT_v); + Args.AddLastArg(CmdArgs, options::OPT_P); + Args.AddLastArg(CmdArgs, options::OPT_mmacosx_version_min_EQ); + Args.AddLastArg(CmdArgs, options::OPT_miphoneos_version_min_EQ); + Args.AddLastArg(CmdArgs, options::OPT_print_ivar_layout); + + // Special case debug options to only pass -g to clang. This is + // wrong. + if (Args.hasArg(options::OPT_g_Group)) + CmdArgs.push_back("-g"); + + Args.AddLastArg(CmdArgs, options::OPT_nostdinc); + + Args.AddLastArg(CmdArgs, options::OPT_isysroot); + + // Add preprocessing options like -I, -D, etc. if we are using the + // preprocessor. + // + // FIXME: Support -fpreprocessed + types::ID InputType = Inputs[0].getType(); + if (types::getPreprocessedType(InputType) != types::TY_INVALID) + AddPreprocessingOptions(D, Args, CmdArgs, Output, Inputs); + + // Manually translate -O to -O1 and -O4 to -O3; let clang reject + // others. + if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { + if (A->getOption().getId() == options::OPT_O4) + CmdArgs.push_back("-O3"); + else if (A->getValue(Args)[0] == '\0') + CmdArgs.push_back("-O1"); + else + A->render(Args, CmdArgs); + } + + Args.AddAllArgs(CmdArgs, options::OPT_W_Group, options::OPT_pedantic_Group); + Args.AddLastArg(CmdArgs, options::OPT_w); + + // Handle -{std, ansi, trigraphs} -- take the last of -{std, ansi} + // (-ansi is equivalent to -std=c89). + // + // If a std is supplied, only add -trigraphs if it follows the + // option. + if (Arg *Std = Args.getLastArg(options::OPT_std_EQ, options::OPT_ansi)) { + if (Std->getOption().matches(options::OPT_ansi)) + CmdArgs.push_back("-std=c89"); + else + Std->render(Args, CmdArgs); + + if (Arg *A = Args.getLastArg(options::OPT_trigraphs)) + if (A->getIndex() > Std->getIndex()) + A->render(Args, CmdArgs); + } else { + // Honor -std-default. + Args.AddAllArgsTranslated(CmdArgs, options::OPT_std_default_EQ, + "-std=", /*Joined=*/true); + Args.AddLastArg(CmdArgs, options::OPT_trigraphs); + } + + if (Arg *A = Args.getLastArg(options::OPT_ftemplate_depth_)) { + CmdArgs.push_back("-ftemplate-depth"); + CmdArgs.push_back(A->getValue(Args)); + } + + // Forward -f options which we can pass directly. + Args.AddLastArg(CmdArgs, options::OPT_femit_all_decls); + Args.AddLastArg(CmdArgs, options::OPT_fexceptions); + Args.AddLastArg(CmdArgs, options::OPT_ffreestanding); + Args.AddLastArg(CmdArgs, options::OPT_fheinous_gnu_extensions); + Args.AddLastArg(CmdArgs, options::OPT_fgnu_runtime); + Args.AddLastArg(CmdArgs, options::OPT_flax_vector_conversions); + Args.AddLastArg(CmdArgs, options::OPT_fmessage_length_EQ); + Args.AddLastArg(CmdArgs, options::OPT_fms_extensions); + Args.AddLastArg(CmdArgs, options::OPT_fnext_runtime); + Args.AddLastArg(CmdArgs, options::OPT_fno_caret_diagnostics); + Args.AddLastArg(CmdArgs, options::OPT_fno_show_column); + Args.AddLastArg(CmdArgs, options::OPT_fobjc_gc_only); + Args.AddLastArg(CmdArgs, options::OPT_fobjc_gc); + Args.AddLastArg(CmdArgs, options::OPT_fobjc_sender_dependent_dispatch); + // FIXME: Should we remove this? + Args.AddLastArg(CmdArgs, options::OPT_fobjc_nonfragile_abi); + Args.AddLastArg(CmdArgs, options::OPT_fobjc_tight_layout); + Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_print_source_range_info); + Args.AddLastArg(CmdArgs, options::OPT_ftime_report); + Args.AddLastArg(CmdArgs, options::OPT_ftrapv); + Args.AddLastArg(CmdArgs, options::OPT_fvisibility_EQ); + Args.AddLastArg(CmdArgs, options::OPT_fwritable_strings); + + // Forward -f options with positive and negative forms; we translate + // these by hand. + + // -fbuiltin is default, only pass non-default. + if (!Args.hasFlag(options::OPT_fbuiltin, options::OPT_fno_builtin)) + CmdArgs.push_back("-fbuiltin=0"); + + // -fblocks default varies depending on platform and language; only + // pass if specified. + if (Arg *A = Args.getLastArg(options::OPT_fblocks, options::OPT_fno_blocks)) { + if (A->getOption().matches(options::OPT_fblocks)) + CmdArgs.push_back("-fblocks"); + else + CmdArgs.push_back("-fblocks=0"); + } + + // -fno-pascal-strings is default, only pass non-default. If the + // -tool chain happened to translate to -mpascal-strings, we want to + // -back translate here. + // + // FIXME: This is gross; that translation should be pulled from the + // tool chain. + if (Args.hasFlag(options::OPT_fpascal_strings, + options::OPT_fno_pascal_strings, + false) || + Args.hasFlag(options::OPT_mpascal_strings, + options::OPT_mno_pascal_strings, + false)) + CmdArgs.push_back("-fpascal-strings"); + + // -fcommon is default, only pass non-default. + if (!Args.hasFlag(options::OPT_fcommon, options::OPT_fno_common)) + CmdArgs.push_back("-fno-common"); + + // -fsigned-bitfields is default, and clang doesn't yet support + // --funsigned-bitfields. + if (!Args.hasFlag(options::OPT_fsigned_bitfields, + options::OPT_funsigned_bitfields)) + D.Diag(clang::diag::warn_drv_clang_unsupported) + << Args.getLastArg(options::OPT_funsigned_bitfields)->getAsString(Args); + + // -fdiagnostics-fixit-info is default, only pass non-default. + if (!Args.hasFlag(options::OPT_fdiagnostics_fixit_info, + options::OPT_fno_diagnostics_fixit_info)) + CmdArgs.push_back("-fno-diagnostics-fixit-info"); + + // Enable -fdiagnostics-show-option by default. + if (Args.hasFlag(options::OPT_fdiagnostics_show_option, + options::OPT_fno_diagnostics_show_option)) + CmdArgs.push_back("-fdiagnostics-show-option"); + + // -fdollars-in-identifiers default varies depending on platform and + // language; only pass if specified. + if (Arg *A = Args.getLastArg(options::OPT_fdollars_in_identifiers, + options::OPT_fno_dollars_in_identifiers)) { + if (A->getOption().matches(options::OPT_fdollars_in_identifiers)) + CmdArgs.push_back("-fdollars-in-identifiers=1"); + else + CmdArgs.push_back("-fdollars-in-identifiers=0"); + } + + // -funit-at-a-time is default, and we don't support -fno-unit-at-a-time for + // practical purposes. + if (Arg *A = Args.getLastArg(options::OPT_funit_at_a_time, + options::OPT_fno_unit_at_a_time)) { + if (A->getOption().matches(options::OPT_fno_unit_at_a_time)) + D.Diag(clang::diag::err_drv_clang_unsupported) << A->getAsString(Args); + } + + Args.AddLastArg(CmdArgs, options::OPT_dM); + Args.AddLastArg(CmdArgs, options::OPT_dD); + + Args.AddAllArgValues(CmdArgs, options::OPT_Xclang); + + if (Output.getType() == types::TY_Dependencies) { + // Handled with other dependency code. + } else if (Output.isPipe()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back("-"); + } else if (Output.isFilename()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + } else { + assert(Output.isNothing() && "Invalid output."); + } + + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + const InputInfo &II = *it; + CmdArgs.push_back("-x"); + CmdArgs.push_back(types::getTypeName(II.getType())); + if (II.isPipe()) + CmdArgs.push_back("-"); + else if (II.isFilename()) + CmdArgs.push_back(II.getFilename()); + else + II.getInputArg().renderAsInput(Args, CmdArgs); + } + + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, "clang-cc").c_str()); + Dest.addCommand(new Command(Exec, CmdArgs)); + + // Explicitly warn that these options are unsupported, even though + // we are allowing compilation to continue. + // FIXME: Use iterator. + for (ArgList::const_iterator + it = Args.begin(), ie = Args.end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(options::OPT_pg)) { + A->claim(); + D.Diag(clang::diag::warn_drv_clang_unsupported) + << A->getAsString(Args); + } + } + + // Claim some arguments which clang supports automatically. + + // -fpch-preprocess is used with gcc to add a special marker in the + // -output to include the PCH file. Clang's PTH solution is + // -completely transparent, so we do not need to deal with it at + // -all. + Args.ClaimAllArgs(options::OPT_fpch_preprocess); + + // Claim some arguments which clang doesn't support, but we don't + // care to warn the user about. + + // FIXME: Use iterator. + for (ArgList::const_iterator + it = Args.begin(), ie = Args.end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(options::OPT_clang_ignored_f_Group) || + A->getOption().matches(options::OPT_clang_ignored_m_Group)) + A->claim(); + } +} + +void gcc::Common::ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + const Driver &D = getToolChain().getHost().getDriver(); + ArgStringList CmdArgs; + + for (ArgList::const_iterator + it = Args.begin(), ie = Args.end(); it != ie; ++it) { + Arg *A = *it; + if (A->getOption().hasForwardToGCC()) { + // It is unfortunate that we have to claim here, as this means + // we will basically never report anything interesting for + // platforms using a generic gcc, even if we are just using gcc + // to get to the assembler. + A->claim(); + A->render(Args, CmdArgs); + } + } + + RenderExtraToolArgs(CmdArgs); + + // If using a driver driver, force the arch. + const std::string &Arch = getToolChain().getArchName(); + if (getToolChain().getHost().useDriverDriver()) { + CmdArgs.push_back("-arch"); + + // FIXME: Remove these special cases. + if (Arch == "powerpc") + CmdArgs.push_back("ppc"); + else if (Arch == "powerpc64") + CmdArgs.push_back("ppc64"); + else + CmdArgs.push_back(Args.MakeArgString(Arch.c_str())); + } + + // Try to force gcc to match the tool chain we want, if we recognize + // the arch. + // + // FIXME: The triple class should directly provide the information we want + // here. + if (Arch == "i386" || Arch == "powerpc") + CmdArgs.push_back("-m32"); + else if (Arch == "x86_64" || Arch == "powerpc64") + CmdArgs.push_back("-m64"); + + if (Output.isPipe()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back("-"); + } else if (Output.isFilename()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + } else { + assert(Output.isNothing() && "Unexpected output"); + CmdArgs.push_back("-fsyntax-only"); + } + + + // Only pass -x if gcc will understand it; otherwise hope gcc + // understands the suffix correctly. The main use case this would go + // wrong in is for linker inputs if they happened to have an odd + // suffix; really the only way to get this to happen is a command + // like '-x foobar a.c' which will treat a.c like a linker input. + // + // FIXME: For the linker case specifically, can we safely convert + // inputs into '-Wl,' options? + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + const InputInfo &II = *it; + + // Don't try to pass LLVM inputs to a generic gcc. + if (II.getType() == types::TY_LLVMBC) + D.Diag(clang::diag::err_drv_no_linker_llvm_support) + << getToolChain().getTripleString().c_str(); + + if (types::canTypeBeUserSpecified(II.getType())) { + CmdArgs.push_back("-x"); + CmdArgs.push_back(types::getTypeName(II.getType())); + } + + if (II.isPipe()) + CmdArgs.push_back("-"); + else if (II.isFilename()) + CmdArgs.push_back(II.getFilename()); + else + // Don't render as input, we need gcc to do the translations. + II.getInputArg().render(Args, CmdArgs); + } + + const char *GCCName = + getToolChain().getHost().getDriver().CCCGenericGCCName.c_str(); + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, GCCName).c_str()); + Dest.addCommand(new Command(Exec, CmdArgs)); +} + +void gcc::Preprocess::RenderExtraToolArgs(ArgStringList &CmdArgs) const { + CmdArgs.push_back("-E"); +} + +void gcc::Precompile::RenderExtraToolArgs(ArgStringList &CmdArgs) const { + // The type is good enough. +} + +void gcc::Compile::RenderExtraToolArgs(ArgStringList &CmdArgs) const { + CmdArgs.push_back("-S"); +} + +void gcc::Assemble::RenderExtraToolArgs(ArgStringList &CmdArgs) const { + CmdArgs.push_back("-c"); +} + +void gcc::Link::RenderExtraToolArgs(ArgStringList &CmdArgs) const { + // The types are (hopefully) good enough. +} + +const char *darwin::CC1::getCC1Name(types::ID Type) const { + switch (Type) { + default: + assert(0 && "Unexpected type for Darwin CC1 tool."); + case types::TY_Asm: + case types::TY_C: case types::TY_CHeader: + case types::TY_PP_C: case types::TY_PP_CHeader: + return "cc1"; + case types::TY_ObjC: case types::TY_ObjCHeader: + case types::TY_PP_ObjC: case types::TY_PP_ObjCHeader: + return "cc1obj"; + case types::TY_CXX: case types::TY_CXXHeader: + case types::TY_PP_CXX: case types::TY_PP_CXXHeader: + return "cc1plus"; + case types::TY_ObjCXX: case types::TY_ObjCXXHeader: + case types::TY_PP_ObjCXX: case types::TY_PP_ObjCXXHeader: + return "cc1objplus"; + } +} + +const char *darwin::CC1::getBaseInputName(const ArgList &Args, + const InputInfoList &Inputs) { + llvm::sys::Path P(Inputs[0].getBaseInput()); + return Args.MakeArgString(P.getLast().c_str()); +} + +const char *darwin::CC1::getBaseInputStem(const ArgList &Args, + const InputInfoList &Inputs) { + const char *Str = getBaseInputName(Args, Inputs); + + if (const char *End = strchr(Str, '.')) + return Args.MakeArgString(std::string(Str, End).c_str()); + + return Str; +} + +const char * +darwin::CC1::getDependencyFileName(const ArgList &Args, + const InputInfoList &Inputs) { + // FIXME: Think about this more. + std::string Res; + + if (Arg *OutputOpt = Args.getLastArg(options::OPT_o)) { + std::string Str(OutputOpt->getValue(Args)); + + Res = Str.substr(0, Str.rfind('.')); + } else + Res = darwin::CC1::getBaseInputStem(Args, Inputs); + + return Args.MakeArgString((Res + ".d").c_str()); +} + +void darwin::CC1::AddCC1Args(const ArgList &Args, + ArgStringList &CmdArgs) const { + // Derived from cc1 spec. + + // FIXME: -fapple-kext seems to disable this too. Investigate. + if (!Args.hasArg(options::OPT_mkernel) && !Args.hasArg(options::OPT_static) && + !Args.hasArg(options::OPT_mdynamic_no_pic)) + CmdArgs.push_back("-fPIC"); + + // gcc has some code here to deal with when no -mmacosx-version-min + // and no -miphoneos-version-min is present, but this never happens + // due to tool chain specific argument translation. + + // FIXME: Remove mthumb + // FIXME: Remove mno-thumb + // FIXME: Remove faltivec + // FIXME: Remove mno-fused-madd + // FIXME: Remove mlong-branch + // FIXME: Remove mlongcall + // FIXME: Remove mcpu=G4 + // FIXME: Remove mcpu=G5 + + if (Args.hasArg(options::OPT_g_Flag) && + !Args.hasArg(options::OPT_fno_eliminate_unused_debug_symbols)) + CmdArgs.push_back("-feliminate-unused-debug-symbols"); +} + +void darwin::CC1::AddCC1OptionsArgs(const ArgList &Args, ArgStringList &CmdArgs, + const InputInfoList &Inputs, + const ArgStringList &OutputArgs) const { + const Driver &D = getToolChain().getHost().getDriver(); + + // Derived from cc1_options spec. + if (Args.hasArg(options::OPT_fast) || + Args.hasArg(options::OPT_fastf) || + Args.hasArg(options::OPT_fastcp)) + CmdArgs.push_back("-O3"); + + if (Arg *A = Args.getLastArg(options::OPT_pg)) + if (Args.hasArg(options::OPT_fomit_frame_pointer)) + D.Diag(clang::diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "-fomit-frame-pointer"; + + AddCC1Args(Args, CmdArgs); + + if (!Args.hasArg(options::OPT_Q)) + CmdArgs.push_back("-quiet"); + + CmdArgs.push_back("-dumpbase"); + CmdArgs.push_back(darwin::CC1::getBaseInputName(Args, Inputs)); + + Args.AddAllArgs(CmdArgs, options::OPT_d_Group); + + Args.AddAllArgs(CmdArgs, options::OPT_m_Group); + Args.AddAllArgs(CmdArgs, options::OPT_a_Group); + + // FIXME: The goal is to use the user provided -o if that is our + // final output, otherwise to drive from the original input + // name. Find a clean way to go about this. + if ((Args.hasArg(options::OPT_c) || Args.hasArg(options::OPT_S)) && + Args.hasArg(options::OPT_o)) { + Arg *OutputOpt = Args.getLastArg(options::OPT_o); + CmdArgs.push_back("-auxbase-strip"); + CmdArgs.push_back(OutputOpt->getValue(Args)); + } else { + CmdArgs.push_back("-auxbase"); + CmdArgs.push_back(darwin::CC1::getBaseInputStem(Args, Inputs)); + } + + Args.AddAllArgs(CmdArgs, options::OPT_g_Group); + + Args.AddAllArgs(CmdArgs, options::OPT_O); + // FIXME: -Wall is getting some special treatment. Investigate. + Args.AddAllArgs(CmdArgs, options::OPT_W_Group, options::OPT_pedantic_Group); + Args.AddLastArg(CmdArgs, options::OPT_w); + Args.AddAllArgs(CmdArgs, options::OPT_std_EQ, options::OPT_ansi, + options::OPT_trigraphs); + if (!Args.getLastArg(options::OPT_std_EQ, options::OPT_ansi)) { + // Honor -std-default. + Args.AddAllArgsTranslated(CmdArgs, options::OPT_std_default_EQ, + "-std=", /*Joined=*/true); + } + + if (Args.hasArg(options::OPT_v)) + CmdArgs.push_back("-version"); + if (Args.hasArg(options::OPT_pg)) + CmdArgs.push_back("-p"); + Args.AddLastArg(CmdArgs, options::OPT_p); + + // The driver treats -fsyntax-only specially. + Args.AddAllArgs(CmdArgs, options::OPT_f_Group, options::OPT_fsyntax_only); + + Args.AddAllArgs(CmdArgs, options::OPT_undef); + if (Args.hasArg(options::OPT_Qn)) + CmdArgs.push_back("-fno-ident"); + + // FIXME: This isn't correct. + //Args.AddLastArg(CmdArgs, options::OPT__help) + //Args.AddLastArg(CmdArgs, options::OPT__targetHelp) + + CmdArgs.append(OutputArgs.begin(), OutputArgs.end()); + + // FIXME: Still don't get what is happening here. Investigate. + Args.AddAllArgs(CmdArgs, options::OPT__param); + + if (Args.hasArg(options::OPT_fmudflap) || + Args.hasArg(options::OPT_fmudflapth)) { + CmdArgs.push_back("-fno-builtin"); + CmdArgs.push_back("-fno-merge-constants"); + } + + if (Args.hasArg(options::OPT_coverage)) { + CmdArgs.push_back("-fprofile-arcs"); + CmdArgs.push_back("-ftest-coverage"); + } + + if (types::isCXX(Inputs[0].getType())) + CmdArgs.push_back("-D__private_extern__=extern"); +} + +void darwin::CC1::AddCPPOptionsArgs(const ArgList &Args, ArgStringList &CmdArgs, + const InputInfoList &Inputs, + const ArgStringList &OutputArgs) const { + // Derived from cpp_options + AddCPPUniqueOptionsArgs(Args, CmdArgs, Inputs); + + CmdArgs.append(OutputArgs.begin(), OutputArgs.end()); + + AddCC1Args(Args, CmdArgs); + + // NOTE: The code below has some commonality with cpp_options, but + // in classic gcc style ends up sending things in different + // orders. This may be a good merge candidate once we drop pedantic + // compatibility. + + Args.AddAllArgs(CmdArgs, options::OPT_m_Group); + Args.AddAllArgs(CmdArgs, options::OPT_std_EQ, options::OPT_ansi, + options::OPT_trigraphs); + if (!Args.getLastArg(options::OPT_std_EQ, options::OPT_ansi)) { + // Honor -std-default. + Args.AddAllArgsTranslated(CmdArgs, options::OPT_std_default_EQ, + "-std=", /*Joined=*/true); + } + Args.AddAllArgs(CmdArgs, options::OPT_W_Group, options::OPT_pedantic_Group); + Args.AddLastArg(CmdArgs, options::OPT_w); + + // The driver treats -fsyntax-only specially. + Args.AddAllArgs(CmdArgs, options::OPT_f_Group, options::OPT_fsyntax_only); + + if (Args.hasArg(options::OPT_g_Group) && !Args.hasArg(options::OPT_g0) && + !Args.hasArg(options::OPT_fno_working_directory)) + CmdArgs.push_back("-fworking-directory"); + + Args.AddAllArgs(CmdArgs, options::OPT_O); + Args.AddAllArgs(CmdArgs, options::OPT_undef); + if (Args.hasArg(options::OPT_save_temps)) + CmdArgs.push_back("-fpch-preprocess"); +} + +void darwin::CC1::AddCPPUniqueOptionsArgs(const ArgList &Args, + ArgStringList &CmdArgs, + const InputInfoList &Inputs) const +{ + const Driver &D = getToolChain().getHost().getDriver(); + + // Derived from cpp_unique_options. + Arg *A; + if ((A = Args.getLastArg(options::OPT_C)) || + (A = Args.getLastArg(options::OPT_CC))) { + if (!Args.hasArg(options::OPT_E)) + D.Diag(clang::diag::err_drv_argument_only_allowed_with) + << A->getAsString(Args) << "-E"; + } + if (!Args.hasArg(options::OPT_Q)) + CmdArgs.push_back("-quiet"); + Args.AddAllArgs(CmdArgs, options::OPT_nostdinc); + Args.AddLastArg(CmdArgs, options::OPT_v); + Args.AddAllArgs(CmdArgs, options::OPT_I_Group, options::OPT_F); + Args.AddLastArg(CmdArgs, options::OPT_P); + + // FIXME: Handle %I properly. + if (getToolChain().getArchName() == "x86_64") { + CmdArgs.push_back("-imultilib"); + CmdArgs.push_back("x86_64"); + } + + if (Args.hasArg(options::OPT_MD)) { + CmdArgs.push_back("-MD"); + CmdArgs.push_back(darwin::CC1::getDependencyFileName(Args, Inputs)); + } + + if (Args.hasArg(options::OPT_MMD)) { + CmdArgs.push_back("-MMD"); + CmdArgs.push_back(darwin::CC1::getDependencyFileName(Args, Inputs)); + } + + Args.AddLastArg(CmdArgs, options::OPT_M); + Args.AddLastArg(CmdArgs, options::OPT_MM); + Args.AddAllArgs(CmdArgs, options::OPT_MF); + Args.AddLastArg(CmdArgs, options::OPT_MG); + Args.AddLastArg(CmdArgs, options::OPT_MP); + Args.AddAllArgs(CmdArgs, options::OPT_MQ); + Args.AddAllArgs(CmdArgs, options::OPT_MT); + if (!Args.hasArg(options::OPT_M) && !Args.hasArg(options::OPT_MM) && + (Args.hasArg(options::OPT_MD) || Args.hasArg(options::OPT_MMD))) { + if (Arg *OutputOpt = Args.getLastArg(options::OPT_o)) { + CmdArgs.push_back("-MQ"); + CmdArgs.push_back(OutputOpt->getValue(Args)); + } + } + + Args.AddLastArg(CmdArgs, options::OPT_remap); + if (Args.hasArg(options::OPT_g3)) + CmdArgs.push_back("-dD"); + Args.AddLastArg(CmdArgs, options::OPT_H); + + AddCPPArgs(Args, CmdArgs); + + Args.AddAllArgs(CmdArgs, options::OPT_D, options::OPT_U, options::OPT_A); + Args.AddAllArgs(CmdArgs, options::OPT_i_Group); + + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + const InputInfo &II = *it; + + if (II.isPipe()) + CmdArgs.push_back("-"); + else + CmdArgs.push_back(II.getFilename()); + } + + Args.AddAllArgValues(CmdArgs, options::OPT_Wp_COMMA, + options::OPT_Xpreprocessor); + + if (Args.hasArg(options::OPT_fmudflap)) { + CmdArgs.push_back("-D_MUDFLAP"); + CmdArgs.push_back("-include"); + CmdArgs.push_back("mf-runtime.h"); + } + + if (Args.hasArg(options::OPT_fmudflapth)) { + CmdArgs.push_back("-D_MUDFLAP"); + CmdArgs.push_back("-D_MUDFLAPTH"); + CmdArgs.push_back("-include"); + CmdArgs.push_back("mf-runtime.h"); + } +} + +void darwin::CC1::AddCPPArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + // Derived from cpp spec. + + if (Args.hasArg(options::OPT_static)) { + // The gcc spec is broken here, it refers to dynamic but + // that has been translated. Start by being bug compatible. + + // if (!Args.hasArg(arglist.parser.dynamicOption)) + CmdArgs.push_back("-D__STATIC__"); + } else + CmdArgs.push_back("-D__DYNAMIC__"); + + if (Args.hasArg(options::OPT_pthread)) + CmdArgs.push_back("-D_REENTRANT"); +} + +void darwin::Preprocess::ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + ArgStringList CmdArgs; + + assert(Inputs.size() == 1 && "Unexpected number of inputs!"); + + CmdArgs.push_back("-E"); + + if (Args.hasArg(options::OPT_traditional) || + Args.hasArg(options::OPT_ftraditional) || + Args.hasArg(options::OPT_traditional_cpp)) + CmdArgs.push_back("-traditional-cpp"); + + ArgStringList OutputArgs; + if (Output.isFilename()) { + OutputArgs.push_back("-o"); + OutputArgs.push_back(Output.getFilename()); + } else { + assert(Output.isPipe() && "Unexpected CC1 output."); + } + + if (Args.hasArg(options::OPT_E)) { + AddCPPOptionsArgs(Args, CmdArgs, Inputs, OutputArgs); + } else { + AddCPPOptionsArgs(Args, CmdArgs, Inputs, ArgStringList()); + CmdArgs.append(OutputArgs.begin(), OutputArgs.end()); + } + + Args.AddAllArgs(CmdArgs, options::OPT_d_Group); + + const char *CC1Name = getCC1Name(Inputs[0].getType()); + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, CC1Name).c_str()); + Dest.addCommand(new Command(Exec, CmdArgs)); +} + +void darwin::Compile::ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + const Driver &D = getToolChain().getHost().getDriver(); + ArgStringList CmdArgs; + + assert(Inputs.size() == 1 && "Unexpected number of inputs!"); + + types::ID InputType = Inputs[0].getType(); + const Arg *A; + if ((A = Args.getLastArg(options::OPT_traditional)) || + (A = Args.getLastArg(options::OPT_ftraditional))) + D.Diag(clang::diag::err_drv_argument_only_allowed_with) + << A->getAsString(Args) << "-E"; + + if (Output.getType() == types::TY_LLVMAsm) + CmdArgs.push_back("-emit-llvm"); + else if (Output.getType() == types::TY_LLVMBC) + CmdArgs.push_back("-emit-llvm-bc"); + + ArgStringList OutputArgs; + if (Output.getType() != types::TY_PCH) { + OutputArgs.push_back("-o"); + if (Output.isPipe()) + OutputArgs.push_back("-"); + else if (Output.isNothing()) + OutputArgs.push_back("/dev/null"); + else + OutputArgs.push_back(Output.getFilename()); + } + + // There is no need for this level of compatibility, but it makes + // diffing easier. + bool OutputArgsEarly = (Args.hasArg(options::OPT_fsyntax_only) || + Args.hasArg(options::OPT_S)); + + if (types::getPreprocessedType(InputType) != types::TY_INVALID) { + AddCPPUniqueOptionsArgs(Args, CmdArgs, Inputs); + if (OutputArgsEarly) { + AddCC1OptionsArgs(Args, CmdArgs, Inputs, OutputArgs); + } else { + AddCC1OptionsArgs(Args, CmdArgs, Inputs, ArgStringList()); + CmdArgs.append(OutputArgs.begin(), OutputArgs.end()); + } + } else { + CmdArgs.push_back("-fpreprocessed"); + + // FIXME: There is a spec command to remove + // -fpredictive-compilation args here. Investigate. + + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + const InputInfo &II = *it; + + if (II.isPipe()) + CmdArgs.push_back("-"); + else + CmdArgs.push_back(II.getFilename()); + } + + if (OutputArgsEarly) { + AddCC1OptionsArgs(Args, CmdArgs, Inputs, OutputArgs); + } else { + AddCC1OptionsArgs(Args, CmdArgs, Inputs, ArgStringList()); + CmdArgs.append(OutputArgs.begin(), OutputArgs.end()); + } + } + + if (Output.getType() == types::TY_PCH) { + assert(Output.isFilename() && "Invalid PCH output."); + + CmdArgs.push_back("-o"); + // NOTE: gcc uses a temp .s file for this, but there doesn't seem + // to be a good reason. + CmdArgs.push_back("/dev/null"); + + CmdArgs.push_back("--output-pch="); + CmdArgs.push_back(Output.getFilename()); + } + + const char *CC1Name = getCC1Name(Inputs[0].getType()); + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, CC1Name).c_str()); + Dest.addCommand(new Command(Exec, CmdArgs)); +} + +void darwin::Assemble::ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + ArgStringList CmdArgs; + + assert(Inputs.size() == 1 && "Unexpected number of inputs."); + const InputInfo &Input = Inputs[0]; + + // Bit of a hack, this is only used for original inputs. + // + // FIXME: This is broken for preprocessed .s inputs. + if (Input.isFilename() && + strcmp(Input.getFilename(), Input.getBaseInput()) == 0) { + if (Args.hasArg(options::OPT_gstabs)) + CmdArgs.push_back("--gstabs"); + else if (Args.hasArg(options::OPT_g_Group)) + CmdArgs.push_back("--gdwarf2"); + } + + // Derived from asm spec. + CmdArgs.push_back("-arch"); + CmdArgs.push_back(Args.MakeArgString(getToolChain().getArchName().c_str())); + + CmdArgs.push_back("-force_cpusubtype_ALL"); + if ((Args.hasArg(options::OPT_mkernel) || + Args.hasArg(options::OPT_static) || + Args.hasArg(options::OPT_fapple_kext)) && + !Args.hasArg(options::OPT_dynamic)) + CmdArgs.push_back("-static"); + + Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, + options::OPT_Xassembler); + + assert(Output.isFilename() && "Unexpected lipo output."); + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + + if (Input.isPipe()) { + CmdArgs.push_back("-"); + } else { + assert(Input.isFilename() && "Invalid input."); + CmdArgs.push_back(Input.getFilename()); + } + + // asm_final spec is empty. + + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, "as").c_str()); + Dest.addCommand(new Command(Exec, CmdArgs)); +} + +/// Helper routine for seeing if we should use dsymutil; this is a +/// gcc compatible hack, we should remove it and use the input +/// type information. +static bool isSourceSuffix(const char *Str) { + // match: 'C', 'CPP', 'c', 'cc', 'cp', 'c++', 'cpp', 'cxx', 'm', + // 'mm'. + switch (strlen(Str)) { + default: + return false; + case 1: + return (memcmp(Str, "C", 1) == 0 || + memcmp(Str, "c", 1) == 0 || + memcmp(Str, "m", 1) == 0); + case 2: + return (memcmp(Str, "cc", 2) == 0 || + memcmp(Str, "cp", 2) == 0 || + memcmp(Str, "mm", 2) == 0); + case 3: + return (memcmp(Str, "CPP", 3) == 0 || + memcmp(Str, "c++", 3) == 0 || + memcmp(Str, "cpp", 3) == 0 || + memcmp(Str, "cxx", 3) == 0); + } +} + +static bool isMacosxVersionLT(unsigned (&A)[3], unsigned (&B)[3]) { + for (unsigned i=0; i < 3; ++i) { + if (A[i] > B[i]) return false; + if (A[i] < B[i]) return true; + } + return false; +} + +static bool isMacosxVersionLT(unsigned (&A)[3], + unsigned V0, unsigned V1=0, unsigned V2=0) { + unsigned B[3] = { V0, V1, V2 }; + return isMacosxVersionLT(A, B); +} + +const toolchains::Darwin_X86 &darwin::Link::getDarwinToolChain() const { + return reinterpret_cast<const toolchains::Darwin_X86&>(getToolChain()); +} + +void darwin::Link::AddDarwinArch(const ArgList &Args, + ArgStringList &CmdArgs) const { + // Derived from darwin_arch spec. + CmdArgs.push_back("-arch"); + CmdArgs.push_back(Args.MakeArgString(getToolChain().getArchName().c_str())); +} + +void darwin::Link::AddDarwinSubArch(const ArgList &Args, + ArgStringList &CmdArgs) const { + // Derived from darwin_subarch spec, not sure what the distinction + // exists for but at least for this chain it is the same. + AddDarwinArch(Args, CmdArgs); +} + +void darwin::Link::AddLinkArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + const Driver &D = getToolChain().getHost().getDriver(); + + // Derived from the "link" spec. + Args.AddAllArgs(CmdArgs, options::OPT_static); + if (!Args.hasArg(options::OPT_static)) + CmdArgs.push_back("-dynamic"); + if (Args.hasArg(options::OPT_fgnu_runtime)) { + // FIXME: gcc replaces -lobjc in forward args with -lobjc-gnu + // here. How do we wish to handle such things? + } + + if (!Args.hasArg(options::OPT_dynamiclib)) { + if (Args.hasArg(options::OPT_force__cpusubtype__ALL)) { + AddDarwinArch(Args, CmdArgs); + CmdArgs.push_back("-force_cpusubtype_ALL"); + } else + AddDarwinSubArch(Args, CmdArgs); + + Args.AddLastArg(CmdArgs, options::OPT_bundle); + Args.AddAllArgs(CmdArgs, options::OPT_bundle__loader); + Args.AddAllArgs(CmdArgs, options::OPT_client__name); + + Arg *A; + if ((A = Args.getLastArg(options::OPT_compatibility__version)) || + (A = Args.getLastArg(options::OPT_current__version)) || + (A = Args.getLastArg(options::OPT_install__name))) + D.Diag(clang::diag::err_drv_argument_only_allowed_with) + << A->getAsString(Args) << "-dynamiclib"; + + Args.AddLastArg(CmdArgs, options::OPT_force__flat__namespace); + Args.AddLastArg(CmdArgs, options::OPT_keep__private__externs); + Args.AddLastArg(CmdArgs, options::OPT_private__bundle); + } else { + CmdArgs.push_back("-dylib"); + + Arg *A; + if ((A = Args.getLastArg(options::OPT_bundle)) || + (A = Args.getLastArg(options::OPT_bundle__loader)) || + (A = Args.getLastArg(options::OPT_client__name)) || + (A = Args.getLastArg(options::OPT_force__flat__namespace)) || + (A = Args.getLastArg(options::OPT_keep__private__externs)) || + (A = Args.getLastArg(options::OPT_private__bundle))) + D.Diag(clang::diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "-dynamiclib"; + + Args.AddAllArgsTranslated(CmdArgs, options::OPT_compatibility__version, + "-dylib_compatibility_version"); + Args.AddAllArgsTranslated(CmdArgs, options::OPT_current__version, + "-dylib_current_version"); + + if (Args.hasArg(options::OPT_force__cpusubtype__ALL)) { + AddDarwinArch(Args, CmdArgs); + // NOTE: We don't add -force_cpusubtype_ALL on this path. Ok. + } else + AddDarwinSubArch(Args, CmdArgs); + + Args.AddAllArgsTranslated(CmdArgs, options::OPT_install__name, + "-dylib_install_name"); + } + + Args.AddLastArg(CmdArgs, options::OPT_all__load); + Args.AddAllArgs(CmdArgs, options::OPT_allowable__client); + Args.AddLastArg(CmdArgs, options::OPT_bind__at__load); + Args.AddLastArg(CmdArgs, options::OPT_dead__strip); + Args.AddLastArg(CmdArgs, options::OPT_no__dead__strip__inits__and__terms); + Args.AddAllArgs(CmdArgs, options::OPT_dylib__file); + Args.AddLastArg(CmdArgs, options::OPT_dynamic); + Args.AddAllArgs(CmdArgs, options::OPT_exported__symbols__list); + Args.AddLastArg(CmdArgs, options::OPT_flat__namespace); + Args.AddAllArgs(CmdArgs, options::OPT_headerpad__max__install__names); + Args.AddAllArgs(CmdArgs, options::OPT_image__base); + Args.AddAllArgs(CmdArgs, options::OPT_init); + + if (!Args.hasArg(options::OPT_mmacosx_version_min_EQ)) { + if (!Args.hasArg(options::OPT_miphoneos_version_min_EQ)) { + // FIXME: I don't understand what is going on here. This is + // supposed to come from darwin_ld_minversion, but gcc doesn't + // seem to be following that; it must be getting overridden + // somewhere. + CmdArgs.push_back("-macosx_version_min"); + CmdArgs.push_back(getDarwinToolChain().getMacosxVersionStr()); + } + } else { + // Adding all arguments doesn't make sense here but this is what + // gcc does. + Args.AddAllArgsTranslated(CmdArgs, options::OPT_mmacosx_version_min_EQ, + "-macosx_version_min"); + } + + Args.AddAllArgsTranslated(CmdArgs, options::OPT_miphoneos_version_min_EQ, + "-iphoneos_version_min"); + Args.AddLastArg(CmdArgs, options::OPT_nomultidefs); + Args.AddLastArg(CmdArgs, options::OPT_multi__module); + Args.AddLastArg(CmdArgs, options::OPT_single__module); + Args.AddAllArgs(CmdArgs, options::OPT_multiply__defined); + Args.AddAllArgs(CmdArgs, options::OPT_multiply__defined__unused); + + if (Args.hasArg(options::OPT_fpie)) + CmdArgs.push_back("-pie"); + + Args.AddLastArg(CmdArgs, options::OPT_prebind); + Args.AddLastArg(CmdArgs, options::OPT_noprebind); + Args.AddLastArg(CmdArgs, options::OPT_nofixprebinding); + Args.AddLastArg(CmdArgs, options::OPT_prebind__all__twolevel__modules); + Args.AddLastArg(CmdArgs, options::OPT_read__only__relocs); + Args.AddAllArgs(CmdArgs, options::OPT_sectcreate); + Args.AddAllArgs(CmdArgs, options::OPT_sectorder); + Args.AddAllArgs(CmdArgs, options::OPT_seg1addr); + Args.AddAllArgs(CmdArgs, options::OPT_segprot); + Args.AddAllArgs(CmdArgs, options::OPT_segaddr); + Args.AddAllArgs(CmdArgs, options::OPT_segs__read__only__addr); + Args.AddAllArgs(CmdArgs, options::OPT_segs__read__write__addr); + Args.AddAllArgs(CmdArgs, options::OPT_seg__addr__table); + Args.AddAllArgs(CmdArgs, options::OPT_seg__addr__table__filename); + Args.AddAllArgs(CmdArgs, options::OPT_sub__library); + Args.AddAllArgs(CmdArgs, options::OPT_sub__umbrella); + Args.AddAllArgsTranslated(CmdArgs, options::OPT_isysroot, "-syslibroot"); + Args.AddLastArg(CmdArgs, options::OPT_twolevel__namespace); + Args.AddLastArg(CmdArgs, options::OPT_twolevel__namespace__hints); + Args.AddAllArgs(CmdArgs, options::OPT_umbrella); + Args.AddAllArgs(CmdArgs, options::OPT_undefined); + Args.AddAllArgs(CmdArgs, options::OPT_unexported__symbols__list); + Args.AddAllArgs(CmdArgs, options::OPT_weak__reference__mismatches); + + if (!Args.hasArg(options::OPT_weak__reference__mismatches)) { + CmdArgs.push_back("-weak_reference_mismatches"); + CmdArgs.push_back("non-weak"); + } + + Args.AddLastArg(CmdArgs, options::OPT_X_Flag); + Args.AddAllArgs(CmdArgs, options::OPT_y); + Args.AddLastArg(CmdArgs, options::OPT_w); + Args.AddAllArgs(CmdArgs, options::OPT_pagezero__size); + Args.AddAllArgs(CmdArgs, options::OPT_segs__read__); + Args.AddLastArg(CmdArgs, options::OPT_seglinkedit); + Args.AddLastArg(CmdArgs, options::OPT_noseglinkedit); + Args.AddAllArgs(CmdArgs, options::OPT_sectalign); + Args.AddAllArgs(CmdArgs, options::OPT_sectobjectsymbols); + Args.AddAllArgs(CmdArgs, options::OPT_segcreate); + Args.AddLastArg(CmdArgs, options::OPT_whyload); + Args.AddLastArg(CmdArgs, options::OPT_whatsloaded); + Args.AddAllArgs(CmdArgs, options::OPT_dylinker__install__name); + Args.AddLastArg(CmdArgs, options::OPT_dylinker); + Args.AddLastArg(CmdArgs, options::OPT_Mach); +} + +void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + assert(Output.getType() == types::TY_Image && "Invalid linker output type."); + // The logic here is derived from gcc's behavior; most of which + // comes from specs (starting with link_command). Consult gcc for + // more information. + + // FIXME: The spec references -fdump= which seems to have + // disappeared? + + ArgStringList CmdArgs; + + // I'm not sure why this particular decomposition exists in gcc, but + // we follow suite for ease of comparison. + AddLinkArgs(Args, CmdArgs); + + // FIXME: gcc has %{x} in here. How could this ever happen? Cruft? + Args.AddAllArgs(CmdArgs, options::OPT_d_Flag); + Args.AddAllArgs(CmdArgs, options::OPT_s); + Args.AddAllArgs(CmdArgs, options::OPT_t); + Args.AddAllArgs(CmdArgs, options::OPT_Z_Flag); + Args.AddAllArgs(CmdArgs, options::OPT_u_Group); + Args.AddAllArgs(CmdArgs, options::OPT_A); + Args.AddLastArg(CmdArgs, options::OPT_e); + Args.AddAllArgs(CmdArgs, options::OPT_m_Separate); + Args.AddAllArgs(CmdArgs, options::OPT_r); + + // FIXME: This is just being pedantically bug compatible, gcc + // doesn't *mean* to forward this, it just does (yay for pattern + // matching). It doesn't work, of course. + Args.AddAllArgs(CmdArgs, options::OPT_object); + + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + + unsigned MacosxVersion[3]; + if (Arg *A = Args.getLastArg(options::OPT_mmacosx_version_min_EQ)) { + bool HadExtra; + if (!Driver::GetReleaseVersion(A->getValue(Args), MacosxVersion[0], + MacosxVersion[1], MacosxVersion[2], + HadExtra) || + HadExtra) { + const Driver &D = getToolChain().getHost().getDriver(); + D.Diag(clang::diag::err_drv_invalid_version_number) + << A->getAsString(Args); + } + } else { + getDarwinToolChain().getMacosxVersion(MacosxVersion); + } + + if (!Args.hasArg(options::OPT_A) && + !Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nostartfiles)) { + // Derived from startfile spec. + if (Args.hasArg(options::OPT_dynamiclib)) { + // Derived from darwin_dylib1 spec. + if (isMacosxVersionLT(MacosxVersion, 10, 5)) + CmdArgs.push_back("-ldylib1.o"); + else if (isMacosxVersionLT(MacosxVersion, 10, 6)) + CmdArgs.push_back("-ldylib1.10.5.o"); + } else { + if (Args.hasArg(options::OPT_bundle)) { + if (!Args.hasArg(options::OPT_static)) { + // Derived from darwin_bundle1 spec. + if (isMacosxVersionLT(MacosxVersion, 10, 6)) + CmdArgs.push_back("-lbundle1.o"); + } + } else { + if (Args.hasArg(options::OPT_pg)) { + if (Args.hasArg(options::OPT_static) || + Args.hasArg(options::OPT_object) || + Args.hasArg(options::OPT_preload)) { + CmdArgs.push_back("-lgcrt0.o"); + } else { + CmdArgs.push_back("-lgcrt1.o"); + + // darwin_crt2 spec is empty. + } + } else { + if (Args.hasArg(options::OPT_static) || + Args.hasArg(options::OPT_object) || + Args.hasArg(options::OPT_preload)) { + CmdArgs.push_back("-lcrt0.o"); + } else { + // Derived from darwin_crt1 spec. + if (isMacosxVersionLT(MacosxVersion, 10, 5)) + CmdArgs.push_back("-lcrt1.o"); + else if (isMacosxVersionLT(MacosxVersion, 10, 6)) + CmdArgs.push_back("-lcrt1.10.5.o"); + else + CmdArgs.push_back("-lcrt1.10.6.o"); + + // darwin_crt2 spec is empty. + } + } + } + } + + if (Args.hasArg(options::OPT_shared_libgcc) && + !Args.hasArg(options::OPT_miphoneos_version_min_EQ) && + isMacosxVersionLT(MacosxVersion, 10, 5)) { + const char *Str = getToolChain().GetFilePath(C, "crt3.o").c_str(); + CmdArgs.push_back(Args.MakeArgString(Str)); + } + } + + Args.AddAllArgs(CmdArgs, options::OPT_L); + + if (Args.hasArg(options::OPT_fopenmp)) + // This is more complicated in gcc... + CmdArgs.push_back("-lgomp"); + + // FIXME: Derive these correctly. + const char *TCDir = getDarwinToolChain().getToolChainDir().c_str(); + if (getToolChain().getArchName() == "x86_64") { + CmdArgs.push_back(MakeFormattedString(Args, + llvm::format("-L/usr/lib/gcc/%s/x86_64", TCDir))); + // Intentionally duplicated for (temporary) gcc bug compatibility. + CmdArgs.push_back(MakeFormattedString(Args, + llvm::format("-L/usr/lib/gcc/%s/x86_64", TCDir))); + } + CmdArgs.push_back(MakeFormattedString(Args, + llvm::format("-L/usr/lib/%s", TCDir))); + CmdArgs.push_back(MakeFormattedString(Args, + llvm::format("-L/usr/lib/gcc/%s", TCDir))); + // Intentionally duplicated for (temporary) gcc bug compatibility. + CmdArgs.push_back(MakeFormattedString(Args, + llvm::format("-L/usr/lib/gcc/%s", TCDir))); + CmdArgs.push_back(MakeFormattedString(Args, + llvm::format("-L/usr/lib/gcc/%s/../../../%s", TCDir, TCDir))); + CmdArgs.push_back(MakeFormattedString(Args, + llvm::format("-L/usr/lib/gcc/%s/../../..", TCDir))); + + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + const InputInfo &II = *it; + if (II.isFilename()) + CmdArgs.push_back(II.getFilename()); + else + II.getInputArg().renderAsInput(Args, CmdArgs); + } + + if (LinkingOutput) { + CmdArgs.push_back("-arch_multiple"); + CmdArgs.push_back("-final_output"); + CmdArgs.push_back(LinkingOutput); + } + + if (Args.hasArg(options::OPT_fprofile_arcs) || + Args.hasArg(options::OPT_fprofile_generate) || + Args.hasArg(options::OPT_fcreate_profile) || + Args.hasArg(options::OPT_coverage)) + CmdArgs.push_back("-lgcov"); + + if (Args.hasArg(options::OPT_fnested_functions)) + CmdArgs.push_back("-allow_stack_execute"); + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs)) { + // FIXME: g++ is more complicated here, it tries to put -lstdc++ + // before -lm, for example. + if (getToolChain().getHost().getDriver().CCCIsCXX) + CmdArgs.push_back("-lstdc++"); + + // link_ssp spec is empty. + + // Derived from libgcc and lib specs but refactored. + if (Args.hasArg(options::OPT_static)) { + CmdArgs.push_back("-lgcc_static"); + } else { + if (Args.hasArg(options::OPT_static_libgcc)) { + CmdArgs.push_back("-lgcc_eh"); + } else if (Args.hasArg(options::OPT_miphoneos_version_min_EQ)) { + // Derived from darwin_iphoneos_libgcc spec. + CmdArgs.push_back("-lgcc_s.10.5"); + } else if (Args.hasArg(options::OPT_shared_libgcc) || + Args.hasArg(options::OPT_fexceptions) || + Args.hasArg(options::OPT_fgnu_runtime)) { + // FIXME: This is probably broken on 10.3? + if (isMacosxVersionLT(MacosxVersion, 10, 5)) + CmdArgs.push_back("-lgcc_s.10.4"); + else if (isMacosxVersionLT(MacosxVersion, 10, 6)) + CmdArgs.push_back("-lgcc_s.10.5"); + } else { + if (isMacosxVersionLT(MacosxVersion, 10, 3, 9)) + ; // Do nothing. + else if (isMacosxVersionLT(MacosxVersion, 10, 5)) + CmdArgs.push_back("-lgcc_s.10.4"); + else if (isMacosxVersionLT(MacosxVersion, 10, 6)) + CmdArgs.push_back("-lgcc_s.10.5"); + } + + if (isMacosxVersionLT(MacosxVersion, 10, 6)) { + CmdArgs.push_back("-lgcc"); + CmdArgs.push_back("-lSystem"); + } else { + CmdArgs.push_back("-lSystem"); + CmdArgs.push_back("-lgcc"); + } + } + } + + if (!Args.hasArg(options::OPT_A) && + !Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nostartfiles)) { + // endfile_spec is empty. + } + + Args.AddAllArgs(CmdArgs, options::OPT_T_Group); + Args.AddAllArgs(CmdArgs, options::OPT_F); + + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, "ld").c_str()); + Dest.addCommand(new Command(Exec, CmdArgs)); + + // Find the first non-empty base input (we want to ignore linker + // inputs). + const char *BaseInput = ""; + for (unsigned i = 0, e = Inputs.size(); i != e; ++i) { + if (Inputs[i].getBaseInput()[0] != '\0') { + BaseInput = Inputs[i].getBaseInput(); + break; + } + } + + // Run dsymutil if we are making an executable in a single step. + // + // FIXME: Currently we don't want to do this when we are part of a + // universal build step, as this would end up creating stray temp + // files. + if (!LinkingOutput && + Args.getLastArg(options::OPT_g_Group) && + !Args.getLastArg(options::OPT_gstabs) && + !Args.getLastArg(options::OPT_g0)) { + // FIXME: This is gross, but matches gcc. The test only considers + // the suffix (not the -x type), and then only of the first + // source input. Awesome. + const char *Suffix = strrchr(BaseInput, '.'); + if (Suffix && isSourceSuffix(Suffix + 1)) { + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, "dsymutil").c_str()); + ArgStringList CmdArgs; + CmdArgs.push_back(Output.getFilename()); + C.getJobs().addCommand(new Command(Exec, CmdArgs)); + } + } +} + +void darwin::Lipo::ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + ArgStringList CmdArgs; + + CmdArgs.push_back("-create"); + assert(Output.isFilename() && "Unexpected lipo output."); + + CmdArgs.push_back("-output"); + CmdArgs.push_back(Output.getFilename()); + + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + const InputInfo &II = *it; + assert(II.isFilename() && "Unexpected lipo input."); + CmdArgs.push_back(II.getFilename()); + } + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, "lipo").c_str()); + Dest.addCommand(new Command(Exec, CmdArgs)); +} + + +void freebsd::Assemble::ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const +{ + ArgStringList CmdArgs; + + // When building 32-bit code on FreeBSD/amd64, we have to explicitly + // instruct as in the base system to assemble 32-bit code. + if (getToolChain().getArchName() == "i386") + CmdArgs.push_back("--32"); + + Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, + options::OPT_Xassembler); + + CmdArgs.push_back("-o"); + if (Output.isPipe()) + CmdArgs.push_back("-"); + else + CmdArgs.push_back(Output.getFilename()); + + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + const InputInfo &II = *it; + if (II.isPipe()) + CmdArgs.push_back("-"); + else + CmdArgs.push_back(II.getFilename()); + } + + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, "as").c_str()); + Dest.addCommand(new Command(Exec, CmdArgs)); +} + +void freebsd::Link::ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + const Driver &D = getToolChain().getHost().getDriver(); + ArgStringList CmdArgs; + + if (Args.hasArg(options::OPT_static)) { + CmdArgs.push_back("-Bstatic"); + } else { + CmdArgs.push_back("--eh-frame-hdr"); + if (Args.hasArg(options::OPT_shared)) { + CmdArgs.push_back("-Bshareable"); + } else { + CmdArgs.push_back("-dynamic-linker"); + CmdArgs.push_back("/libexec/ld-elf.so.1"); + } + } + + // When building 32-bit code on FreeBSD/amd64, we have to explicitly + // instruct ld in the base system to link 32-bit code. + if (getToolChain().getArchName() == "i386") { + CmdArgs.push_back("-m"); + CmdArgs.push_back("elf_i386_fbsd"); + } + + if (Output.isPipe()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back("-"); + } else if (Output.isFilename()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + } else { + assert(Output.isNothing() && "Invalid output."); + } + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nostartfiles)) { + if (!Args.hasArg(options::OPT_shared)) { + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crt1.o").c_str())); + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crti.o").c_str())); + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crtbegin.o").c_str())); + } else { + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crti.o").c_str())); + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crtbeginS.o").c_str())); + } + } + + Args.AddAllArgs(CmdArgs, options::OPT_L); + Args.AddAllArgs(CmdArgs, options::OPT_T_Group); + Args.AddAllArgs(CmdArgs, options::OPT_e); + + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + const InputInfo &II = *it; + + // Don't try to pass LLVM inputs to a generic gcc. + if (II.getType() == types::TY_LLVMBC) + D.Diag(clang::diag::err_drv_no_linker_llvm_support) + << getToolChain().getTripleString().c_str(); + + if (II.isPipe()) + CmdArgs.push_back("-"); + else if (II.isFilename()) + CmdArgs.push_back(II.getFilename()); + else + II.getInputArg().renderAsInput(Args, CmdArgs); + } + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs)) { + // FIXME: For some reason GCC passes -lgcc and -lgcc_s before adding + // the default system libraries. Just mimic this for now. + CmdArgs.push_back("-lgcc"); + if (Args.hasArg(options::OPT_static)) { + CmdArgs.push_back("-lgcc_eh"); + } else { + CmdArgs.push_back("--as-needed"); + CmdArgs.push_back("-lgcc_s"); + CmdArgs.push_back("--no-as-needed"); + } + + if (Args.hasArg(options::OPT_pthread)) + CmdArgs.push_back("-lpthread"); + CmdArgs.push_back("-lc"); + + CmdArgs.push_back("-lgcc"); + if (Args.hasArg(options::OPT_static)) { + CmdArgs.push_back("-lgcc_eh"); + } else { + CmdArgs.push_back("--as-needed"); + CmdArgs.push_back("-lgcc_s"); + CmdArgs.push_back("--no-as-needed"); + } + } + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nostartfiles)) { + if (!Args.hasArg(options::OPT_shared)) + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crtend.o").c_str())); + else + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crtendS.o").c_str())); + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crtn.o").c_str())); + } + + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, "ld").c_str()); + Dest.addCommand(new Command(Exec, CmdArgs)); +} + +/// DragonFly Tools + +// For now, DragonFly Assemble does just about the same as for +// FreeBSD, but this may change soon. +void dragonfly::Assemble::ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + ArgStringList CmdArgs; + + // When building 32-bit code on DragonFly/pc64, we have to explicitly + // instruct as in the base system to assemble 32-bit code. + if (getToolChain().getArchName() == "i386") + CmdArgs.push_back("--32"); + + Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, + options::OPT_Xassembler); + + CmdArgs.push_back("-o"); + if (Output.isPipe()) + CmdArgs.push_back("-"); + else + CmdArgs.push_back(Output.getFilename()); + + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + const InputInfo &II = *it; + if (II.isPipe()) + CmdArgs.push_back("-"); + else + CmdArgs.push_back(II.getFilename()); + } + + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, "as").c_str()); + Dest.addCommand(new Command(Exec, CmdArgs)); +} + +void dragonfly::Link::ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + const Driver &D = getToolChain().getHost().getDriver(); + ArgStringList CmdArgs; + + if (Args.hasArg(options::OPT_static)) { + CmdArgs.push_back("-Bstatic"); + } else { + if (Args.hasArg(options::OPT_shared)) + CmdArgs.push_back("-Bshareable"); + else { + CmdArgs.push_back("-dynamic-linker"); + CmdArgs.push_back("/usr/libexec/ld-elf.so.2"); + } + } + + // When building 32-bit code on DragonFly/pc64, we have to explicitly + // instruct ld in the base system to link 32-bit code. + if (getToolChain().getArchName() == "i386") { + CmdArgs.push_back("-m"); + CmdArgs.push_back("elf_i386"); + } + + if (Output.isPipe()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back("-"); + } else if (Output.isFilename()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + } else { + assert(Output.isNothing() && "Invalid output."); + } + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nostartfiles)) { + if (!Args.hasArg(options::OPT_shared)) { + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crt1.o").c_str())); + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crti.o").c_str())); + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crtbegin.o").c_str())); + } else { + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crti.o").c_str())); + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crtbeginS.o").c_str())); + } + } + + Args.AddAllArgs(CmdArgs, options::OPT_L); + Args.AddAllArgs(CmdArgs, options::OPT_T_Group); + Args.AddAllArgs(CmdArgs, options::OPT_e); + + for (InputInfoList::const_iterator + it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { + const InputInfo &II = *it; + + // Don't try to pass LLVM inputs to a generic gcc. + if (II.getType() == types::TY_LLVMBC) + D.Diag(clang::diag::err_drv_no_linker_llvm_support) + << getToolChain().getTripleString().c_str(); + + if (II.isPipe()) + CmdArgs.push_back("-"); + else if (II.isFilename()) + CmdArgs.push_back(II.getFilename()); + else + II.getInputArg().renderAsInput(Args, CmdArgs); + } + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs)) { + // FIXME: GCC passes on -lgcc, -lgcc_pic and a whole lot of + // rpaths + CmdArgs.push_back("-L/usr/lib/gcc41"); + + if (!Args.hasArg(options::OPT_static)) { + CmdArgs.push_back("-rpath"); + CmdArgs.push_back("/usr/lib/gcc41"); + + CmdArgs.push_back("-rpath-link"); + CmdArgs.push_back("/usr/lib/gcc41"); + + CmdArgs.push_back("-rpath"); + CmdArgs.push_back("/usr/lib"); + + CmdArgs.push_back("-rpath-link"); + CmdArgs.push_back("/usr/lib"); + } + + if (Args.hasArg(options::OPT_shared)) { + CmdArgs.push_back("-lgcc_pic"); + } else { + CmdArgs.push_back("-lgcc"); + } + + + if (Args.hasArg(options::OPT_pthread)) + CmdArgs.push_back("-lthread_xu"); + + if (!Args.hasArg(options::OPT_nolibc)) { + CmdArgs.push_back("-lc"); + } + + if (Args.hasArg(options::OPT_shared)) { + CmdArgs.push_back("-lgcc_pic"); + } else { + CmdArgs.push_back("-lgcc"); + } + } + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nostartfiles)) { + if (!Args.hasArg(options::OPT_shared)) + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crtend.o").c_str())); + else + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crtendS.o").c_str())); + CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath(C, "crtn.o").c_str())); + } + + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath(C, "ld").c_str()); + Dest.addCommand(new Command(Exec, CmdArgs)); +} diff --git a/lib/Driver/Tools.h b/lib/Driver/Tools.h new file mode 100644 index 0000000..db108db --- /dev/null +++ b/lib/Driver/Tools.h @@ -0,0 +1,316 @@ +//===--- Tools.h - Tool Implementations -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_LIB_DRIVER_TOOLS_H_ +#define CLANG_LIB_DRIVER_TOOLS_H_ + +#include "clang/Driver/Tool.h" +#include "clang/Driver/Types.h" +#include "clang/Driver/Util.h" + +#include "llvm/Support/Compiler.h" + +namespace clang { +namespace driver { + class Driver; + +namespace toolchains { + class Darwin_X86; +} + +namespace tools { + + class VISIBILITY_HIDDEN Clang : public Tool { + void AddPreprocessingOptions(const Driver &D, + const ArgList &Args, + ArgStringList &CmdArgs, + const InputInfo &Output, + const InputInfoList &Inputs) const; + + public: + Clang(const ToolChain &TC) : Tool("clang", TC) {} + + virtual bool acceptsPipedInput() const { return true; } + virtual bool canPipeOutput() const { return true; } + virtual bool hasIntegratedCPP() const { return true; } + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; + + /// gcc - Generic GCC tool implementations. +namespace gcc { + class VISIBILITY_HIDDEN Common : public Tool { + public: + Common(const char *Name, const ToolChain &TC) : Tool(Name, TC) {} + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + + /// RenderExtraToolArgs - Render any arguments necessary to force + /// the particular tool mode. + virtual void RenderExtraToolArgs(ArgStringList &CmdArgs) const = 0; + }; + + + class VISIBILITY_HIDDEN Preprocess : public Common { + public: + Preprocess(const ToolChain &TC) : Common("gcc::Preprocess", TC) {} + + virtual bool acceptsPipedInput() const { return true; } + virtual bool canPipeOutput() const { return true; } + virtual bool hasIntegratedCPP() const { return false; } + + virtual void RenderExtraToolArgs(ArgStringList &CmdArgs) const; + }; + + class VISIBILITY_HIDDEN Precompile : public Common { + public: + Precompile(const ToolChain &TC) : Common("gcc::Precompile", TC) {} + + virtual bool acceptsPipedInput() const { return true; } + virtual bool canPipeOutput() const { return false; } + virtual bool hasIntegratedCPP() const { return true; } + + virtual void RenderExtraToolArgs(ArgStringList &CmdArgs) const; + }; + + class VISIBILITY_HIDDEN Compile : public Common { + public: + Compile(const ToolChain &TC) : Common("gcc::Compile", TC) {} + + virtual bool acceptsPipedInput() const { return true; } + virtual bool canPipeOutput() const { return true; } + virtual bool hasIntegratedCPP() const { return true; } + + virtual void RenderExtraToolArgs(ArgStringList &CmdArgs) const; + }; + + class VISIBILITY_HIDDEN Assemble : public Common { + public: + Assemble(const ToolChain &TC) : Common("gcc::Assemble", TC) {} + + virtual bool acceptsPipedInput() const { return true; } + virtual bool canPipeOutput() const { return false; } + virtual bool hasIntegratedCPP() const { return false; } + + virtual void RenderExtraToolArgs(ArgStringList &CmdArgs) const; + }; + + class VISIBILITY_HIDDEN Link : public Common { + public: + Link(const ToolChain &TC) : Common("gcc::Link", TC) {} + + virtual bool acceptsPipedInput() const { return false; } + virtual bool canPipeOutput() const { return false; } + virtual bool hasIntegratedCPP() const { return false; } + + virtual void RenderExtraToolArgs(ArgStringList &CmdArgs) const; + }; +} // end namespace gcc + +namespace darwin { + class VISIBILITY_HIDDEN CC1 : public Tool { + public: + static const char *getBaseInputName(const ArgList &Args, + const InputInfoList &Input); + static const char *getBaseInputStem(const ArgList &Args, + const InputInfoList &Input); + static const char *getDependencyFileName(const ArgList &Args, + const InputInfoList &Inputs); + + protected: + const char *getCC1Name(types::ID Type) const; + + void AddCC1Args(const ArgList &Args, ArgStringList &CmdArgs) const; + void AddCC1OptionsArgs(const ArgList &Args, ArgStringList &CmdArgs, + const InputInfoList &Inputs, + const ArgStringList &OutputArgs) const; + void AddCPPOptionsArgs(const ArgList &Args, ArgStringList &CmdArgs, + const InputInfoList &Inputs, + const ArgStringList &OutputArgs) const; + void AddCPPUniqueOptionsArgs(const ArgList &Args, + ArgStringList &CmdArgs, + const InputInfoList &Inputs) const; + void AddCPPArgs(const ArgList &Args, ArgStringList &CmdArgs) const; + + public: + CC1(const char *Name, const ToolChain &TC) : Tool(Name, TC) {} + + virtual bool acceptsPipedInput() const { return true; } + virtual bool canPipeOutput() const { return true; } + virtual bool hasIntegratedCPP() const { return true; } + }; + + class VISIBILITY_HIDDEN Preprocess : public CC1 { + public: + Preprocess(const ToolChain &TC) : CC1("darwin::Preprocess", TC) {} + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; + + class VISIBILITY_HIDDEN Compile : public CC1 { + public: + Compile(const ToolChain &TC) : CC1("darwin::Compile", TC) {} + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; + + class VISIBILITY_HIDDEN Assemble : public Tool { + public: + Assemble(const ToolChain &TC) : Tool("darwin::Assemble", TC) {} + + virtual bool acceptsPipedInput() const { return true; } + virtual bool canPipeOutput() const { return false; } + virtual bool hasIntegratedCPP() const { return false; } + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; + + class VISIBILITY_HIDDEN Link : public Tool { + void AddDarwinArch(const ArgList &Args, ArgStringList &CmdArgs) const; + void AddDarwinSubArch(const ArgList &Args, ArgStringList &CmdArgs) const; + void AddLinkArgs(const ArgList &Args, ArgStringList &CmdArgs) const; + + /// The default macosx-version-min. + const char *MacosxVersionMin; + + const toolchains::Darwin_X86 &getDarwinToolChain() const; + + public: + Link(const ToolChain &TC, + const char *_MacosxVersionMin) + : Tool("darwin::Link", TC), MacosxVersionMin(_MacosxVersionMin) { + } + + virtual bool acceptsPipedInput() const { return false; } + virtual bool canPipeOutput() const { return false; } + virtual bool hasIntegratedCPP() const { return false; } + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; + + class VISIBILITY_HIDDEN Lipo : public Tool { + public: + Lipo(const ToolChain &TC) : Tool("darwin::Lipo", TC) {} + + virtual bool acceptsPipedInput() const { return false; } + virtual bool canPipeOutput() const { return false; } + virtual bool hasIntegratedCPP() const { return false; } + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; +} + + /// freebsd -- Directly call GNU Binutils assembler and linker +namespace freebsd { + class VISIBILITY_HIDDEN Assemble : public Tool { + public: + Assemble(const ToolChain &TC) : Tool("freebsd::Assemble", TC) {} + + virtual bool acceptsPipedInput() const { return true; } + virtual bool canPipeOutput() const { return true; } + virtual bool hasIntegratedCPP() const { return false; } + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; + class VISIBILITY_HIDDEN Link : public Tool { + public: + Link(const ToolChain &TC) : Tool("freebsd::Link", TC) {} + + virtual bool acceptsPipedInput() const { return true; } + virtual bool canPipeOutput() const { return true; } + virtual bool hasIntegratedCPP() const { return false; } + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; +} + + /// dragonfly -- Directly call GNU Binutils assembler and linker +namespace dragonfly { + class VISIBILITY_HIDDEN Assemble : public Tool { + public: + Assemble(const ToolChain &TC) : Tool("dragonfly::Assemble", TC) {} + + virtual bool acceptsPipedInput() const { return true; } + virtual bool canPipeOutput() const { return true; } + virtual bool hasIntegratedCPP() const { return false; } + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; + class VISIBILITY_HIDDEN Link : public Tool { + public: + Link(const ToolChain &TC) : Tool("dragonfly::Link", TC) {} + + virtual bool acceptsPipedInput() const { return true; } + virtual bool canPipeOutput() const { return true; } + virtual bool hasIntegratedCPP() const { return false; } + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + Job &Dest, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; +} + +} // end namespace toolchains +} // end namespace driver +} // end namespace clang + +#endif diff --git a/lib/Driver/Types.cpp b/lib/Driver/Types.cpp new file mode 100644 index 0000000..e89e973 --- /dev/null +++ b/lib/Driver/Types.cpp @@ -0,0 +1,205 @@ +//===--- Types.cpp - Driver input & temporary type information ----------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Types.h" + +#include <string.h> +#include <cassert> + +using namespace clang::driver; +using namespace clang::driver::types; + +struct Info { + const char *Name; + const char *Flags; + const char *TempSuffix; + ID PreprocessedType; +}; + +static Info TypeInfos[] = { +#define TYPE(NAME, ID, PP_TYPE, TEMP_SUFFIX, FLAGS) \ + { NAME, FLAGS, TEMP_SUFFIX, TY_##PP_TYPE, }, +#include "clang/Driver/Types.def" +#undef TYPE +}; +static const unsigned numTypes = sizeof(TypeInfos) / sizeof(TypeInfos[0]); + +static Info &getInfo(unsigned id) { + assert(id > 0 && id - 1 < numTypes && "Invalid Type ID."); + return TypeInfos[id - 1]; +} + +const char *types::getTypeName(ID Id) { + return getInfo(Id).Name; +} + +types::ID types::getPreprocessedType(ID Id) { + return getInfo(Id).PreprocessedType; +} + +const char *types::getTypeTempSuffix(ID Id) { + return getInfo(Id).TempSuffix; +} + +bool types::onlyAssembleType(ID Id) { + return strchr(getInfo(Id).Flags, 'a'); +} + +bool types::onlyPrecompileType(ID Id) { + return strchr(getInfo(Id).Flags, 'p'); +} + +bool types::canTypeBeUserSpecified(ID Id) { + return strchr(getInfo(Id).Flags, 'u'); +} + +bool types::appendSuffixForType(ID Id) { + return strchr(getInfo(Id).Flags, 'A'); +} + +bool types::canLipoType(ID Id) { + return (Id == TY_Nothing || + Id == TY_Image || + Id == TY_Object); +} + +bool types::isAcceptedByClang(ID Id) { + switch (Id) { + default: + return false; + + case TY_Asm: + case TY_C: case TY_PP_C: + case TY_ObjC: case TY_PP_ObjC: + case TY_CXX: case TY_PP_CXX: + case TY_ObjCXX: case TY_PP_ObjCXX: + case TY_CHeader: case TY_PP_CHeader: + case TY_ObjCHeader: case TY_PP_ObjCHeader: + case TY_CXXHeader: case TY_PP_CXXHeader: + case TY_ObjCXXHeader: case TY_PP_ObjCXXHeader: + return true; + } +} + +bool types::isCXX(ID Id) { + switch (Id) { + default: + return false; + + case TY_CXX: case TY_PP_CXX: + case TY_ObjCXX: case TY_PP_ObjCXX: + case TY_CXXHeader: case TY_PP_CXXHeader: + case TY_ObjCXXHeader: case TY_PP_ObjCXXHeader: + return true; + } +} + +types::ID types::lookupTypeForExtension(const char *Ext) { + unsigned N = strlen(Ext); + + switch (N) { + case 1: + if (memcmp(Ext, "c", 1) == 0) return TY_C; + if (memcmp(Ext, "i", 1) == 0) return TY_PP_C; + if (memcmp(Ext, "m", 1) == 0) return TY_ObjC; + if (memcmp(Ext, "M", 1) == 0) return TY_ObjCXX; + if (memcmp(Ext, "h", 1) == 0) return TY_CHeader; + if (memcmp(Ext, "C", 1) == 0) return TY_CXX; + if (memcmp(Ext, "H", 1) == 0) return TY_CXXHeader; + if (memcmp(Ext, "f", 1) == 0) return TY_PP_Fortran; + if (memcmp(Ext, "F", 1) == 0) return TY_Fortran; + if (memcmp(Ext, "s", 1) == 0) return TY_PP_Asm; + if (memcmp(Ext, "S", 1) == 0) return TY_Asm; + case 2: + if (memcmp(Ext, "ii", 2) == 0) return TY_PP_CXX; + if (memcmp(Ext, "mi", 2) == 0) return TY_PP_ObjC; + if (memcmp(Ext, "mm", 2) == 0) return TY_ObjCXX; + if (memcmp(Ext, "cc", 2) == 0) return TY_CXX; + if (memcmp(Ext, "cc", 2) == 0) return TY_CXX; + if (memcmp(Ext, "cp", 2) == 0) return TY_CXX; + if (memcmp(Ext, "hh", 2) == 0) return TY_CXXHeader; + break; + case 3: + if (memcmp(Ext, "ads", 3) == 0) return TY_Ada; + if (memcmp(Ext, "adb", 3) == 0) return TY_Ada; + if (memcmp(Ext, "cxx", 3) == 0) return TY_CXX; + if (memcmp(Ext, "cpp", 3) == 0) return TY_CXX; + if (memcmp(Ext, "CPP", 3) == 0) return TY_CXX; + if (memcmp(Ext, "cXX", 3) == 0) return TY_CXX; + if (memcmp(Ext, "for", 3) == 0) return TY_PP_Fortran; + if (memcmp(Ext, "FOR", 3) == 0) return TY_PP_Fortran; + if (memcmp(Ext, "fpp", 3) == 0) return TY_Fortran; + if (memcmp(Ext, "FPP", 3) == 0) return TY_Fortran; + if (memcmp(Ext, "f90", 3) == 0) return TY_PP_Fortran; + if (memcmp(Ext, "f95", 3) == 0) return TY_PP_Fortran; + if (memcmp(Ext, "F90", 3) == 0) return TY_Fortran; + if (memcmp(Ext, "F95", 3) == 0) return TY_Fortran; + if (memcmp(Ext, "mii", 3) == 0) return TY_PP_ObjCXX; + break; + } + + return TY_INVALID; +} + +types::ID types::lookupTypeForTypeSpecifier(const char *Name) { + unsigned N = strlen(Name); + + for (unsigned i=0; i<numTypes; ++i) { + types::ID Id = (types::ID) (i + 1); + if (canTypeBeUserSpecified(Id) && + memcmp(Name, getInfo(Id).Name, N + 1) == 0) + return Id; + } + + return TY_INVALID; +} + +// FIXME: Why don't we just put this list in the defs file, eh. + +unsigned types::getNumCompilationPhases(ID Id) { + if (Id == TY_Object) + return 1; + + unsigned N = 0; + if (getPreprocessedType(Id) != TY_INVALID) + N += 1; + + if (onlyAssembleType(Id)) + return N + 2; // assemble, link + if (onlyPrecompileType(Id)) + return N + 1; // precompile + + return N + 3; // compile, assemble, link +} + +phases::ID types::getCompilationPhase(ID Id, unsigned N) { + assert(N < getNumCompilationPhases(Id) && "Invalid index."); + + if (Id == TY_Object) + return phases::Link; + + if (getPreprocessedType(Id) != TY_INVALID) { + if (N == 0) + return phases::Preprocess; + --N; + } + + if (onlyAssembleType(Id)) + return N == 0 ? phases::Assemble : phases::Link; + + if (onlyPrecompileType(Id)) + return phases::Precompile; + + if (N == 0) + return phases::Compile; + if (N == 1) + return phases::Assemble; + + return phases::Link; +} |