| #!/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.""" |
| |
| from __future__ import print_function |
| 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) < 3: |
| sys.stderr.write('usage: %s <bazel exec root> ' |
| '<.tulsiouts JSON files>\n' % sys.argv[0]) |
| exit(1) |
| |
| Installer(sys.argv[1]).InstallForTulsiouts(sys.argv[2:]) |