Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

342 lines
15 KiB

  1. //========= Copyright 1996-2016, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Clang utility code
  4. //
  5. //=====================================================================================//
  6. #include "clang/clang_utils.h"
  7. #include "tier1/strtools.h"
  8. #include "tier1/utlstring.h"
  9. #include "tier1/utlvector.h"
  10. #include "environment_utils.h"
  11. // Include the main Clang interface header:
  12. #include "clang-c/Index.h"
  13. CUtlString & TransformArgumentForCommandLineUsage( CUtlString &argument, bool bEscapeContainedQuotes )
  14. {
  15. bool bHasSpace = V_containsWhitespace( argument.Get() );
  16. if ( V_strstr( argument.Get(), "\"" ) )
  17. {
  18. if ( bEscapeContainedQuotes )
  19. {
  20. // Transform " to \"
  21. char temp[ MAX_PATH ];
  22. bool bResult = V_StrSubst( argument.Get(), "\"", "\\\"", temp, ARRAYSIZE( temp ), true );
  23. NOTE_UNUSED( bResult );
  24. Assert( bResult );
  25. argument = temp;
  26. }
  27. // TODO: support arguments containing both quotes and spaces, e.g: -Dmystring="hi there"
  28. Assert( !bHasSpace );
  29. }
  30. if ( bHasSpace )
  31. {
  32. // Wrap the argument with quotes
  33. argument = CUtlString( "\"" ) + argument + "\"";
  34. }
  35. // Append a space to separate arguments
  36. argument += " ";
  37. return argument;
  38. }
  39. //--------------------------------------------------------------------------------------------------
  40. //--------------------------------------------------------------------------------------------------
  41. bool Clang_IsHeaderFile( const char *pFilename )
  42. {
  43. // These extensions are used for header files in VPCs in the Source2 tree:
  44. // TODO: move this util into VPC common code... optimize it
  45. if ( V_striEndsWith( pFilename, ".h" ) ) return true;
  46. if ( V_striEndsWith( pFilename, ".inl" ) ) return true;
  47. return false;
  48. }
  49. //--------------------------------------------------------------------------------------------------
  50. //--------------------------------------------------------------------------------------------------
  51. bool Clang_IsSourceFile( const char *pFilename )
  52. {
  53. // These extensions are used for source files in VPCs in the Source2 tree:
  54. // TODO: move this util into VPC common code... optimize it
  55. if ( V_striEndsWith( pFilename, ".cpp" ) ) return true;
  56. if ( V_striEndsWith( pFilename, ".cc" ) ) return true;
  57. if ( V_striEndsWith( pFilename, ".c" ) ) return true;
  58. return false;
  59. }
  60. //--------------------------------------------------------------------------------------------------
  61. //--------------------------------------------------------------------------------------------------
  62. bool Clang_AddCompilerOptions( CUtlVector< CUtlString > &arguments, const char *pCompiler )
  63. {
  64. if ( !V_stricmp_fast( pCompiler, "VS2005" ) || !V_stricmp_fast( pCompiler, "VS2010" ) )
  65. {
  66. // Add Microsoft extensions/compatibility
  67. arguments.AddToTail( "-fms-extensions" );
  68. arguments.AddToTail( "-fms-compatibility" );
  69. arguments.AddToTail( "-mms-bitfields" );
  70. // Omit '_MSC_VER' (we'll get this in VPC's list of defines)
  71. arguments.AddToTail( "-fmsc-version=0" );
  72. // Define the appropriate C/C++ standard
  73. if ( !V_stricmp_fast( pCompiler, "VS2005" ) )
  74. {
  75. arguments.AddToTail( "-std=c++98" );
  76. return true;
  77. }
  78. if ( !V_stricmp_fast( pCompiler, "VS2010" ) )
  79. {
  80. // [sergiy 2012/10/09] this used to crash for src/particles/builtin_constraints.cpp (get the latest Clang drop and/or submit a minimal repro)
  81. // I recompiled clang and it doesn't seem to crash, so I'm swithing it to C++11 to get Rubikon ( which uses c++11 features) to compile. But we can switch back any time
  82. arguments.AddToTail( "-std=c++11" );
  83. //arguments.AddToTail( "-std=c++98" );
  84. arguments.AddToTail( "-fmsc-version=1600" );
  85. return true;
  86. }
  87. }
  88. // NOTE: clang defaults to the 'gnu99' C/C++ standard
  89. // we may want to use '-std=gnu++11' when we add mac+linux support
  90. arguments.AddToTail( "-std=gnu99" );
  91. AssertMsg1( false, "ERROR: Clang_GenerateOptions not implemented for this compiler yet! (%s)\n", pCompiler );
  92. return false;
  93. }
  94. //--------------------------------------------------------------------------------------------------
  95. //--------------------------------------------------------------------------------------------------
  96. bool Clang_AddPlatformOptions( CUtlVector< CUtlString > &arguments, const char *pPlatform )
  97. {
  98. // See LLVM\include\llvm\ADT\Triple.h and LLVM\tools\clang\lib\Basic\Targets.cpp
  99. // for information about supported targets... the target 'triple' is of the form:
  100. //
  101. // architecture-vendor-os[-environment]
  102. //
  103. // The fourth part is optional. Some of the supported values:
  104. //
  105. // [x86|x86_64|ppc|ppc64] - [pc|apple|scei|unknown] - [win32|mingw32|cygwin|macosx|lv2|linux] - [gnu|ANDROIDEABI]
  106. if ( !V_stricmp_fast( pPlatform, "WIN32" ) )
  107. {
  108. // TODO: might get better support with 'mingw32' or 'cygwin' than 'win32' (wtf are these things??)
  109. arguments.AddToTail( "-target" );
  110. arguments.AddToTail( "i686-pc-win32" );
  111. return true;
  112. }
  113. if ( !V_stricmp_fast( pPlatform, "WIN64" ) )
  114. {
  115. // Need to tell Clang if this is a 64-bit CPU (or it will complain about usage of size_t in operator new)
  116. // [ NOTE: the option '-m64' will internally transform i686-pc-win32 into x86_64-pc-win32 ]
  117. arguments.AddToTail( "-target" );
  118. arguments.AddToTail( "x86_64-pc-win32" );
  119. // Define away unsupported intrinsics (breaks Rubikon due to __debugbreak being declared in system header intrin.h). We can put it back any time
  120. //arguments.AddToTail( "-D__debugbreak(...)=__asm { int 3 }" );
  121. return true;
  122. }
  123. if ( !V_stricmp_fast( pPlatform, "OSX" ) )
  124. {
  125. // TODO: for OSX/iOS, the os string should include a version number, e.g 'macosx10.8.0'
  126. AssertMsg( 0, "Untested!\n" );
  127. arguments.AddToTail( "-target" );
  128. arguments.AddToTail( "ppc-apple-macosx" );
  129. return true;
  130. }
  131. if ( !V_stricmp_fast( pPlatform, "X360" ) )
  132. {
  133. // TODO: figure out which target triple to use... we want to pretend we're using PPC for Win32 (Clang doesn't know about XBox)
  134. AssertMsg( 0, "Untested!\n" );
  135. arguments.AddToTail( "-target" );
  136. arguments.AddToTail( "ppc-pc-win32" );
  137. return true;
  138. }
  139. if ( !V_stricmp_fast( pPlatform, "PS3" ) )
  140. {
  141. // TODO: PS3 uses 'lv2' for OS and 'scei' for the vendor, but for some reason 'ppc64-scei-lv2' doesn't work.
  142. // NOTE: SPU compilation is also supported, via 'cellspu-scei-lv2'
  143. AssertMsg( 0, "Untested!\n" );
  144. arguments.AddToTail( "-target" );
  145. arguments.AddToTail( "ppc64-scei-lv2" );
  146. return true;
  147. }
  148. AssertMsg1( false, "ERROR: Clang_GenerateOptions not implemented for this platform yet! (%s)\n", pPlatform );
  149. return false;
  150. }
  151. //--------------------------------------------------------------------------------------------------
  152. //--------------------------------------------------------------------------------------------------
  153. bool Clang_AddSystemIncludePaths( CUtlVector< CUtlString > &arguments, const char *pPlatform, const char *pCompiler )
  154. {
  155. CUtlVector< CUtlString > systemPaths;
  156. if ( !GetSystemIncludePaths( systemPaths, pPlatform, pCompiler ) )
  157. {
  158. AssertMsg2( false, "ERROR: Clang_GenerateOptions not implemented for this platform/compiler yet! (%s/%s)\n", pPlatform, pCompiler );
  159. return false;
  160. }
  161. // System include paths (added with '-isystem') will be searched *after* all the regular '-I' includes
  162. // [http://gcc.gnu.org/onlinedocs/cpp/System-Headers.html]
  163. CUtlString includePrefix( "-isystem" );
  164. for ( int i=0; i < systemPaths.Count(); i++ )
  165. {
  166. arguments.AddToTail( includePrefix + systemPaths[i] );
  167. }
  168. return true;
  169. }
  170. //--------------------------------------------------------------------------------------------------
  171. //--------------------------------------------------------------------------------------------------
  172. bool Clang_GenerateCommandString( CUtlString &command, bool bForLibClangDLL, const char *pFilename, const char *pPlatform, const char *pCompiler,
  173. const CUtlVector< CUtlString > &defines, const CUtlVector< CUtlString > &includes,
  174. const CUtlString &pchName, bool bBuildPCH, int parseFlags )
  175. {
  176. CUtlVector< CUtlString > arguments;
  177. arguments.EnsureCapacity( 512 ); // Avoid needless string copying
  178. command.Clear();
  179. // Detect file type (source or header)
  180. bool bIsHeader = Clang_IsHeaderFile( pFilename );
  181. if ( !bIsHeader && !Clang_IsSourceFile( pFilename ) )
  182. {
  183. AssertMsg2( Clang_IsHeaderFile( pFilename ) || Clang_IsSourceFile( pFilename ), "ERROR (%s): unrecognised file type '%s'!\n", __FUNCTION__, pFilename );
  184. return false;
  185. }
  186. if ( bIsHeader )
  187. {
  188. // By default, Clang interprets '.h' files as C, not C++:
  189. // TODO: should we just always specify "c++"? (i.e should we parse ".c" files as C++? and is "c++-header" actually better for headers?)
  190. arguments.AddToTail( "-x" ); // "Treat subsequent input files as having type <language>"
  191. arguments.AddToTail( "c++-header" );
  192. }
  193. if ( !bBuildPCH )
  194. {
  195. // TODO: -fsyntax-only yields invalid PCHs, but building without it causes clang to crash on other files (e.g src/client/c_baseanimating.cpp)
  196. // need to drill into this (build clang in debug, make a minimal repro), so we can submit bug reports
  197. arguments.AddToTail( "-fsyntax-only" ); // "Check the code for syntax errors, but don't do anything beyond that"
  198. }
  199. // Add standard options
  200. arguments.AddToTail( "-c" ); //[ignored] "Compile or assemble the source files, but do not link."
  201. arguments.AddToTail( "-nostdinc" ); // "Disable standard system #include directories"
  202. // NOTE: -nostdinc works and overrides -nobuiltininc, while
  203. // -nostdsysteminc and nostdinc++ do not work (had to GREP the LLVM source code to determine this !)
  204. // These help to diagnose parse errors:
  205. arguments.AddToTail( "-ferror-limit=100" ); // "Max errors to allow before parsing terminates"
  206. arguments.AddToTail( "-fmacro-backtrace-limit=0" ); // "Max macro expansion depth in diagnostic spew"
  207. //arguments.AddToTail( "-v" ); // "Show commands to run and use verbose output"
  208. // Add options corresponding to the passed parse flags:
  209. if ( parseFlags & CXTranslationUnit_DetailedPreprocessingRecord )
  210. {
  211. arguments.AddToTail( "-Xclang" ); // "Pass <arg> to the clang compiler"
  212. arguments.AddToTail( "-detailed-preprocessing-record" );// "Include pre-processor data in the AST"
  213. }
  214. // Suppress warnings we don't want to see:
  215. // TODO: can these be removed now I've added the VS2010 options?
  216. arguments.AddToTail( "-Wno-microsoft" ); // "XXX is a microsoft extension"
  217. arguments.AddToTail( "-Wno-c++11-extensions" ); // "warnings about using C++11 features as extensions in earlier C++ versions"
  218. // TODO: these suppress warnings we should actually fix later:
  219. arguments.AddToTail( "-Wno-return-type-c-linkage" ); // "XXX has C-linkage specified, but returns user-defined type YYY which is incompatible with C"
  220. arguments.AddToTail( "-Wno-invalid-token-paste" ); // "pasting formed 'XXX', an invalid preprocessing token"
  221. // __FUNCTION__ is implemented by Clang in the GCC style; it's a compiler-generated variable, not
  222. // a preprocessor-generated string literal, so you can't do things like: "Error in " __FUNCTION__ "!\n"
  223. // [ http://stackoverflow.com/questions/11142779/function-and-friends-act-weird-in-xcode ]
  224. arguments.AddToTail( "-D__FUNCTION__=__FILE__" );
  225. if ( bIsHeader && pchName.IsEmpty() )
  226. {
  227. // Clang cannot parse many of our standalone .h files, since they don't include everything they
  228. // need (e.g baseschema.h), so if we're not using a PCH then define away schema stuff:
  229. // TODO: remove this once we port Schemacompiler to use Clang!
  230. arguments.AddToTail( "-Dschema=" );
  231. arguments.AddToTail( "-Dnoschema=" );
  232. arguments.AddToTail( "-Dschema_pragma(...)=" );
  233. arguments.AddToTail( "-DMETA(...)=" );
  234. arguments.AddToTail( "-DTYPEMETA(...)=" );
  235. arguments.AddToTail( "-DSCHEMA_IGNORE_NEXT_PARENS(...)=" );
  236. arguments.AddToTail( "-DMETA_ADD(...)=SCHEMA_IGNORE_NEXT_PARENS" );
  237. arguments.AddToTail( "-DMETA_REMOVE(...)=SCHEMA_IGNORE_NEXT_PARENS" );
  238. arguments.AddToTail( "-DMETA_OVERRIDE(...)=SCHEMA_IGNORE_NEXT_PARENS" );
  239. arguments.AddToTail( "-DMETA_REMOVE_ALL(...)=SCHEMA_IGNORE_NEXT_PARENS" );
  240. }
  241. // __FUNCTION__ is implemented by Clang in the GCC style; it's a compiler-generated variable, not
  242. // a preprocessor-generated string literal, so you can't do things like: "Error in " __FUNCTION__ "!\n"
  243. // [ http://stackoverflow.com/questions/11142779/function-and-friends-act-weird-in-xcode ]
  244. arguments.AddToTail( "-D__FUNCTION__=__FILE__" );
  245. // Add platform-specific options:
  246. if ( !Clang_AddPlatformOptions( arguments, pPlatform ) )
  247. return false;
  248. // Add compiler-specific options:
  249. if ( !Clang_AddCompilerOptions( arguments, pCompiler ) )
  250. return false;
  251. // Add system include dirs explicitly (options above disable Clang's built-in include paths)
  252. if ( !Clang_AddSystemIncludePaths( arguments, pPlatform, pCompiler ) )
  253. return false;
  254. // Add the caller's preprocessor defines and include directories:
  255. CUtlString definePrefix( "-D" ), includePrefix( "-I" );
  256. for ( int i=0; i < defines.Count(); i++ ) arguments.AddToTail( definePrefix + defines[i] );
  257. for ( int i=0; i < includes.Count(); i++ ) arguments.AddToTail( includePrefix + includes[i] );
  258. if ( bBuildPCH )
  259. {
  260. // This file is used as a PCH, so output the file <pchName>.pch:
  261. arguments.AddToTail( "-emit-ast" );
  262. arguments.AddToTail( "-o" );
  263. arguments.AddToTail( pchName + ".pch" );
  264. }
  265. else if ( !pchName.IsEmpty() )
  266. {
  267. // If the file uses a PCH, add "-include <pchName>"
  268. // NOTE: this is safely ignored for files which do not actually include the PCH file (e.g headers)
  269. arguments.AddToTail( "-include" );
  270. arguments.AddToTail( pchName );
  271. }
  272. // Put the filename last, after all the options:
  273. arguments.AddToTail( pFilename );
  274. // Transform each argument as appropriate for its intended usage
  275. // (either passing via the libclang.dll API, or passing via the command-line):
  276. for ( int i = 0; i < arguments.Count(); i++ )
  277. {
  278. command += TransformArgumentForCommandLineUsage( arguments[i], !bForLibClangDLL );
  279. }
  280. return true;
  281. }
  282. //--------------------------------------------------------------------------------------------------
  283. //--------------------------------------------------------------------------------------------------
  284. bool Clang_GenerateDLLArguments( ICommandLine *pArguments, const char *pFilename, const char *pPlatform, const char *pCompiler,
  285. const CUtlVector< CUtlString > &defines, const CUtlVector< CUtlString > &includes,
  286. const CUtlString &pchName, bool bBuildPCH, int parseFlags )
  287. {
  288. CUtlString command;
  289. bool bResult = Clang_GenerateCommandString( command, true, pFilename, pPlatform, pCompiler, defines, includes, pchName, bBuildPCH, parseFlags );
  290. pArguments->CreateCmdLine( command.Get() );
  291. return bResult;
  292. }
  293. //--------------------------------------------------------------------------------------------------
  294. //--------------------------------------------------------------------------------------------------
  295. bool Clang_GenerateCommandLine( CUtlString &command, const char *pFilename, const char *pPlatform, const char *pCompiler,
  296. const CUtlVector< CUtlString > &defines, const CUtlVector< CUtlString > &includes,
  297. const CUtlString &pchName, bool bBuildPCH, int parseFlags )
  298. {
  299. return Clang_GenerateCommandString( command, false, pFilename, pPlatform, pCompiler, defines, includes, pchName, bBuildPCH, parseFlags );
  300. }