yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
46149eeb2
master
1// slang-visual-studio-compiler-util.cpp 2#include "slang-visual-studio-compiler-util.h" 3 4#include "../core/slang-common.h" 5#include "../core/slang-string-slice-pool.h" 6#include "../core/slang-string-util.h" 7#include "slang-com-helper.h" 8 9// if Visual Studio import the visual studio platform specific header 10#if SLANG_VC 11#include "windows/slang-win-visual-studio-util.h" 12#endif 13 14#include "../core/slang-io.h" 15#include "slang-artifact-desc-util.h" 16#include "slang-artifact-diagnostic-util.h" 17#include "slang-artifact-representation-impl.h" 18#include "slang-artifact-util.h" 19 20namespace Slang 21{ 22 23static void _addFile ( 24const String & path , 25const ArtifactDesc & desc , 26IOSFileArtifactRepresentation * lockFile , 27List < ComPtr < IArtifact >>& outArtifacts ) 28{ 29auto fileRep = OSFileArtifactRepresentation ::create ( 30IOSFileArtifactRepresentation ::Kind ::Owned , 31path .getUnownedSlice (), 32lockFile ); 33auto artifact = ArtifactUtil ::createArtifact (desc ); 34artifact -> addRepresentation (fileRep ); 35 36outArtifacts .add (artifact ); 37} 38 39/* static */ SlangResult VisualStudioCompilerUtil ::calcCompileProducts ( 40const CompileOptions & options , 41ProductFlags flags , 42IOSFileArtifactRepresentation * lockFile , 43List < ComPtr < IArtifact >>& outArtifacts ) 44{ 45SLANG_ASSERT (options .modulePath .count ); 46 47const String modulePath = asString (options .modulePath ); 48 49const auto targetDesc = ArtifactDescUtil ::makeDescForCompileTarget (options .targetType ); 50 51outArtifacts .clear (); 52 53if (flags & ProductFlag ::Execution ) 54 { 55StringBuilder builder ; 56const auto desc = ArtifactDescUtil ::makeDescForCompileTarget (options .targetType ); 57SLANG_RETURN_ON_FAIL ( 58ArtifactDescUtil ::calcPathForDesc (desc ,modulePath .getUnownedSlice (),builder )); 59 60_addFile (builder ,desc ,lockFile ,outArtifacts ); 61 } 62if (flags & ProductFlag ::Miscellaneous ) 63 { 64 65_addFile ( 66modulePath + ".ilk" , 67ArtifactDesc ::make ( 68ArtifactKind ::BinaryFormat , 69ArtifactPayload ::Unknown , 70ArtifactStyle ::None ), 71lockFile , 72outArtifacts ); 73 74if (options .targetType == SLANG_SHADER_SHARED_LIBRARY ) 75 { 76_addFile ( 77modulePath + ".exp" , 78ArtifactDesc ::make ( 79ArtifactKind ::BinaryFormat , 80ArtifactPayload ::Unknown , 81ArtifactStyle ::None ), 82lockFile , 83outArtifacts ); 84_addFile ( 85modulePath + ".lib" , 86ArtifactDesc ::make (ArtifactKind ::Library ,ArtifactPayload ::HostCPU ,targetDesc ), 87lockFile , 88outArtifacts ); 89 } 90 } 91if (flags & ProductFlag ::Compile ) 92 { 93_addFile ( 94modulePath + ".obj" , 95ArtifactDesc ::make (ArtifactKind ::ObjectCode ,ArtifactPayload ::HostCPU ,targetDesc ), 96lockFile , 97outArtifacts ); 98 } 99if (flags & ProductFlag ::Debug ) 100 { 101// TODO(JS): Could try and determine based on debug information 102_addFile ( 103modulePath + ".pdb" , 104ArtifactDesc ::make ( 105ArtifactKind ::BinaryFormat , 106ArtifactPayload ::PdbDebugInfo , 107targetDesc ), 108lockFile , 109outArtifacts ); 110 } 111 112return SLANG_OK ; 113} 114 115/* static */ SlangResult VisualStudioCompilerUtil ::calcArgs ( 116const CompileOptions & options , 117CommandLine & cmdLine ) 118{ 119SLANG_ASSERT (options .modulePath .count ); 120 121// https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically?view=vs-2019 122 123cmdLine .addArg ("/nologo" ); 124 125// Display full path of source files in diagnostics 126cmdLine .addArg ("/FC" ); 127 128if (options .sourceLanguage == SLANG_SOURCE_LANGUAGE_CPP ) 129 { 130if (options .flags & CompileOptions ::Flag ::EnableExceptionHandling ) 131 { 132// https://docs.microsoft.com/en-us/cpp/build/reference/eh-exception-handling-model?view=vs-2019 133// Assumes c functions cannot throw 134cmdLine .addArg ("/EHsc" ); 135 } 136 137// To maintain parity with the slang compiler headers which are shared 138cmdLine .addArg ("/std:c++17" ); 139 } 140 141if (options .flags & CompileOptions ::Flag ::Verbose ) 142 { 143// Doesn't appear to be a VS equivalent 144 } 145 146if (options .flags & CompileOptions ::Flag ::EnableSecurityChecks ) 147 { 148cmdLine .addArg ("/GS" ); 149 } 150else 151 { 152cmdLine .addArg ("/GS-" ); 153 } 154 155switch (options .debugInfoType ) 156 { 157default : 158 { 159// Multithreaded statically linked runtime library 160cmdLine .addArg ("/MD" ); 161break ; 162 } 163case DebugInfoType ::None : 164 { 165break ; 166 } 167case DebugInfoType ::Maximal : 168 { 169// Multithreaded statically linked *debug* runtime library 170cmdLine .addArg ("/MDd" ); 171break ; 172 } 173 } 174 175// /Fd - followed by name of the pdb file 176if (options .debugInfoType != DebugInfoType ::None ) 177 { 178// Generate complete debugging information 179cmdLine .addArg ("/Zi" ); 180cmdLine .addPrefixPathArg ("/Fd" ,asString (options .modulePath ),".pdb" ); 181 } 182 183switch (options .optimizationLevel ) 184 { 185case OptimizationLevel ::None : 186 { 187// No optimization 188cmdLine .addArg ("/Od" ); 189break ; 190 } 191case OptimizationLevel ::Default : 192 { 193break ; 194 } 195case OptimizationLevel ::High : 196 { 197cmdLine .addArg ("/O2" ); 198break ; 199 } 200case OptimizationLevel ::Maximal : 201 { 202cmdLine .addArg ("/Ox" ); 203break ; 204 } 205default : 206break ; 207 } 208 209switch (options .floatingPointMode ) 210 { 211case FloatingPointMode ::Default : 212break ; 213case FloatingPointMode ::Precise : 214 { 215// precise is default behavior, VS also has 'strict' 216// 217// ```/fp:strict has behavior similar to /fp:precise, that is, the compiler preserves 218// the source ordering and rounding properties of floating-point code when it generates 219// and optimizes object code for the target machine, and observes the standard when 220// handling special values. In addition, the program may safely access or modify the 221// floating-point environment at runtime.``` 222 223cmdLine .addArg ("/fp:precise" ); 224break ; 225 } 226case FloatingPointMode ::Fast : 227 { 228cmdLine .addArg ("/fp:fast" ); 229break ; 230 } 231 } 232 233const auto modulePath = asString (options .modulePath ); 234 235switch (options .targetType ) 236 { 237case SLANG_SHADER_SHARED_LIBRARY : 238case SLANG_HOST_SHARED_LIBRARY : 239 { 240// Create dynamic link library 241if (options .debugInfoType == DebugInfoType ::None ) 242 { 243cmdLine .addArg ("/LDd" ); 244 } 245else 246 { 247cmdLine .addArg ("/LD" ); 248 } 249 250cmdLine .addPrefixPathArg ("/Fe" ,modulePath ,".dll" ); 251break ; 252 } 253case SLANG_HOST_EXECUTABLE : 254 { 255cmdLine .addPrefixPathArg ("/Fe" ,modulePath ,".exe" ); 256break ; 257 } 258default : 259break ; 260 } 261 262// Object file specify it's location - needed if we are out 263cmdLine .addPrefixPathArg ("/Fo" ,modulePath ,".obj" ); 264 265// Add defines 266for (const auto & define :options .defines ) 267 { 268StringBuilder builder ; 269builder <<"/D" ; 270builder <<asStringSlice (define .nameWithSig ); 271if (define .value .count ) 272 { 273builder <<"=" <<asStringSlice (define .value ); 274 } 275 276cmdLine .addArg (builder ); 277 } 278 279// Add includes 280for (const auto & include :options .includePaths ) 281 { 282cmdLine .addArg ("/I" ); 283cmdLine .addArg (asString (include )); 284 } 285 286// https://docs.microsoft.com/en-us/cpp/build/reference/eh-exception-handling-model?view=vs-2019 287// /Eha - Specifies the model of exception handling. (a, s, c, r are options) 288 289// Files to compile, need to be on the file system. 290for (IArtifact * sourceArtifact :options .sourceArtifacts ) 291 { 292ComPtr < IOSFileArtifactRepresentation > fileRep ; 293 294// TODO(JS): 295// Do we want to keep the file on the file system? It's probably reasonable to do so. 296SLANG_RETURN_ON_FAIL (sourceArtifact -> requireFile (ArtifactKeep ::Yes ,fileRep .writeRef ())); 297cmdLine .addArg (fileRep -> getPath ()); 298 } 299 300// Link options (parameters past /link go to linker) 301cmdLine .addArg ("/link" ); 302 303StringSlicePool libPathPool (StringSlicePool ::Style ::Default ); 304 305for (const auto & libPath :options .libraryPaths ) 306 { 307libPathPool .add (libPath ); 308 } 309 310// Link libraries. 311for (IArtifact * artifact :options .libraries ) 312 { 313auto desc = artifact -> getDesc (); 314 315if (ArtifactDescUtil ::isCpuBinary (desc )&& desc .kind == ArtifactKind ::Library ) 316 { 317// Get the libray name and path 318ComPtr < IOSFileArtifactRepresentation > fileRep ; 319SLANG_RETURN_ON_FAIL (artifact -> requireFile (ArtifactKeep ::Yes ,fileRep .writeRef ())); 320 321const UnownedStringSlice path (fileRep -> getPath ()); 322libPathPool .add (Path ::getParentDirectory (path )); 323// We need the extension for windows 324cmdLine .addArg (ArtifactDescUtil ::getBaseNameFromPath (desc ,path )+ ".lib" ); 325 } 326 } 327 328// Add all the library paths 329for (const auto & libPath :libPathPool .getAdded ()) 330 { 331// Note that any escaping of the path is handled in the ProcessUtil:: 332cmdLine .addPrefixPathArg ("/LIBPATH:" ,libPath ); 333 } 334 335// Add compiler specific options from user. 336for (auto compilerSpecificArg :options .compilerSpecificArguments ) 337 { 338const char * const arg = compilerSpecificArg ; 339cmdLine .addArg (arg ); 340 } 341 342return SLANG_OK ; 343} 344 345static SlangResult _parseSeverity ( 346const UnownedStringSlice & in , 347ArtifactDiagnostic ::Severity & outSeverity ) 348{ 349typedef ArtifactDiagnostic ::Severity Severity ; 350 351if (in == "error" || in == "fatal error" ) 352 { 353outSeverity = Severity ::Error ; 354 } 355else if (in == "warning" ) 356 { 357outSeverity = Severity ::Warning ; 358 } 359else if (in == "info" ) 360 { 361outSeverity = Severity ::Info ; 362 } 363else 364 { 365return SLANG_FAIL ; 366 } 367return SLANG_OK ; 368} 369 370static SlangResult _parseVisualStudioLine ( 371SliceAllocator & allocator , 372const UnownedStringSlice & line , 373ArtifactDiagnostic & outDiagnostic ) 374{ 375typedef IArtifactDiagnostics ::Diagnostic Diagnostic ; 376 377UnownedStringSlice linkPrefix = UnownedStringSlice ::fromLiteral ("LINK :" ); 378if (line .startsWith (linkPrefix )) 379 { 380outDiagnostic .stage = ArtifactDiagnostic ::Stage ::Link ; 381outDiagnostic .severity = ArtifactDiagnostic ::Severity ::Info ; 382 383outDiagnostic .text = allocator .allocate (line .begin ()+ linkPrefix .getLength (),line .end ()); 384 385return SLANG_OK ; 386 } 387 388outDiagnostic .stage = ArtifactDiagnostic ::Stage ::Compile ; 389 390const char * const start = line .begin (); 391const char * const end = line .end (); 392 393UnownedStringSlice postPath ; 394// Handle the path and line no 395 { 396const char * cur = start ; 397 398// We have to assume it is a path up to the first : that isn't part of a drive specification 399 400if ((end - cur > 2 )&& Path ::isDriveSpecification (UnownedStringSlice (start ,start + 2 ))) 401 { 402// Skip drive spec 403cur += 2 ; 404 } 405 406// Find the first colon after this 407Index colonIndex = UnownedStringSlice (cur ,end ).indexOf (':' ); 408if (colonIndex < 0 ) 409 { 410return SLANG_FAIL ; 411 } 412 413// Looks like we have a line number 414if (cur [colonIndex - 1 ]== ')' ) 415 { 416const char * lineNoEnd = cur + colonIndex - 1 ; 417const char * lineNoStart = lineNoEnd ; 418while (lineNoStart > start && * lineNoStart != '(' ) 419 { 420lineNoStart -- ; 421 } 422// Check this appears plausible 423if (* lineNoStart != '(' || * lineNoEnd != ')' ) 424 { 425return SLANG_FAIL ; 426 } 427Int numDigits = 0 ; 428Int lineNo = 0 ; 429for (const char * digitCur = lineNoStart + 1 ;digitCur < lineNoEnd ;++ digitCur ) 430 { 431char c = * digitCur ; 432if (c >='0' && c <='9' ) 433 { 434lineNo = lineNo * 10 + (c - '0' ); 435numDigits ++ ; 436 } 437else 438 { 439return SLANG_FAIL ; 440 } 441 } 442if (numDigits == 0 ) 443 { 444return SLANG_FAIL ; 445 } 446 447outDiagnostic .filePath = allocator .allocate (start ,lineNoStart ); 448outDiagnostic .location .line = lineNo ; 449 } 450else 451 { 452outDiagnostic .filePath = allocator .allocate (start ,cur + colonIndex ); 453outDiagnostic .location .line = 0 ; 454 } 455 456// Save the remaining text in 'postPath' 457postPath = UnownedStringSlice (cur + colonIndex + 1 ,end ); 458 } 459 460// Split up the error section 461UnownedStringSlice postError ; 462 { 463// tests/cpp-compiler/c-compile-link-error.exe : fatal error LNK1120: 1 unresolved externals 464 465const Index errorColonIndex = postPath .indexOf (':' ); 466if (errorColonIndex < 0 ) 467 { 468return SLANG_FAIL ; 469 } 470 471const UnownedStringSlice errorSection = 472UnownedStringSlice (postPath .begin (),postPath .begin ()+ errorColonIndex ); 473Index errorCodeIndex = errorSection .lastIndexOf (' ' ); 474if (errorCodeIndex < 0 ) 475 { 476return SLANG_FAIL ; 477 } 478 479// Extract the code 480outDiagnostic .code = 481allocator .allocate (errorSection .begin ()+ errorCodeIndex + 1 ,errorSection .end ()); 482if (asStringSlice (outDiagnostic .code ).startsWith (UnownedStringSlice ::fromLiteral ("LNK" ))) 483 { 484outDiagnostic .stage = Diagnostic ::Stage ::Link ; 485 } 486 487// Extract the bit before the code 488SLANG_RETURN_ON_FAIL (_parseSeverity ( 489UnownedStringSlice (errorSection .begin (),errorSection .begin ()+ errorCodeIndex ).trim (), 490outDiagnostic .severity )); 491 492// Link codes start with LNK prefix 493postError = UnownedStringSlice (postPath .begin ()+ errorColonIndex + 1 ,end ); 494 } 495 496outDiagnostic .text = allocator .allocate (postError ); 497 498return SLANG_OK ; 499} 500 501/* static */ SlangResult VisualStudioCompilerUtil ::parseOutput ( 502const ExecuteResult & exeRes , 503IArtifactDiagnostics * diagnostics ) 504{ 505diagnostics -> reset (); 506 507diagnostics -> setRaw (SliceUtil ::asTerminatedCharSlice (exeRes .standardOutput )); 508 509SliceAllocator allocator ; 510 511for (auto line :LineParser (exeRes .standardOutput .getUnownedSlice ())) 512 { 513#if 0 514fwrite (line .begin (),1 ,line .size (),stdout ); 515fprintf (stdout ,"\n" ); 516#endif 517 518ArtifactDiagnostic diagnostic ; 519if (SLANG_SUCCEEDED (_parseVisualStudioLine (allocator ,line ,diagnostic ))) 520 { 521diagnostics -> add (diagnostic ); 522 } 523 } 524 525// if it has a compilation error.. set on output 526if (diagnostics -> hasOfAtLeastSeverity (ArtifactDiagnostic ::Severity ::Error )) 527 { 528diagnostics -> setResult (SLANG_FAIL ); 529 } 530 531return SLANG_OK ; 532} 533 534/* static */ SlangResult VisualStudioCompilerUtil ::locateCompilers ( 535const String & path , 536ISlangSharedLibraryLoader * loader , 537 [[maybe_unused ]]DownstreamCompilerSet * set ) 538{ 539SLANG_UNUSED (loader ); 540 541// TODO(JS): We don't support fixed path for visual studio just yet 542if (path .getLength ()== 0 ) 543 { 544#if SLANG_VC 545return WinVisualStudioUtil ::find (set ); 546#endif 547 } 548 549return SLANG_OK ; 550} 551 552}// namespace Slang