Ccache (#711)

* Add support for ccache.

* Simplify ccache activation.

* Fix macOS ccache build

* Make ccache executable

* Add Ninja build support. Change ccache name to git.

* Fix merge conflict
diff --git a/emsdk.py b/emsdk.py
index 04b083e..97a3fd2 100644
--- a/emsdk.py
+++ b/emsdk.py
@@ -1229,6 +1229,111 @@
   return success
 
 
+def build_ninja(tool):
+  debug_print('build_ninja(' + str(tool) + ')')
+  root = os.path.normpath(tool.installation_path())
+  src_root = os.path.join(root, 'src')
+  success = git_clone_checkout_and_pull(tool.download_url(), src_root, tool.git_branch)
+  if not success:
+    return False
+
+  build_dir = llvm_build_dir(tool)
+  build_root = os.path.join(root, build_dir)
+
+  build_type = decide_cmake_build_type(tool)
+
+  # Configure
+  cmake_generator = CMAKE_GENERATOR
+  args = []
+  if 'Visual Studio 16' in CMAKE_GENERATOR:  # VS2019
+    # With Visual Studio 16 2019, CMake changed the way they specify target arch.
+    # Instead of appending it into the CMake generator line, it is specified
+    # with a -A arch parameter.
+    args += ['-A', 'x64' if tool.bitness == 64 else 'x86']
+    args += ['-Thost=x64']
+  elif 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64:
+    cmake_generator += ' Win64'
+    args += ['-Thost=x64']
+
+  cmakelists_dir = os.path.join(src_root)
+  success = cmake_configure(cmake_generator, build_root, cmakelists_dir, build_type, args)
+  if not success:
+    return False
+
+  # Make
+  success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32')
+
+  if success:
+    bin_dir = os.path.join(root, 'bin')
+    mkdir_p(bin_dir)
+    exe_paths = [os.path.join(build_root, 'Release', 'ninja'), os.path.join(build_root, 'ninja')]
+    for e in exe_paths:
+      for s in ['.exe', '']:
+        ninja = e + s
+        if os.path.isfile(ninja):
+          dst = os.path.join(bin_dir, 'ninja' + s)
+          shutil.copyfile(ninja, dst)
+          os.chmod(dst, os.stat(dst).st_mode | stat.S_IEXEC)
+
+  return success
+
+
+def build_ccache(tool):
+  debug_print('build_ccache(' + str(tool) + ')')
+  root = os.path.normpath(tool.installation_path())
+  src_root = os.path.join(root, 'src')
+  success = git_clone_checkout_and_pull(tool.download_url(), src_root, tool.git_branch)
+  if not success:
+    return False
+
+  build_dir = llvm_build_dir(tool)
+  build_root = os.path.join(root, build_dir)
+
+  build_type = decide_cmake_build_type(tool)
+
+  # Configure
+  cmake_generator = CMAKE_GENERATOR
+  args = ['-DZSTD_FROM_INTERNET=ON']
+  if 'Visual Studio 16' in CMAKE_GENERATOR:  # VS2019
+    # With Visual Studio 16 2019, CMake changed the way they specify target arch.
+    # Instead of appending it into the CMake generator line, it is specified
+    # with a -A arch parameter.
+    args += ['-A', 'x64' if tool.bitness == 64 else 'x86']
+    args += ['-Thost=x64']
+  elif 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64:
+    cmake_generator += ' Win64'
+    args += ['-Thost=x64']
+
+  cmakelists_dir = os.path.join(src_root)
+  success = cmake_configure(cmake_generator, build_root, cmakelists_dir, build_type, args)
+  if not success:
+    return False
+
+  # Make
+  success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32')
+
+  if success:
+    bin_dir = os.path.join(root, 'bin')
+    mkdir_p(bin_dir)
+    exe_paths = [os.path.join(build_root, 'Release', 'ccache'), os.path.join(build_root, 'ccache')]
+    for e in exe_paths:
+      for s in ['.exe', '']:
+        ccache = e + s
+        if os.path.isfile(ccache):
+          dst = os.path.join(bin_dir, 'ccache' + s)
+          shutil.copyfile(ccache, dst)
+          os.chmod(dst, os.stat(dst).st_mode | stat.S_IEXEC)
+
+    cache_dir = os.path.join(root, 'cache')
+    open(os.path.join(root, 'emcc_ccache.conf'), 'w').write('''# Set maximum cache size to 10 GB:
+max_size = 10G
+cache_dir = %s
+''' % cache_dir)
+    mkdir_p(cache_dir)
+
+  return success
+
+
 # Emscripten asm.js optimizer build scripts:
 def optimizer_build_root(tool):
   build_root = tool.installation_path().strip()
@@ -1898,6 +2003,10 @@
       success = build_fastcomp(self)
     elif hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_llvm':
       success = build_llvm(self)
+    elif hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_ninja':
+      success = build_ninja(self)
+    elif hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_ccache':
+      success = build_ccache(self)
     elif hasattr(self, 'git_branch'):
       success = git_clone_checkout_and_pull(url, self.installation_path(), self.git_branch)
     elif url.endswith(ARCHIVE_SUFFIXES):
@@ -1925,7 +2034,7 @@
         success = emscripten_post_install(self)
       elif self.custom_install_script == 'emscripten_npm_install':
         success = emscripten_npm_install(self, self.installation_path())
-      elif self.custom_install_script in ('build_fastcomp', 'build_llvm'):
+      elif self.custom_install_script in ('build_fastcomp', 'build_llvm', 'build_ninja', 'build_ccache'):
         # 'build_fastcomp' is a special one that does the download on its
         # own, others do the download manually.
         pass
diff --git a/emsdk_manifest.json b/emsdk_manifest.json
index 379545a..b0000e0 100644
--- a/emsdk_manifest.json
+++ b/emsdk_manifest.json
@@ -476,6 +476,29 @@
     "windows_url": "mingw_7.1.0_64bit.zip",
     "activated_cfg": "MINGW_ROOT='%installation_dir%'",
     "activated_path": "%installation_dir%/bin"
+  },
+  {
+    "id": "ninja",
+    "version": "git-release",
+    "bitness": 64,
+    "url": "https://github.com/ninja-build/ninja.git",
+    "git_branch": "release",
+    "activated_cfg": "NINJA=%installation_dir%/bin",
+    "activated_path": "%installation_dir%/bin",
+    "cmake_build_type": "Release",
+    "custom_install_script": "build_ninja"
+  },
+  {
+    "id": "ccache",
+    "version": "git-emscripten",
+    "bitness": 64,
+    "url": "https://github.com/juj/ccache.git",
+    "git_branch": "emscripten",
+    "activated_cfg": "EMCC_CCACHE=1",
+    "activated_path": "%installation_dir%/bin",
+    "activated_env": "EMCC_CCACHE=1;CCACHE_CONFIGPATH=%installation_dir%/emcc_ccache.conf",
+    "cmake_build_type": "Release",
+    "custom_install_script": "build_ccache"
   }
   ],