Index: cfe/trunk/lib/Driver/Job.cpp =================================================================== --- cfe/trunk/lib/Driver/Job.cpp +++ cfe/trunk/lib/Driver/Job.cpp @@ -98,7 +98,9 @@ return; } - // In regular response files, we send all arguments to the response file + // In regular response files, we send all arguments to the response file. + // Wrapping all arguments in double quotes ensures that both Unix tools and + // Windows tools understand the response file. for (const char *Arg : Arguments) { OS << '"'; Index: cfe/trunk/test/Driver/cl-response-file.c =================================================================== --- cfe/trunk/test/Driver/cl-response-file.c +++ cfe/trunk/test/Driver/cl-response-file.c @@ -0,0 +1,13 @@ +// Don't attempt slash switches on msys bash. +// REQUIRES: shell-preserves-root + +// Test that we use the Windows tokenizer for clang-cl response files. The +// trailing backslash before the space should be interpreted as a literal +// backslash. PR23709 + + + +// RUN: echo '/I%S\Inputs\cl-response-file\ /DFOO=2' > %t.rsp +// RUN: %clang_cl /c -### @%t.rsp -- %s 2>&1 | FileCheck %s + +// CHECK: "-D" "FOO=2" "-I" "{{.*}}\\Inputs\\cl-response-file\\" Index: cfe/trunk/tools/driver/driver.cpp =================================================================== --- cfe/trunk/tools/driver/driver.cpp +++ cfe/trunk/tools/driver/driver.cpp @@ -231,8 +231,18 @@ return nullptr; } -static void ParseProgName(SmallVectorImpl &ArgVector, - std::set &SavedStrings) { +/// Normalize the program name from argv[0] by stripping the file extension if +/// present and lower-casing the string on Windows. +static std::string normalizeProgramName(const char *Argv0) { + std::string ProgName = llvm::sys::path::stem(Argv0); +#ifdef LLVM_ON_WIN32 + // Transform to lowercase for case insensitive file systems. + std::transform(ProgName.begin(), ProgName.end(), ProgName.begin(), ::tolower); +#endif + return ProgName; +} + +static const DriverSuffix *parseDriverSuffix(StringRef ProgName) { // Try to infer frontend type and default target from the program name by // comparing it against DriverSuffixes in order. @@ -240,54 +250,53 @@ // E.g. "x86_64-linux-clang" as interpreted as suffix "clang" with target // prefix "x86_64-linux". If such a target prefix is found, is gets added via // -target as implicit first argument. - - std::string ProgName =llvm::sys::path::stem(ArgVector[0]); -#ifdef LLVM_ON_WIN32 - // Transform to lowercase for case insensitive file systems. - ProgName = StringRef(ProgName).lower(); -#endif - - StringRef ProgNameRef = ProgName; - const DriverSuffix *DS = FindDriverSuffix(ProgNameRef); + const DriverSuffix *DS = FindDriverSuffix(ProgName); if (!DS) { // Try again after stripping any trailing version number: // clang++3.5 -> clang++ - ProgNameRef = ProgNameRef.rtrim("0123456789."); - DS = FindDriverSuffix(ProgNameRef); + ProgName = ProgName.rtrim("0123456789."); + DS = FindDriverSuffix(ProgName); } if (!DS) { // Try again after stripping trailing -component. // clang++-tot -> clang++ - ProgNameRef = ProgNameRef.slice(0, ProgNameRef.rfind('-')); - DS = FindDriverSuffix(ProgNameRef); + ProgName = ProgName.slice(0, ProgName.rfind('-')); + DS = FindDriverSuffix(ProgName); } + return DS; +} - if (DS) { - if (const char *Flag = DS->ModeFlag) { - // Add Flag to the arguments. - auto it = ArgVector.begin(); - if (it != ArgVector.end()) - ++it; - ArgVector.insert(it, Flag); - } - - StringRef::size_type LastComponent = ProgNameRef.rfind( - '-', ProgNameRef.size() - strlen(DS->Suffix)); - if (LastComponent == StringRef::npos) - return; - - // Infer target from the prefix. - StringRef Prefix = ProgNameRef.slice(0, LastComponent); - std::string IgnoredError; - if (llvm::TargetRegistry::lookupTarget(Prefix, IgnoredError)) { - auto it = ArgVector.begin(); - if (it != ArgVector.end()) - ++it; - const char *arr[] = { "-target", GetStableCStr(SavedStrings, Prefix) }; - ArgVector.insert(it, std::begin(arr), std::end(arr)); - } +static void insertArgsFromProgramName(StringRef ProgName, + const DriverSuffix *DS, + SmallVectorImpl &ArgVector, + std::set &SavedStrings) { + if (!DS) + return; + + if (const char *Flag = DS->ModeFlag) { + // Add Flag to the arguments. + auto it = ArgVector.begin(); + if (it != ArgVector.end()) + ++it; + ArgVector.insert(it, Flag); + } + + StringRef::size_type LastComponent = ProgName.rfind( + '-', ProgName.size() - strlen(DS->Suffix)); + if (LastComponent == StringRef::npos) + return; + + // Infer target from the prefix. + StringRef Prefix = ProgName.slice(0, LastComponent); + std::string IgnoredError; + if (llvm::TargetRegistry::lookupTarget(Prefix, IgnoredError)) { + auto it = ArgVector.begin(); + if (it != ArgVector.end()) + ++it; + const char *arr[] = { "-target", GetStableCStr(SavedStrings, Prefix) }; + ArgVector.insert(it, std::begin(arr), std::end(arr)); } } @@ -380,16 +389,33 @@ return 1; } + std::string ProgName = normalizeProgramName(argv[0]); + const DriverSuffix *DS = parseDriverSuffix(ProgName); + llvm::BumpPtrAllocator A; llvm::BumpPtrStringSaver Saver(A); + // Parse response files using the GNU syntax, unless we're in CL mode. There + // are two ways to put clang in CL compatibility mode: argv[0] is either + // clang-cl or cl, or --driver-mode=cl is on the command line. The normal + // command line parsing can't happen until after response file parsing, so we + // have to manually search for a --driver-mode=cl argument the hard way. + // Finally, our -cc1 tools don't care which tokenization mode we use because + // response files written by clang will tokenize the same way in either mode. + llvm::cl::TokenizerCallback Tokenizer = &llvm::cl::TokenizeGNUCommandLine; + if ((DS && DS->ModeFlag && strcmp(DS->ModeFlag, "--driver-mode=cl") == 0) || + std::find_if(argv.begin(), argv.end(), [](const char *F) { + return F && strcmp(F, "--driver-mode=cl") == 0; + }) != argv.end()) { + Tokenizer = &llvm::cl::TokenizeWindowsCommandLine; + } + // Determines whether we want nullptr markers in argv to indicate response // files end-of-lines. We only use this for the /LINK driver argument. bool MarkEOLs = true; if (argv.size() > 1 && StringRef(argv[1]).startswith("-cc1")) MarkEOLs = false; - llvm::cl::ExpandResponseFiles(Saver, llvm::cl::TokenizeGNUCommandLine, argv, - MarkEOLs); + llvm::cl::ExpandResponseFiles(Saver, Tokenizer, argv, MarkEOLs); // Handle -cc1 integrated tools, even if -cc1 was expanded from a response // file. @@ -450,7 +476,7 @@ SetInstallDir(argv, TheDriver); llvm::InitializeAllTargets(); - ParseProgName(argv, SavedStrings); + insertArgsFromProgramName(ProgName, DS, argv, SavedStrings); SetBackdoorDriverOutputsFromEnvVars(TheDriver);