Fix shell_apptest for android.

This CL introduces mojo/tools/embed/rules.gni that allows to embed a
file in the data section of an object file. It refactors icu_data to use
this, and uses it in shell_apptest to bundle the ping application.

R=abarth@chromium.org, davemoore@chromium.org

Review URL: https://codereview.chromium.org/979043003
diff --git a/mojo/tools/embed/data.h b/mojo/tools/embed/data.h
new file mode 100644
index 0000000..50bf074
--- /dev/null
+++ b/mojo/tools/embed/data.h
@@ -0,0 +1,22 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_TOOLS_EMBED_DATA_H_
+#define MOJO_TOOLS_EMBED_DATA_H_
+
+#include <stddef.h>  // For size_t.
+
+namespace mojo {
+namespace embed {
+
+struct Data {
+  const char* const hash;
+  const char* const data;
+  const size_t size;
+};
+
+}  // namespace embed
+}  // namespace mojo
+
+#endif  // MOJO_TOOLS_EMBED_DATA_H_
diff --git a/mojo/tools/embed/embed_data.py b/mojo/tools/embed/embed_data.py
new file mode 100755
index 0000000..f34348a
--- /dev/null
+++ b/mojo/tools/embed/embed_data.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import hashlib
+import os
+import sys
+
+def main():
+  """Command line utility to embed file in a C executable."""
+  parser = argparse.ArgumentParser(
+      description='Generate a source file to embed the content of a file')
+
+  parser.add_argument('source')
+  parser.add_argument('out_dir')
+  parser.add_argument('namespace')
+  parser.add_argument('variable')
+
+  opts = parser.parse_args()
+
+  if not os.path.exists(opts.out_dir):
+    os.makedirs(opts.out_dir)
+
+  header = os.path.join(opts.out_dir, '%s.h' % opts.variable)
+  c_file = os.path.join(opts.out_dir, '%s.cc' % opts.variable)
+  namespaces = opts.namespace.split('::')
+
+  data = None
+  with open(opts.source, "rb") as f:
+    data = f.read()
+
+  with open(header, "w") as f:
+    f.write('// Generated file. Do not modify.\n')
+    f.write('\n')
+    f.write('#include "mojo/tools/embed/data.h"\n')
+    f.write('\n')
+    for n in namespaces:
+      f.write('namespace %s {\n' % n)
+    f.write('extern const mojo::embed::Data %s;\n' % opts.variable);
+    for n in reversed(namespaces):
+      f.write('}  // namespace %s\n' % n)
+
+  sha1hash = hashlib.sha1(data).hexdigest()
+  values = ["0x%02x" % ord(c) for c in data]
+  lines = []
+  chunk_size = 16
+  for i in range(0, len(values), chunk_size):
+    lines.append("  " + ", ".join(values[i: i + chunk_size]))
+
+  with open(c_file, "w") as f:
+    f.write('// Generated file. Do not modify.\n')
+    f.write('\n')
+    f.write('#include "mojo/tools/embed/data.h"\n')
+    f.write('\n')
+    for n in namespaces:
+      f.write('namespace %s {\n' % n)
+    f.write('namespace {\n')
+    f.write("const char data[%d] = {\n" % len(data))
+    f.write(",\n".join(lines))
+    f.write("\n};\n")
+    f.write('}  // namespace\n')
+    f.write('\n')
+    f.write('extern const mojo::embed::Data %s;\n' % opts.variable);
+    f.write('const mojo::embed::Data %s = {\n' % opts.variable);
+    f.write('  "%s",\n' % sha1hash)
+    f.write('  data,\n')
+    f.write('  sizeof(data)\n')
+    f.write('};\n');
+    f.write('\n')
+    for n in reversed(namespaces):
+      f.write('}  // namespace %s\n' % n)
+
+if __name__ == '__main__':
+  main()
diff --git a/mojo/tools/embed/rules.gni b/mojo/tools/embed/rules.gni
new file mode 100644
index 0000000..6171812
--- /dev/null
+++ b/mojo/tools/embed/rules.gni
@@ -0,0 +1,49 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+template("embed_file") {
+  assert(defined(invoker.variable))
+  assert(defined(invoker.source))
+  assert(defined(invoker.namespace))
+
+  variable = invoker.variable
+  generator_target_name = "__${target_name}_generator"
+  generator_outputs = [
+    "${target_gen_dir}/${variable}.cc",
+    "${target_gen_dir}/${variable}.h",
+  ]
+
+  action(generator_target_name) {
+    script = "//mojo/tools/embed/embed_data.py"
+    sources = [
+      invoker.source,
+    ]
+    outputs = generator_outputs
+    args = [
+      rebase_path(invoker.source),
+      rebase_path(target_gen_dir),
+      invoker.namespace,
+      variable,
+    ]
+
+    if (defined(invoker.testonly)) {
+      testonly = invoker.testonly
+    }
+    if (defined(invoker.deps)) {
+      deps = invoker.deps
+    }
+  }
+
+  source_set(target_name) {
+    sources = generator_outputs
+
+    if (defined(invoker.testonly)) {
+      testonly = invoker.testonly
+    }
+
+    deps = [
+      ":${generator_target_name}",
+    ]
+  }
+}
diff --git a/services/icu_data/BUILD.gn b/services/icu_data/BUILD.gn
index 98b5a56..069e451 100644
--- a/services/icu_data/BUILD.gn
+++ b/services/icu_data/BUILD.gn
@@ -4,19 +4,12 @@
 
 import("//mojo/public/mojo_application.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
+import("//mojo/tools/embed/rules.gni")
 
-action("embed_icu_data") {
-  script = "embed_icu_data.py"
-
-  inputs = [
-    "$root_build_dir/icudtl.dat",
-  ]
-
-  outputs = [
-    "$root_gen_dir/mojo/icu_data/data.cc",
-  ]
-
-  args = rebase_path(inputs + outputs, root_build_dir)
+embed_file("embed_icu_data") {
+  source = "$root_build_dir/icudtl.dat"
+  namespace = "icu_data"
+  variable = "kICUData"
 
   deps = [
     "//third_party/icu:icudata",
@@ -26,8 +19,6 @@
 mojo_native_application("icu_data") {
   sources = [
     "icu_data_impl.cc",
-    "data.h",
-    "$root_gen_dir/mojo/icu_data/data.cc",
   ]
 
   deps = [
diff --git a/services/icu_data/data.h b/services/icu_data/data.h
deleted file mode 100644
index 49097e5..0000000
--- a/services/icu_data/data.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_ICU_DATA_DATA_H_
-#define SERVICES_ICU_DATA_DATA_H_
-
-#include <cstddef>
-
-namespace icu_data {
-
-extern const char kICUDataTableHash[];
-extern const char kICUDataTable[];
-extern const size_t kICUDataTableSize;
-}
-
-#endif  // SERVICES_ICU_DATA_DATA_H_
diff --git a/services/icu_data/embed_icu_data.py b/services/icu_data/embed_icu_data.py
deleted file mode 100755
index 515a3bb..0000000
--- a/services/icu_data/embed_icu_data.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import os
-import sys
-import hashlib
-
-in_file = sys.argv[1]
-out_file = sys.argv[2]
-
-out_dir = os.path.dirname(out_file)
-
-data = None
-with open(in_file, "rb") as f:
-    data = f.read()
-
-if not os.path.exists(out_dir):
-    os.makedirs(out_dir)
-
-sha1hash = hashlib.sha1(data).hexdigest()
-
-values = ["0x%02x" % ord(c) for c in data]
-lines = []
-chunk_size = 16
-for i in range(0, len(values), chunk_size):
-    lines.append(", ".join(values[i: i + chunk_size]))
-
-with open(out_file, "w") as f:
-    f.write('#include "services/icu_data/data.h"\n')
-    f.write("namespace icu_data {\n")
-    f.write("const char kICUDataTable[%d] = {\n" % len(data))
-    f.write(",\n".join(lines))
-    f.write("\n};\n")
-    f.write("const size_t kICUDataTableSize = sizeof(kICUDataTable);\n")
-    f.write("const char kICUDataTableHash[] = \"%s\";\n" % sha1hash)
-    f.write("}\n")
diff --git a/services/icu_data/icu_data_impl.cc b/services/icu_data/icu_data_impl.cc
index a2eb6f6..be52bc0 100644
--- a/services/icu_data/icu_data_impl.cc
+++ b/services/icu_data/icu_data_impl.cc
@@ -9,8 +9,8 @@
 #include "mojo/public/cpp/application/application_delegate.h"
 #include "mojo/public/cpp/application/interface_factory.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "services/icu_data/data.h"
 #include "services/icu_data/icu_data.mojom.h"
+#include "services/icu_data/kICUData.h"
 
 namespace icu_data {
 
@@ -37,8 +37,8 @@
   void Map(const mojo::String& sha1hash,
            const mojo::Callback<void(mojo::ScopedSharedBufferHandle)>& callback)
       override {
-    if (std::string(sha1hash) != std::string(kICUDataTableHash)) {
-      LOG(WARNING) << "Failed to match sha1sum. Expected " << kICUDataTableHash;
+    if (std::string(sha1hash) != std::string(kICUData.hash)) {
+      LOG(WARNING) << "Failed to match sha1sum. Expected " << kICUData.hash;
       callback.Run(mojo::ScopedSharedBufferHandle());
       return;
     }
@@ -53,12 +53,12 @@
   void EnsureBuffer() {
     if (buffer_)
       return;
-    buffer_.reset(new mojo::SharedBuffer(kICUDataTableSize));
+    buffer_.reset(new mojo::SharedBuffer(kICUData.size));
     void* ptr = nullptr;
-    MojoResult rv = mojo::MapBuffer(buffer_->handle.get(), 0, kICUDataTableSize,
+    MojoResult rv = mojo::MapBuffer(buffer_->handle.get(), 0, kICUData.size,
                                     &ptr, MOJO_MAP_BUFFER_FLAG_NONE);
     CHECK_EQ(rv, MOJO_RESULT_OK);
-    memcpy(ptr, kICUDataTable, kICUDataTableSize);
+    memcpy(ptr, kICUData.data, kICUData.size);
     rv = mojo::UnmapBuffer(ptr);
     CHECK_EQ(rv, MOJO_RESULT_OK);
   }
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
index e20c0e5..8e0e5ff 100644
--- a/shell/BUILD.gn
+++ b/shell/BUILD.gn
@@ -6,6 +6,7 @@
 import("//mojo/public/mojo.gni")
 import("//mojo/public/mojo_application.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
+import("//mojo/tools/embed/rules.gni")
 import("//testing/test.gni")
 
 # We don't support building in the component build since mojo apps are
@@ -507,6 +508,17 @@
   }
 }
 
+embed_file("embed_pingable") {
+  source = "$root_build_dir/pingable_app.mojo"
+  namespace = "mojo"
+  variable = "kPingable"
+  testonly = true
+
+  deps = [
+    "//shell/test:pingable_app",
+  ]
+}
+
 mojo_native_application("apptests") {
   output_name = "shell_apptests"
 
@@ -528,10 +540,8 @@
     "//services/http_server/public",
     "//services/http_server/public:util",
     "//shell/test:bindings",
+    ":embed_pingable",
   ]
 
-  data_deps = [
-    "//services/http_server:http_server",
-    "//shell/test:pingable_app",
-  ]
+  data_deps = [ "//services/http_server:http_server" ]
 }
diff --git a/shell/shell_apptest.cc b/shell/shell_apptest.cc
index 30f4fd7..7b610a7 100644
--- a/shell/shell_apptest.cc
+++ b/shell/shell_apptest.cc
@@ -18,8 +18,8 @@
 #include "services/http_server/public/http_server.mojom.h"
 #include "services/http_server/public/http_server_factory.mojom.h"
 #include "services/http_server/public/http_server_util.h"
+#include "shell/kPingable.h"
 #include "shell/test/pingable.mojom.h"
-
 namespace mojo {
 
 namespace {
@@ -34,9 +34,6 @@
  public:
   GetHandler(InterfaceRequest<http_server::HttpHandler> request, uint16_t port)
       : binding_(this, request.Pass()), port_(port) {
-    CHECK(PathService::Get(base::FILE_MODULE, &app_path_));
-    app_path_ = app_path_.DirName().Append("pingable_app.mojo");
-    CHECK(base::PathExists(app_path_));
   }
   ~GetHandler() override {}
 
@@ -47,10 +44,8 @@
       const Callback<void(http_server::HttpResponsePtr)>& callback) override {
     http_server::HttpResponsePtr response;
     if (StartsWithASCII(request->relative_url, "/app", true)) {
-      // Super inefficient, but meh.
-      std::string data;
-      base::ReadFileToString(app_path_, &data);
-      response = http_server::CreateHttpResponse(200, data);
+      response = http_server::CreateHttpResponse(
+          200, std::string(kPingable.data, kPingable.size));
       response->content_type = "application/octet-stream";
     } else if (request->relative_url == "/redirect") {
       response = http_server::HttpResponse::New();
@@ -64,7 +59,6 @@
   }
 
   Binding<http_server::HttpHandler> binding_;
-  base::FilePath app_path_;
   uint16_t port_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(GetHandler);
@@ -120,10 +114,6 @@
   MOJO_DISALLOW_COPY_AND_ASSIGN(ShellHTTPAppTest);
 };
 
-#if defined(OS_ANDROID)
-// These tests rely on data that needs to be bundled into the apptest binary in
-// order to work on Android.
-#else  // !OS_ANDROID
 // Test that we can load apps over http.
 TEST_F(ShellHTTPAppTest, Http) {
   InterfacePtr<Pingable> pingable;
@@ -188,7 +178,6 @@
   pingable2->Ping("hello", callback);
   base::RunLoop().Run();
 }
-#endif  // OS_ANDROID
 
 // mojo: URLs can have querystrings too
 TEST_F(ShellAppTest, MojoURLQueryHandling) {