Move j2objc helper scripts into open-source Bazel.

--
MOS_MIGRATED_REVID=100490916
diff --git a/tools/objc/j2objc_dead_code_pruner.py b/tools/objc/j2objc_dead_code_pruner.py
new file mode 100755
index 0000000..546a9ef
--- /dev/null
+++ b/tools/objc/j2objc_dead_code_pruner.py
@@ -0,0 +1,258 @@
+#!/usr/bin/python2.7
+
+# Copyright 2015 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""A script for J2ObjC dead code removal in Blaze.
+
+This script removes unused J2ObjC-translated classes from compilation and
+linking by:
+  1. Build a class dependency tree among translated source files.
+  2. Use user-provided Java class entry points to get a list of reachable
+     classes.
+  3. Go through all translated source files and rewrite unreachable ones with
+     dummy content.
+"""
+
+import argparse
+from collections import OrderedDict
+import multiprocessing
+import os
+import Queue
+import shutil
+import threading
+
+PRUNED_SRC_CONTENT = 'static int DUMMY_unused __attribute__((unused,used)) = 0;'
+
+
+def BuildReachabilityTree(dependency_mapping_files, file_open=open):
+  """Builds a reachability tree using entries from dependency mapping files.
+
+  Args:
+    dependency_mapping_files: A comma separated list of J2ObjC-generated
+        dependency mapping files.
+    file_open: Reference to the builtin open function so it may be
+        overridden for testing.
+  Returns:
+    A dict mapping J2ObjC-generated source files to the corresponding direct
+    dependent source files.
+  """
+  tree = dict()
+  for dependency_mapping_file in dependency_mapping_files.split(','):
+    with file_open(dependency_mapping_file, 'r') as f:
+      for line in f:
+        entry = line.strip().split(':')[0]
+        dep = line.strip().split(':')[1]
+        if entry in tree:
+          tree[entry].append(dep)
+        else:
+          tree[entry] = [dep]
+  return tree
+
+
+def BuildHeaderMapping(header_mapping_files, file_open=open):
+  """Builds a mapping between Java classes and J2ObjC-translated header files.
+
+  Args:
+    header_mapping_files: A comma separated list of J2ObjC-generated
+        header mapping files.
+    file_open: Reference to the builtin open function so it may be
+        overridden for testing.
+  Returns:
+    An ordered dict mapping Java class names to corresponding J2ObjC-translated
+    source files.
+  """
+  header_mapping = OrderedDict()
+  for header_mapping_file in header_mapping_files.split(','):
+    with file_open(header_mapping_file, 'r') as f:
+      for line in f:
+        java_class_name = line.strip().split('=')[0]
+        transpiled_file_name = os.path.splitext(line.strip().split('=')[1])[0]
+        header_mapping[java_class_name] = transpiled_file_name
+  return header_mapping
+
+
+def BuildReachableFileSet(entry_classes, reachability_tree, header_mapping):
+  """Builds a set of reachable translated files from entry Java classes.
+
+  Args:
+    entry_classes: A comma separated list of Java entry classes.
+    reachability_tree: A dict mapping translated files to their direct
+        dependencies.
+    header_mapping: A dict mapping Java class names to translated source files.
+  Returns:
+    A set of reachable translated files from the given list of entry classes.
+  Raises:
+    Exception: If there is an entry class that is not being transpiled in this
+        j2objc_library.
+  """
+  reachable_files = set()
+  for entry_class in entry_classes.split(','):
+    if entry_class not in header_mapping:
+      raise Exception(entry_class +
+                      'is not in the transitive Java deps of included ' +
+                      'j2objc_library rules.')
+    transpiled_file_name = header_mapping[entry_class]
+    reachable_files.add(transpiled_file_name)
+    current_level_deps = reachability_tree[transpiled_file_name]
+    while current_level_deps:
+      next_level_deps = []
+      for dep in current_level_deps:
+        if dep not in reachable_files:
+          reachable_files.add(dep)
+          if dep in reachability_tree:
+            next_level_deps.extend(reachability_tree[dep])
+      current_level_deps = next_level_deps
+  return reachable_files
+
+
+def PruneFiles(input_files, output_files, objc_file_path, reachable_files,
+               file_open=open, file_shutil=shutil):
+  """Copies over translated files and remove the contents of unreachable files.
+
+  Args:
+    input_files: A comma separated list of input source files to prune. It has
+        a one-on-one pair mapping with the output_file list.
+    output_files: A comma separated list of output source files to write pruned
+        source files to. It has a one-on-one pair mapping with the input_file
+        list.
+    objc_file_path: The file path which represents a directory where the
+        generated ObjC files reside.
+    reachable_files: A set of reachable source files.
+    file_open: Reference to the builtin open function so it may be
+        overridden for testing.
+    file_shutil: Reference to the builtin shutil module so it may be
+        overridden for testing.
+  Returns:
+    None.
+  """
+  file_queue = Queue.Queue()
+  for input_file, output_file in zip(input_files.split(','),
+                                     output_files.split(',')):
+    file_queue.put((input_file, output_file))
+
+  for _ in xrange(multiprocessing.cpu_count()):
+    t = threading.Thread(target=_PruneFile, args=(file_queue,
+                                                  reachable_files,
+                                                  objc_file_path,
+                                                  file_open,
+                                                  file_shutil))
+    t.start()
+
+  file_queue.join()
+
+
+def _PruneFile(file_queue, reachable_files, objc_file_path, file_open=open,
+               file_shutil=shutil):
+  while True:
+    try:
+      input_file, output_file = file_queue.get_nowait()
+    except Queue.Empty:
+      return
+    file_name = os.path.relpath(os.path.splitext(input_file)[0],
+                                objc_file_path)
+    # Translated files from package-info.java are also preserved because
+    # they are needed to resolve ObjC class names with prefixes.
+    if file_name in reachable_files or file_name.endswith('package-info'):
+      file_shutil.copy(input_file, output_file)
+    else:
+      f = file_open(output_file, 'w')
+      # Use a static variable scoped to the source file to supress
+      # the "has no symbols" linker warning for empty object files.
+      f.write(PRUNED_SRC_CONTENT)
+      f.close()
+    file_queue.task_done()
+
+
+def PruneDeadCode(input_files, output_files, dependency_mapping_files,
+                  header_mapping_files, entry_classes, objc_file_path,
+                  file_open=open, file_shutil=shutil):
+  """Copies over translated files and remove the contents of unreachable files.
+
+  Args:
+    input_files: A comma separated list of input source files to prune. It has
+        a one-on-one pair mapping with the output_file list.
+    output_files: A comma separated list of output source files to write pruned
+        source files to. It has a one-on-one pair mapping with the input_file
+        list.
+    dependency_mapping_files: A comma separated list of J2ObjC-generated
+        dependency mapping files.
+    header_mapping_files: A comma separated list of J2ObjC-generated
+        header mapping files.
+    entry_classes: A comma separated list of Java entry classes.
+    objc_file_path: The file path which represents a directory where the
+        generated ObjC files reside.
+    file_open: Reference to the builtin open function so it may be
+        overridden for testing.
+    file_shutil: Reference to the builtin shutil module so it may be
+        overridden for testing.
+  Returns:
+    None.
+  """
+  reachability_file_mapping = BuildReachabilityTree(
+      dependency_mapping_files, file_open)
+  header_map = BuildHeaderMapping(header_mapping_files, file_open)
+  reachable_files_set = BuildReachableFileSet(entry_classes,
+                                              reachability_file_mapping,
+                                              header_map)
+  PruneFiles(input_files,
+             output_files,
+             objc_file_path,
+             reachable_files_set,
+             file_open,
+             file_shutil)
+
+
+if __name__ == '__main__':
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--input_files',
+      required=True,
+      help=('The comma-separated file paths of translated source files to '
+            'prune.'))
+  parser.add_argument(
+      '--output_files',
+      required=True,
+      help='The comma-separated file paths of pruned source files to write to.')
+  parser.add_argument(
+      '--dependency_mapping_files',
+      required=True,
+      help='The comma-separated file paths of dependency mapping files.')
+  parser.add_argument(
+      '--header_mapping_files',
+      required=True,
+      help='The comma-separated file paths of header mapping files.')
+  parser.add_argument(
+      '--entry_classes',
+      required=True,
+      help=('The comma-separated list of Java entry classes to be used as entry'
+            ' point of the dead code anlysis.'))
+  parser.add_argument(
+      '--objc_file_path',
+      required=True,
+      help='The file path which represents a directory where the generated ObjC'
+      ' files reside')
+  args = parser.parse_args()
+
+  if not args.entry_classes:
+    raise Exception('J2objC dead code removal is on but no entry class is ',
+                    'specified in any j2objc_library targets in the transitive',
+                    ' closure')
+  PruneDeadCode(
+      args.input_files,
+      args.output_files,
+      args.dependency_mapping_files,
+      args.header_mapping_files,
+      args.entry_classes,
+      args.objc_file_path)