Index: include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- include/clang/Basic/DiagnosticDriverKinds.td +++ include/clang/Basic/DiagnosticDriverKinds.td @@ -39,6 +39,7 @@ "cannot specify '%0%1' when compiling multiple source files">; def err_no_external_assembler : Error< "there is no external assembler that can be used on this platform">; +def err_drv_unable_to_open_file : Error< "unable to open file: %0">; def err_drv_unable_to_remove_file : Error< "unable to remove file: %0">; def err_drv_command_failure : Error< Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1384,6 +1384,8 @@ def : Flag<["-"], "no-integrated-as">, Alias, Flags<[CC1Option, DriverOption]>; +def fuse_linker_response_files : Joined<["-"], "fuse-linker-response-files">; + def working_directory : JoinedOrSeparate<["-"], "working-directory">, Flags<[CC1Option]>, HelpText<"Resolve file paths relative to the specified directory">; def working_directory_EQ : Joined<["-"], "working-directory=">, Flags<[CC1Option]>, Index: lib/Driver/Tools.h =================================================================== --- lib/Driver/Tools.h +++ lib/Driver/Tools.h @@ -40,6 +40,9 @@ const InputInfoList &Inputs); static const char *getDependencyFileName(const llvm::opt::ArgList &Args, const InputInfoList &Inputs); + static const char *getLinkerFileListName(const llvm::opt::ArgList &Args, + const InputInfoList &Inputs, + unsigned SequenceNumber); private: void AddPreprocessingOptions(Compilation &C, const JobAction &JA, Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -39,6 +39,7 @@ #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include +#include using namespace clang::driver; using namespace clang::driver::tools; @@ -168,11 +169,17 @@ } } -static void AddLinkerInputs(const ToolChain &TC, +static void AddLinkerInputs(Compilation &C, const ToolChain &TC, const InputInfoList &Inputs, const ArgList &Args, ArgStringList &CmdArgs) { + const bool UseLinkerResponseFiles = + Args.hasArg(options::OPT_fuse_linker_response_files); const Driver &D = TC.getDriver(); + const char *CurrentFileListName; + std::ofstream CurrentFileList; + unsigned FileListSequence = 0; + // Add extra linker input arguments which are not treated as inputs // (constructed via -Xarch_). Args.AddAllArgValues(CmdArgs, options::OPT_Zlinker_input); @@ -190,10 +197,35 @@ // Add filenames immediately. if (II.isFilename()) { - CmdArgs.push_back(II.getFilename()); + if (UseLinkerResponseFiles && II.getType() == types::TY_Object) { + if (!CurrentFileList.is_open()) { + CurrentFileListName = + Clang::getLinkerFileListName(Args, Inputs, FileListSequence++); + + // Skip the leading '@' + assert(CurrentFileListName[0] == '@' && "expected '@' prefixed name"); + CurrentFileList.open(&CurrentFileListName[1], + std::ios_base::trunc | std::ios_base::out); + if (!CurrentFileList.is_open()) + D.Diag(diag::err_drv_unable_to_open_file) << CurrentFileListName; + C.addTempFile(CurrentFileListName); + } + CurrentFileList << II.getFilename() << std::endl; + } else { + if (CurrentFileList.is_open()) { + CurrentFileList.close(); + CmdArgs.push_back(CurrentFileListName); + } + CmdArgs.push_back(II.getFilename()); + } continue; } + if (CurrentFileList.is_open()) { + CurrentFileList.close(); + CmdArgs.push_back(CurrentFileListName); + } + // Otherwise, this is a linker input argument. const Arg &A = II.getInputArg(); @@ -206,6 +238,13 @@ A.renderAsInput(Args, CmdArgs); } + // NOTE this is needed here to handle the case where an object file is the + // last argument; otherwise, we don't properly close the file list. + if (CurrentFileList.is_open()) { + CurrentFileList.close(); + CmdArgs.push_back(CurrentFileListName); + } + // LIBRARY_PATH - included following the user specified library paths. // and only supported on native toolchains. if (!TC.isCrossCompiling()) @@ -4886,7 +4925,7 @@ Args.AddAllArgs(CmdArgs, options::OPT_t); Args.AddAllArgs(CmdArgs, options::OPT_u_Group); - AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs); + AddLinkerInputs(C, ToolChain, Inputs, Args, CmdArgs); //---------------------------------------------------------------------------- // Libraries @@ -5153,6 +5192,26 @@ return Args.MakeArgString(Res + ".d"); } +const char *Clang::getLinkerFileListName(const llvm::opt::ArgList &Args, + const InputInfoList &Inputs, + unsigned SequenceNumber) { + std::string Buffer; + llvm::raw_string_ostream Name(Buffer); + + // Add the prefix so as to ensure that it is made part of the argument string + Name << "@"; + + if (Arg *Output = Args.getLastArg(options::OPT_o)) { + StringRef Value = Output->getValue(); + Name << Value.substr(0, Value.rfind('.')); + } else { + Name << getBaseInputStem(Args, Inputs); + } + + Name << SequenceNumber << ".rsp"; + return Args.MakeArgString(Name.str()); +} + void darwin::Assemble::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, @@ -5503,7 +5562,7 @@ break; } - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + AddLinkerInputs(C, getToolChain(), Inputs, Args, CmdArgs); if (isObjCRuntimeLinked(Args) && !Args.hasArg(options::OPT_nostdlib) && @@ -5545,8 +5604,7 @@ Args.AddAllArgs(CmdArgs, options::OPT_T_Group); Args.AddAllArgs(CmdArgs, options::OPT_F); - const char *Exec = - Args.MakeArgString(getToolChain().GetProgramPath("ld")); + const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("ld")); C.addCommand(new Command(JA, *this, Exec, CmdArgs)); } @@ -5713,7 +5771,7 @@ Args.AddAllArgs(CmdArgs, options::OPT_e); Args.AddAllArgs(CmdArgs, options::OPT_r); - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + AddLinkerInputs(C, getToolChain(), Inputs, Args, CmdArgs); if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs)) { @@ -5735,8 +5793,7 @@ addProfileRT(getToolChain(), Args, CmdArgs); - const char *Exec = - Args.MakeArgString(getToolChain().GetProgramPath("ld")); + const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("ld")); C.addCommand(new Command(JA, *this, Exec, CmdArgs)); } @@ -5819,7 +5876,7 @@ Args.AddAllArgs(CmdArgs, options::OPT_T_Group); Args.AddAllArgs(CmdArgs, options::OPT_e); - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + AddLinkerInputs(C, getToolChain(), Inputs, Args, CmdArgs); if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs)) { @@ -6001,7 +6058,7 @@ Args.AddAllArgs(CmdArgs, options::OPT_Z_Flag); Args.AddAllArgs(CmdArgs, options::OPT_r); - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + AddLinkerInputs(C, getToolChain(), Inputs, Args, CmdArgs); if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs)) { @@ -6127,7 +6184,7 @@ Args.AddAllArgs(CmdArgs, options::OPT_T_Group); Args.AddAllArgs(CmdArgs, options::OPT_e); - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + AddLinkerInputs(C, getToolChain(), Inputs, Args, CmdArgs); if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs)) { @@ -6376,7 +6433,7 @@ if (D.IsUsingLTO(Args)) AddGoldPlugin(ToolChain, Args, CmdArgs); - AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs); + AddLinkerInputs(C, ToolChain, Inputs, Args, CmdArgs); if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs)) { @@ -6638,7 +6695,7 @@ Args.AddAllArgs(CmdArgs, options::OPT_Z_Flag); Args.AddAllArgs(CmdArgs, options::OPT_r); - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + AddLinkerInputs(C, getToolChain(), Inputs, Args, CmdArgs); unsigned Major, Minor, Micro; getToolChain().getTriple().getOSVersion(Major, Minor, Micro); @@ -7100,7 +7157,7 @@ if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) CmdArgs.push_back("--no-demangle"); - AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs); + AddLinkerInputs(C, ToolChain, Inputs, Args, CmdArgs); addSanitizerRuntimes(getToolChain(), Args, CmdArgs); // The profile runtime also needs access to system libraries. @@ -7229,7 +7286,7 @@ Args.AddAllArgs(CmdArgs, options::OPT_T_Group); Args.AddAllArgs(CmdArgs, options::OPT_e); - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + AddLinkerInputs(C, getToolChain(), Inputs, Args, CmdArgs); addProfileRT(getToolChain(), Args, CmdArgs); @@ -7357,7 +7414,7 @@ Args.AddAllArgs(CmdArgs, options::OPT_T_Group); Args.AddAllArgs(CmdArgs, options::OPT_e); - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + AddLinkerInputs(C, getToolChain(), Inputs, Args, CmdArgs); if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs)) { @@ -7693,7 +7750,7 @@ if (EH.ShouldUseExceptionTables) CmdArgs.push_back("-fexceptions"); - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + AddLinkerInputs(C, getToolChain(), Inputs, Args, CmdArgs); const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("xcc")); C.addCommand(new Command(JA, *this, Exec, CmdArgs)); Index: test/Driver/filelist.c =================================================================== --- /dev/null +++ test/Driver/filelist.c @@ -0,0 +1,54 @@ +// Test handling of -fuse-linker-response-files + +// RUN: %clang -### %S/Inputs/filelist/a.o -o test 2>&1 \ +// RUN: | FileCheck %s -check-prefix CHECK-NO-FILELIST + +// CHECK-NO-FILELIST-NOT: "@test0.rsp" +// CHECK-NO-FILELIST: "{{.*}}/test/Driver/Inputs/filelist/a.o" + +// RUN: %clang -### -fuse-linker-response-files -save-temps %S/Inputs/filelist/a.o -o test 2>&1 \ +// RUN: | FileCheck %s -check-prefix CHECK-FILELIST +// RUN: FileCheck -check-prefix CHECK-FILELIST-0 %s < test0.rsp + +// CHECK-FILELIST-NOT: "{{.*}}/test/Driver/Inputs/filelist/a.o" +// CHECK-FILELIST: "@test0.rsp" + +// CHECK-FILELIST-0: {{.*}}/test/Driver/Inputs/filelist/a.o + +// RUN: %clang -### -fuse-linker-response-files -save-temps %S/Inputs/filelist/a.o -library %S/Inputs/filelist/b.o -o test 2>&1 \ +// RUN: | FileCheck %s -check-prefix CHECK-FILELIST-SPLIT +// RUN: FileCheck -check-prefix CHECK-FILELIST-SPLIT-0 %s < test0.rsp +// RUN: FileCheck -check-prefix CHECK-FILELIST-SPLIT-1 %s < test1.rsp + +// CHECK-FILELIST-SPLIT-NOT: "{{.*}}/test/Driver/Inputs/filelist/a.o" +// CHECK-FILELIST-SPLIT: @test0.rsp +// CHECK-FILELIST-SPLIT: -library +// CHECK-FILELIST-SPLIT-NOT: "{{.*}}/test/Driver/Inputs/filelist/b.o" +// CHECK-FILELIST-SPLIT: @test1.rsp + +// CHECK-FILELIST-SPLIT-0: a.o + +// CHECK-FILELIST-SPLIT-1: b.o + +// RUN: %clang '-###' -fuse-linker-response-files -save-temps %S/Inputs/filelist/a.o %S/Inputs/filelist/c.a %S/Inputs/filelist/b.o -o test 2>&1 \ +// RUN: | FileCheck %s -check-prefix CHECK-FILELIST-ARCHIVES +// RUN: FileCheck -check-prefix CHECK-FILELIST-ARCHIVES-0 %s < test0.rsp + +// CHECK-FILELIST-ARCHIVES-NOT: "{{.*}}/test/Driver/Inputs/filelist/a.o" +// CHECK-FILELIST-ARCHIVES-NOT: "{{.*}}/test/Driver/Inputs/filelist/c.a" +// CHECK-FILELIST-ARCHIVES-NOT: "{{.*}}/test/Driver/Inputs/filelist/b.o" +// CHECK-FILELIST-ARCHIVES: @test0.rsp + +// CHECK-FILELIST-ARCHIVES-0: {{.*}}/test/Driver/Inputs/filelist/a.o +// CHECK-FILELIST-ARCHIVES-0: {{.*}}/test/Driver/Inputs/filelist/c.a +// CHECK-FILELIST-ARCHIVES-0: {{.*}}/test/Driver/Inputs/filelist/b.o + +// RUN: %clang -### -fuse-linker-response-files -save-temps %S/Inputs/filelist/a.obj -o test 2>&1 \ +// RUN: | FileCheck %s -check-prefix CHECK-ALT-SUFFIX +// RUN: FileCheck -check-prefix CHECK-ALT-SUFFIX-0 %s < test0.rsp + +// CHECK-ALT-SUFFIX-NOT: "{{.*}}/test/Driver/Inputs/filelist/a.obj" +// CHECK-ALT-SUFFIX: "@test0.rsp" + +// CHECK-ALT-SUFFIX-0: {{.*}}/test/Driver/Inputs/filelist/a.obj +