Prechádzať zdrojové kódy

Support generating NMake/Jom-style depfiles.

NMake is a Make-like builder that comes with Microsoft Visual Studio.
Jom (https://wiki.qt.io/Jom) is an NMake-compatible build tool.
Dependency files for NMake/Jom need to use double-quotes to wrap
filespecs containing special characters, instead of the backslash
escapes that GNU Make wants.

Adds the -MV option, which specifies to use double-quotes as needed
instead of backslash escapes when writing the dependency file.

Differential Revision: http://reviews.llvm.org/D9260


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@235903 91177308-0d34-0410-b5e6-96231b3b80d8
Paul Robinson 10 rokov pred
rodič
commit
cc44eec4a2

+ 19 - 0
docs/UsersManual.rst

@@ -589,6 +589,25 @@ Current limitations
    translated from debug annotations. That translation can be lossy,
    translated from debug annotations. That translation can be lossy,
    which results in some remarks having no location information.
    which results in some remarks having no location information.
 
 
+Other Options
+-------------
+Clang options that that don't fit neatly into other categories.
+
+.. option:: -MV
+
+  When emitting a dependency file, use formatting conventions appropriate
+  for NMake or Jom. Ignored unless another option causes Clang to emit a
+  dependency file.
+
+When Clang emits a dependency file (e.g., you supplied the -M option)
+most filenames can be written to the file without any special formatting.
+Different Make tools will treat different sets of characters as "special"
+and use different conventions for telling the Make tool that the character
+is actually part of the filename. Normally Clang uses backslash to "escape"
+a special character, which is the convention used by GNU Make. The -MV
+option tells Clang to put double-quotes around the entire filename, which
+is the convention used by NMake and Jom.
+
 
 
 Language and Target-Independent Features
 Language and Target-Independent Features
 ========================================
 ========================================

+ 2 - 0
include/clang/Driver/Options.td

@@ -250,6 +250,8 @@ def MQ : JoinedOrSeparate<["-"], "MQ">, Group<M_Group>, Flags<[CC1Option]>,
     HelpText<"Specify name of main file output to quote in depfile">;
     HelpText<"Specify name of main file output to quote in depfile">;
 def MT : JoinedOrSeparate<["-"], "MT">, Group<M_Group>, Flags<[CC1Option]>,
 def MT : JoinedOrSeparate<["-"], "MT">, Group<M_Group>, Flags<[CC1Option]>,
     HelpText<"Specify name of main file output in depfile">;
     HelpText<"Specify name of main file output in depfile">;
+def MV : Flag<["-"], "MV">, Group<M_Group>, Flags<[CC1Option]>,
+    HelpText<"Use NMake/Jom format for the depfile">;
 def Mach : Flag<["-"], "Mach">;
 def Mach : Flag<["-"], "Mach">;
 def O0 : Flag<["-"], "O0">, Group<O_Group>, Flags<[CC1Option]>;
 def O0 : Flag<["-"], "O0">, Group<O_Group>, Flags<[CC1Option]>;
 def O4 : Flag<["-"], "O4">, Group<O_Group>, Flags<[CC1Option]>;
 def O4 : Flag<["-"], "O4">, Group<O_Group>, Flags<[CC1Option]>;

+ 8 - 1
include/clang/Frontend/DependencyOutputOptions.h

@@ -15,6 +15,9 @@
 
 
 namespace clang {
 namespace clang {
 
 
+/// DependencyOutputFormat - Format for the compiler dependency file.
+enum class DependencyOutputFormat { Make, NMake };
+
 /// DependencyOutputOptions - Options for controlling the compiler dependency
 /// DependencyOutputOptions - Options for controlling the compiler dependency
 /// file generation.
 /// file generation.
 class DependencyOutputOptions {
 class DependencyOutputOptions {
@@ -27,7 +30,10 @@ public:
   unsigned AddMissingHeaderDeps : 1; ///< Add missing headers to dependency list
   unsigned AddMissingHeaderDeps : 1; ///< Add missing headers to dependency list
   unsigned PrintShowIncludes : 1; ///< Print cl.exe style /showIncludes info.
   unsigned PrintShowIncludes : 1; ///< Print cl.exe style /showIncludes info.
   unsigned IncludeModuleFiles : 1; ///< Include module file dependencies.
   unsigned IncludeModuleFiles : 1; ///< Include module file dependencies.
-  
+
+  /// The format for the dependency file.
+  DependencyOutputFormat OutputFormat;
+
   /// The file to write dependency output to.
   /// The file to write dependency output to.
   std::string OutputFile;
   std::string OutputFile;
 
 
@@ -55,6 +61,7 @@ public:
     AddMissingHeaderDeps = 0;
     AddMissingHeaderDeps = 0;
     PrintShowIncludes = 0;
     PrintShowIncludes = 0;
     IncludeModuleFiles = 0;
     IncludeModuleFiles = 0;
+    OutputFormat = DependencyOutputFormat::Make;
   }
   }
 };
 };
 
 

+ 1 - 0
lib/Driver/Tools.cpp

@@ -338,6 +338,7 @@ void Clang::AddPreprocessingOptions(Compilation &C,
   }
   }
 
 
   Args.AddLastArg(CmdArgs, options::OPT_MP);
   Args.AddLastArg(CmdArgs, options::OPT_MP);
+  Args.AddLastArg(CmdArgs, options::OPT_MV);
 
 
   // Convert all -MQ <target> args to -MT <quoted target>
   // Convert all -MQ <target> args to -MT <quoted target>
   for (arg_iterator it = Args.filtered_begin(options::OPT_MT,
   for (arg_iterator it = Args.filtered_begin(options::OPT_MT,

+ 2 - 0
lib/Frontend/CompilerInvocation.cpp

@@ -661,6 +661,8 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts,
   Opts.DOTOutputFile = Args.getLastArgValue(OPT_dependency_dot);
   Opts.DOTOutputFile = Args.getLastArgValue(OPT_dependency_dot);
   Opts.ModuleDependencyOutputDir =
   Opts.ModuleDependencyOutputDir =
       Args.getLastArgValue(OPT_module_dependency_dir);
       Args.getLastArgValue(OPT_module_dependency_dir);
+  if (Args.hasArg(OPT_MV))
+    Opts.OutputFormat = DependencyOutputFormat::NMake;
 }
 }
 
 
 bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
 bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,

+ 23 - 5
lib/Frontend/DependencyFile.cpp

@@ -150,6 +150,8 @@ class DFGImpl : public PPCallbacks {
   bool AddMissingHeaderDeps;
   bool AddMissingHeaderDeps;
   bool SeenMissingHeader;
   bool SeenMissingHeader;
   bool IncludeModuleFiles;
   bool IncludeModuleFiles;
+  DependencyOutputFormat OutputFormat;
+
 private:
 private:
   bool FileMatchesDepCriteria(const char *Filename,
   bool FileMatchesDepCriteria(const char *Filename,
                               SrcMgr::CharacteristicKind FileType);
                               SrcMgr::CharacteristicKind FileType);
@@ -162,7 +164,8 @@ public:
       PhonyTarget(Opts.UsePhonyTargets),
       PhonyTarget(Opts.UsePhonyTargets),
       AddMissingHeaderDeps(Opts.AddMissingHeaderDeps),
       AddMissingHeaderDeps(Opts.AddMissingHeaderDeps),
       SeenMissingHeader(false),
       SeenMissingHeader(false),
-      IncludeModuleFiles(Opts.IncludeModuleFiles) {}
+      IncludeModuleFiles(Opts.IncludeModuleFiles),
+      OutputFormat(Opts.OutputFormat) {}
 
 
   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
                    SrcMgr::CharacteristicKind FileType,
                    SrcMgr::CharacteristicKind FileType,
@@ -290,8 +293,23 @@ void DFGImpl::AddFilename(StringRef Filename) {
 }
 }
 
 
 /// PrintFilename - GCC escapes spaces, # and $, but apparently not ' or " or
 /// PrintFilename - GCC escapes spaces, # and $, but apparently not ' or " or
-/// other scary characters.
-static void PrintFilename(raw_ostream &OS, StringRef Filename) {
+/// other scary characters. NMake/Jom has a different set of scary characters,
+/// but wraps filespecs in double-quotes to avoid misinterpreting them;
+/// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info,
+/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+/// for Windows file-naming info.
+static void PrintFilename(raw_ostream &OS, StringRef Filename,
+                          DependencyOutputFormat OutputFormat) {
+  if (OutputFormat == DependencyOutputFormat::NMake) {
+    // Add quotes if needed. These are the characters listed as "special" to
+    // NMake, that are legal in a Windows filespec, and that could cause
+    // misinterpretation of the dependency string.
+    if (Filename.find_first_of(" #${}^!") != StringRef::npos)
+      OS << '\"' << Filename << '\"';
+    else
+      OS << Filename;
+    return;
+  }
   for (unsigned i = 0, e = Filename.size(); i != e; ++i) {
   for (unsigned i = 0, e = Filename.size(); i != e; ++i) {
     if (Filename[i] == ' ' || Filename[i] == '#')
     if (Filename[i] == ' ' || Filename[i] == '#')
       OS << '\\';
       OS << '\\';
@@ -354,7 +372,7 @@ void DFGImpl::OutputDependencyFile() {
       Columns = 2;
       Columns = 2;
     }
     }
     OS << ' ';
     OS << ' ';
-    PrintFilename(OS, *I);
+    PrintFilename(OS, *I, OutputFormat);
     Columns += N + 1;
     Columns += N + 1;
   }
   }
   OS << '\n';
   OS << '\n';
@@ -365,7 +383,7 @@ void DFGImpl::OutputDependencyFile() {
     for (std::vector<std::string>::iterator I = Files.begin() + 1,
     for (std::vector<std::string>::iterator I = Files.begin() + 1,
            E = Files.end(); I != E; ++I) {
            E = Files.end(); I != E; ++I) {
       OS << '\n';
       OS << '\n';
-      PrintFilename(OS, *I);
+      PrintFilename(OS, *I, OutputFormat);
       OS << ":\n";
       OS << ":\n";
     }
     }
   }
   }

+ 9 - 0
test/Frontend/dependency-gen-escaping.c

@@ -4,13 +4,22 @@
 // RUN: echo > '%t.dir/    .h'
 // RUN: echo > '%t.dir/    .h'
 // RUN: echo > '%t.dir/$$.h'
 // RUN: echo > '%t.dir/$$.h'
 // RUN: echo > '%t.dir/##.h'
 // RUN: echo > '%t.dir/##.h'
+// RUN: echo > '%t.dir/normal.h'
 // RUN: cd %t.dir
 // RUN: cd %t.dir
 // RUN: %clang -MD -MF - %s -fsyntax-only -I. | FileCheck -strict-whitespace %s
 // RUN: %clang -MD -MF - %s -fsyntax-only -I. | FileCheck -strict-whitespace %s
+// RUN: %clang -MD -MF - -MV %s -fsyntax-only -I. | FileCheck -strict-whitespace %s --check-prefix=QUOTE
 
 
 // CHECK: \ \ \ \ .h
 // CHECK: \ \ \ \ .h
 // CHECK: $$$$.h
 // CHECK: $$$$.h
 // CHECK: \#\#.h
 // CHECK: \#\#.h
+// QUOTE: "    .h"
+// QUOTE: "$$.h"
+// QUOTE: "##.h"
+// QUOTE-NOT: "
+// QUOTE: normal.h
+// QUOTE-NOT: "
 
 
 #include "    .h"
 #include "    .h"
 #include "$$.h"
 #include "$$.h"
 #include "##.h"
 #include "##.h"
+#include "normal.h"