build regenerating SkJumper stages into GN

I _think_ this makes it so changes to _stages.cpp or _lowp.cpp get
noticed, regenerated, and baked into Skia all in the same Ninja
invocation.

Now you just need to set up the tools we use in GN:

  skia_jumper_clang = ...
  skia_jumper_objdump = ...
  skia_jumper_ccache = ...

Change-Id: I09fb54d965644ff6e5825056fb0be2c7cab2ea92
Reviewed-on: https://skia-review.googlesource.com/21140
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Mike Klein <mtklein@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 9ba71b2..0401d08 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -41,6 +41,10 @@
   skia_enable_tools = is_skia_dev_build
   skia_enable_vulkan_debug_layers = is_skia_dev_build && is_debug
   skia_vulkan_sdk = getenv("VULKAN_SDK")
+
+  skia_jumper_clang = ""
+  skia_jumper_objdump = ""
+  skia_jumper_ccache = ""
 }
 declare_args() {
   skia_use_dng_sdk = !is_fuchsia && skia_use_libjpeg_turbo && skia_use_zlib
@@ -1542,3 +1546,28 @@
     }
   }
 }
+
+if (skia_jumper_clang != "") {
+  action("regen_jumper") {
+    script = "src/jumper/build_stages.py"
+
+    inputs = [
+      "src/jumper/SkJumper_stages.cpp",
+      "src/jumper/SkJumper_stages_lowp.cpp",
+    ]
+
+    # GN insists its outputs should go somewhere underneath target_out_dir, so we trick it.
+    outputs = [
+      "$target_out_dir/" +
+          rebase_path("src/jumper/SkJumper_generated.S", target_out_dir),
+      "$target_out_dir/" +
+          rebase_path("src/jumper/SkJumper_generated_win.S", target_out_dir),
+    ]
+
+    args = [
+             skia_jumper_clang,
+             skia_jumper_objdump,
+             skia_jumper_ccache,
+           ] + rebase_path(inputs) + rebase_path(outputs)
+  }
+}
diff --git a/site/dev/contrib/jumper.md b/site/dev/contrib/jumper.md
index 520876c..757697f 100644
--- a/site/dev/contrib/jumper.md
+++ b/site/dev/contrib/jumper.md
@@ -8,19 +8,16 @@
 (This is where I'd put my link to design document if I had one...)
 
 SkJumper is more annoying to contribute to than most Skia code because of its
-offline compilation step.  `src/jumper/build_stages.py` compiles
-`src/jumper/SkJumper_stages.cpp` several different ways and parses the object
-files it generates into `src/jumper/SkJumper_generated.S` and
-`src/jumper/SkJumper_generated_win.S`.  This document is designed to guide you
+offline compilation step.  You'll need particular tools installed on your
+machine and to tell GN about them.  This document is designed to guide you
 through this process and ease some of that annoyance.
 
 One-time Setup
 --------------
 
-To run `build_stages.py` you need Clang 4.0 and objdump, and probably want
-ccache.  It's best that Clang is exactly the same version we typically use (as
-of writing 4.0.0) and you'll need objdump to be compiled with support for
-x86-64, ARMv7, and ARMv8.
+To generate stage code you need Clang 4.0, objdump, and ccache.  It's best that
+Clang is exactly the same version we typically use (as of writing 4.0.0) and
+you'll need objdump to be compiled with support for x86-64, ARMv7, and ARMv8.
 
 The easiest way to satisfy these contraints is to get your hands on a Mac and
 install [Homebrew](https://brew.sh).  Once you have `brew` installed, run this
@@ -30,28 +27,22 @@
 
     brew install llvm binutils ccache
 
-Running `build_stages.py`
+Setting up GN
 -------------------------
 
-With your tools installed, try a no-op run of `build_stages.py`:
+With your tools installed, tell GN about them
 
-<!--?prettify lang=sh?-->
+    skia_jumper_clang = path/to/clang-4.0
+    skia_jumper_objdump = path/to/gobjdump
+    skia_jumper_ccache = path/to/ccache
 
-    python src/jumper/build_stages.py path/to/clang-4.0 path/to/gobjdump path/to/ccache
-    git status
+then regenerate and build as normal.
 
-When you run `git status` you should see a bunch of untracked `.o` files
-sitting in skia/, and no changes to `src/jumper/SkJumper_generated*.S`.
+If you look in your GN out directory, you should now see a bunch of `.o` files,
+and `git status` should show no changes to `src/jumper/SkJumper_generated*.S`.
 That's good.  Those object files are the intermediates we parse to produce
 the assembly files.  We just leave them around in case you want to look at
-them yourself.  If you don't like them, it's safe to just
-
-<!--?prettify lang=sh?-->
-
-    rm *.o
-
-If `clang-4.0`, `gobjdump`, and `ccache` are on your path, `build_stages.py`
-should find them without you needing to pass them on the command line.
+them yourself.
 
 Make A Change
 -------------
@@ -65,7 +56,9 @@
 <!--?prettify lang=cc?-->
 
     STAGE(from_srgb) {
-        ...
+        r = from_srgb(r);
+        g = from_srgb(g);
+        b = from_srgb(b);
     }
 
 Let's replace whatever's there with our fast approximation:
@@ -78,7 +71,7 @@
         b *= b;
     }
 
-When you save and re-run `build_stages.py`, you should now see changes to
+When you save and re-Ninja, you should now see changes to
 `src/jumper/SkJumper_generated.S` and `src/jumper/SkJumper_generated_win.S`.
 If you can't read assembly, no big deal.  If you can, run `git diff`.  You
 should see the various `sk_from_srgb_*` functions get dramatically simpler,
@@ -94,17 +87,14 @@
 any of this worries you, please do go running to someone who knows more for
 help, but odds are everything is fine.
 
-At this point you can re-build Skia, run DM, compare images, etc. as normal.
-Any time you change `SkJumper_stages.cpp`, you need to re-run `build_stages.py`
-for those changes to take effect.  Believe me, I'd bake this into our GN build
-if I could figure out a way.
+At this point things should just be business as usual.  Any time you change
+`SkJumper_stages.cpp`, Ninja ought to notice and regenerate the assembly files.
 
 Adding a new Stage
 ------------------
 
 Adding a new stage is a lot like changing an existing stage.  Edit
-`SkJumper_stages.cpp`, run `build_stages.py`, build Skia, test, repeat until
-correct.
+`SkJumper_stages.cpp`, build Skia, test, repeat until correct.
 
 You'll just need to also edit `SkRasterPipeline.h` to add your new stage to the
 macro listing all the stages.  The stage name is the handle normal Skia code
diff --git a/src/jumper/build_stages.py b/src/jumper/build_stages.py
index 6678020..c1a22b0 100755
--- a/src/jumper/build_stages.py
+++ b/src/jumper/build_stages.py
@@ -9,9 +9,21 @@
 import subprocess
 import sys
 
-clang   = sys.argv[1] if len(sys.argv) > 1 else 'clang-4.0'
-objdump = sys.argv[2] if len(sys.argv) > 2 else 'gobjdump'
-ccache  = sys.argv[3] if len(sys.argv) > 3 else 'ccache'
+clang         = 'clang-4.0'
+objdump       = 'gobjdump'
+ccache        = 'ccache'
+stages        = 'src/jumper/SkJumper_stages.cpp'
+stages_lowp   = 'src/jumper/SkJumper_stages_lowp.cpp'
+generated     = 'src/jumper/SkJumper_generated.S'
+generated_win = 'src/jumper/SkJumper_generated_win.S'
+
+clang         = sys.argv[1] if len(sys.argv) > 1 else clang
+objdump       = sys.argv[2] if len(sys.argv) > 2 else objdump
+ccache        = sys.argv[3] if len(sys.argv) > 3 else ccache
+stages        = sys.argv[4] if len(sys.argv) > 4 else stages
+stages_lowp   = sys.argv[5] if len(sys.argv) > 5 else stages_lowp
+generated     = sys.argv[6] if len(sys.argv) > 6 else generated
+generated_win = sys.argv[7] if len(sys.argv) > 7 else generated_win
 
 clang = [ccache, clang, '-x', 'c++']
 
@@ -24,56 +36,56 @@
 win = ['-DWIN', '-mno-red-zone']
 sse2 = ['-msse2', '-mno-sse3', '-mno-ssse3', '-mno-sse4.1']
 subprocess.check_call(clang + cflags + sse2 +
-                      ['-c', 'src/jumper/SkJumper_stages.cpp'] +
+                      ['-c', stages] +
                       ['-o', 'sse2.o'])
 subprocess.check_call(clang + cflags + sse2 + win +
-                      ['-c', 'src/jumper/SkJumper_stages.cpp'] +
+                      ['-c', stages] +
                       ['-o', 'win_sse2.o'])
 subprocess.check_call(clang + cflags + sse2 + x86 +
-                      ['-c', 'src/jumper/SkJumper_stages.cpp'] +
+                      ['-c', stages] +
                       ['-o', 'x86_sse2.o'])
 
 ssse3 = ['-mssse3', '-mno-sse4.1']
 subprocess.check_call(clang + cflags + ssse3 +
-                      ['-c', 'src/jumper/SkJumper_stages_lowp.cpp'] +
+                      ['-c', stages_lowp] +
                       ['-o', 'lowp_ssse3.o'])
 subprocess.check_call(clang + cflags + ssse3 + win +
-                      ['-c', 'src/jumper/SkJumper_stages_lowp.cpp'] +
+                      ['-c', stages_lowp] +
                       ['-o', 'win_lowp_ssse3.o'])
 
 sse41 = ['-msse4.1']
 subprocess.check_call(clang + cflags + sse41 +
-                      ['-c', 'src/jumper/SkJumper_stages.cpp'] +
+                      ['-c', stages] +
                       ['-o', 'sse41.o'])
 subprocess.check_call(clang + cflags + sse41 + win +
-                      ['-c', 'src/jumper/SkJumper_stages.cpp'] +
+                      ['-c', stages] +
                       ['-o', 'win_sse41.o'])
 
 avx = ['-mavx']
 subprocess.check_call(clang + cflags + avx +
-                      ['-c', 'src/jumper/SkJumper_stages.cpp'] +
+                      ['-c', stages] +
                       ['-o', 'avx.o'])
 subprocess.check_call(clang + cflags + avx + win +
-                      ['-c', 'src/jumper/SkJumper_stages.cpp'] +
+                      ['-c', stages] +
                       ['-o', 'win_avx.o'])
 
 hsw = ['-mavx2', '-mfma', '-mf16c']
 subprocess.check_call(clang + cflags + hsw +
-                      ['-c', 'src/jumper/SkJumper_stages.cpp'] +
+                      ['-c', stages] +
                       ['-o', 'hsw.o'])
 subprocess.check_call(clang + cflags + hsw + win +
-                      ['-c', 'src/jumper/SkJumper_stages.cpp'] +
+                      ['-c', stages] +
                       ['-o', 'win_hsw.o'])
 subprocess.check_call(clang + cflags + hsw +
-                      ['-c', 'src/jumper/SkJumper_stages_lowp.cpp'] +
+                      ['-c', stages_lowp] +
                       ['-o', 'lowp_hsw.o'])
 subprocess.check_call(clang + cflags + hsw + win +
-                      ['-c', 'src/jumper/SkJumper_stages_lowp.cpp'] +
+                      ['-c', stages_lowp] +
                       ['-o', 'win_lowp_hsw.o'])
 
 aarch64 = [ '--target=aarch64' ]
 subprocess.check_call(clang + cflags + aarch64 +
-                      ['-c', 'src/jumper/SkJumper_stages.cpp'] +
+                      ['-c', stages] +
                       ['-o', 'aarch64.o'])
 
 vfp4 = [
@@ -81,7 +93,7 @@
     '-mfpu=neon-vfpv4',
 ]
 subprocess.check_call(clang + cflags + vfp4 +
-                      ['-c', 'src/jumper/SkJumper_stages.cpp'] +
+                      ['-c', stages] +
                       ['-o', 'vfp4.o'])
 
 def parse_object_file(dot_o, directive, target=None):
@@ -162,7 +174,7 @@
     print '  ' + directive + '  ' + hexed + ' '*(36-len(hexed)) + \
           comment + inst + (' '*(14-len(inst)) + args if args else '')
 
-sys.stdout = open('src/jumper/SkJumper_generated.S', 'w')
+sys.stdout = open(generated, 'w')
 
 print '''# Copyright 2017 Google Inc.
 #
@@ -216,7 +228,7 @@
 
 print '#endif'
 
-sys.stdout = open('src/jumper/SkJumper_generated_win.S', 'w')
+sys.stdout = open(generated_win, 'w')
 print '''; Copyright 2017 Google Inc.
 ;
 ; Use of this source code is governed by a BSD-style license that can be