Merge remote-tracking branch 'aosp/upstream-master' into update-shaderc

Includes:
be8e087 Travis: download and install pip
4f36a64 Force binary mode when writing a binary to stdout
87a8420 Adjust configuration to link SPIRV-Tools into Glslang

Change-Id: I3913a24661c82c3c601474af4b3e4d9dccafd1ec
Testing: checkbuild.py on Linux; unit tests on Windows
diff --git a/.travis.yml b/.travis.yml
index f0b2513..c1aaee6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -56,11 +56,12 @@
       git clone --depth=1 https://github.com/taka-no-me/android-cmake.git $HOME/android-cmake;
       export TOOLCHAIN_PATH=$HOME/android-cmake/android.toolchain.cmake;
     fi
+  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
+      curl https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py;
+      sudo python /tmp/get-pip.py;
+    fi
 
 install:
-  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
-      sudo easy_install pip;
-    fi
   - pip install --user nose
   - pip install --user cpp-coveralls
 
diff --git a/glslc/src/file_compiler.cc b/glslc/src/file_compiler.cc
index b115d07..839cc6e 100644
--- a/glslc/src/file_compiler.cc
+++ b/glslc/src/file_compiler.cc
@@ -214,7 +214,13 @@
       case SpirvBinaryEmissionFormat::Unspecified:
       case SpirvBinaryEmissionFormat::Binary:
         // The output format is unspecified or specified as binary output.
+        // On Windows, the output stream must be set to binary mode.  By
+        // default the standard output stream is set to text mode, which
+        // translates newlines (\n) to carriage-return newline pairs
+        // (\r\n).
+        if (out == &std::cout) shaderc_util::FlushAndSetBinaryModeOnStdout();
         out->write(compilation_output.data(), compilation_output.size());
+        if (out == &std::cout) shaderc_util::FlushAndSetTextModeOnStdout();
         break;
       case SpirvBinaryEmissionFormat::Numbers:
         // The output format is specified to be a list of hex numbers, the
diff --git a/glslc/test/expect.py b/glslc/test/expect.py
index 366fdec..0226119 100644
--- a/glslc/test/expect.py
+++ b/glslc/test/expect.py
@@ -113,11 +113,20 @@
             return False, 'Extra files generated: {}'.format(generated_files)
 
 
-class CorrectObjectFilePreamble(GlslCTest):
-    """Provides methods for verifying preamble for a SPV object file."""
+class CorrectBinaryLengthAndPreamble(GlslCTest):
+    """Provides methods for verifying preamble for a SPIR-V binary."""
 
-    def verify_object_file_preamble(self, filename, spv_version = 0x10000):
-        """Checks that the given SPIR-V binary file has correct preamble."""
+    def verify_binary_length_and_header(self, binary, spv_version = 0x10000):
+        """Checks that the given SPIR-V binary has valid length and header.
+
+        Returns:
+            False, error string if anything is invalid
+            True, '' otherwise
+        Args:
+            binary: a bytes object containing the SPIR-V binary
+            spv_version: target SPIR-V version number, with same encoding
+                 as the version word in a SPIR-V header.
+        """
 
         def read_word(binary, index, little_endian):
             """Reads the index-th word from the given binary file."""
@@ -127,7 +136,7 @@
             return reduce(lambda w, b: (w << 8) | ord(b), word, 0)
 
         def check_endianness(binary):
-            """Checks the endianness of the given SPIR-V binary file.
+            """Checks the endianness of the given SPIR-V binary.
 
             Returns:
               True if it's little endian, False if it's big endian.
@@ -141,6 +150,45 @@
                 return False
             return None
 
+        num_bytes = len(binary)
+        if num_bytes % 4 != 0:
+            return False, ('Incorrect SPV binary: size should be a multiple'
+                           ' of words')
+        if num_bytes < 20:
+            return False, 'Incorrect SPV binary: size less than 5 words'
+
+        preamble = binary[0:19]
+        little_endian = check_endianness(preamble)
+        # SPIR-V module magic number
+        if little_endian is None:
+            return False, 'Incorrect SPV binary: wrong magic number'
+
+        # SPIR-V version number
+        version = read_word(preamble, 1, little_endian)
+        # TODO(dneto): Recent Glslang uses version word 0 for opengl_compat
+        # profile
+
+        if version != spv_version and version != 0:
+            return False, 'Incorrect SPV binary: wrong version number'
+        # Shaderc-over-Glslang (0x000d....) or
+        # SPIRV-Tools (0x0007....) generator number
+        if read_word(preamble, 2, little_endian) != 0x000d0006 and \
+                read_word(preamble, 2, little_endian) != 0x00070000:
+            return False, ('Incorrect SPV binary: wrong generator magic '
+                           'number')
+        # reserved for instruction schema
+        if read_word(preamble, 4, little_endian) != 0:
+            return False, 'Incorrect SPV binary: the 5th byte should be 0'
+
+        return True, ''
+
+
+class CorrectObjectFilePreamble(CorrectBinaryLengthAndPreamble):
+    """Provides methods for verifying preamble for a SPV object file."""
+
+    def verify_object_file_preamble(self, filename, spv_version = 0x10000):
+        """Checks that the given SPIR-V binary file has correct preamble."""
+
         success, message = verify_file_non_empty(filename)
         if not success:
             return False, message
@@ -148,36 +196,11 @@
         with open(filename, 'rb') as object_file:
             object_file.seek(0, os.SEEK_END)
             num_bytes = object_file.tell()
-            if num_bytes % 4 != 0:
-                return False, ('Incorrect SPV binary: size should be a multiple'
-                               ' of words')
-            if num_bytes < 20:
-                return False, 'Incorrect SPV binary: size less than 5 words'
 
             object_file.seek(0)
-            preamble = bytes(object_file.read(20))
 
-            little_endian = check_endianness(preamble)
-            # SPIR-V module magic number
-            if little_endian is None:
-                return False, 'Incorrect SPV binary: wrong magic number'
-
-            # SPIR-V version number
-            version = read_word(preamble, 1, little_endian)
-            # TODO(dneto): Recent Glslang uses version word 0 for opengl_compat
-            # profile
-
-            if version != spv_version and version != 0:
-                return False, 'Incorrect SPV binary: wrong version number'
-            # Shaderc-over-Glslang (0x000d....) or
-            # SPIRV-Tools (0x0007....) generator number
-            if read_word(preamble, 2, little_endian) != 0x000d0006 and \
-                    read_word(preamble, 2, little_endian) != 0x00070000:
-                return False, ('Incorrect SPV binary: wrong generator magic '
-                               'number')
-            # reserved for instruction schema
-            if read_word(preamble, 4, little_endian) != 0:
-                return False, 'Incorrect SPV binary: the 5th byte should be 0'
+            binary = bytes(object_file.read())
+            return self.verify_binary_length_and_header(binary, spv_version)
 
         return True, ''
 
diff --git a/glslc/test/option_dash_o.py b/glslc/test/option_dash_o.py
index 1a32c12..8fdb89b 100644
--- a/glslc/test/option_dash_o.py
+++ b/glslc/test/option_dash_o.py
@@ -63,3 +63,35 @@
     glslc_args = ['-o']
     expected_error = ['glslc: error: argument to \'-o\' is missing ' +
                       '(expected 1 value)\n']
+
+
+@inside_glslc_testsuite('OptionDashO')
+class OutputFileBinaryAvoidsCRLFTranslation(expect.ReturnCodeIsZero,
+                                            expect.NoOutputOnStderr,
+                                            expect.NoGeneratedFiles,
+                                            expect.CorrectBinaryLengthAndPreamble):
+    """Tests that the -o flag emits a binary file without CR/LF translation.
+    """
+
+    # A shader whose compiled output has three bytes that are newlines.
+    # If the output stream converts the newlines to CR/LF, then we end up
+    # with a file that is 4k + 3 bytes long.  That will be caught by the
+    # object file checks.
+    SHADER_WITH_THREE_NEWLINES_IN_BINARY = """#version 450
+       layout(location = 0) out uint ovar;
+       void main() { ovar = 10; }
+    """
+
+    shader = FileShader(SHADER_WITH_THREE_NEWLINES_IN_BINARY, '.vert')
+    glslc_args = [shader, '-o', '-']
+
+    def check_stdout_binary(self, status):
+        binary = status.stdout
+        newlines = [x for x in binary if x == '\n']
+        num_newlines = len(newlines)
+        if num_newlines % 4 == 0:
+            return False, "Bad test. Need nontrivial number of newlines"
+        if num_newlines != 3:
+            return False, ("Update this test. Expected 3 newlines in the "
+                           "binary, but found {}").format(num_newlines)
+        return self.verify_binary_length_and_header(status.stdout)
diff --git a/libshaderc_util/include/libshaderc_util/io.h b/libshaderc_util/include/libshaderc_util/io.h
index eb67091..b9116c1 100644
--- a/libshaderc_util/include/libshaderc_util/io.h
+++ b/libshaderc_util/include/libshaderc_util/io.h
@@ -57,6 +57,13 @@
 // is "-", writes to std::cout.
 bool WriteFile(std::ostream* output_stream, const string_piece& output_data);
 
+// Flush the standard output stream and set it to binary mode.  Subsequent
+// output will not translate newlines to carriage-return newline pairs.
+void FlushAndSetBinaryModeOnStdout();
+// Flush the standard output stream and set it to text mode.  Subsequent
+// output will translate newlines to carriage-return newline pairs.
+void FlushAndSetTextModeOnStdout();
+
 }  // namespace shaderc_util
 
 #endif  // LIBSHADERC_UTIL_IO_H_
diff --git a/libshaderc_util/src/io.cc b/libshaderc_util/src/io.cc
index 72a4055..42ae89d 100644
--- a/libshaderc_util/src/io.cc
+++ b/libshaderc_util/src/io.cc
@@ -16,7 +16,15 @@
 
 #include "libshaderc_util/universal_unistd.h"
 
+#if _WIN32
+// Need _fileno from stdio.h
+// Need _O_BINARY and _O_TEXT from fcntl.h
+#include <fcntl.h>
+#include <stdio.h>
+#endif
+
 #include <errno.h>
+#include <cstdio>
 #include <cstring>
 #include <fstream>
 #include <iostream>
@@ -120,4 +128,18 @@
   return true;
 }
 
+void FlushAndSetBinaryModeOnStdout() {
+  std::fflush(stdout);
+#if _WIN32
+  _setmode(_fileno(stdout), _O_BINARY);
+#endif
+}
+
+void FlushAndSetTextModeOnStdout() {
+  std::fflush(stdout);
+#if _WIN32
+  _setmode(_fileno(stdout), _O_TEXT);
+#endif
+}
+
 }  // namespace shaderc_util
diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt
index 373d5b4..b2f677b 100644
--- a/third_party/CMakeLists.txt
+++ b/third_party/CMakeLists.txt
@@ -32,6 +32,19 @@
   add_subdirectory(${SHADERC_SPIRV_HEADERS_DIR} spirv-headers)
 endif()
 
+# Check SPIRV-Tools before glslang so that it is linked into glslang.
+# we control optimizations via glslang API calls directly.
+if (IS_DIRECTORY ${SHADERC_SPIRV_TOOLS_DIR})
+  if ("${SHADERC_SKIP_TESTS}")
+    # Also skip building tests in SPIRV-Tools.
+    set(SPIRV_SKIP_TESTS ON CACHE BOOL "Skip building SPIRV-Tools tests")
+  endif()
+  add_subdirectory(${SHADERC_SPIRV_TOOLS_DIR} spirv-tools)
+endif()
+if (NOT TARGET SPIRV-Tools)
+  message(FATAL_ERROR "SPIRV-Tools was not found - required for compilation")
+endif()
+
 if (IS_DIRECTORY ${SHADERC_GLSLANG_DIR})
   add_subdirectory(${SHADERC_GLSLANG_DIR} glslang)
 endif()
@@ -46,19 +59,6 @@
       "Platform Toolset" FORCE)
 endif()
 
-# Check SPIRV-Tools after glslang so that it is not linked into glslang and
-# we can control optimizations.
-if (IS_DIRECTORY ${SHADERC_SPIRV_TOOLS_DIR})
-  if ("${SHADERC_SKIP_TESTS}")
-    # Also skip building tests in SPIRV-Tools.
-    set(SPIRV_SKIP_TESTS ON CACHE BOOL "Skip building SPIRV-Tools tests")
-  endif()
-  add_subdirectory(${SHADERC_SPIRV_TOOLS_DIR} spirv-tools)
-endif()
-if (NOT TARGET SPIRV-Tools)
-  message(FATAL_ERROR "SPIRV-Tools was not found - required for compilation")
-endif()
-
 if(${SHADERC_ENABLE_TESTS})
   # Configure out-of-source-directory tests for glslang.
   # The glslang project uses a bash script called "runtests" to run tests.