Delete now-unused python2.7 versions of j2objc tool scrips.
PiperOrigin-RevId: 441542792
diff --git a/tools/j2objc/BUILD.tools b/tools/j2objc/BUILD.tools
index 7e3e60b..c9b7b65 100644
--- a/tools/j2objc/BUILD.tools
+++ b/tools/j2objc/BUILD.tools
@@ -11,17 +11,6 @@
runtime_deps = ["@bazel_j2objc//:j2objc"],
)
-# TODO(b/225174999): Remove filegroups once blaze update is rolled out.
-filegroup(
- name = "j2objc_wrapper",
- srcs = ["j2objc_wrapper.py"],
-)
-
-filegroup(
- name = "j2objc_header_map",
- srcs = ["j2objc_header_map.py"],
-)
-
py_binary(
name = "j2objc_wrapper_binary",
srcs = ["j2objc_wrapper_binary.py"],
diff --git a/tools/j2objc/j2objc_header_map.py b/tools/j2objc/j2objc_header_map.py
deleted file mode 100755
index cb6faaf..0000000
--- a/tools/j2objc/j2objc_header_map.py
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/usr/bin/python2.7
-
-# Copyright 2017 The Bazel Authors. 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 to generate Java class to ObjC header mapping for J2ObjC.
-
-This script generates a text file containing mapping between top-level Java
-classes to associated ObjC headers, generated by J2ObjC.
-
-The mapping file is used by dependent J2ObjC transpilation actions to locate
-the correct header import paths for dependent Java classes.
-
-Inside the script, we read the Java source files and source jars of a single
-Java rule, and parse out the package names from the package statements, using
-regular expression matching.
-
-Note that we cannot guarantee 100% correctness by using just regular expression,
-but it should be good enough. This allows us to avoid doing any further complex
-parsing of the source files and keep the script light-weight without other
-dependencies. In the future, we can consider implementing a simple Java lexer
-here that correctly parses the package statements out of Java files.
-"""
-
-import argparse
-import os
-import re
-import zipfile
-
-_PACKAGE_RE = re.compile(r'(package)\s+([\w\.]+);')
-
-
-def _get_file_map_entry(java_file_path, java_file):
- """Returns the top-level Java class and header file path tuple.
-
- Args:
- java_file_path: The file path of the source Java file.
- java_file: The actual file of the source java file.
- Returns:
- A tuple containing top-level Java class and associated header file path. Or
- None if no package statement exists in the source file.
- """
- for line in java_file:
- stripped_line = line.strip()
- package_statement = _PACKAGE_RE.search(stripped_line)
-
- # We identified a potential package statement.
- if package_statement:
- preceding_characters = stripped_line[0:package_statement.start(1)]
- # We have preceding characters before the package statement. We need to
- # look further into them.
- if preceding_characters:
- # Skip comment line.
- if preceding_characters.startswith('//'):
- continue
-
- # Preceding characters also must end with a space, represent an end
- # of comment, or end of a statement.
- # Otherwise, we skip the current line.
- if not (preceding_characters[len(preceding_characters) - 1].isspace() or
- preceding_characters.endswith(';') or
- preceding_characters.endswith('*/')):
- continue
- package_name = package_statement.group(2)
- class_name = os.path.splitext(os.path.basename(java_file_path))[0]
- header_file = os.path.splitext(java_file_path)[0] + '.h'
- return (package_name + '.' + class_name, header_file)
- return None
-
-
-def main():
- parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
- parser.add_argument(
- '--source_files',
- required=False,
- help='The source files')
- parser.add_argument(
- '--source_jars',
- required=False,
- help='The source jars.')
- parser.add_argument(
- '--output_mapping_file',
- required=False,
- help='The output mapping file')
-
- args, _ = parser.parse_known_args()
- class_to_header_map = dict()
-
- # Process the source files.
- if args.source_files:
- source_files = args.source_files.split(',')
- for source_file in source_files:
- with open(source_file, 'r') as f:
- entry = _get_file_map_entry(source_file, f)
- if entry:
- class_to_header_map[entry[0]] = entry[1]
-
- # Process the source jars.
- if args.source_jars:
- source_jars = args.source_jars.split(',')
- for source_jar in source_jars:
- with zipfile.ZipFile(source_jar, 'r') as jar:
- for jar_entry in jar.namelist():
- if jar_entry.endswith('.java'):
- with jar.open(jar_entry) as jar_entry_file:
- entry = _get_file_map_entry(jar_entry, jar_entry_file)
- if entry:
- class_to_header_map[entry[0]] = entry[1]
-
- # Generate the output header mapping file.
- if args.output_mapping_file:
- with open(args.output_mapping_file, 'w') as output_mapping_file:
- for class_name in sorted(class_to_header_map):
- header_path = class_to_header_map[class_name]
- output_mapping_file.write(class_name + '=' + header_path + '\n')
-
-if __name__ == '__main__':
- main()
diff --git a/tools/j2objc/j2objc_wrapper.py b/tools/j2objc/j2objc_wrapper.py
deleted file mode 100755
index 298e99e..0000000
--- a/tools/j2objc/j2objc_wrapper.py
+++ /dev/null
@@ -1,539 +0,0 @@
-#!/usr/bin/python2.7
-
-# Copyright 2015 The Bazel Authors. 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 wrapper script for J2ObjC transpiler.
-
-This script wraps around J2ObjC transpiler to also output a dependency mapping
-file by scanning the import and include directives of the J2ObjC-translated
-files.
-"""
-
-import argparse
-import errno
-import multiprocessing
-import os
-import Queue
-import re
-import shutil
-import subprocess
-import tempfile
-import threading
-import zipfile
-
-_INCLUDE_RE = re.compile('#(include|import) "([^"]+)"')
-_CONST_DATE_TIME = [1980, 1, 1, 0, 0, 0]
-_ADD_EXPORTS = [
- '--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED',
- '--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
- '--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED',
- '--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED',
- '--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED',
-]
-
-
-def RunJ2ObjC(java, jvm_flags, j2objc, main_class, output_file_path,
- j2objc_args, source_paths, files_to_translate):
- """Runs J2ObjC transpiler to translate Java source files to ObjC.
-
- Args:
- java: The path of the Java executable.
- jvm_flags: A comma-separated list of flags to pass to JVM.
- j2objc: The deploy jar of J2ObjC.
- main_class: The J2ObjC main class to invoke.
- output_file_path: The output file directory.
- j2objc_args: A list of args to pass to J2ObjC transpiler.
- source_paths: A list of directories that contain sources to translate.
- files_to_translate: A list of relative paths (relative to source_paths) that
- point to sources to translate.
- Returns:
- None.
- """
- j2objc_args.extend(['-sourcepath', ':'.join(source_paths)])
- j2objc_args.extend(['-d', output_file_path])
- j2objc_args.extend(files_to_translate)
- param_file_content = ' '.join(j2objc_args)
- fd = None
- param_filename = None
- try:
- fd, param_filename = tempfile.mkstemp(text=True)
- os.write(fd, param_file_content)
- finally:
- if fd:
- os.close(fd)
- try:
- j2objc_cmd = [java]
- j2objc_cmd.extend(filter(None, jvm_flags.split(',')))
- j2objc_cmd.extend(_ADD_EXPORTS)
- j2objc_cmd.extend(['-cp', j2objc, main_class])
- j2objc_cmd.append('@%s' % param_filename)
- subprocess.check_call(j2objc_cmd, stderr=subprocess.STDOUT)
- finally:
- if param_filename:
- os.remove(param_filename)
-
-
-def WriteDepMappingFile(objc_files,
- objc_file_root,
- output_dependency_mapping_file,
- file_open=open):
- """Scans J2ObjC-translated files and outputs a dependency mapping file.
-
- The mapping file contains mappings between translated source files and their
- imported source files scanned from the import and include directives.
-
- Args:
- objc_files: A list of ObjC files translated by J2ObjC.
- objc_file_root: The file path which represents a directory where the
- generated ObjC files reside.
- output_dependency_mapping_file: The path of the dependency mapping file to
- write to.
- file_open: Reference to the builtin open function so it may be
- overridden for testing.
- Raises:
- RuntimeError: If spawned threads throw errors during processing.
- Returns:
- None.
- """
- dep_mapping = dict()
- input_file_queue = Queue.Queue()
- output_dep_mapping_queue = Queue.Queue()
- error_message_queue = Queue.Queue()
- for objc_file in objc_files:
- input_file_queue.put(os.path.join(objc_file_root, objc_file))
-
- for _ in xrange(multiprocessing.cpu_count()):
- t = threading.Thread(target=_ReadDepMapping, args=(input_file_queue,
- output_dep_mapping_queue,
- error_message_queue,
- objc_file_root,
- file_open))
- t.start()
-
- input_file_queue.join()
-
- if not error_message_queue.empty():
- error_messages = [error_message for error_message in
- error_message_queue.queue]
- raise RuntimeError('\n'.join(error_messages))
-
- while not output_dep_mapping_queue.empty():
- entry_file, deps = output_dep_mapping_queue.get()
- dep_mapping[entry_file] = deps
-
- with file_open(output_dependency_mapping_file, 'w') as f:
- for entry in sorted(dep_mapping):
- for dep in dep_mapping[entry]:
- f.write(entry + ':' + dep + '\n')
-
-
-def _ReadDepMapping(input_file_queue, output_dep_mapping_queue,
- error_message_queue, output_root, file_open=open):
- while True:
- try:
- input_file = input_file_queue.get_nowait()
- except Queue.Empty:
- # No more work left in the queue.
- return
-
- try:
- deps = set()
- input_file_name = os.path.splitext(input_file)[0]
- entry = os.path.relpath(input_file_name, output_root)
- for file_ext in ['.m', '.h']:
- with file_open(input_file_name + file_ext, 'r') as f:
- for line in f:
- include = _INCLUDE_RE.match(line)
- if include:
- include_path = include.group(2)
- dep = os.path.splitext(include_path)[0]
- if dep != entry:
- deps.add(dep)
-
- output_dep_mapping_queue.put((entry, sorted(deps)))
- except Exception as e: # pylint: disable=broad-except
- error_message_queue.put(str(e))
- finally:
- # We need to mark the task done to prevent blocking the main process
- # indefinitely.
- input_file_queue.task_done()
-
-
-def WriteArchiveSourceMappingFile(compiled_archive_file_path,
- output_archive_source_mapping_file,
- objc_files,
- file_open=open):
- """Writes a mapping file between archive file to associated ObjC source files.
-
- Args:
- compiled_archive_file_path: The path of the archive file.
- output_archive_source_mapping_file: A path of the mapping file to write to.
- objc_files: A list of ObjC files translated by J2ObjC.
- file_open: Reference to the builtin open function so it may be
- overridden for testing.
- Returns:
- None.
- """
- with file_open(output_archive_source_mapping_file, 'w') as f:
- for objc_file in objc_files:
- f.write(compiled_archive_file_path + ':' + objc_file + '\n')
-
-
-def _ParseArgs(j2objc_args):
- """Separate arguments passed to J2ObjC into source files and J2ObjC flags.
-
- Args:
- j2objc_args: A list of args to pass to J2ObjC transpiler.
- Returns:
- A tuple containing source files and J2ObjC flags
- """
- source_files = []
- flags = []
- is_next_flag_value = False
- for j2objc_arg in j2objc_args:
- if j2objc_arg.startswith('-'):
- flags.append(j2objc_arg)
- is_next_flag_value = True
- elif is_next_flag_value:
- flags.append(j2objc_arg)
- is_next_flag_value = False
- else:
- source_files.append(j2objc_arg)
- return (source_files, flags)
-
-
-def _J2ObjcOutputObjcFiles(java_files):
- """Returns the relative paths of the associated output ObjC source files.
-
- Args:
- java_files: The list of Java files to translate.
- Returns:
- A list of associated output ObjC source files.
- """
- return [os.path.splitext(java_file)[0] + '.m' for java_file in java_files]
-
-
-def UnzipSourceJarSources(source_jars):
- """Unzips the source jars containing Java source files.
-
- Args:
- source_jars: The list of input Java source jars.
- Returns:
- A tuple of the temporary output root and a list of root-relative paths of
- unzipped Java files
- """
- srcjar_java_files = []
- if source_jars:
- tmp_input_root = tempfile.mkdtemp()
- for source_jar in source_jars:
- zip_ref = zipfile.ZipFile(source_jar, 'r')
- zip_entries = []
-
- for file_entry in zip_ref.namelist():
- # We only care about Java source files.
- if file_entry.endswith('.java'):
- zip_entries.append(file_entry)
-
- zip_ref.extractall(tmp_input_root, zip_entries)
- zip_ref.close()
- srcjar_java_files.extend(zip_entries)
-
- return (tmp_input_root, srcjar_java_files)
- else:
- return None
-
-
-def RenameGenJarObjcFileRootInFileContent(tmp_objc_file_root,
- j2objc_source_paths,
- gen_src_jar, genjar_objc_files,
- execute=subprocess.check_call):
- """Renames references to temporary root inside ObjC sources from gen srcjar.
-
- Args:
- tmp_objc_file_root: The temporary output root containing ObjC sources.
- j2objc_source_paths: The source paths used by J2ObjC.
- gen_src_jar: The path of the gen srcjar.
- genjar_objc_files: The list of ObjC sources translated from the gen srcjar.
- execute: The function used to execute shell commands.
- Returns:
- None.
- """
- if genjar_objc_files:
- abs_genjar_objc_source_files = [
- os.path.join(tmp_objc_file_root, genjar_objc_file)
- for genjar_objc_file in genjar_objc_files
- ]
- abs_genjar_objc_header_files = [
- os.path.join(tmp_objc_file_root,
- os.path.splitext(genjar_objc_file)[0] + '.h')
- for genjar_objc_file in genjar_objc_files
- ]
-
- # We execute a command to change all references of the temporary Java root
- # where we unzipped the gen srcjar sources, to the actual gen srcjar that
- # contains the original Java sources.
- cmd = [
- 'sed',
- '-i',
- '-e',
- 's|%s/|%s::|g' % (j2objc_source_paths[1], gen_src_jar)
- ]
- cmd.extend(abs_genjar_objc_source_files)
- cmd.extend(abs_genjar_objc_header_files)
- execute(cmd, stderr=subprocess.STDOUT)
-
-
-def MoveObjcFileToFinalOutputRoot(objc_files,
- tmp_objc_file_root,
- final_objc_file_root,
- suffix,
- os_module=os,
- shutil_module=shutil):
- """Moves ObjC files from temporary location to the final output location.
-
- Args:
- objc_files: The list of objc files to move.
- tmp_objc_file_root: The temporary output root containing ObjC sources.
- final_objc_file_root: The final output root.
- suffix: The suffix of the files to move.
- os_module: The os python module.
- shutil_module: The shutil python module.
- Returns:
- None.
- """
- for objc_file in objc_files:
- file_with_suffix = os_module.path.splitext(objc_file)[0] + suffix
- dest_path = os_module.path.join(
- final_objc_file_root, file_with_suffix)
- dest_path_dir = os_module.path.dirname(dest_path)
-
- if not os_module.path.isdir(dest_path_dir):
- try:
- os_module.makedirs(dest_path_dir)
- except OSError as e:
- if e.errno != errno.EEXIST or not os_module.path.isdir(dest_path_dir):
- raise
-
- shutil_module.move(
- os_module.path.join(tmp_objc_file_root, file_with_suffix),
- dest_path)
-
-
-def PostJ2ObjcFileProcessing(normal_objc_files, genjar_objc_files,
- tmp_objc_file_root, final_objc_file_root,
- j2objc_source_paths, gen_src_jar,
- output_gen_source_dir, output_gen_header_dir):
- """Performs cleanups on ObjC files and moves them to final output location.
-
- Args:
- normal_objc_files: The list of objc files translated from normal Java files.
- genjar_objc_files: The list of ObjC sources translated from the gen srcjar.
- tmp_objc_file_root: The temporary output root containing ObjC sources.
- final_objc_file_root: The final output root.
- j2objc_source_paths: The source paths used by J2ObjC.
- gen_src_jar: The path of the gen srcjar.
- output_gen_source_dir: The final output directory of ObjC source files
- translated from gen srcjar. Maybe null.
- output_gen_header_dir: The final output directory of ObjC header files
- translated from gen srcjar. Maybe null.
- Returns:
- None.
- """
- RenameGenJarObjcFileRootInFileContent(tmp_objc_file_root,
- j2objc_source_paths,
- gen_src_jar,
- genjar_objc_files)
- MoveObjcFileToFinalOutputRoot(normal_objc_files,
- tmp_objc_file_root,
- final_objc_file_root,
- '.m')
- MoveObjcFileToFinalOutputRoot(normal_objc_files,
- tmp_objc_file_root,
- final_objc_file_root,
- '.h')
-
- if output_gen_source_dir:
- MoveObjcFileToFinalOutputRoot(
- genjar_objc_files,
- tmp_objc_file_root,
- output_gen_source_dir,
- '.m')
-
- if output_gen_header_dir:
- MoveObjcFileToFinalOutputRoot(
- genjar_objc_files,
- tmp_objc_file_root,
- output_gen_header_dir,
- '.h')
-
-
-def GenerateJ2objcMappingFiles(normal_objc_files,
- genjar_objc_files,
- tmp_objc_file_root,
- output_dependency_mapping_file,
- output_archive_source_mapping_file,
- compiled_archive_file_path):
- """Generates J2ObjC mapping files.
-
- Args:
- normal_objc_files: The list of objc files translated from normal Java files.
- genjar_objc_files: The list of ObjC sources translated from the gen srcjar.
- tmp_objc_file_root: The temporary output root containing ObjC sources.
- output_dependency_mapping_file: The path of the dependency mapping file to
- write to.
- output_archive_source_mapping_file: A path of the mapping file to write to.
- compiled_archive_file_path: The path of the archive file.
- Returns:
- None.
- """
- WriteDepMappingFile(normal_objc_files + genjar_objc_files,
- tmp_objc_file_root,
- output_dependency_mapping_file)
-
- if output_archive_source_mapping_file:
- WriteArchiveSourceMappingFile(compiled_archive_file_path,
- output_archive_source_mapping_file,
- normal_objc_files + genjar_objc_files)
-
-
-def main():
- parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
- parser.add_argument(
- '--java',
- required=True,
- help='The path to the Java executable.')
- parser.add_argument(
- '--jvm_flags',
- default='-Xss4m,-XX:+UseParallelGC',
- help='A comma-separated list of flags to pass to the JVM.')
- parser.add_argument(
- '--j2objc',
- required=True,
- help='The path to the J2ObjC deploy jar.')
- parser.add_argument(
- '--main_class',
- required=True,
- help='The main class of the J2ObjC deploy jar to execute.')
- # TODO(rduan): Remove, no longer needed.
- parser.add_argument(
- '--translated_source_files',
- required=False,
- help=('A comma-separated list of file paths where J2ObjC will write the '
- 'translated files to.'))
- parser.add_argument(
- '--output_dependency_mapping_file',
- required=True,
- help='The file path of the dependency mapping file to write to.')
- parser.add_argument(
- '--objc_file_path', '-d',
- required=True,
- help=('The file path which represents a directory where the generated '
- 'ObjC files reside.'))
- parser.add_argument(
- '--output_archive_source_mapping_file',
- help='The file path of the mapping file containing mappings between the '
- 'translated source files and the to-be-generated archive file '
- 'compiled from those source files. --compile_archive_file_path must '
- 'be specified if this option is specified.')
- parser.add_argument(
- '--compiled_archive_file_path',
- required=False,
- help=('The archive file path that will be produced by ObjC compile action'
- ' later'))
- # TODO(rduan): Remove this flag once it is fully replaced by flag --src_jars.
- parser.add_argument(
- '--gen_src_jar',
- required=False,
- help='The jar containing Java sources generated by annotation processor.')
- parser.add_argument(
- '--src_jars',
- required=False,
- help='The list of Java source jars containing Java sources to translate.')
- parser.add_argument(
- '--output_gen_source_dir',
- required=False,
- help='The output directory of ObjC source files translated from the gen'
- ' srcjar')
- parser.add_argument(
- '--output_gen_header_dir',
- required=False,
- help='The output directory of ObjC header files translated from the gen'
- ' srcjar')
-
- args, pass_through_args = parser.parse_known_args()
- normal_java_files, j2objc_flags = _ParseArgs(pass_through_args)
- srcjar_java_files = []
- j2objc_source_paths = [os.getcwd()]
-
- # Unzip the source jars, so J2ObjC can translate the contained sources.
- # Also add the temporary directory containing the unzipped sources as a source
- # path for J2ObjC, so it can find these sources.
- source_jars = []
- if args.gen_src_jar:
- source_jars.append(args.gen_src_jar)
- if args.src_jars:
- source_jars.extend(args.src_jars.split(','))
-
- srcjar_source_tuple = UnzipSourceJarSources(source_jars)
- if srcjar_source_tuple:
- j2objc_source_paths.append(srcjar_source_tuple[0])
- srcjar_java_files = srcjar_source_tuple[1]
-
- # Run J2ObjC over the normal input Java files and unzipped gen jar Java files.
- # The output is stored in a temporary directory.
- tmp_objc_file_root = tempfile.mkdtemp()
-
- # If we do not generate the header mapping from J2ObjC, we still
- # need to specify --output-header-mapping, as it signals to J2ObjC that we
- # are using source paths as import paths, not package paths.
- # TODO(rduan): Make another flag in J2ObjC to specify using source paths.
- if '--output-header-mapping' not in j2objc_flags:
- j2objc_flags.extend(['--output-header-mapping', '/dev/null'])
-
- RunJ2ObjC(args.java,
- args.jvm_flags,
- args.j2objc,
- args.main_class,
- tmp_objc_file_root,
- j2objc_flags,
- j2objc_source_paths,
- normal_java_files + srcjar_java_files)
-
- # Calculate the relative paths of generated objc files.
- normal_objc_files = _J2ObjcOutputObjcFiles(normal_java_files)
- genjar_objc_files = _J2ObjcOutputObjcFiles(srcjar_java_files)
-
- # Generate J2ObjC mapping files needed for distributed builds.
- GenerateJ2objcMappingFiles(normal_objc_files,
- genjar_objc_files,
- tmp_objc_file_root,
- args.output_dependency_mapping_file,
- args.output_archive_source_mapping_file,
- args.compiled_archive_file_path)
-
- # Post J2ObjC-run processing, involving file editing, zipping and moving
- # files to their final output locations.
- PostJ2ObjcFileProcessing(
- normal_objc_files,
- genjar_objc_files,
- tmp_objc_file_root,
- args.objc_file_path,
- j2objc_source_paths,
- args.gen_src_jar,
- args.output_gen_source_dir,
- args.output_gen_header_dir)
-
-if __name__ == '__main__':
- main()
diff --git a/tools/objc/BUILD b/tools/objc/BUILD
index b50136d..6a43160 100644
--- a/tools/objc/BUILD
+++ b/tools/objc/BUILD
@@ -27,11 +27,6 @@
name = "host_xcodes",
)
-filegroup(
- name = "j2objc_dead_code_pruner",
- srcs = ["j2objc_dead_code_pruner.py"],
-)
-
py_binary(
name = "j2objc_dead_code_pruner_binary",
srcs = ["j2objc_dead_code_pruner_binary.py"],
diff --git a/tools/objc/j2objc_dead_code_pruner.py b/tools/objc/j2objc_dead_code_pruner.py
deleted file mode 100755
index fc8944f..0000000
--- a/tools/objc/j2objc_dead_code_pruner.py
+++ /dev/null
@@ -1,495 +0,0 @@
-#!/usr/bin/python2.7
-
-# Copyright 2015 The Bazel Authors. 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 pipes # swap to shlex once on Python 3
-import re
-import shutil
-import subprocess
-import threading
-
-from six.moves import queue as Queue # pylint: disable=redefined-builtin
-from six.moves import xrange # pylint: disable=redefined-builtin
-
-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.
- """
- return BuildArtifactSourceTree(dependency_mapping_files, file_open)
-
-
-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,
- archive_source_file_mapping=None):
- """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.
- archive_source_file_mapping: A dict mapping source files to the associated
- archive file that contains them.
- 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.
- """
- transpiled_entry_files = []
- 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_entry_files.append(header_mapping[entry_class])
-
- # Translated files going into the same static library archive with duplicated
- # base names also need to be added to the set of entry files.
- #
- # This edge case is ignored because we currently cannot correctly perform
- # dead code removal in this case. The object file entries in static library
- # archives are named by the base names of the original source files. If two
- # source files (e.g., foo/bar.m, bar/bar.m) go into the same archive and
- # share the same base name (bar.m), their object file entries inside the
- # archive will have the same name (bar.o). We cannot correctly handle this
- # case because current archive tools (ar, ranlib, etc.) do not handle this
- # case very well.
- if archive_source_file_mapping:
- transpiled_entry_files.extend(_DuplicatedFiles(archive_source_file_mapping))
-
- # Translated files from package-info.java are also added to the entry files
- # because they are needed to resolve ObjC class names with prefixes and these
- # files may also have dependencies.
- for transpiled_file in reachability_tree:
- if transpiled_file.endswith('package-info'):
- transpiled_entry_files.append(transpiled_file)
-
- reachable_files = set()
- for transpiled_entry_file in transpiled_entry_files:
- reachable_files.add(transpiled_entry_file)
- current_level_deps = []
- # We need to check if the transpiled file is in the reachability tree
- # because J2ObjC protos are not analyzed for dead code stripping and
- # therefore are not in the reachability tree at all.
- if transpiled_entry_file in reachability_tree:
- current_level_deps = reachability_tree[transpiled_entry_file]
- 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)
- if file_name in reachable_files:
- file_shutil.copy(input_file, output_file)
- else:
- with file_open(output_file, 'w') as f:
- # Use a static variable scoped to the source file to suppress
- # the "has no symbols" linker warning for empty object files.
- f.write(PRUNED_SRC_CONTENT)
- file_queue.task_done()
-
-
-def _DuplicatedFiles(archive_source_file_mapping):
- """Returns a list of file with duplicated base names in each archive file.
-
- Args:
- archive_source_file_mapping: A dict mapping source files to the associated
- archive file that contains them.
- Returns:
- A list containing files with duplicated base names.
- """
- duplicated_files = []
- dict_with_duplicates = dict()
-
- for source_files in archive_source_file_mapping.values():
- for source_file in source_files:
- file_basename = os.path.basename(source_file)
- file_without_ext = os.path.splitext(source_file)[0]
- if file_basename in dict_with_duplicates:
- dict_with_duplicates[file_basename].append(file_without_ext)
- else:
- dict_with_duplicates[file_basename] = [file_without_ext]
- for basename in dict_with_duplicates:
- if len(dict_with_duplicates[basename]) > 1:
- duplicated_files.extend(dict_with_duplicates[basename])
- dict_with_duplicates = dict()
-
- return duplicated_files
-
-
-def BuildArchiveSourceFileMapping(archive_source_mapping_files, file_open):
- """Builds a mapping between archive files and their associated source files.
-
- Args:
- archive_source_mapping_files: A comma separated list of J2ObjC-generated
- mapping between archive files and their associated source files.
- file_open: Reference to the builtin open function so it may be
- overridden for testing.
- Returns:
- A dict mapping between archive files and their associated source files.
- """
- return BuildArtifactSourceTree(archive_source_mapping_files, file_open)
-
-
-def PruneSourceFiles(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.
- """
- 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)
-
-
-def MatchObjectNamesInArchive(xcrunwrapper, archive, object_names):
- """Returns object names matching their identity in an archive file.
-
- The linker that blaze uses appends an md5 hash to object file
- names prior to inclusion in the archive file. Thus, object names
- such as 'foo.o' need to be matched to their appropriate name in
- the archive file, such as 'foo_<hash>.o'.
-
- Args:
- xcrunwrapper: A wrapper script over xcrun.
- archive: The location of the archive file.
- object_names: The expected basenames of object files to match,
- sans extension. For example 'foo' (not 'foo.o').
- Returns:
- A list of basenames of matching members of the given archive
- """
- ar_contents_cmd = [xcrunwrapper, 'ar', '-t', archive]
- real_object_names = subprocess.check_output(ar_contents_cmd)
- expected_object_name_regex = r'^(?:%s)(?:_[0-9a-f]{32}(?:-[0-9]+)?)?\.o$' % (
- '|'.join([re.escape(name) for name in object_names]))
- return re.findall(expected_object_name_regex, real_object_names,
- flags=re.MULTILINE)
-
-
-def PruneArchiveFile(input_archive, output_archive, dummy_archive,
- dependency_mapping_files, header_mapping_files,
- archive_source_mapping_files, entry_classes, xcrunwrapper,
- file_open=open):
- """Remove unreachable objects from archive file.
-
- Args:
- input_archive: The source archive file to prune.
- output_archive: The location of the pruned archive file.
- dummy_archive: A dummy archive file that contains no object.
- 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.
- archive_source_mapping_files: A comma separated list of J2ObjC-generated
- mapping between archive files and their associated source files.
- entry_classes: A comma separated list of Java entry classes.
- xcrunwrapper: A wrapper script over xcrun.
- file_open: Reference to the builtin open function so it may be
- overridden for testing.
- """
- reachability_file_mapping = BuildReachabilityTree(
- dependency_mapping_files, file_open)
- header_map = BuildHeaderMapping(header_mapping_files, file_open)
- archive_source_file_mapping = BuildArchiveSourceFileMapping(
- archive_source_mapping_files, file_open)
- reachable_files_set = BuildReachableFileSet(entry_classes,
- reachability_file_mapping,
- header_map,
- archive_source_file_mapping)
-
- # Copy the current processes' environment, as xcrunwrapper depends on these
- # variables.
- cmd_env = dict(os.environ)
- j2objc_cmd = ''
- if input_archive in archive_source_file_mapping:
- source_files = archive_source_file_mapping[input_archive]
- unreachable_object_names = []
-
- for source_file in source_files:
- if os.path.splitext(source_file)[0] not in reachable_files_set:
- unreachable_object_names.append(
- os.path.basename(os.path.splitext(source_file)[0]))
-
- # There are unreachable objects in the archive to prune
- if unreachable_object_names:
- # If all objects in the archive are unreachable, just copy over a dummy
- # archive that contains no object
- if len(unreachable_object_names) == len(source_files):
- j2objc_cmd = 'cp %s %s' % (pipes.quote(dummy_archive),
- pipes.quote(output_archive))
- # Else we need to prune the archive of unreachable objects
- else:
- cmd_env['ZERO_AR_DATE'] = '1'
- # Copy the input archive to the output location
- j2objc_cmd += 'cp %s %s && ' % (pipes.quote(input_archive),
- pipes.quote(output_archive))
- # Make the output archive editable
- j2objc_cmd += 'chmod +w %s && ' % (pipes.quote(output_archive))
- # Remove the unreachable objects from the archive
- unreachable_object_names = MatchObjectNamesInArchive(
- xcrunwrapper, input_archive, unreachable_object_names)
- j2objc_cmd += '%s ar -d -s %s %s && ' % (
- pipes.quote(xcrunwrapper),
- pipes.quote(output_archive),
- ' '.join(pipes.quote(uon) for uon in unreachable_object_names))
- # Update the table of content of the archive file
- j2objc_cmd += '%s ranlib %s' % (pipes.quote(xcrunwrapper),
- pipes.quote(output_archive))
- # There are no unreachable objects, we just copy over the original archive
- else:
- j2objc_cmd = 'cp %s %s' % (pipes.quote(input_archive),
- pipes.quote(output_archive))
- # The archive cannot be pruned by J2ObjC dead code removal, just copy over
- # the original archive
- else:
- j2objc_cmd = 'cp %s %s' % (pipes.quote(input_archive),
- pipes.quote(output_archive))
-
- try:
- subprocess.check_output(
- j2objc_cmd, stderr=subprocess.STDOUT, shell=True, env=cmd_env)
- except OSError as e:
- raise Exception(
- 'executing command failed: %s (%s)' % (j2objc_cmd, e.strerror))
-
- # "Touch" the output file.
- # Prevents a pre-Xcode-8 bug in which passing zero-date archive files to ld
- # would cause ld to error.
- os.utime(output_archive, None)
-
-
-def BuildArtifactSourceTree(files, file_open=open):
- """Builds a dependency tree using from dependency mapping files.
-
- Args:
- files: A comma separated list of dependency mapping files.
- file_open: Reference to the builtin open function so it may be overridden for
- testing.
-
- Returns:
- A dict mapping build artifacts (possibly generated source files) to the
- corresponding direct dependent source files.
- """
- tree = dict()
- if not files:
- return tree
- for filename in files.split(','):
- with file_open(filename, '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
-
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
-
- # TODO(rduan): Remove these three flags once J2ObjC compile actions are fully
- # moved to the edges.
- parser.add_argument(
- '--input_files',
- help=('The comma-separated file paths of translated source files to '
- 'prune.'))
- parser.add_argument(
- '--output_files',
- help='The comma-separated file paths of pruned source files to write to.')
- parser.add_argument(
- '--objc_file_path',
- help='The file path which represents a directory where the generated ObjC'
- ' files reside')
-
- parser.add_argument(
- '--input_archive',
- help=('The path of the translated archive to prune.'))
- parser.add_argument(
- '--output_archive',
- help='The path of the pruned archive file to write to.')
- parser.add_argument(
- '--dummy_archive',
- help='The dummy archive file that contains no symbol.')
- parser.add_argument(
- '--dependency_mapping_files',
- help='The comma-separated file paths of dependency mapping files.')
- parser.add_argument(
- '--header_mapping_files',
- help='The comma-separated file paths of header mapping files.')
- parser.add_argument(
- '--archive_source_mapping_files',
- help='The comma-separated file paths of archive to source mapping files.'
- 'These mapping files should contain mappings between the '
- 'translated source files and the archive file compiled from those '
- 'source files.')
- parser.add_argument(
- '--entry_classes',
- help=('The comma-separated list of Java entry classes to be used as entry'
- ' point of the dead code anlysis.'))
- parser.add_argument(
- '--xcrunwrapper',
- help=('The xcrun wrapper script.'))
-
- 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')
- if args.input_archive and args.output_archive:
- PruneArchiveFile(
- args.input_archive,
- args.output_archive,
- args.dummy_archive,
- args.dependency_mapping_files,
- args.header_mapping_files,
- args.archive_source_mapping_files,
- args.entry_classes,
- args.xcrunwrapper)
- else:
- # TODO(rduan): Remove once J2ObjC compile actions are fully moved to the
- # edges.
- PruneSourceFiles(
- args.input_files,
- args.output_files,
- args.dependency_mapping_files,
- args.header_mapping_files,
- args.entry_classes,
- args.objc_file_path)