SKQP Build for Fuchsia SDK

Change-Id: I2619784eca0f7a4dd66f2db0104cb746d9266b4e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/244369
Commit-Queue: John Rosasco <rosasco@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index a8041ca..136455a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -8,6 +8,11 @@
 import("gn/shared_sources.gni")
 import("gn/skia.gni")
 
+if (is_fuchsia) {
+  import("//build/fuchsia/sdk.gni")
+  import("build/fuchsia/fuchsia_download_sdk.gni")
+}
+
 if (skia_use_dawn) {
   import("third_party/externals/dawn/scripts/dawn_features.gni")
 }
@@ -316,9 +321,9 @@
   deps = []
 
   if (is_fuchsia && using_fuchsia_sdk) {
-    deps += [ "$fuchsia_sdk_root/fidl:fuchsia.fonts" ]
+    deps += [ "//build/fuchsia/fidl:fuchsia.fonts" ]
   } else {
-    deps += [ "//sdk/fidl/fuchsia.fonts" ]
+    deps = [ "//sdk/fidl/fuchsia.fonts" ]
   }
   sources = [
     "src/ports/SkFontMgr_fuchsia.cpp",
@@ -1167,8 +1172,6 @@
   }
 
   config("our_vulkan_headers") {
-    # We add this directory to simulate the client already have
-    # vulkan/vulkan_core.h on their path.
     include_dirs = [ "include/third_party/vulkan" ]
   }
 
@@ -1294,11 +1297,15 @@
       "tools/gpu/TestContext.cpp",
       "tools/gpu/YUVUtils.cpp",
       "tools/gpu/YUVUtils.h",
-      "tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp",
-      "tools/gpu/gl/GLTestContext.cpp",
-      "tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp",
       "tools/gpu/mock/MockTestContext.cpp",
     ]
+    if (skia_use_gl) {
+      sources += [
+        "tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp",
+        "tools/gpu/gl/GLTestContext.cpp",
+        "tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp",
+      ]
+    }
 
     libs = []
 
@@ -1321,6 +1328,8 @@
       if (target_cpu != "arm64") {
         libs += [ "OpenGL32.lib" ]
       }
+    } else if (is_fuchsia && using_fuchsia_sdk) {
+      libs += [ "${fuchsia_sdk_path}/arch/${target_cpu}/sysroot/lib/libzircon.so" ]
     }
 
     cflags_objcc = [ "-fobjc-arc" ]
@@ -1341,7 +1350,7 @@
       public_deps += [ "//third_party/dawn:dawn_headers" ]
       sources += [ "tools/gpu/dawn/DawnTestContext.cpp" ]
     }
-  }
+  }  # test_lib("gpu_tool_utils")
 
   test_lib("flags") {
     sources = [
@@ -1489,6 +1498,9 @@
   import("gn/gm.gni")
   test_lib("gm") {
     sources = gm_sources
+    if (skia_use_gl) {
+      sources += gl_gm_sources
+    }
     deps = [
       ":etc1",
       ":flags",
@@ -1527,6 +1539,9 @@
       sources += metal_tests_sources
       cflags_objcc = [ "-fobjc-arc" ]
     }
+    if (skia_use_gl) {
+      sources += gl_tests_sources
+    }
     if (!skia_enable_fontmgr_android) {
       sources -= [ "//tests/FontMgrAndroidParserTest.cpp" ]
     }
@@ -2028,7 +2043,6 @@
       ]
       deps = [
         ":gm",
-        ":gpu_tool_utils",
         ":skia",
         ":tests",
         ":tool_utils",
@@ -2038,11 +2052,9 @@
       sources = [
         "tools/skqp/src/skqp_main.cpp",
       ]
-      deps = [
-        ":skia",
-        ":skqp_lib",
-        ":tool_utils",
-      ]
+      include_dirs = [ "//" ]
+      lib_dirs = []
+      deps = [ ":skqp_lib", ]
     }
     test_app("jitter_gms") {
       sources = [
@@ -2055,6 +2067,15 @@
       ]
     }
   }
+  if (is_fuchsia) {
+    # Build a package repository for skqp on Fuchsia.
+    group("skqp_repo") {
+      testonly = true
+      deps = [
+        "//build/fuchsia/skqp:skqp_repo"
+      ]
+    }
+  }
   if (is_android) {
     shared_library("libskqp_app") {
       configs += [ ":skia_private" ]
diff --git a/build/fuchsia/BUILD.gn b/build/fuchsia/BUILD.gn
new file mode 100644
index 0000000..584b9a5
--- /dev/null
+++ b/build/fuchsia/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+
+import ("//build/fuchsia/sdk.gni")
+
+fuchsia_sdk_manifest_exists = false
+if (is_fuchsia && using_fuchsia_sdk) {
+  manifest_exists = exec_script("//build/fuchsia/file_exists",
+                                [ "-file_name",
+                                  rebase_path(fuchsia_sdk_manifest_path) ],
+                                "list lines",
+                                [ "//build/fuchsia/file_exists" ])
+  if (manifest_exists == [ "true" ]) {
+    fuchsia_sdk_manifest_exists = true
+  }
+}
+
+group("fuchsia") {
+  if(fuchsia_sdk_manifest_exists == true) {
+    deps = [
+      "fidl",
+      "pkg",
+      "sysroot",
+    ]
+  } else {
+    assert(false, "Fuchsia SDK not found. Set arg skia_update_fuchsia_sdk=True " +
+      "to initialize.")
+  }
+}
diff --git a/build/fuchsia/fidl/BUILD.gn b/build/fuchsia/fidl/BUILD.gn
new file mode 100644
index 0000000..db7239b4
--- /dev/null
+++ b/build/fuchsia/fidl/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+import("//build/fuchsia/sdk.gni")
+
+#
+# Generate gn targets for fidl libraries in the Fuchsia SDK.
+#
+fuchsia_sdk("fidl") {
+  meta = fuchsia_sdk_manifest_path
+  enabled_parts = [ "fidl_library" ]
+}
diff --git a/build/fuchsia/fidl_gen_cpp b/build/fuchsia/fidl_gen_cpp
new file mode 100755
index 0000000..6d2c10d
--- /dev/null
+++ b/build/fuchsia/fidl_gen_cpp
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+ Generate C/C++ headers and source files from the set of FIDL files
+ specified in the meta.json manifest.
+"""
+
+import argparse
+import collections
+import json
+import os
+import subprocess
+import sys
+
+def GetFIDLFilesRecursive(libraries, sdk_base, path):
+  with open(path) as json_file:
+    parsed = json.load(json_file)
+    result = []
+    deps = parsed['deps']
+    for dep in deps:
+      dep_meta_json = os.path.abspath('%s/fidl/%s/meta.json' % (sdk_base, dep))
+      GetFIDLFilesRecursive(libraries, sdk_base, dep_meta_json)
+    libraries[parsed['name']] = result + parsed['sources']
+
+def GetFIDLFilesByLibraryName(sdk_base, root):
+  libraries = collections.OrderedDict()
+  GetFIDLFilesRecursive(libraries, sdk_base, root)
+  return libraries
+
+def main():
+  parser = argparse.ArgumentParser()
+
+  parser.add_argument('--fidlc-bin', dest='fidlc_bin', action='store', required=True)
+  parser.add_argument('--fidlgen-bin', dest='fidlgen_bin', action='store', required=True)
+
+  parser.add_argument('--sdk-base', dest='sdk_base', action='store', required=True)
+  parser.add_argument('--root', dest='root', action='store', required=True)
+  parser.add_argument('--json', dest='json', action='store', required=True)
+  parser.add_argument('--include-base', dest='include_base', action='store', required=True)
+  parser.add_argument('--output-base-cc', dest='output_base_cc', action='store', required=True)
+  parser.add_argument('--output-c-header', dest='output_header_c', action='store', required=True)
+  parser.add_argument('--output-c-tables', dest='output_c_tables', action='store', required=True)
+
+  args = parser.parse_args()
+
+  assert os.path.exists(args.fidlc_bin)
+  assert os.path.exists(args.fidlgen_bin)
+
+  fidl_files_by_name = GetFIDLFilesByLibraryName(args.sdk_base, args.root)
+
+  fidlc_command = [
+    args.fidlc_bin,
+    '--c-header',
+    args.output_header_c,
+    '--tables',
+    args.output_c_tables,
+    '--json',
+    args.json
+  ]
+
+  for _, fidl_files in fidl_files_by_name.iteritems():
+    fidlc_command.append('--files')
+    for fidl_file in fidl_files:
+      fidl_abspath = os.path.abspath('%s/%s' % (args.sdk_base, fidl_file))
+      fidlc_command.append(fidl_abspath)
+
+  subprocess.check_call(fidlc_command)
+
+  assert os.path.exists(args.json)
+
+  fidlgen_command = [
+    args.fidlgen_bin,
+    '-generators',
+    'cpp',
+    '-include-base',
+    args.include_base,
+    '-json',
+    args.json,
+    '-output-base',
+    args.output_base_cc
+  ]
+
+  subprocess.check_call(fidlgen_command)
+
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/fuchsia/file_exists b/build/fuchsia/file_exists
new file mode 100755
index 0000000..0d487af
--- /dev/null
+++ b/build/fuchsia/file_exists
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+  Prints "true" if the input |file_name| exists.
+"""
+
+import argparse
+import os
+
+parser = argparse.ArgumentParser()
+parser.add_argument("-file_name", type=str,
+        help="File name for which to check existence for.")
+args = parser.parse_args()
+if os.path.exists(args.file_name):
+  print "true"
diff --git a/build/fuchsia/fuchsia_download_sdk.gni b/build/fuchsia/fuchsia_download_sdk.gni
new file mode 100644
index 0000000..8e63f1b
--- /dev/null
+++ b/build/fuchsia/fuchsia_download_sdk.gni
@@ -0,0 +1,18 @@
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import ("../../gn/skia.gni")
+import ("//build/fuchsia/sdk.gni")
+
+if(is_fuchsia && skia_update_fuchsia_sdk &&
+   current_toolchain == default_toolchain) {
+  cipd_dir = "${fuchsia_sdk_path}/../cipd"
+  update_sdk_out = exec_script("//build/fuchsia/update_fuchsia_sdk",
+              [ "-sdk_dir=" + rebase_path(fuchsia_sdk_path),
+                "-clang_dir=" + rebase_path(fuchsia_toolchain_path),
+                "-cipd_cache_dir=" + rebase_path(cipd_dir),
+                "-cipd_clang_version=git_revision:a6e1de4afc51560df18c95cb616dec51248ed660" ],
+              "list lines",
+              [ "//build/fuchsia/update_fuchsia_sdk" ])
+}
diff --git a/build/fuchsia/gen_package b/build/fuchsia/gen_package
new file mode 100755
index 0000000..407ee7e
--- /dev/null
+++ b/build/fuchsia/gen_package
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+ Builds a Fuchsia FAR archive.
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+def main():
+  parser = argparse.ArgumentParser()
+
+  parser.add_argument('--pm-bin', dest='pm_bin', action='store', required=True)
+  parser.add_argument(
+      '--pkg-dir', dest='pkg_dir', action='store', required=True)
+  parser.add_argument(
+      '--pkg-name', dest='pkg_name', action='store', required=True)
+  parser.add_argument(
+      '--pkg-version', dest='pkg_version', action='store', required=True)
+  parser.add_argument(
+      '--pkg-manifest', dest='pkg_manifest', action='store', required=True)
+
+  args = parser.parse_args()
+
+  assert os.path.exists(args.pm_bin)
+  assert os.path.exists(args.pkg_dir)
+
+  pkg_dir = args.pkg_dir
+  pkg_name = args.pkg_name
+  pkg_manifest = args.pkg_manifest
+  pkg_version = args.pkg_version
+
+  pm_command_base = [
+      args.pm_bin,
+      '-o',
+      pkg_dir,
+  ]
+
+  # Create the package ID file.
+  subprocess.check_call(pm_command_base + ['-n'] + [pkg_name] + ['init'])
+
+  # Build the package.
+  subprocess.check_call(pm_command_base + ['-m'] + [pkg_manifest] + ['build'])
+
+  # Archive the package.
+  subprocess.check_call(pm_command_base + ['-m'] + [pkg_manifest] + ['-version'] + [pkg_version] + ['archive'])
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/fuchsia/gen_repo b/build/fuchsia/gen_repo
new file mode 100755
index 0000000..ed69ff0
--- /dev/null
+++ b/build/fuchsia/gen_repo
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+  Generate a Fuchsia repo capable of serving Fuchsia archives over the
+  network.
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+def main():
+  parser = argparse.ArgumentParser()
+
+  parser.add_argument('--pm-bin', dest='pm_bin', action='store', required=True)
+  parser.add_argument(
+      '--repo-dir', dest='repo_dir', action='store', required=True)
+  parser.add_argument(
+      '--archive', dest='archives', action='append', required=True)
+
+  args = parser.parse_args()
+
+  assert os.path.exists(args.pm_bin)
+
+  if not os.path.exists(args.repo_dir):
+    pm_newrepo_command = [args.pm_bin, 'newrepo', '-repo', args.repo_dir]
+    subprocess.check_call(pm_newrepo_command)
+
+  pm_publish_command = [
+      args.pm_bin,
+      'publish',
+      '-C',  # Remove all previous registrations.
+      '-a',  # Publish archives from an archive (mode).
+      '-repo',
+      args.repo_dir
+  ]
+
+  for archive in args.archives:
+    pm_publish_command.append('-f')
+    pm_publish_command.append(archive)
+
+  print "PM Publish: "
+  print pm_publish_command
+  subprocess.check_call(pm_publish_command)
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/fuchsia/pkg/BUILD.gn b/build/fuchsia/pkg/BUILD.gn
new file mode 100644
index 0000000..5d6b19f
--- /dev/null
+++ b/build/fuchsia/pkg/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+import("//build/fuchsia/sdk.gni")
+
+#
+# Generate ninja targets for cc_source_libraries and cc_prebuilt_libraries
+# in the SDK.  "pkg" is the parent directory within the SDK that contain
+# the "meta.json" files for each library that defines their build
+# dependencies.
+#
+fuchsia_sdk("pkg") {
+  meta = fuchsia_sdk_manifest_path
+  enabled_parts = [
+    "cc_source_library",
+    "cc_prebuilt_library",
+  ]
+}
diff --git a/build/fuchsia/sdk.gni b/build/fuchsia/sdk.gni
new file mode 100644
index 0000000..0cefb8f
--- /dev/null
+++ b/build/fuchsia/sdk.gni
@@ -0,0 +1,406 @@
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# import("//gn/skia.gni")
+
+declare_args() {
+  using_fuchsia_sdk = true
+
+  # Fuchsia SDK install dir.
+  fuchsia_sdk_path = "//fuchsia/sdk/$host_os"
+
+  # Clang install dir.
+  fuchsia_toolchain_path = "//fuchsia/toolchain/$host_os"
+
+  # Path to GN-generated GN targets derived from parsing json file at
+  # |fuchsia_sdk_manifest_path|.  The parsing logic can be found in sdk.gni.
+  fuchsia_sdk_root = "//build/fuchsia"
+}
+
+declare_args() {
+  fuchsia_sdk_manifest_path = "${fuchsia_sdk_path}/meta/manifest.json"
+}
+
+template("_fuchsia_sysroot") {
+  assert(defined(invoker.meta), "The meta.json file path must be specified.")
+  assert(target_cpu == "x64" || target_cpu == "arm64",
+         "We currently only support 'x64' and 'arm64' targets for fuchsia.")
+
+  meta_json = read_file(invoker.meta, "json")
+
+  assert(meta_json.type == "sysroot")
+
+  meta_json_versions = meta_json.versions
+  if (target_cpu == "x64") {
+    defs = meta_json_versions.x64
+  } else {
+    defs = meta_json_versions.arm64
+  }
+
+  _libs = []
+  _lib_dirs = []
+  _include_dirs = []
+
+  foreach(link_lib, defs.link_libs) {
+    if (link_lib != "arch/${target_cpu}/sysroot/lib/Scrt1.o") {
+      _libs += [ rebase_path("$fuchsia_sdk_path/$link_lib") ]
+    }
+  }
+
+  defs_include_dir = defs.include_dir
+  _include_dirs += [ rebase_path("$fuchsia_sdk_path/$defs_include_dir") ]
+
+  config_name = "config_$target_name"
+  config(config_name) {
+    lib_dirs = _lib_dirs
+    libs = _libs
+    include_dirs = _include_dirs
+  }
+
+  group(target_name) {
+    public_configs = [ ":$config_name" ]
+  }
+}
+
+template("_fuchsia_fidl_library") {
+  assert(defined(invoker.meta), "The meta.json file path must be specified.")
+  assert(target_cpu == "x64" || target_cpu == "arm64",
+         "We currently only support 'x64' and 'arm64' targets for fuchsia.")
+
+  meta_json = read_file(invoker.meta, "json")
+
+  assert(meta_json.type == "fidl_library")
+
+  _deps = [ "../pkg:fidl_cpp" ]
+
+  library_name = string_replace(meta_json.name, "fuchsia.", "")
+  library_name_json = "$library_name.json"
+
+  foreach(dep, meta_json.deps) {
+    _deps += [ ":$dep" ]
+  }
+
+  config_name = "config_$target_name"
+  config(config_name) {
+    include_dirs = [ target_gen_dir ]
+  }
+
+  fidl_gen_target_name = "fidlgen_$target_name"
+  action(fidl_gen_target_name) {
+    script = "//build/fuchsia/fidl_gen_cpp"
+
+    library_name_slashes = string_replace(library_name, ".", "/")
+
+    inputs = [
+      invoker.meta,
+    ]
+
+    outputs = [
+      "$target_gen_dir/fuchsia/$library_name_slashes/c/fidl.h",
+      "$target_gen_dir/fuchsia/$library_name_slashes/cpp/fidl.h",
+      "$target_gen_dir/fuchsia/$library_name_slashes/cpp/fidl.cc",
+      "$target_gen_dir/fuchsia/$library_name_slashes/cpp/tables.cc",
+    ]
+
+    args = [
+      "--fidlc-bin",
+        rebase_path("$fuchsia_sdk_path/tools/fidlc"),
+      "--fidlgen-bin",
+        rebase_path("$fuchsia_sdk_path/tools/fidlgen"),
+      "--sdk-base",
+        rebase_path("$fuchsia_sdk_path"),
+      "--root",
+      rebase_path(invoker.meta),
+      "--json",
+      rebase_path("$target_gen_dir/$library_name_json"),
+      "--include-base",
+      rebase_path("$target_gen_dir"),
+      "--output-base-cc",
+      rebase_path("$target_gen_dir/fuchsia/$library_name_slashes/cpp/fidl"),
+      "--output-c-header",
+      rebase_path("$target_gen_dir/fuchsia/$library_name_slashes/c/fidl.h"),
+      "--output-c-tables",
+      rebase_path(
+          "$target_gen_dir/fuchsia/$library_name_slashes/cpp/tables.cc"),
+    ]
+  }
+
+  source_set(target_name) {
+    public_configs = [ ":$config_name" ]
+
+    sources = get_target_outputs(":$fidl_gen_target_name")
+
+    deps = [
+      ":$fidl_gen_target_name",
+    ]
+
+    public_deps = _deps
+  }
+}
+
+#
+# Produce a cc source library from invoker's json file.
+# Primary output is the source_set.
+#
+template("_fuchsia_cc_source_library") {
+  assert(defined(invoker.meta), "The meta.json file path must be specified.")
+
+  meta_json = read_file(invoker.meta, "json")
+
+  assert(meta_json.type == "cc_source_library")
+
+  _output_name = meta_json.name
+  _include_dirs = []
+  _public_headers = []
+  _sources = []
+  _deps = []
+
+  meta_json_include_dir = meta_json.include_dir
+  _include_dirs += [ rebase_path("$fuchsia_sdk_path/$meta_json_include_dir") ]
+
+  foreach(header, meta_json.headers) {
+    rebased_header = []
+    rebased_header = [ rebase_path("$fuchsia_sdk_path/$header") ]
+    _public_headers += rebased_header
+    _sources += rebased_header
+  }
+
+  foreach(source, meta_json.sources) {
+    _sources += [ "$fuchsia_sdk_path/$source" ]
+  }
+
+  config_name = "config_$target_name"
+  config(config_name) {
+    include_dirs = _include_dirs
+  }
+
+  foreach(dep, meta_json.deps) {
+    _deps += [ "../pkg:$dep" ]
+  }
+
+  foreach(dep, meta_json.fidl_deps) {
+    _deps += [ "../fidl:$dep" ]
+  }
+
+  source_set(target_name) {
+    output_name = _output_name
+    public = _public_headers
+    sources = _sources
+    public_configs = [ ":$config_name" ]
+    public_deps = _deps
+  }
+}
+
+template("_fuchsia_cc_prebuilt_library") {
+  assert(defined(invoker.meta), "The meta.json file path must be specified.")
+  meta_json = read_file(invoker.meta, "json")
+
+  _include_dirs = []
+  _deps = []
+  _libs = []
+
+  meta_json_include_dir = meta_json.include_dir
+  _include_dirs += [ "$fuchsia_sdk_path/$meta_json_include_dir" ]
+
+  foreach(dep, meta_json.deps) {
+    _deps += [ ":$dep" ]
+  }
+
+  meta_json_binaries = meta_json.binaries
+  if (target_cpu == "x64") {
+    meta_json_binaries_arch = meta_json_binaries.x64
+  } else {
+    meta_json_binaries_arch = meta_json_binaries.arm64
+  }
+  prebuilt_lib = meta_json_binaries_arch.link
+  _libs = [ "$fuchsia_sdk_path/$prebuilt_lib" ]
+
+  config_name = "config_$target_name"
+  config(config_name) {
+    include_dirs = _include_dirs
+    libs = _libs
+  }
+
+  group(target_name) {
+    public_configs = [ ":$config_name" ]
+    public_deps = _deps
+  }
+}
+
+#
+# Read SDK manifest json file and produce gn build targets for all
+# "enabled_parts" as specified by the template invoker.
+#
+# Fuchsia SDK manifest is primarily a "parts" array.
+#
+template("fuchsia_sdk") {
+  assert(defined(invoker.meta), "The meta.json file path must be specified.")
+  assert(defined(invoker.enabled_parts),
+         "A list containing the parts of the SDK to generate targets for.")
+
+  meta_json = read_file(invoker.meta, "json")
+
+  foreach(part, meta_json.parts) {
+    part_meta_json = {
+    }
+
+    part_meta = part.meta
+    part_meta_rebased = "$fuchsia_sdk_path/$part_meta"
+
+    part_meta_json = read_file(part_meta_rebased, "json")
+    subtarget_name = part_meta_json.name
+
+    foreach(enabled_part, invoker.enabled_parts) {
+      if (part.type == "cc_source_library") {
+        if (part.type == enabled_part) {
+          _fuchsia_cc_source_library(subtarget_name) {
+            meta = part_meta_rebased
+          }
+        }
+      } else if (part.type == "sysroot") {
+        if (part.type == enabled_part) {
+          _fuchsia_sysroot(subtarget_name) {
+            meta = part_meta_rebased
+          }
+        }
+      } else if (part.type == "fidl_library") {
+        if (part.type == enabled_part) {
+          _fuchsia_fidl_library(subtarget_name) {
+            meta = part_meta_rebased
+          }
+        }
+      } else if (part.type == "cc_prebuilt_library") {
+        if (part.type == enabled_part) {
+          _fuchsia_cc_prebuilt_library(subtarget_name) {
+            meta = part_meta_rebased
+          }
+        }
+      }
+    }
+  }
+
+  group(target_name) {
+  }
+}
+
+#
+# Create package in 'gen' directory.
+#
+template("fuchsia_package") {
+  assert(defined(invoker.name), "The name of the package must be specified.")
+  assert(defined(invoker.version), "The package version must be specified.")
+
+  pkg_dir = target_gen_dir
+  pkg_name = invoker.name
+  pkg_version = invoker.version
+  pkg_manifest = invoker.pkg_manifest
+
+  pkg_id_path = "${pkg_dir}/meta/package"
+  gen_far_target_name = "gen_far_${target_name}"
+  pkg_archive = "${pkg_dir}/${pkg_name}-${pkg_version}.far"
+
+  action(gen_far_target_name) {
+    script = "//build/fuchsia/gen_package"
+
+    pm_binary = rebase_path("$fuchsia_sdk_path/tools/pm")
+
+    inputs = [
+      pm_binary,
+    ]
+
+    outputs = [
+      pkg_id_path,
+      pkg_archive,
+    ]
+
+    args = [
+      "--pm-bin",
+      pm_binary,
+      "--pkg-dir",
+      rebase_path(pkg_dir),
+      "--pkg-name",
+      pkg_name,
+      "--pkg-version",
+      "$pkg_version",
+      "--pkg-manifest",
+      rebase_path(pkg_manifest),
+    ]
+
+    if (defined(invoker.deps)) {
+      deps = invoker.deps
+    }
+    if (defined(invoker.testonly)) {
+      testonly = invoker.testonly
+    }
+  }
+
+  copy(target_name) {
+    if (defined(invoker.testonly)) {
+      testonly = invoker.testonly
+    }
+
+    sources = [
+      pkg_archive,
+    ]
+
+    output_name = "${root_out_dir}/far/${pkg_name}.far"
+    outputs = [
+      output_name,
+    ]
+
+    deps = [
+      ":$gen_far_target_name",
+    ]
+  }
+}
+
+#
+# Places repo in output ('obj') directory.
+#
+template("fuchsia_repo") {
+  assert(defined(invoker.archives),
+         "The list of archives to publish must be specified.")
+  assert(defined(invoker.repo), "The location of the repo should be specified.")
+
+  action(target_name) {
+    if (defined(invoker.testonly)) {
+      testonly = invoker.testonly
+    }
+    script = "//build/fuchsia/gen_repo"
+
+    pm_binary = rebase_path("$fuchsia_sdk_path/tools/pm")
+    repo_directory = invoker.repo
+
+    inputs = [
+      pm_binary,
+    ]
+
+    archive_flags = []
+
+    foreach(archive, invoker.archives) {
+      assert(get_path_info(archive, "extension") == "far",
+             "Archive '$archive' does not have the .far extension.")
+      inputs += [ archive ]
+      archive_flags += [
+        "--archive",
+        rebase_path(archive),
+      ]
+    }
+
+    outputs = [
+      repo_directory,
+    ]
+
+    args = [
+             "--pm-bin",
+             pm_binary,
+             "--repo-dir",
+             rebase_path(repo_directory),
+           ] + archive_flags
+
+    if (defined(invoker.deps)) {
+      deps = invoker.deps
+    }
+  }
+}
diff --git a/build/fuchsia/skqp/BUILD.gn b/build/fuchsia/skqp/BUILD.gn
new file mode 100644
index 0000000..3d511fd
--- /dev/null
+++ b/build/fuchsia/skqp/BUILD.gn
@@ -0,0 +1,115 @@
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+
+import ("//build/fuchsia/sdk.gni")
+
+pkg_dir = target_gen_dir
+pkg_name = "skqp_pkg"
+
+fuchsia_package(pkg_name) {
+  testonly = true
+  name = pkg_name
+  version = 0
+  deps = [
+    "//build/fuchsia", "//:skqp",
+  ]
+  pkg_manifest = "${pkg_dir}/${target_name}.manifest"
+}
+
+fuchsia_repo("skqp_repo") {
+  testonly = true
+  deps = [ ":base_manifest", ":append_assets_to_manifest", ":skqp_pkg" ]
+
+  # Put repo directory in out dir (not gen dir).
+  repo = "$target_out_dir/skqp_repo"
+  archives = [ "$root_out_dir/far/skqp_pkg.far" ]
+}
+
+#
+# Base manifest entries (w/o assets) for SKQP.
+#
+group("base_manifest") {
+  base_pkg_manifest = "${pkg_dir}/base_${pkg_name}.manifest"
+
+  skqp_exec = rebase_path("${root_out_dir}/skqp")
+  skqp_path = rebase_path("${target_gen_dir}")
+
+  sdk_dist_path = rebase_path("${fuchsia_sdk_path}/arch/${target_cpu}/dist")
+  sdk_sysroot_dist_path = rebase_path("${fuchsia_sdk_path}/arch/${target_cpu}/sysroot/dist/lib")
+
+  skqp_component_manifest = rebase_path("//build/fuchsia/skqp/skqp.cmx")
+
+  if (target_cpu == "x64" || target_cpu == "x86_64") {
+    clang_path=rebase_path("${fuchsia_toolchain_path}/lib/x86_64-unknown-fuchsia/c++")
+  } else if (target_cpu == "arm64") {
+    clang_path=rebase_path("${fuchsia_toolchain_path}/lib/aarch64-unknown-fuchsia/c++")
+  } else {
+    assert(false, "Unknown target cpu for Fuchsia target.")
+  }
+
+  manifest_entries = [
+    # Binary
+    "bin/skqp=${skqp_exec}",
+
+    # Meta Data
+    "meta/package=${skqp_path}/meta/package",
+    "meta/skqp.cmx=${skqp_component_manifest}",
+
+    # Shared Libs (clang c++)
+    "lib/libc++.so.2=${clang_path}/libc++.so.2",
+    "lib/libc++abi.so.1=${clang_path}/libc++abi.so.1",
+    "lib/libunwind.so.1=${clang_path}/libunwind.so.1",
+
+    # Shared Libs (sdk dist)
+    "lib/libvulkan.so=${sdk_dist_path}/libvulkan.so",
+    "lib/libasync-default.so=${sdk_dist_path}/libasync-default.so",
+    "lib/libfdio.so=${sdk_dist_path}/libfdio.so",
+    "lib/libtrace-engine.so=${sdk_dist_path}/libtrace-engine.so",
+
+    # Loader
+    "lib/ld.so.1=${sdk_sysroot_dist_path}/ld.so.1",
+
+    # Vulkan ICD / Validation / Loader
+    "lib/libvulkan.so=${sdk_dist_path}/libvulkan.so",
+    "lib/libVkLayer_image_pipe_swapchain.so=${sdk_dist_path}/libVkLayer_image_pipe_swapchain.so",
+    "lib/VkLayer_core_validation.so=${sdk_dist_path}/VkLayer_core_validation.so",
+    "lib/VkLayer_khronos_validation.so=${sdk_dist_path}/VkLayer_khronos_validation.so",
+    "lib/VkLayer_thread_safety.so=${sdk_dist_path}/VkLayer_thread_safety.so",
+    "lib/VkLayer_stateless_validation.so=${sdk_dist_path}/VkLayer_stateless_validation.so",
+    "lib/VkLayer_object_lifetimes.so=${sdk_dist_path}/VkLayer_object_lifetimes.so",
+    "lib/VkLayer_unique_objects.so=${sdk_dist_path}/VkLayer_unique_objects.so",
+  ]
+  write_file(base_pkg_manifest, manifest_entries)
+}
+
+#
+# Asset manifest entries for SKQP.
+#
+action("append_assets_to_manifest") {
+  script = "append_assets_to_manifest"
+
+  base_pkg_manifest = "${pkg_dir}/base_${pkg_name}.manifest"
+  pkg_manifest = "${pkg_dir}/${pkg_name}.manifest"
+  assets_path = "//platform_tools/android/apps/skqp/src/main/assets"
+
+  inputs = [
+    assets_path,
+  ]
+
+  outputs = [
+    base_pkg_manifest,
+    pkg_manifest,
+  ]
+
+  args = [
+    "--root_dir",
+    rebase_path(assets_path),
+    "--base_manifest",
+    rebase_path(base_pkg_manifest),
+    "--manifest",
+    rebase_path(pkg_manifest),
+  ]
+}
diff --git a/build/fuchsia/skqp/append_assets_to_manifest b/build/fuchsia/skqp/append_assets_to_manifest
new file mode 100755
index 0000000..2b1d4d4
--- /dev/null
+++ b/build/fuchsia/skqp/append_assets_to_manifest
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+ Opens |base_manifest| and copies the contents to |manifest| then traverses
+ |root_dir| and appends every file as a Fuchsia package manifest entry to
+ |manifest|.
+"""
+
+import argparse
+import os
+import subprocess
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--root_dir', dest='root_dir', action='store', required=True)
+parser.add_argument('--base_manifest', dest='base_manifest', action='store', required=True)
+parser.add_argument('--manifest', dest='manifest', action='store', required=True)
+args = parser.parse_args()
+
+root_dir = args.root_dir
+if not os.path.exists(root_dir):
+    print "--root_dir path specified: " + root_dir + " doesn't exist."
+    exit(1)
+
+base_manifest = args.base_manifest
+if not os.path.exists(base_manifest):
+    print "--base_manifest specified: " + base_manifest + " doesn't exist."
+    exit(1)
+
+manifest = args.manifest
+
+# Prepend |base_manifest| contents to |manifest|.
+out_file = open(manifest, 'w')
+with open(base_manifest, 'r') as in_file:
+    out_file.write(in_file.read())
+
+# Append all files discovered under |root_dir| to |manifest|.
+files = subprocess.check_output(["find", root_dir, "-type", "f"])
+file_lines = files.splitlines()
+
+for file in file_lines:
+        source = file
+        if not source.startswith(root_dir):
+            print "Error: source path " + source + " is not under |root_dir|\n"
+            exit(1)
+        dest = source[len(root_dir):]
+        out_file.write('data%s=' % dest)
+        out_file.write('%s\n' % source)
+
+out_file.close()
diff --git a/build/fuchsia/skqp/skqp.cmx b/build/fuchsia/skqp/skqp.cmx
new file mode 100644
index 0000000..7ec1543
--- /dev/null
+++ b/build/fuchsia/skqp/skqp.cmx
@@ -0,0 +1,20 @@
+{
+    "program": {
+        "binary": "bin/skqp",
+        "args" : [ "/pkg/data", "data/logs" ]
+    },
+    "sandbox": {
+        "dev": [
+            "class/display-controller"
+        ],
+        "features": [
+            "vulkan",
+            "isolated-persistent-storage"
+        ],
+        "services": [
+            "fuchsia.sysmem.Allocator",
+            "fuchsia.tracing.provider.Registry",
+            "fuchsia.vulkan.loader.Loader"
+        ]
+    }
+}
diff --git a/build/fuchsia/sysroot/BUILD.gn b/build/fuchsia/sysroot/BUILD.gn
new file mode 100644
index 0000000..69d93cd
--- /dev/null
+++ b/build/fuchsia/sysroot/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+import("//build/fuchsia/sdk.gni")
+
+fuchsia_sdk("sdk_sysroot") {
+  meta = fuchsia_sdk_manifest_path
+  enabled_parts = [ "sysroot" ]
+}
diff --git a/build/fuchsia/update_fuchsia_sdk b/build/fuchsia/update_fuchsia_sdk
new file mode 100755
index 0000000..065c4b6
--- /dev/null
+++ b/build/fuchsia/update_fuchsia_sdk
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+update_fuchsia_sdk
+
+  Downloads both the Fuchsia SDK and Fuchsia-compatible clang
+  zip archives from chrome infra (CIPD) and extracts them to
+  the arg-provide |sdk_dir| and |clang_dir| respectively.  This
+  provides the complete toolchain required to build Fuchsia binaries
+  from the Fuchsia SDK.
+
+"""
+
+import argparse
+import errno
+import logging
+import os
+import platform
+import shutil
+import subprocess
+import tempfile
+
+def MessageExit(message):
+  logging.error(message)
+  sys.exit(1)
+
+# Verify that "cipd" tool is readily available.
+def CipdLives():
+    err_msg = "Cipd not found, please install. See: " + \
+              "https://commondatastorage.googleapis.com/chrome-infra-docs/flat" + \
+              "/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up"
+    try:
+        subprocess.call(["cipd", "--version"])
+    except OSError as e:
+        if e.errno == errno.ENOENT:
+            MessageExit(err_msg)
+        else:
+            MessageExit("cipd command execution failed.")
+
+# Download and unzip CIPD package archive.
+def DownloadAndUnzip(pkg_name, version, cipd_cache_dir, output_dir):
+  pkg_suffix = pkg_name.replace('/', '-') + ".zip"
+  zip_file = tempfile.NamedTemporaryFile(suffix=pkg_suffix, delete=False)
+  cipd_cmd = "cipd pkg-fetch " + pkg_name + " -version \"" + version + "\" -out " + \
+      zip_file.name + " -cache-dir " + cipd_cache_dir
+  unzip_cmd = "unzip -q " + zip_file.name + " -d " + output_dir
+  os.system(cipd_cmd)
+  os.system(unzip_cmd)
+
+def Main():
+  CipdLives()
+  parser = argparse.ArgumentParser()
+  parser.add_argument("-sdk_dir", type=str,
+          help="Destination directory for the fuchsia SDK.")
+  parser.add_argument("-clang_dir", type=str,
+          help="Destination directory for the fuchsia toolchain.")
+  parser.add_argument("-overwrite_dirs", type=bool, default=False,
+          help="REMOVES existing sdk and clang dirs and makes new ones.  When false " +
+               "  the unzip command issue will require file overwrite confirmation.")
+  parser.add_argument("-cipd_cache_dir", type=str, default="/tmp", required=False,
+          help="Cache directory for CIPD downloads to prevent redundant downloads.")
+  parser.add_argument("-cipd_sdk_version", type=str, default="latest", required=False,
+          help="CIPD sdk version to download, e.g.: git_revision:fce11c6904c888e6d39f71e03806a540852dec41")
+  parser.add_argument("-cipd_clang_version", type=str, default="latest", required=False,
+          help="CIPD clang version to download, e.g.: git_revision:fce11c6904c888e6d39f71e03806a540852dec41")
+  args = parser.parse_args()
+
+  sdk_dir = args.sdk_dir
+  clang_dir = args.clang_dir
+  cipd_sdk_version = args.cipd_sdk_version
+  cipd_clang_version = args.cipd_clang_version
+
+  if args.overwrite_dirs:
+    dirs = [sdk_dir, clang_dir]
+    for curr_dir in dirs:
+      try:
+        if os.path.exists(curr_dir):
+            shutil.rmtree(curr_dir)
+        os.makedirs(curr_dir)
+      except OSError:
+        MessageExit("Creation of the directory %s failed" % curr_dir)
+  else:
+    # Make dirs for sdk and clang.
+    if not os.path.exists(sdk_dir):
+        os.makedirs(sdk_dir)
+    if not os.path.exists(clang_dir):
+        os.makedirs(clang_dir)
+
+    # Verify that existing dirs are writable.
+    if (not os.access(sdk_dir, os.W_OK)) or (not os.path.isdir(sdk_dir)):
+      MessageExit("Can't write to sdk dir " + sdk_dir)
+    if (not os.access(clang_dir, os.W_OK)) or (not os.path.isdir(clang_dir)):
+      MessageExit("Can't write to clang dir " + clang_dir)
+ 
+  ostype = platform.system()
+  if ostype == "Linux":
+    os_string = "linux-amd64"
+  elif ostype == "Darwin":
+    os_string = "mac-amd64"
+  else:
+    MessageExit("Unknown host " + ostype)
+
+  # |sdk_pkg| and |clang_pkg| below are prescribed paths defined by chrome-infra.
+  sdk_pkg = "fuchsia/sdk/core/" + os_string
+  DownloadAndUnzip(sdk_pkg, cipd_sdk_version, args.cipd_cache_dir, sdk_dir)
+  clang_pkg = "fuchsia/clang/" + os_string
+  DownloadAndUnzip(clang_pkg, cipd_clang_version, args.cipd_cache_dir, clang_dir)
+
+if __name__ == "__main__":
+  import sys
+  Main()
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index 1e75813..7bb971f 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -3,6 +3,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+if (is_fuchsia) {
+  import ("//build/fuchsia/sdk.gni")
+}
+
 declare_args() {
   extra_asmflags = []
   extra_cflags = []
@@ -56,6 +60,22 @@
     }
   }
 
+  if (is_fuchsia && using_fuchsia_sdk) {
+    ldflags += [ "-v", "--sysroot=" + rebase_path("$fuchsia_sdk_path/arch/$target_cpu/sysroot") ]
+    cflags += [ "--sysroot=" + rebase_path("$fuchsia_sdk_path/arch/$target_cpu/sysroot") ]
+    if (target_cpu == "x64") {
+      target_triple = "--target=x86_64-${target_os}"
+    } else if (target_cpu == "arm64") {
+      target_triple = "--target=aarch64-unknown-${target_os}"
+    } else {
+      print("Unknown target CPU for Fuchsia target build.")
+      assert(false)
+    }
+    ldflags += [ target_triple ]
+    cflags += [ target_triple ]
+    asmflags += [ target_triple ]
+  }
+
   if (is_win) {
     if (is_clang && target_cpu == "arm64") {
       cflags += [ "--target=arm64-windows" ]
diff --git a/gn/BUILDCONFIG.gn b/gn/BUILDCONFIG.gn
index 0d54914..6ffe637 100644
--- a/gn/BUILDCONFIG.gn
+++ b/gn/BUILDCONFIG.gn
@@ -78,7 +78,7 @@
   current_cpu = target_cpu
 }
 
-is_clang = is_android || is_ios || is_mac ||
+is_clang = is_android || is_ios || is_mac || is_fuchsia ||
            (cc == "clang" && cxx == "clang++") || clang_win != ""
 if (!is_clang && !is_win) {
   is_clang = exec_script("//gn/is_clang.py",
diff --git a/gn/gm.gni b/gn/gm.gni
index b8b1f19..8607479 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -25,7 +25,6 @@
   "$_gm/arcto.cpp",
   "$_gm/arithmode.cpp",
   "$_gm/asyncrescaleandread.cpp",
-  "$_gm/atlastext.cpp",
   "$_gm/b_119394958.cpp",
   "$_gm/backdrop.cpp",
   "$_gm/backdrop_imagefilter_croprect.cpp",
@@ -291,7 +290,6 @@
   "$_gm/rasterhandleallocator.cpp",
   "$_gm/readpixels.cpp",
   "$_gm/recordopts.cpp",
-  "$_gm/rectangletexture.cpp",
   "$_gm/rects.cpp",
   "$_gm/repeated_bitmap.cpp",
   "$_gm/resizeimagefilter.cpp",
@@ -383,3 +381,9 @@
   "$_gm/xfermodes3.cpp",
   "$_gm/yuvtorgbeffect.cpp",
 ]
+
+gl_gm_sources = [
+  "$_gm/atlastext.cpp",
+  "$_gm/rectangletexture.cpp",
+]
+
diff --git a/gn/skia.gni b/gn/skia.gni
index 3e0c07c..9cb3bc3 100644
--- a/gn/skia.gni
+++ b/gn/skia.gni
@@ -5,12 +5,8 @@
 if (!defined(is_skia_standalone)) {
   is_skia_standalone = false
 }
-is_skia_dev_build = is_skia_standalone && !is_official_build
 
-if (is_fuchsia) {
-  import("//build/fuchsia/sdk.gni")
-  import("//build/vulkan/config.gni")
-}
+is_skia_dev_build = is_skia_standalone && !is_official_build
 
 declare_args() {
   skia_android_serial = ""
@@ -37,6 +33,7 @@
   skia_qt_path = getenv("QT_PATH")
   skia_skqp_global_error_tolerance = 0
   skia_tools_require_resources = false
+  skia_update_fuchsia_sdk = false
   skia_use_angle = false
   skia_use_dawn = false
   skia_use_egl = false
@@ -47,7 +44,6 @@
   skia_use_fontconfig = is_linux
   skia_use_fonthost_mac = is_mac
   skia_use_freetype = is_android || is_fuchsia || is_linux
-  skia_use_gl = !is_fuchsia
   skia_use_harfbuzz = true
   skia_use_gl = !is_fuchsia
   skia_use_icu = !is_fuchsia && !is_ios
@@ -80,7 +76,7 @@
   if (is_android) {
     skia_use_vulkan = defined(ndk_api) && ndk_api >= 24
   } else if (is_fuchsia) {
-    skia_use_vulkan = fuchsia_use_vulkan
+    skia_use_vulkan = true
   } else {
     skia_use_vulkan = defined(skia_moltenvk_path) && skia_moltenvk_path != ""
   }
diff --git a/gn/tests.gni b/gn/tests.gni
index f488058..1741549 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -15,7 +15,6 @@
   "$_tests/ApplyGammaTest.cpp",
   "$_tests/ArenaAllocTest.cpp",
   "$_tests/AsADashTest.cpp",
-  "$_tests/BackendAllocationTest.cpp",
   "$_tests/BadIcoTest.cpp",
   "$_tests/BitSetTest.cpp",
   "$_tests/BitmapCopyTest.cpp",
@@ -66,7 +65,6 @@
   "$_tests/DrawPathTest.cpp",
   "$_tests/DrawTextTest.cpp",
   "$_tests/DynamicHashTest.cpp",
-  "$_tests/EGLImageTest.cpp",
   "$_tests/EmptyPathTest.cpp",
   "$_tests/EncodeTest.cpp",
   "$_tests/EncodedInfoTest.cpp",
@@ -101,7 +99,6 @@
   "$_tests/GrContextAbandonTest.cpp",
   "$_tests/GrContextFactoryTest.cpp",
   "$_tests/GrFinishedFlushTest.cpp",
-  "$_tests/GrGLExtensionsTest.cpp",
   "$_tests/GrMemoryPoolTest.cpp",
   "$_tests/GrMeshTest.cpp",
   "$_tests/GrMipMappedTest.cpp",
@@ -196,7 +193,6 @@
   "$_tests/PromiseImageTest.cpp",
   "$_tests/ProxyConversionTest.cpp",
   "$_tests/ProxyRefTest.cpp",
-  "$_tests/ProxyTest.cpp",
   "$_tests/QuickRejectTest.cpp",
   "$_tests/RRectInPathTest.cpp",
   "$_tests/RTreeTest.cpp",
@@ -212,13 +208,11 @@
   "$_tests/RecorderTest.cpp",
   "$_tests/RecordingXfermodeTest.cpp",
   "$_tests/RectTest.cpp",
-  "$_tests/RectangleTextureTest.cpp",
   "$_tests/RefCntTest.cpp",
   "$_tests/RegionTest.cpp",
   "$_tests/RenderTargetContextTest.cpp",
   "$_tests/RepeatedClippedBlurTest.cpp",
   "$_tests/ResourceAllocatorTest.cpp",
-  "$_tests/ResourceCacheTest.cpp",
   "$_tests/RoundRectTest.cpp",
   "$_tests/SRGBReadWritePixelsTest.cpp",
   "$_tests/SRGBTest.cpp",
@@ -273,7 +267,6 @@
   "$_tests/StrokerTest.cpp",
   "$_tests/SubsetPath.cpp",
   "$_tests/SubsetPath.h",
-  "$_tests/SurfaceSemaphoreTest.cpp",
   "$_tests/SurfaceTest.cpp",
   "$_tests/SwizzlerTest.cpp",
   "$_tests/TArrayTest.cpp",
@@ -288,12 +281,10 @@
   "$_tests/TestUtils.h",
   "$_tests/TextBlobCacheTest.cpp",
   "$_tests/TextBlobTest.cpp",
-  "$_tests/TextureBindingsResetTest.cpp",
   "$_tests/TextureProxyTest.cpp",
   "$_tests/TextureStripAtlasManagerTest.cpp",
   "$_tests/Time.cpp",
   "$_tests/TopoSortTest.cpp",
-  "$_tests/TraceMemoryDumpTest.cpp",
   "$_tests/TracingTest.cpp",
   "$_tests/TransferPixelsTest.cpp",
   "$_tests/TypefaceTest.cpp",
@@ -315,6 +306,18 @@
   "$_tests/YUVTest.cpp",
 ]
 
+gl_tests_sources = [
+  "$_tests/BackendAllocationTest.cpp",
+  "$_tests/EGLImageTest.cpp",
+  "$_tests/GrGLExtensionsTest.cpp",
+  "$_tests/ProxyTest.cpp",
+  "$_tests/RectangleTextureTest.cpp",
+  "$_tests/ResourceCacheTest.cpp",
+  "$_tests/SurfaceSemaphoreTest.cpp",
+  "$_tests/TextureBindingsResetTest.cpp",
+  "$_tests/TraceMemoryDumpTest.cpp",
+]
+
 metal_tests_sources = [
   "$_tests/MtlBackendAllocationTest.mm",
   "$_tests/MtlCopySurfaceTest.mm",
diff --git a/gn/toolchain/BUILD.gn b/gn/toolchain/BUILD.gn
index 97eaf20..70caeb6 100644
--- a/gn/toolchain/BUILD.gn
+++ b/gn/toolchain/BUILD.gn
@@ -1,3 +1,7 @@
+if (is_fuchsia) {
+  import ("//build/fuchsia/sdk.gni")
+}
+
 declare_args() {
   host_ar = ar
   host_cc = cc
@@ -15,6 +19,12 @@
       target_cc = "$_prefix/$ndk_target$ndk_api-clang"
       target_cxx = "$_prefix/$ndk_target$ndk_api-clang++"
     }
+  } else if (is_fuchsia && using_fuchsia_sdk) {
+    target_ar = rebase_path("$fuchsia_toolchain_path/bin/llvm-ar")
+    target_cc = rebase_path("$fuchsia_toolchain_path/bin/clang")
+    target_cxx = rebase_path("$fuchsia_toolchain_path/bin/clang++")
+    cflags = "--sysroot=" + rebase_path("$fuchsia_toolchain_path/$target_cpu/sysroot")
+    link = rebase_path("$fuchsia_toolchain_path/bin/ld.lld")
   } else {
     target_ar = ar
     target_cc = cc
@@ -294,7 +304,7 @@
       # LLD doesn't need these flags, but accepts and ignores them.
       _start_group = "-Wl,--start-group"
       _end_group = "-Wl,--end-group"
-      if (is_mac || is_ios) {
+      if (is_mac || is_ios || is_fuchsia) {
         _start_group = ""
         _end_group = ""
       }
@@ -321,7 +331,7 @@
       # LLD doesn't need these flags, but accepts and ignores them.
       _start_group = "-Wl,--start-group"
       _end_group = "-Wl,--end-group"
-      if (is_mac || is_ios) {
+      if (is_mac || is_ios || is_fuchsia) {
         _start_group = ""
         _end_group = ""
       }
diff --git a/infra/bots/compile.isolate b/infra/bots/compile.isolate
index 85accb8..a21c4ad 100644
--- a/infra/bots/compile.isolate
+++ b/infra/bots/compile.isolate
@@ -12,6 +12,7 @@
       '../../bench',
       '../../bin/fetch-clang-format',
       '../../bin/fetch-gn',
+      '../../build/fuchsia',
       '../../build_overrides/dawn.gni',
       '../../buildtools',
       '../../dm',
diff --git a/src/gpu/GrPathProcessor.cpp b/src/gpu/GrPathProcessor.cpp
index 861ff1b..8524b72 100644
--- a/src/gpu/GrPathProcessor.cpp
+++ b/src/gpu/GrPathProcessor.cpp
@@ -10,7 +10,9 @@
 #include "include/private/SkTo.h"
 #include "src/gpu/GrShaderCaps.h"
 #include "src/gpu/gl/GrGLGpu.h"
+#ifdef SK_GL
 #include "src/gpu/gl/GrGLVaryingHandler.h"
+#endif
 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLPrimitiveProcessor.h"
 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
@@ -65,10 +67,11 @@
             SkString strVaryingName;
             strVaryingName.printf("TransformedCoord_%d", i);
             GrGLSLVarying v(varyingType);
+#ifdef SK_GL
             GrGLVaryingHandler* glVaryingHandler = (GrGLVaryingHandler*) varyingHandler;
             fInstalledTransforms.push_back().fHandle =
-                    glVaryingHandler->addPathProcessingVarying(strVaryingName.c_str(),
-                                                               &v).toIndex();
+                    glVaryingHandler->addPathProcessingVarying(strVaryingName.c_str(), &v).toIndex();
+#endif
             fInstalledTransforms.back().fType = varyingType;
 
             transformHandler->specifyCoordsForCurrCoordTransform(
diff --git a/tests/DeferredDisplayListTest.cpp b/tests/DeferredDisplayListTest.cpp
index ec7048d..d31fcb6 100644
--- a/tests/DeferredDisplayListTest.cpp
+++ b/tests/DeferredDisplayListTest.cpp
@@ -188,6 +188,7 @@
                                         ? GrMipMapped::kNo
                                         : GrMipMapped(fShouldCreateMipMaps);
 
+#ifdef SK_GL
         if (fUsesGLFBO0) {
             if (GrBackendApi::kOpenGL != context->backend()) {
                 return nullptr;
@@ -210,6 +211,7 @@
             SkASSERT(result->isCompatible(c));
             return result;
         }
+#endif
 
         *backend = context->createBackendTexture(fWidth, fHeight, fColorType,
                                                  SkColors::kTransparent,
diff --git a/third_party/freetype2/BUILD.gn b/third_party/freetype2/BUILD.gn
index b76c2df..426a3fa 100644
--- a/third_party/freetype2/BUILD.gn
+++ b/third_party/freetype2/BUILD.gn
@@ -6,7 +6,7 @@
 declare_args() {
   # TODO: build from source all the time for testing?
   skia_use_system_freetype2 =
-      is_official_build || !(is_android || sanitize == "MSAN")
+      (is_official_build || !(is_android || sanitize == "MSAN")) && !is_fuchsia
 }
 
 import("../third_party.gni")
diff --git a/tools/CrashHandler.cpp b/tools/CrashHandler.cpp
index 5a2186f..c769ebc 100644
--- a/tools/CrashHandler.cpp
+++ b/tools/CrashHandler.cpp
@@ -54,10 +54,48 @@
         // both 32 and 64 bit on bots.  Doesn't matter much: catchsegv is best anyway.
         #include <cxxabi.h>
         #include <dlfcn.h>
-        #include <execinfo.h>
         #include <string.h>
+#if defined(__Fuchsia__)
+        #include <stdint.h>
+
+        // syslog crash reporting from Fuchsia's backtrace_request.h
+        //
+        // Special value we put in the first register to let the exception handler know
+        // that we are just requesting a backtrace and we should resume the thread.
+        #define BACKTRACE_REQUEST_MAGIC ((uint64_t)0xee726573756d65ee)
+
+        // Prints a backtrace, resuming the thread without killing the process.
+        __attribute__((always_inline)) static inline void backtrace_request(void) {
+          // Two instructions: one that sets a software breakpoint ("int3" on x64,
+          // "brk" on arm64) and one that writes the "magic" value in the first
+          // register ("a" on x64, "x0" on arm64).
+          //
+          // We set a software breakpoint to trigger the exception handling in
+          // crashsvc, which will print the debug info, including the backtrace.
+          //
+          // We write the "magic" value in the first register so that the exception
+          // handler can check for it and resume the thread if present.
+          #ifdef __x86_64__
+            __asm__("int3" : : "a"(BACKTRACE_REQUEST_MAGIC));
+          #endif
+          #ifdef __aarch64__
+            // This is what gdb uses.
+            __asm__(
+                "mov x0, %0\n"
+                "\tbrk 0"
+                :
+                : "r"(BACKTRACE_REQUEST_MAGIC)
+                : "x0");
+          #endif
+        }
+#else
+        #include <execinfo.h>
+#endif
 
         static void handler(int sig) {
+#if defined(__Fuchsia__)
+            backtrace_request();
+#else
             void* stack[64];
             const int count = backtrace(stack, SK_ARRAY_COUNT(stack));
             char** symbols = backtrace_symbols(stack, count);
@@ -78,11 +116,11 @@
                 }
                 SkDebugf("    %s\n", symbols[i]);
             }
-
-            // Exit NOW.  Don't notify other threads, don't call anything registered with atexit().
+#endif
+            // Exit NOW.  Don't notify other threads, don't call anything registered with
+            // atexit().
             _Exit(sig);
         }
-
     #endif
 
     #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)
diff --git a/tools/ProcStats.cpp b/tools/ProcStats.cpp
index b9eae31..5c7bb46 100644
--- a/tools/ProcStats.cpp
+++ b/tools/ProcStats.cpp
@@ -8,7 +8,23 @@
 #include "include/core/SkTypes.h"
 #include "tools/ProcStats.h"
 
-#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) || defined(SK_BUILD_FOR_ANDROID)
+#if defined(__Fuchsia__)
+    #include <zircon/process.h>
+    #include <zircon/syscalls.h>
+    #include <zircon/syscalls/object.h>
+    #include <zircon/types.h>
+
+    int sk_tools::getMaxResidentSetSizeMB() {
+      zx_info_task_stats_t task_stats;
+      zx_handle_t process = zx_process_self();
+      zx_status_t status = zx_object_get_info(
+      process, ZX_INFO_TASK_STATS, &task_stats, sizeof(task_stats), NULL, NULL);
+      if (status != ZX_OK) {
+        return -1;
+      }
+      return (task_stats.mem_private_bytes + task_stats.mem_shared_bytes) / (1 << 20);
+    }
+#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) || defined(SK_BUILD_FOR_ANDROID)
     #include <sys/resource.h>
     int sk_tools::getMaxResidentSetSizeMB() {
         struct rusage ru;
diff --git a/tools/gpu/vk/GrVulkanDefines.h b/tools/gpu/vk/GrVulkanDefines.h
index ad65a84..739bdc7 100644
--- a/tools/gpu/vk/GrVulkanDefines.h
+++ b/tools/gpu/vk/GrVulkanDefines.h
@@ -19,7 +19,7 @@
 #      define VK_USE_PLATFORM_ANDROID_KHR
 #   endif
 #elif defined(SK_BUILD_FOR_UNIX)
-#   if !defined(VK_USE_PLATFORM_XCB_KHR)
+#   if !defined(__Fuchsia__) && !defined(VK_USE_PLATFORM_XCB_KHR)
 #      define VK_USE_PLATFORM_XCB_KHR
 #   endif
 #elif defined(SK_BUILD_FOR_MAC)