From b00a5d7ee089e03ef50ea114b42447c5ed6359b1 Mon Sep 17 00:00:00 2001
From: Kun Zhang <zhangkun@google.com>
Date: Thu, 2 Apr 2015 13:14:29 -0700
Subject: [PATCH] Document more about cross-compilation; Post-build check for
 the actual arch of the artifact

---
 protoc-artifacts/README.md       | 17 ++++++---
 protoc-artifacts/build-protoc.sh | 61 +++++++++++++++++++++++++++-----
 2 files changed, 66 insertions(+), 12 deletions(-)

diff --git a/protoc-artifacts/README.md b/protoc-artifacts/README.md
index c629646270..2f3ed21861 100644
--- a/protoc-artifacts/README.md
+++ b/protoc-artifacts/README.md
@@ -32,9 +32,19 @@ $ mvn install
 The Maven script will try to detect the OS and the architecture from Java
 system properties. It's possible to build a protoc binary for an architecture
 that is different from what Java has detected, as long as you have the proper
-compilers installed. For example, MingGW32 only ships with 32-bit compilers,
-but you can still build 32-bit protoc under a 64-bit system, with the following
-command:
+compilers installed.
+
+You can override the Maven properties ``os.detected.name`` and
+``os.detected.arch`` to force the script to generate binaries for a specific OS
+and/or architecture. Valid values are defined as the return values of
+``normalizeOs()`` and ``normalizeArch()`` of ``Detector`` from
+[os-maven-plugin](https://github.com/trustin/os-maven-plugin/blob/master/src/main/java/kr/motd/maven/os/Detector.java).
+Frequently used values are:
+- ``os.detected.name``: ``linux``, ``osx``, ``windows``.
+- ``os.detected.arch``: ``x86_32``, ``x86_64``
+
+For example, MingGW32 only ships with 32-bit compilers, but you can still build
+32-bit protoc under 64-bit Windows, with the following command:
 ```
 $ mvn install -Dos.detected.arch=x86_32
 ```
@@ -48,4 +58,3 @@ Use the following command to upload artifacts:
 ```
 $ mvn clean deploy -P release
 ```
-
diff --git a/protoc-artifacts/build-protoc.sh b/protoc-artifacts/build-protoc.sh
index f02ed01b13..50c4b34927 100755
--- a/protoc-artifacts/build-protoc.sh
+++ b/protoc-artifacts/build-protoc.sh
@@ -7,6 +7,11 @@
 OS=$1
 ARCH=$2
 
+if [[ $# < 2 ]]; then
+  echo "No arguments provided. This script is intended to be run from Maven."
+  exit 1
+fi
+
 # Under Cygwin, bash doesn't have these in PATH when called from Maven which
 # runs in Windows version of Java.
 export PATH="/bin:/usr/bin:$PATH"
@@ -17,6 +22,7 @@ export PATH="/bin:/usr/bin:$PATH"
 E_PARAM_ERR=98
 E_ASSERT_FAILED=99
 
+# Usage:
 fail()
 {
   echo "Error: $1"
@@ -38,11 +44,45 @@ assertEq ()
     exit $E_ASSERT_FAILED
   fi
 }
+
+# Checks the artifact is for the expected architecture
+# Usage: checkArch <path-to-protoc>
+checkArch ()
+{
+  if [[ "$OS" == windows || "$OS" == linux ]]; then
+    format="$(objdump -f "$1" | grep -o "file format .*$" | grep -o "[^ ]*$")"
+    if [[ "$OS" == linux ]]; then
+      if [[ "$ARCH" == x86_32 ]]; then
+        assertEq $format "elf32-i386" $LINENO
+      elif [[ "$ARCH" == x86_64 ]]; then
+        assertEq $format "elf64-x86-64" $LINENO
+      else
+        fail "Unsupported arch: $ARCH"
+      fi
+    else
+      # $OS == windows
+      if [[ "$ARCH" == x86_32 ]]; then
+        assertEq $format "pei-i386" $LINENO
+      elif [[ "$ARCH" == x86_64 ]]; then
+        assertEq $format "pei-x86-64" $LINENO
+      else
+        fail "Unsupported arch: $ARCH"
+      fi
+    fi
+  elif [[ "$OS" == osx ]]; then
+    format="$(file -b "$1" | grep -o "[^ ]*$")"
+    assertEq $format "x86_64" $LINENO
+  else
+    fail "Unsupported system: $(uname)"
+  fi
+}
 ############################################################################
 
 echo "Building protoc, OS=$OS ARCH=$ARCH"
 
-cd "$(dirname \"$0\")"
+# Nested double quotes are unintuitive, but it works.
+cd "$(dirname "$0")"
+
 WORKING_DIR=$(pwd)
 CONFIGURE_ARGS="--disable-shared"
 
@@ -51,6 +91,10 @@ if [[ "$OS" == windows ]]; then
   MAKE_TARGET="${MAKE_TARGET}.exe"
 fi
 
+# Override the default value set in configure.ac that has '-g' which produces
+# huge binary.
+CXXFLAGS="-DNDEBUG"
+
 if [[ "$(uname)" == CYGWIN* ]]; then
   assertEq "$OS" windows $LINENO
   # Use mingw32 compilers because executables produced by Cygwin compiler
@@ -68,11 +112,11 @@ elif [[ "$(uname)" == MINGW32* ]]; then
 elif [[ "$(uname)" == Linux* ]]; then
   assertEq "$OS" linux $LINENO
   if [[ "$ARCH" == x86_64 ]]; then
-    CONFIGURE_ARGS="$CONFIGURE_ARGS --host=x86_64-linux-gnu"
+    CXXFLAGS="$CXXFLAGS -m64"
   elif [[ "$ARCH" == x86_32 ]]; then
-    CONFIGURE_ARGS="$CONFIGURE_ARGS --host=i686-linux-gnu"
+    CXXFLAGS="$CXXFLAGS -m32"
   else
-    fail "Unsupported arch by CYGWIN: $ARCH"
+    fail "Unsupported arch: $ARCH"
   fi
 elif [[ "$(uname)" == Darwin* ]]; then
   assertEq "$OS" osx $LINENO
@@ -80,9 +124,7 @@ else
   fail "Unsupported system: $(uname)"
 fi
 
-# Override the default value set in configure.ac that has '-g' which produces
-# huge binary.
-export CXXFLAGS="-DNDEBUG"
+export CXXFLAGS
 
 # Statically link libgcc and libstdc++.
 # -s to produce stripped binary.
@@ -91,7 +133,10 @@ if [[ "$OS" != osx ]]; then
   export LDFLAGS="-static-libgcc -static-libstdc++ -s"
 fi
 
+TARGET_FILE=target/protoc.exe
+
 cd "$WORKING_DIR"/.. && ./configure $CONFIGURE_ARGS &&
   cd src && make clean && make $MAKE_TARGET &&
   cd "$WORKING_DIR" && mkdir -p target &&
-  (cp ../src/protoc target/protoc.exe || cp ../src/protoc.exe target/protoc.exe)
+  (cp ../src/protoc $TARGET_FILE || cp ../src/protoc.exe $TARGET_FILE) &&
+  checkArch $TARGET_FILE