Add link validation infrastructure for multiple compilation units per stage.  Includes a new, straightforward, C++ interface to the front end.


git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@22927 e7fa87d3-cd2b-0410-9028-fcbf551c1848
diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp
index 07e5257..3b26488 100644
--- a/StandAlone/StandAlone.cpp
+++ b/StandAlone/StandAlone.cpp
@@ -161,18 +161,16 @@
     return true;
 }
 
-// Thread entry point
+// Thread entry point, for non-linking asynchronous mode.
 unsigned int
 #ifdef _WIN32
     __stdcall
 #endif
 CompileShaders(void*)
 {
-    ShHandle compiler;
-
     std::string shaderName;
     while (Worklist.remove(shaderName)) {
-        compiler = ShConstructCompiler(FindLanguage(shaderName), Options);
+        ShHandle compiler = ShConstructCompiler(FindLanguage(shaderName), Options);
         if (compiler == 0)
             return false;
 
@@ -189,6 +187,77 @@
     return 0;
 }
 
+//
+// For linking mode: Will independently parse each item in the worklist, but then put them
+// in the same program and link them together.
+//
+// Uses the new C++ interface instead of the old handle-based interface.
+//
+void CompileAndLinkShaders()
+{
+    // keep track of what to free
+    std::list<glslang::TShader*> shaders;
+
+    EShMessages messages = EShMsgDefault;
+    if (Options & EOptionRelaxedErrors)
+        messages = (EShMessages)(messages | EShMsgRelaxedErrors);
+    if (Options & EOptionIntermediate)
+        messages = (EShMessages)(messages | EShMsgAST);
+
+    TBuiltInResource resources;
+    GenerateResources(resources);
+
+    //
+    // Per-shader processing...
+    //
+
+    glslang::TProgram program;
+    std::string shaderName;
+    while (Worklist.remove(shaderName)) {
+        EShLanguage stage = FindLanguage(shaderName);
+        glslang::TShader* shader = new glslang::TShader(stage);
+        shaders.push_back(shader);
+    
+        char** shaderStrings = ReadFileData(shaderName.c_str());
+        if (! shaderStrings) {
+            usage();
+            return;
+        }
+
+        shader->setStrings(shaderStrings, 1);
+
+        shader->parse(&resources, 100, false, messages);
+        
+        program.addShader(shader);
+
+        if (! (Options & EOptionSuppressInfolog)) {
+            puts(shaderName.c_str());
+            puts(shader->getInfoLog());
+            puts(shader->getInfoDebugLog());
+        }
+
+        FreeFileData(shaderStrings);
+    }
+
+    //
+    // Program-level processing...
+    //
+
+    program.link(messages);
+    if (! (Options & EOptionSuppressInfolog)) {
+        puts(program.getInfoLog());
+        puts(program.getInfoDebugLog());
+    }
+
+    // free everything up
+    while (shaders.size() > 0) {
+        delete shaders.back();
+        shaders.pop_back();
+    }
+
+    // TODO: memory: for each compile, need a GetThreadPoolAllocator().pop();
+}
+
 int C_DECL main(int argc, char* argv[])
 {
     bool compileFailed = false;
@@ -205,6 +274,12 @@
         return EFailUsage;
     }
 
+    //
+    // Two modes:
+    // 1) linking all arguments together, single-threaded
+    // 2) independent arguments, can be tackled by multiple asynchronous threads, for testing thread safety
+    //
+
     // TODO: finish threading, allow external control over number of threads
     const int NumThreads = 1;
     if (NumThreads > 1) {
@@ -218,8 +293,12 @@
         }
         glslang::OS_WaitForAllThreads(threads, NumThreads);
     } else {
-        if (! CompileShaders(0))
-            compileFailed = true;
+        if (Options & EOptionsLinkProgram) {
+            CompileAndLinkShaders();
+        } else {
+            if (! CompileShaders(0))
+                compileFailed = true;
+        }
     }
 
     if (Delay)
@@ -271,9 +350,10 @@
 }
 
 //
-//   Read a file's data into a string, and compile it using ShCompile
+// Read a file's data into a string, and compile it using the old interface ShCompile, 
+// for non-linkable results.
 //
-bool CompileFile(const char *fileName, ShHandle compiler, int Options, const TBuiltInResource *resources)
+bool CompileFile(const char *fileName, ShHandle compiler, int Options, const TBuiltInResource* resources)
 {
     int ret;
     char** shaderStrings = ReadFileData(fileName);
@@ -296,6 +376,7 @@
         messages = (EShMessages)(messages | EShMsgRelaxedErrors);
     if (Options & EOptionIntermediate)
         messages = (EShMessages)(messages | EShMsgAST);
+    
     for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) {
         for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) {
             //ret = ShCompile(compiler, shaderStrings, NumShaderStrings, lengths, EShOptNone, resources, Options, 100, false, messages);
@@ -315,7 +396,6 @@
     return ret ? true : false;
 }
 
-
 //
 //   print usage to stdout
 //
@@ -429,16 +509,12 @@
     return return_data;
 }
 
-
-
 void FreeFileData(char **data)
 {
     for(int i=0;i<NumShaderStrings;i++)
         free(data[i]);
 }
 
-
-
 void InfoLogMsg(const char* msg, const char* name, const int num)
 {
     printf(num >= 0 ? "#### %s %s %d INFO LOG ####\n" :