Build gamesdk using pre-built NDKs
(cherry picked from internal d35e90b16f7126427363d49050bac493dadbd03a)

Bug: 120609511
Test: ./gradlew will build gamesdk.zip
Test: From android studio:
Test: Build and run samples/bouncyball
Test: Build and run samples/tuningfork/tftestapp
Test: Build and run samples/cube
Change-Id: I6d5e77d52013e7f23999f312823b4f6d8cb126ef
diff --git a/README b/README
new file mode 100644
index 0000000..32e61bf
--- /dev/null
+++ b/README
@@ -0,0 +1,17 @@
+
+In order to build using prebuild NDK versions, this project must be initialized from a custom repo using:
+mkdir android-games-sdk
+cd android-games-sdk
+Corp -> repo init -u persistent-https://googleplex-android.git.corp.google.com/platform/manifest -b android-games-sdk
+AOSP -> repo init -u https://android.googlesource.com/platform/manifest -b android-games-sdk
+repo sync -c -j8
+
+Then:
+./gradlew gamesdkZip
+will build static and dynamic libraries for several NDK versions.
+
+By default, the gradle script builds target archiveZip, which will use a locally installed SDK/NDK pointed
+to by ANDROID_HOME (and ANDROID_NDK, if the ndk isn't in ANDROID_HOME/ndk-bundle).
+
+It is also possible to build from a direct AOSP checkout, but then you won't be able to build for multiple
+NDKs: archiveZip will still work but the gamesdkZip target will fail.
diff --git a/build.gradle b/build.gradle
index 64b0dc9..69f5c71 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,181 +1,358 @@
 
+import java.nio.file.Paths
 import org.gradle.internal.logging.text.StyledTextOutput;
 import org.gradle.internal.logging.text.StyledTextOutputFactory;
 import static org.gradle.internal.logging.text.StyledTextOutput.Style;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.api.Project;
 
 defaultTasks 'clean', 'archiveZip'
 
-def getAndroidNDKPath() {
-  return System.getenv("ANDROID_NDK")
+task get_protoc_version(type: Exec) {
+    commandLine "protoc"
+    args = ["--version"]
+    standardOutput = new ByteArrayOutputStream()
+    ext.output = {
+        return standardOutput.toString().trim()
+    }
 }
 
-def getAndroidSDKPath() {
-  return System.getenv("ANDROID_HOME")
+task check_protoc_version(dependsOn: get_protoc_version) {
+    doLast {
+        def version_needed = "3.0.0"
+        def is_protoc_version_correct = tasks.get_protoc_version.output().endsWith(version_needed)
+        def version_error_msg = "Make sure you have protoc installed and its version is exactly " + version_needed
+        assert is_protoc_version_correct : version_error_msg
+    }
 }
 
-ext.tempFolder = '.temp'
-ext.buildFolder = 'build'
-ext.androidNDK = getAndroidNDKPath()
-ext.androidNDKVersion = "26"
-ext.androidSDK = getAndroidSDKPath()
-
-if (ext.androidSDK == null) {
-  throw new GradleException('Must set ANDROID_HOME')
+task make_proto(type: Exec) {
+    workingDir getExternalPath() + '/nanopb-c/generator/proto'
+    commandLine 'make'
 }
 
-if (ext.androidNDK == null) {
-  ext.androidNDK = ext.androidSDK + '/ndk-bundle'
-  if (!file(ext.androidNDK).exists())
-    throw new GradleException('No NDK found in SDK: must set ANDROID_NDK')
-}
-
-def findCMake() {
-  def cmake = System.getenv("CMAKE");
-  if (cmake != null) {
-    return cmake;
-  }
-
-  def cmakes = fileTree(project.property('androidSDK')).matching { include 'cmake/*/bin/cmake' }
-  if (cmakes.size() == 0) {
-    throw new GradleException('No cmake found in ' + project.property('androidSDK') + 'cmake')
-  }
-  return cmakes.last();
-}
-
-ext.cmake = findCMake()
-
-println "Build folder: $buildFolder"
-println "Android SDK folder: $androidSDK"
-println "Android NDK folder: $androidNDK"
-println "Using cmake from: $cmake"
-
 buildscript {
-  repositories {
-    google()
-    jcenter()
-  }
-  dependencies {
-    classpath 'com.android.tools.build:gradle:3.2.1'
-  }
+    repositories {
+        google()
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.2.1'
+    }
 }
 
 allprojects {
-  repositories {
-    google()
-    jcenter()
-  }
+    buildDir = getOutPath()
+    repositories {
+        google()
+        jcenter()
+    }
 }
 
-def cmake(projectFolder, cmakeFolder, arch, ndkVersion) {
-  exec {
-    workingDir cmakeFolder
-
-    commandLine "$cmake",
-     "$projectFolder",
-     "-DCMAKE_BUILD_TYPE=Release ",
-     "-DANDROID_PLATFORM=android-" + ndkVersion,
-     "-DANDROID_NDK=" + project.property('androidNDK') + " ",
-     "-DANDROID_STL=c++_static ",
-     "-DANDROID_ABI=" + arch,
-     "-DCMAKE_CXX_FLAGS= ",
-     "-DCMAKE_TOOLCHAIN_FILE=" + project.property('androidNDK') + "/build/cmake/android.toolchain.cmake",
-     "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=./"
-  }
+abstract class Toolchain {
+    abstract String getAndroidNDKPath();
+    abstract String getCMakePath();
+    abstract String getNinjaPath();
+    abstract String getAndroidVersion();
+    abstract String getBuildKey(String arch, String stl);
+}
+class SpecificToolchain extends Toolchain {
+    private String androidVersion_;
+    private String ndkVersion_;
+    SpecificToolchain(String androidVersion, String ndkVersion) {
+        androidVersion_ = androidVersion;
+        ndkVersion_ = ndkVersion;
+    }
+    public String getAndroidNDKPath() {
+        return new File("../prebuilts/ndk/" + ndkVersion_).getCanonicalPath()
+    }
+    public String getCMakePath() {
+        return new File("../prebuilts/cmake/linux-x86/bin/cmake").getCanonicalPath()
+    }
+    public String getNinjaPath() {
+        return new File("../prebuilts/cmake/linux-x86/bin/ninja").getCanonicalPath()
+    }
+    public String getAndroidVersion() { return androidVersion_; }
+    public String getBuildKey(String arch, String stl) {
+        return arch + '_' + ndkVersion_ + '_' + stl
+    }
+}
+class LocalToolchain extends Toolchain {
+    Project project_;
+    String sdkPath_;
+    String ndkPath_;
+    String cmakePath_;
+    String ninjaPath_;
+    String androidVersion_;
+    LocalToolchain(Project project, String androidVersion) {
+        project_ = project
+        androidVersion_ = androidVersion
+        sdkPath_ = System.getenv("ANDROID_HOME")
+        if(sdkPath_ == null)
+            throw new GradleException('Must set ANDROID_HOME')
+        ndkPath_ = System.getenv("ANDROID_NDK")
+        if(ndkPath_ == null) {
+            ndkPath_ = Paths.get(sdkPath_,'ndk-bundle').toString()
+            if (!project_.file(ndkPath_).exists())
+                throw new GradleException('No NDK found in SDK: must set ANDROID_NDK')
+        }
+        cmakePath_ = findCMakeTool("CMAKE", "cmake")
+        ninjaPath_ = findCMakeTool("NINJA", "ninja")
+    }
+    String getAndroidNDKPath() {
+        return ndkPath_;
+    }
+    String getCMakePath() {
+        return cmakePath_;
+    }
+    String getNinjaPath() {
+        return ninjaPath_;
+    }
+    String getAndroidVersion() {
+        return androidVersion_;
+    }
+    String getBuildKey(String arch, String stl) {
+        return arch + '_local_' + stl
+    }
+    String findCMakeTool(String envVar, String name) {
+        def tool = System.getenv(envVar);
+        if (tool) {
+            return tool;
+        }
+        def osname = OperatingSystem.current().isWindows() ? name + '.exe' : name
+        def tools = project_.fileTree( dir: Paths.get(sdkPath_, 'cmake').toString(), include: ['**/bin/'+osname] )
+        if (tools==null || tools.size() == 0) {
+            throw new GradleException('No ' + osname + ' found in ' + sdkPath_ + '/cmake')
+        }
+        return tools.getFiles().last().toString();
+    }
 }
 
-def buildNative(arch, ndkVersion) {
-  def cmakeFolder = project.property('tempFolder') + '/' + arch + '/.cmake'
-  def buildFolder = project.property('buildFolder') + '/lib/' + arch
-
-  def out = services.get(StyledTextOutputFactory).create("ouput")
-  out.style(Style.Identifier).println("\n========= Building native [" + arch + "]");
-  println "cmake folder: $cmakeFolder";
-
-  mkdir cmakeFolder
-  mkdir buildFolder
-
-  exec {
-    commandLine "$cmake", '--version'
-  }
-
-  cmake("$projectDir/src/", cmakeFolder, arch, ndkVersion)
-
-  exec {
-    workingDir cmakeFolder
-
-    commandLine "make", "-j"
-  }
-
-  out.style(Style.ProgressStatus).println("\nCopying libraries to " + buildFolder);
-
-  copy {
-    from file(cmakeFolder)
-    include "*/lib*_static.a"
-    into file(buildFolder)
-    includeEmptyDirs = false
-  }
-  copy {
-    from file(cmakeFolder)
-    include "*/lib*.so"
-    into file(buildFolder)
-    includeEmptyDirs = false
-  }
+def getOutPath() {
+    return new File("../out").getCanonicalPath()
 }
 
+def getPackagePath() {
+    return new File("../package").getCanonicalPath()
+}
+
+def getTempPath() {
+    return new File("../.temp").getCanonicalPath()
+}
+
+def getExternalPath() {
+    def f =  new File("../external/");
+    if ( !f.exists() )
+        f = new File("../../../external/");
+    return f.getCanonicalPath()
+}
+def useNinja() {
+    return true;
+}
+def cmake(projectFolder, workingFolder, outputFolder, arch, toolchain, stl,
+          threadChecks) {
+    def ndkPath = toolchain.getAndroidNDKPath()
+    def toolchainFilePath = ndkPath + "/build/cmake/android.toolchain.cmake"
+    def externalPath = getExternalPath();
+    def androidVersion = toolchain.getAndroidVersion()
+    def ninjaPath = toolchain.getNinjaPath()
+    mkdir workingFolder
+    mkdir outputFolder
+
+    def threadFlags = ""
+    if (threadChecks) {
+        threadFlags = "-DGAMESDK_THREAD_CHECKS=1"
+    } else {
+        threadFlags = "-DGAMESDK_THREAD_CHECKS=0"
+    }
+
+    def cmdLine = [toolchain.getCMakePath(),
+                   "$projectFolder",
+                   "-DCMAKE_BUILD_TYPE=Release",
+                   "-DANDROID_PLATFORM=android-$androidVersion",
+                   "-DANDROID_NDK=$ndkPath",
+                   "-DANDROID_STL=$stl",
+                   "-DANDROID_ABI=$arch",
+                   "-DCMAKE_CXX_FLAGS=",
+                   "-DCMAKE_TOOLCHAIN_FILE=$toolchainFilePath",
+                   "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$outputFolder",
+                   "-DEXTERNAL_ROOT=$externalPath",
+                   threadFlags]
+    if (useNinja()) {
+        cmdLine += ["-DCMAKE_MAKE_PROGRAM=" + "$ninjaPath",
+                    "-GNinja"]
+    }
+    exec {
+        workingDir workingFolder
+        commandLine cmdLine
+    }
+}
+
+def buildNativeModules(arch, toolchain, stl, threadChecks) {
+    def buildKey = toolchain.getBuildKey(arch, stl)
+    def workingFolder = getTempPath() + '/' + buildKey + '/.cmake'
+    def outputFolder = getOutPath() + '/' + buildKey
+    def cmakeProjectLocation = "$projectDir/src/"
+
+    cmake(cmakeProjectLocation, workingFolder, outputFolder, arch, toolchain, stl, threadChecks)
+    def cmdLine = useNinja() ? [toolchain.getNinjaPath()] : ["make", "-j"]
+    exec {
+        workingDir workingFolder
+        commandLine cmdLine
+    }
+    return [arch: arch, buildKey: buildKey]
+}
+def buildNativeModules(arch, androidVersion, ndkVersion, stl, threadChecks) {
+    return buildNativeModules(arch,
+                              new SpecificToolchain(androidVersion, ndkVersion),
+                              stl, threadChecks)
+}
 task clean(type: Delete) {
-  delete project.property('tempFolder')
-  delete project.property('buildFolder')
-}
-
-task buildGameSDKJavaLibs() {
-  def buildFolder = project.property('buildFolder') + '/aar'
-  def outputFolder = 'src/extras/build/outputs/aar'
-  dependsOn ':extras:assembleRelease'
-
-  doFirst {
-    delete buildFolder
-    mkdir buildFolder
-  }
-  doLast {
-    copy {
-      from file(outputFolder + "/extras-release.aar")
-      into file(buildFolder + "/extras-release.aar")
+    doLast {
+        delete getOutPath(), getPackagePath(), getTempPath()
     }
-  }
 }
 
-task build(dependsOn: buildGameSDKJavaLibs) {
-  def includeFolder = project.property('buildFolder') + '/include'
-  doFirst {
-    delete includeFolder
-    mkdir includeFolder
-  }
-
-  def ndkVersion = project.property('androidNDKVersion')
-
-  doLast {
-    buildNative("armeabi-v7a", ndkVersion)
-    buildNative("arm64-v8a", ndkVersion)
-    copy {
-      from "$projectDir/include"
-      into includeFolder
+// Create outAr from the contents of inArs
+// All files taken/created in dir
+def repackArchives(dir, inArs, outAr) {
+    def cmd = /pushd $dir &&/
+    inArs.each {
+        cmd <<= /ar -x $it &&/
     }
-  }
+    cmd <<= /ar -qc $outAr *.o && rm *.o && popd/
+    ['/bin/bash', '-c', cmd.toString()].execute().waitFor()
+}
+def sdkCopy(buildInfo, outFolder, all = true, staticToo = false,
+            useFullBuildKey = false) {
+    def arch = buildInfo.arch
+    def buildKey = buildInfo.buildKey
+    def cmakeFolder = getTempPath() + '/' + buildKey + '/.cmake'
+    def buildFolder = getPackagePath() + '/' + outFolder
+    def libBuildFolder = buildFolder + '/libs/' +
+        (useFullBuildKey ? buildKey : arch ) + '/lib'
+
+    copy {
+        from file(cmakeFolder)
+        include all ? "*/lib*.so" : "swappy*/lib*.so"
+        into file(libBuildFolder)
+        includeEmptyDirs = false
+    }
+
+    if (staticToo) {
+        def staticsFolder = getOutPath() + '/' + buildKey
+        def staticLibsBuildFolder = buildFolder + '/libs/' + buildKey
+        def staticLibs = ['libswappy_static.a',
+                          'libswappyVk_static.a']
+        if (all)
+            staticLibs += 'libtuningfork_static.a'
+        repackArchives(staticsFolder, staticLibs, 'libgamesdk.a')
+        copy {
+            from file(staticsFolder)
+            include "libgamesdk.a"
+            into file(staticLibsBuildFolder)
+        }
+    }
+}
+def copyExtras(outFolder, all = true) {
+    def buildFolder = getPackagePath() + '/' + outFolder
+    def headerFolder = './include'
+    def aarFolder = getOutPath() + '/outputs/aar'
+    def includeBuildFolder = buildFolder + '/include'
+    def aarBuildFolder = buildFolder + '/aar'
+
+    copy {
+        from file(headerFolder)
+        include all ? "*/*.h" : "swappy*/*.h"
+        into file(includeBuildFolder)
+        includeEmptyDirs = false
+    }
+    copy {
+        from file(aarFolder)
+        into file(aarBuildFolder)
+        includeEmptyDirs = false
+    }
+}
+// The latest Unity is using NDK 16b and SDK 26 with gnu stl
+def defaultAbis() { return ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"] }
+def unityNativeBuild() {
+    return defaultAbis().collect { buildNativeModules(it, "26", "r16", "gnustl_shared", false) }
+}
+def sdkNativeBuild() {
+    return defaultAbis().collectMany {
+        [ buildNativeModules(it, "26", "r16", "gnustl_shared", false) ] +
+        [ buildNativeModules(it, "28", "r17", "gnustl_shared", false) ] +
+        [ buildNativeModules(it, "28", "r17", "c++_static", false) ] }
+}
+def localNativeBuild() {
+    def kLocalMinSdk = "26"
+    def stl = "c++_static"
+    def toolchain = new LocalToolchain(project, kLocalMinSdk)
+    return defaultAbis().collect {
+        buildNativeModules(it, toolchain , stl, true)
+    }
+}
+// Just build swappy
+task swappyUnityBuild() {
+    dependsOn ':extras:assembleRelease', 'check_protoc_version', 'make_proto'
+    ext.packageName = 'swappyUnity'
+    doLast {
+        unityNativeBuild().each { sdkCopy(it, packageName, false) }
+        copyExtras(packageName, false)
+    }
+}
+// Full build including tuning fork for unity
+task unityBuild() {
+    dependsOn ':extras:assembleRelease', 'check_protoc_version', 'make_proto'
+    ext.packageName = 'unity'
+    doLast {
+        unityNativeBuild().each { sdkCopy(it, packageName, true) }
+        copyExtras(packageName, true)
+    }
 }
 
-task archiveZip(type: Zip, dependsOn: 'build') {
-  baseName = project.property('buildFolder') + '/gamesdk'
-
-  from fileTree(project.property('buildFolder'))
-  exclude "*.zip"
-
-  into 'gamesdk'
-
-  doLast {
-    def out = services.get(StyledTextOutputFactory).create("ouput")
-
-    out.style(Style.Identifier).text('\nGameSDK zip is in ')
-      .style(Style.ProgressStatus)
-      .println(project.property('buildFolder') + '/gamesdk.zip' );
-  }
+// Build everything
+task sdkBuild() {
+    dependsOn ':extras:assembleRelease', 'check_protoc_version', 'make_proto'
+    ext.packageName = 'gamesdk'
+    doLast {
+        sdkNativeBuild().each { sdkCopy(it, packageName, true, true, true) }
+        copyExtras(packageName, true)
+    }
 }
+
+// Build using local SDK
+task localBuild() {
+    dependsOn ':extras:assembleRelease', 'check_protoc_version', 'make_proto'
+    ext.packageName = 'local'
+    doLast {
+        localNativeBuild().each { sdkCopy(it, packageName, true, true, false) }
+        copyExtras(packageName, true)
+    }
+}
+// Zipping things up
+def addZipTask(name, buildTask, zipName, rootName = "gamesdk") {
+    def packPath = buildTask.packageName
+    tasks.register(name, Zip) {
+        dependsOn buildTask
+        def buildFolder = Paths.get(getPackagePath(), packPath).toString()
+        baseName = Paths.get(buildFolder, zipName).toString()
+
+        from fileTree(buildFolder)
+        exclude "*.zip"
+
+        into rootName
+
+        doLast {
+            def out = services.get(StyledTextOutputFactory).create("ouput")
+            out.style(Style.Identifier).text('\n'+packPath+' zip is in ')
+            .style(Style.ProgressStatus)
+                .println(baseName + '.zip' );
+        }
+    }
+
+}
+addZipTask("swappyUnityZip", swappyUnityBuild, "builds")
+addZipTask("unityZip", unityBuild, "builds")
+addZipTask("archiveZip", localBuild, "gamesdk")
+addZipTask("gamesdkZip", sdkBuild, "gamesdk")
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..8046488
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1 @@
+android.builder.sdkDownload=false
diff --git a/samples/bouncyball/app/CMakeLists.txt b/samples/bouncyball/app/CMakeLists.txt
index 5e141f0..7b9cb3c 100644
--- a/samples/bouncyball/app/CMakeLists.txt
+++ b/samples/bouncyball/app/CMakeLists.txt
@@ -4,9 +4,14 @@
 
 # ============== Games SDK
 
-add_subdirectory(../../../src/swappy ./swappy-output)   # Swappy Library
+# This will include and build swappy from the gamesdk
+#add_subdirectory(../../../src/swappy ./swappy-output)   # Swappy Library
+#include_directories( "../../../include" ) # Games SDK Public Includes
 
-include_directories( ../../../include ) # Games SDK Public Includes
+# This will use swappy from a packaged version
+include("../../gamesdk.cmake")
+add_gamesdk_target()
+
 include_directories( ../../common/include ) # Samples Includes
 
 # ============== Bouncy Ball
@@ -21,15 +26,14 @@
              src/main/cpp/Orbit.cpp
              src/main/cpp/Renderer.cpp
              src/main/cpp/Settings.cpp
-             ../../common/src/Thread.cpp
+             ../../../samples/common/src/Thread.cpp
         )
 
-
 target_link_libraries( native-lib
 
                        android
                        EGL
                        GLESv2
                        log
-                       swappy
-                       )
\ No newline at end of file
+                       gamesdk
+                       )
diff --git a/samples/bouncyball/app/build.gradle b/samples/bouncyball/app/build.gradle
index b4c5193..bd1a902 100644
--- a/samples/bouncyball/app/build.gradle
+++ b/samples/bouncyball/app/build.gradle
@@ -1,5 +1,7 @@
 apply plugin: 'com.android.application'
 
+def GAMESDK_ROOT = '../../../..'
+
 android {
     compileSdkVersion 28
     defaultConfig {
@@ -11,7 +13,7 @@
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         externalNativeBuild {
             cmake {
-                cppFlags ""
+                arguments "-DGAMESDK_ROOT=$GAMESDK_ROOT"
             }
         }
     }
@@ -55,6 +57,4 @@
     implementation 'androidx.preference:preference:1.0.0-rc01'
     implementation project(':extras')
     testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'com.androidx.test:runner:1.0.2'
-    androidTestImplementation 'com.androidx.test.espresso:espresso-core:3.1.1'
 }
diff --git a/samples/gamesdk.cmake b/samples/gamesdk.cmake
new file mode 100644
index 0000000..32cb6a0
--- /dev/null
+++ b/samples/gamesdk.cmake
@@ -0,0 +1,22 @@
+function(add_gamesdk_target)
+
+    set(GAMESDK_PACKAGE_ROOT "${GAMESDK_ROOT}/package/gamesdk")
+    set(GAMESDK_GEN_TASK sdkBuild)
+    set(NDK_VERSION ${ANDROID_NDK_REVISION})
+    string(REGEX REPLACE "^([^.]+).*" "\\1" NDK_VERSION ${ANDROID_NDK_REVISION} )
+    set(GAMESDK_LIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/${GAMESDK_PACKAGE_ROOT}/libs/${ANDROID_ABI}_r${NDK_VERSION}_${ANDROID_STL}")
+
+    include_directories( "${GAMESDK_PACKAGE_ROOT}/include" ) # Games SDK Public Includes
+    get_filename_component(DEP_LIB "${GAMESDK_LIB_ROOT}/libgamesdk.a" REALPATH)
+    set(GAMESDK_LIB ${DEP_LIB} PARENT_SCOPE)
+
+    add_custom_command(OUTPUT ${DEP_LIB}
+        COMMAND ./gradlew ${GAMESDK_GEN_TASK} VERBATIM
+        WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${GAMESDK_ROOT}/gamesdk"
+        )
+    add_custom_target(gamesdk_lib DEPENDS ${DEP_LIB})
+    add_library( gamesdk STATIC IMPORTED GLOBAL)
+    add_dependencies(gamesdk gamesdk_lib)
+    set_target_properties(gamesdk PROPERTIES IMPORTED_LOCATION ${DEP_LIB})
+
+endfunction()
diff --git a/samples/tuningfork/tftestapp/app/CMakeLists.txt b/samples/tuningfork/tftestapp/app/CMakeLists.txt
index ddd7be9..07adc93 100644
--- a/samples/tuningfork/tftestapp/app/CMakeLists.txt
+++ b/samples/tuningfork/tftestapp/app/CMakeLists.txt
@@ -2,14 +2,19 @@
 
 # If you have protobuf installed from a different directory, set it here. The source version
 #  must match the protoc version.
-#set(PROTOBUF_SRC_DIR ~/3rdParty/protobuf-3.0.2/src)
+set(PROTOBUF_SRC_DIR ~/3rdParty/protobuf-3.0.2/src)
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Werror -Wthread-safety" )
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -Os -fPIC" )
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti" )
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGOOGLE_PROTOBUF_NO_RTTI -DHAVE_PTHREAD")
 
-set( BUILD_PROTOBUF_FULL_AND_LITE "True")
-add_subdirectory(../../../../src/tuningfork ./tuningfork_output)
+#set( BUILD_PROTOBUF_FULL_AND_LITE "True")
+#add_subdirectory(../../../../src/tuningfork ./tuningfork_output)
+
+# This will use tuningfork from a packaged version
+include("../../../gamesdk.cmake")
+add_gamesdk_target()
+include("../../../../src/protobuf/protobuf.cmake")
 
 protobuf_generate_nano_c( ${CMAKE_CURRENT_SOURCE_DIR}/src/main/proto src/main/proto/tuningfork_extensions.proto)
 protobuf_generate_full_cpp( ${CMAKE_CURRENT_SOURCE_DIR}/src/main/proto src/main/proto/tuningfork_extensions.proto)
@@ -20,6 +25,11 @@
 include_directories(${PROTOBUF_NANO_SRC_DIR})
 include_directories(../../../../src)
 
+add_library( protobuf-static
+        STATIC ${PROTOBUF_LITE_SRCS} ${PROTOBUF_SRCS}
+        )
+target_compile_options(protobuf-static PUBLIC "-Wno-tautological-constant-compare" "-Wno-enum-compare-switch")
+
 add_library( tuningfork_test
              SHARED
              src/test/cpp/nativetests.cpp
@@ -31,7 +41,7 @@
 
 # Note that we use the FULL version of protobuf in the tests, so we can get debug strings
 target_link_libraries( tuningfork_test
-        tuningfork-static
+        gamesdk
         protobuf-static
         log
 )
diff --git a/samples/tuningfork/tftestapp/app/build.gradle b/samples/tuningfork/tftestapp/app/build.gradle
index dd156e1..4b34757 100644
--- a/samples/tuningfork/tftestapp/app/build.gradle
+++ b/samples/tuningfork/tftestapp/app/build.gradle
@@ -1,5 +1,7 @@
 apply plugin: 'com.android.application'
 
+def GAMESDK_ROOT = '../../../../..'
+
 android {
     compileSdkVersion 28
     defaultConfig {
@@ -11,7 +13,7 @@
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         externalNativeBuild {
             cmake {
-                cppFlags ""
+                arguments "-DGAMESDK_ROOT=$GAMESDK_ROOT"
             }
         }
     }
@@ -36,6 +38,4 @@
     implementation "com.google.android.gms:play-services-clearcut:16.0.0"
 
     testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'com.androidx.test:runner:1.1.1'
-    androidTestImplementation 'com.androidx.test.espresso:espresso-core:3.1.1'
 }
diff --git a/src/common/Log.h b/src/common/Log.h
index 9984267..c5a74bd 100644
--- a/src/common/Log.h
+++ b/src/common/Log.h
@@ -24,4 +24,10 @@
 #define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__);
 #else
 #define ALOGV(...)
-#endif
\ No newline at end of file
+#endif
+
+namespace swappy {
+
+std::string to_string(int value);
+
+}
diff --git a/src/extras/build.gradle b/src/extras/build.gradle
index c71ac7f..a173f85 100644
--- a/src/extras/build.gradle
+++ b/src/extras/build.gradle
@@ -25,6 +25,4 @@
 dependencies {
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'com.androidx.test:runner:1.1.1'
-    androidTestImplementation 'com.androidx.test.espresso:espresso-core:3.1.1'
 }
diff --git a/src/protobuf/full/CMakeLists.txt b/src/protobuf/full/CMakeLists.txt
index 73df24e..13ffc79 100644
--- a/src/protobuf/full/CMakeLists.txt
+++ b/src/protobuf/full/CMakeLists.txt
@@ -8,8 +8,12 @@
 add_library( protobuf-static
   STATIC ${PROTOBUF_LITE_SRCS} ${PROTOBUF_SRCS}
 )
+target_compile_options(protobuf-static PUBLIC
+  "-Wno-tautological-constant-compare" "-Wno-enum-compare-switch")
 
 add_library( protobuf
   SHARED ${PROTOBUF_LITE_SRCS} ${PROTOBUF_SRCS}
 )
+target_compile_options(protobuf PUBLIC
+  "-Wno-tautological-constant-compare" "-Wno-enum-compare-switch")
 extra_pb_link_options(protobuf)
diff --git a/src/protobuf/lite/CMakeLists.txt b/src/protobuf/lite/CMakeLists.txt
index f9a1000..2b913df 100644
--- a/src/protobuf/lite/CMakeLists.txt
+++ b/src/protobuf/lite/CMakeLists.txt
@@ -9,9 +9,13 @@
   STATIC
   ${PROTOBUF_LITE_SRCS}
 )
+target_compile_options(protobuf-lite-static PUBLIC
+  "-Wno-tautological-constant-compare" "-Wno-enum-compare-switch")
 
 add_library( protobuf-lite
   SHARED
   ${PROTOBUF_LITE_SRCS}
 )
+target_compile_options(protobuf-lite PUBLIC
+  "-Wno-tautological-constant-compare" "-Wno-enum-compare-switch")
 extra_pb_link_options(protobuf-lite)
diff --git a/src/protobuf/protobuf.cmake b/src/protobuf/protobuf.cmake
index 08c7f19..1184ca1 100644
--- a/src/protobuf/protobuf.cmake
+++ b/src/protobuf/protobuf.cmake
@@ -4,12 +4,12 @@
 #  and use the same version of protoc below.
 # If autogen.sh complains about gmock, comment out the check for it
 if( NOT DEFINED PROTOBUF_SRC_DIR)
-  set( PROTOBUF_SRC_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../../../external/protobuf/src")
+  set( PROTOBUF_SRC_DIR "${EXTERNAL_ROOT}/protobuf/src")
 endif()
 # To use this default, you'll need to run 'protoc-gen-nanopb'
 #  in external/nanopb-c/generator
 if( NOT DEFINED PROTOBUF_NANO_SRC_DIR)
-  set( PROTOBUF_NANO_SRC_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../../../external/nanopb-c")
+  set( PROTOBUF_NANO_SRC_DIR "${EXTERNAL_ROOT}/nanopb-c")
 endif()
 if( DEFINED ENV{PROTOBUF_INSTALL_DIR})
   set( PROTOBUF_INSTALL_DIR $ENV{PROTOBUF_INSTALL_DIR})
@@ -22,15 +22,11 @@
   set( PROTOBUF_INCLUDE_DIR ${PROTOBUF_INSTALL_DIR}/include )
 endif()
 
-#message(STATUS "PROTOC_EXE=${PROTOC_EXE}")
-
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Werror -Wthread-safety" )
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -Os -fPIC" )
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti" )
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGOOGLE_PROTOBUF_NO_RTTI -DHAVE_PTHREAD")
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections" )
-#The following are needed for some of the protobuf code to compile
-set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-tautological-constant-compare -Wno-enum-compare-switch")
 
 function(set_link_options libname versionscript)
   if (${CMAKE_BUILD_TYPE} STREQUAL "Release")
diff --git a/src/swappy/CMakeLists.txt b/src/swappy/CMakeLists.txt
index 54a5ab6..3ff9566 100644
--- a/src/swappy/CMakeLists.txt
+++ b/src/swappy/CMakeLists.txt
@@ -5,6 +5,9 @@
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions" )
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti" )
 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections" )
+if ( DEFINED GAMESDK_THREAD_CHECKS )
+  set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGAMESDK_THREAD_CHECKS=${GAMESDK_THREAD_CHECKS}" )
+endif()
 
 set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections" )
 set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-s" )
diff --git a/src/swappy/ChoreographerFilter.cpp b/src/swappy/ChoreographerFilter.cpp
index 7cd731c..5361ad8 100644
--- a/src/swappy/ChoreographerFilter.cpp
+++ b/src/swappy/ChoreographerFilter.cpp
@@ -173,7 +173,7 @@
     }
 
     std::string threadName = "Filter";
-    threadName += std::to_string(thread);
+    threadName += swappy::to_string(thread);
     pthread_setname_np(pthread_self(), threadName.c_str());
 
     std::unique_lock<std::mutex> lock(mMutex);
diff --git a/src/swappy/ChoreographerThread.cpp b/src/swappy/ChoreographerThread.cpp
index c29229e..876bf16 100644
--- a/src/swappy/ChoreographerThread.cpp
+++ b/src/swappy/ChoreographerThread.cpp
@@ -24,6 +24,10 @@
 #include "Thread.h"
 #include "CpuInfo.h"
 
+#include <condition_variable>
+#include <cstring>
+#include <cstdlib>
+
 #include <sched.h>
 #include <pthread.h>
 #include <unistd.h>
diff --git a/src/swappy/CpuInfo.cpp b/src/swappy/CpuInfo.cpp
index e29cadd..9112643 100644
--- a/src/swappy/CpuInfo.cpp
+++ b/src/swappy/CpuInfo.cpp
@@ -20,6 +20,8 @@
 
 #include <limits>
 #include <bitset>
+#include <cstdlib>
+#include <cstring>
 
 #include "Log.h"
 
@@ -62,6 +64,13 @@
 
 namespace swappy {
 
+std::string to_string(int n) {
+  constexpr int kBufSize = 12; // strlen("−2147483648")+1
+  static char buf[kBufSize];
+  snprintf(buf, kBufSize, "%d", n);
+  return buf;
+}
+
 CpuInfo::CpuInfo() {
     const auto BUFFER_LENGTH = 10240;
 
@@ -84,13 +93,13 @@
             core.id = mCpus.size();
 
             auto core_path = std::string("/sys/devices/system/cpu/cpu")
-                             + std::to_string(core.id);
+                             + to_string(core.id);
 
             auto package_id = ReadFile(core_path + "/topology/physical_package_id");
             auto frequency = ReadFile(core_path + "/cpufreq/cpuinfo_max_freq");
 
-            core.package_id = std::atol(package_id.c_str());
-            core.frequency = std::atol(frequency.c_str());
+            core.package_id = atol(package_id.c_str());
+            core.frequency = atol(frequency.c_str());
 
             mMinFrequency = std::min(mMinFrequency, core.frequency);
             mMaxFrequency = std::max(mMaxFrequency, core.frequency);
diff --git a/src/swappy/EGL.h b/src/swappy/EGL.h
index a019548..be8df40 100644
--- a/src/swappy/EGL.h
+++ b/src/swappy/EGL.h
@@ -19,9 +19,9 @@
 #include <chrono>
 #include <condition_variable>
 #include <mutex>
-#include <optional>
 #include <thread>
 #include <memory>
+#include <atomic>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
diff --git a/src/swappy/FrameStatistics.cpp b/src/swappy/FrameStatistics.cpp
index 6865f2d..cb16841 100644
--- a/src/swappy/FrameStatistics.cpp
+++ b/src/swappy/FrameStatistics.cpp
@@ -123,31 +123,31 @@
     ALOGI("total frames: %" PRIu64, mStats.totalFrames);
     message += "Buckets:                    ";
     for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
-        message += "\t[" + std::to_string(i) + "]";
+        message += "\t[" + swappy::to_string(i) + "]";
     ALOGI("%s", message.c_str());
 
     message = "";
     message += "idle frames:                ";
     for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
-        message += "\t " + std::to_string(mStats.idleFrames[i]);
+        message += "\t " + swappy::to_string(mStats.idleFrames[i]);
     ALOGI("%s", message.c_str());
 
     message = "";
     message += "late frames:                ";
     for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
-        message += "\t " + std::to_string(mStats.lateFrames[i]);
+        message += "\t " + swappy::to_string(mStats.lateFrames[i]);
     ALOGI("%s", message.c_str());
 
     message = "";
     message += "offset from previous frame: ";
     for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
-        message += "\t " + std::to_string(mStats.offsetFromPreviousFrame[i]);
+        message += "\t " + swappy::to_string(mStats.offsetFromPreviousFrame[i]);
     ALOGI("%s", message.c_str());
 
     message = "";
     message += "frame latency:              ";
     for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
-        message += "\t " + std::to_string(mStats.latencyFrames[i]);
+        message += "\t " + swappy::to_string(mStats.latencyFrames[i]);
     ALOGI("%s", message.c_str());
 
     previousLogTime = std::chrono::steady_clock::now();
diff --git a/src/swappy/Swappy.cpp b/src/swappy/Swappy.cpp
index f24c0a5..bb32d57 100644
--- a/src/swappy/Swappy.cpp
+++ b/src/swappy/Swappy.cpp
@@ -398,8 +398,7 @@
     Settings::getInstance()->addListener([this]() { onSettingsChanged(); });
 
     ALOGI("Initialized Swappy with refreshPeriod=%lld, appOffset=%lld, sfOffset=%lld",
-          refreshPeriod.count(), appOffset.count(), sfOffset.count());
-
+          (long long)refreshPeriod.count(), (long long)appOffset.count(), (long long)sfOffset.count());
     std::lock_guard<std::mutex> lock(mEglMutex);
     mEgl = EGL::create(refreshPeriod);
     if (!mEgl) {
@@ -413,7 +412,7 @@
 
 void Swappy::onSettingsChanged() {
     std::lock_guard<std::mutex> lock(mFrameDurationsMutex);
-    int32_t newSwapInterval = std::round(float(Settings::getInstance()->getSwapIntervalNS()) /
+    int32_t newSwapInterval = ::round(float(Settings::getInstance()->getSwapIntervalNS()) /
                                float(mRefreshPeriod.count()));
     if (mSwapInterval != newSwapInterval || mAutoSwapInterval != newSwapInterval) {
         mSwapInterval = newSwapInterval;
@@ -607,7 +606,7 @@
     // are exactly at the edge.
     lowerBound -= FRAME_HYSTERESIS;
 
-    auto div_result = std::div((averageFrameTime.getTime(true) + FRAME_HYSTERESIS).count(),
+    auto div_result = ::div((averageFrameTime.getTime(true) + FRAME_HYSTERESIS).count(),
                                mRefreshPeriod.count());
     auto framesPerRefresh = div_result.quot;
     auto framesPerRefreshRemainder = div_result.rem;
diff --git a/src/swappy/SystemProperties.cpp b/src/swappy/SystemProperties.cpp
index 95aa7d3..e6cdd70 100644
--- a/src/swappy/SystemProperties.cpp
+++ b/src/swappy/SystemProperties.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <cstdlib>
 #include <string>
 
 #include "Log.h"
@@ -37,7 +38,7 @@
     if (bufferLen > PROP_VALUE_MAX || bufferLen == 0) {
         return default_value;
     }
-    return atoi(buffer);
+    return ::atoi(buffer);
 }
 
 int getSystemPropViaGetAsBool(const char* key, bool default_value = false) {
diff --git a/src/swappyVk/SwappyVk.cpp b/src/swappyVk/SwappyVk.cpp
index 2b30da0..77536c0 100644
--- a/src/swappyVk/SwappyVk.cpp
+++ b/src/swappyVk/SwappyVk.cpp
@@ -17,6 +17,8 @@
 #include <swappyVk/SwappyVk.h>
 
 #include <map>
+#include <condition_variable>
+#include <cstring>
 #include <unistd.h>
 
 #include <dlfcn.h>
@@ -250,7 +252,7 @@
 }
 
 void SwappyVkBase::calcRefreshRate(long frameTimeNanos) {
-    long refresh_nano = abs(frameTimeNanos - mLastframeTimeNanos);
+  long refresh_nano = std::abs(frameTimeNanos - mLastframeTimeNanos);
 
     if (mRefreshDur != 0 || mLastframeTimeNanos == 0) {
         return;
diff --git a/src/tuningfork/CMakeLists.txt b/src/tuningfork/CMakeLists.txt
index 9556b3d..04c256f 100644
--- a/src/tuningfork/CMakeLists.txt
+++ b/src/tuningfork/CMakeLists.txt
@@ -1,7 +1,7 @@
 cmake_minimum_required(VERSION 3.4.1)
 
 include("../protobuf/protobuf.cmake")
-set( MODPB64_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../external/modp_b64")
+set( MODPB64_DIR "${EXTERNAL_ROOT}/modp_b64")
 
 if (${CMAKE_BUILD_TYPE} STREQUAL "Release")
 set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,-s")
@@ -38,9 +38,9 @@
   ${PROTO_GENS_DIR}/nano/tuningfork_clearcut_log.pb.c
   ${PROTO_GENS_DIR}/nano/eng_tuningfork.pb.c)
 
-add_library( tuningfork-static
+add_library( tuningfork_static
   STATIC ${TUNINGFORK_SRCS} ${PROTOBUF_NANO_SRCS})
-set_target_properties( tuningfork-static PROPERTIES
+set_target_properties( tuningfork_static PROPERTIES
   COMPILE_OPTIONS "-DPROTOBUF_NANO" )
 
 add_library( tuningfork
diff --git a/src/tuningfork/prong.h b/src/tuningfork/prong.h
index b3d254d..e94eabc 100644
--- a/src/tuningfork/prong.h
+++ b/src/tuningfork/prong.h
@@ -21,6 +21,7 @@
 #include <vector>
 #include <map>
 #include <string>
+#include <memory>
 
 namespace tuningfork {
 
diff --git a/src/tuningfork/tuningfork_c.cpp b/src/tuningfork/tuningfork_c.cpp
index 97aff92..9440de8 100644
--- a/src/tuningfork/tuningfork_c.cpp
+++ b/src/tuningfork/tuningfork_c.cpp
@@ -15,6 +15,8 @@
 #include "tuningfork/tuningfork.h"
 #include "tuningfork_internal.h"
 
+#include <cstdlib>
+
 namespace {
 tuningfork::ProtobufSerialization ToProtobufSerialization(const CProtobufSerialization& cpbs) {
   return tuningfork::ProtobufSerialization(cpbs.bytes, cpbs.bytes + cpbs.size);
diff --git a/src/tuningfork/uploadthread.h b/src/tuningfork/uploadthread.h
index 55f51de..85767ab 100644
--- a/src/tuningfork/uploadthread.h
+++ b/src/tuningfork/uploadthread.h
@@ -18,6 +18,7 @@
 #include <thread>
 #include <mutex>
 #include <map>
+#include <condition_variable>
 #include "prong.h"
 
 namespace tuningfork {
diff --git a/third_party/cube/app/CMakeLists.txt b/third_party/cube/app/CMakeLists.txt
index 9ebec21..819a77d 100644
--- a/third_party/cube/app/CMakeLists.txt
+++ b/third_party/cube/app/CMakeLists.txt
@@ -4,10 +4,14 @@
 
 # ============== Games SDK
 
-include_directories( ../../../include ) # Games SDK Public Includes
-include_directories( ../../../src ) # Games SDK Internal Includes
+# This will use the in-source version of swappyVk
+#include_directories( ../../../include ) # Games SDK Public Includes
+#include_directories( ../../../src ) # Games SDK Internal Includes
+#add_subdirectory(../../../src/swappyVk ./swappyVk-output)   # SwappyVk Library
 
-add_subdirectory(../../../src/swappyVk ./swappyVk-output)   # SwappyVk Library
+# This will use swappy from a packaged version
+include("../../../samples/gamesdk.cmake")
+add_gamesdk_target()
 
 # ============== cube
 
@@ -65,13 +69,12 @@
                            ${SHADERC_SRC}/third_party/spirv-tools/include/spirv-tools
                            src/main/cpp/include
                            src/main/cpp/common
-			   ../../../src/swappyVk
                            ${ANDROID_NDK}/sources/android/native_app_glue)
 
 
 target_link_libraries( native-lib
 
-                       swappyVk
+                       gamesdk
                        android
                        log
                        )
diff --git a/third_party/cube/app/build.gradle b/third_party/cube/app/build.gradle
index 909fc4e..8b6f82b 100644
--- a/third_party/cube/app/build.gradle
+++ b/third_party/cube/app/build.gradle
@@ -14,6 +14,8 @@
 
 apply plugin: 'com.android.application'
 
+def GAMESDK_ROOT = '../../../..'
+
 def ndkDir
 if (project.rootProject.file('local.properties').exists()) {
     Properties properties = new Properties()
@@ -45,8 +47,11 @@
         }
         externalNativeBuild {
             cmake {
-                arguments '-DANDROID_PLATFORM=android-24', '-DANDROID_TOOLCHAIN=clang',
-                          "-DANDROID_STL=${stlType}", '-DSAMPLE_NAME=' + project.getName()
+                arguments "-DANDROID_PLATFORM=android-24"
+                arguments "-DANDROID_TOOLCHAIN=clang"
+                arguments "-DANDROID_STL=${stlType}"
+                arguments "-DSAMPLE_NAME=" + project.getName()
+                arguments "-DGAMESDK_ROOT=$GAMESDK_ROOT"
             }
         }
     }
@@ -55,7 +60,6 @@
             path 'CMakeLists.txt'
         }
     }
-    
     buildTypes {
         release {
             minifyEnabled false
@@ -63,8 +67,6 @@
                     'proguard-rules.pro'
         }
     }
-    
-    
 }
 
 dependencies {