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.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; @@ -164,11 +165,36 @@ } } -static void AddLinkerInputs(const ToolChain &TC, +static StringRef GetFileListName(const ArgList &Args, + const InputInfoList &Inputs, int Sequence) { + 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 << Clang::getBaseInputStem(Args, Inputs); + } + + Name << Sequence << ".rsp"; + return Args.MakeArgString(Name.str()); +} + +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(); + std::ofstream FileList; + StringRef FileListName; + int FileListSequence = 0; + // Add extra linker input arguments which are not treated as inputs // (constructed via -Xarch_). Args.AddAllArgValues(CmdArgs, options::OPT_Zlinker_input); @@ -186,10 +212,33 @@ // Add filenames immediately. if (II.isFilename()) { - CmdArgs.push_back(II.getFilename()); + if (UseLinkerResponseFiles && II.getType() == types::TY_Object) { + if (!FileList.is_open()) { + FileListName = GetFileListName(Args, Inputs, FileListSequence++); + + // Skip the leading '@' + FileList.open(FileListName.substr(1).data(), + std::ios_base::trunc | std::ios_base::out); + if (!FileList.is_open()) + D.Diag(diag::err_drv_unable_to_open_file) << FileListName; + C.addTempFile(FileListName.data()); + } + FileList << II.getFilename() << std::endl; + } else { + if (FileList.is_open()) { + FileList.close(); + CmdArgs.push_back(FileListName.data()); + } + CmdArgs.push_back(II.getFilename()); + } continue; } + if (FileList.is_open()) { + FileList.close(); + CmdArgs.push_back(FileListName.data()); + } + // Otherwise, this is a linker input argument. const Arg &A = II.getInputArg(); @@ -202,6 +251,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 (FileList.is_open()) { + FileList.close(); + CmdArgs.push_back(FileListName.data()); + } + // LIBRARY_PATH - included following the user specified library paths. // and only supported on native toolchains. if (!TC.isCrossCompiling()) @@ -4882,7 +4938,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 @@ -5499,7 +5555,7 @@ break; } - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + AddLinkerInputs(C, getToolChain(), Inputs, Args, CmdArgs); if (isObjCRuntimeLinked(Args) && !Args.hasArg(options::OPT_nostdlib) && @@ -5541,8 +5597,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)); } @@ -5709,7 +5764,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)) { @@ -5731,8 +5786,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)); } @@ -5815,7 +5869,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)) { @@ -5997,7 +6051,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)) { @@ -6123,7 +6177,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)) { @@ -6372,7 +6426,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)) { @@ -6634,7 +6688,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); @@ -7096,7 +7150,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. @@ -7225,7 +7279,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); @@ -7353,7 +7407,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)) { @@ -7689,7 +7743,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 +