| #!/usr/bin/python | |
| # Copyright 2018 The Tulsi 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. | |
| """Symlinks files generated by Bazel into _tulsi-includes for indexing.""" | |
| import json | |
| import os | |
| import shutil | |
| import sys | |
| class Installer(object): | |
| """Symlinks generated files into _tulsi-includes.""" | |
| def __init__(self, bazel_exec_root, preserve_tulsi_includes=True, | |
| output_root=None): | |
| """Initializes the installer with the proper Bazel paths.""" | |
| self.bazel_exec_root = bazel_exec_root | |
| self.preserve_tulsi_includes = preserve_tulsi_includes | |
| # The folder must begin with an underscore as otherwise Bazel will delete | |
| # it whenever it builds. See tulsi_aspects.bzl for futher explanation. | |
| if not output_root: | |
| output_root = bazel_exec_root | |
| self.tulsi_root = os.path.join(output_root, '_tulsi-includes') | |
| def PrepareTulsiIncludes(self): | |
| """Creates tulsi includes, possibly removing the old folder.""" | |
| tulsi_root = self.tulsi_root | |
| if not self.preserve_tulsi_includes and os.path.exists(tulsi_root): | |
| shutil.rmtree(tulsi_root) | |
| if not os.path.exists(tulsi_root): | |
| os.mkdir(tulsi_root) | |
| def InstallForTulsiouts(self, tulsiouts): | |
| """Creates tulsi includes and symlinks generated sources.""" | |
| self.PrepareTulsiIncludes() | |
| for file_path in tulsiouts: | |
| try: | |
| output_data = json.load(open(file_path)) | |
| self.InstallForData(output_data) | |
| except (ValueError, IOError) as e: | |
| print('Failed to load output data file "%s". %s' | |
| % (file_path, e)) | |
| def InstallForData(self, output_data): | |
| """Symlinks generated sources present in the output_data.""" | |
| bazel_exec_root = self.bazel_exec_root | |
| tulsi_root = self.tulsi_root | |
| for gs in output_data['generated_sources']: | |
| real_path, link_path = gs | |
| src = os.path.join(bazel_exec_root, real_path) | |
| # Bazel outputs are not guaranteed to be created if nothing references | |
| # them. This check skips the processing if an output was declared | |
| # but not created. | |
| if not os.path.exists(src): | |
| continue | |
| # The /x/x/ part is here to match the number of directory components | |
| # between tulsi root and bazel root. See tulsi_aspects.bzl for futher | |
| # explanation. | |
| dst = os.path.join(tulsi_root, 'x/x/', link_path) | |
| dst_dir = os.path.dirname(dst) | |
| if not os.path.exists(dst_dir): | |
| os.makedirs(dst_dir) | |
| # It's important to use lexists() here in case dst is a broken symlink | |
| # (in which case exists() would return False). | |
| if os.path.lexists(dst): | |
| # Don't need to do anything if the link hasn't changed. | |
| if os.readlink(dst) == src: | |
| continue | |
| # Link changed; must remove it otherwise os.symlink will fail. | |
| os.unlink(dst) | |
| os.symlink(src, dst) | |
| if __name__ == '__main__': | |
| if len(sys.argv) <= 2: | |
| sys.stderr.write('usage: %s <bazel exec root> <preserve tulsi includes?> ' | |
| '<.tulsiouts JSON files>\n' % sys.argv[0]) | |
| exit(1) | |
| preserve = sys.argv[2] == 'True' | |
| Installer(sys.argv[1], preserve).InstallForTulsiouts(sys.argv[3:]) |