blob: 8ab1ff7e04a42e653da589294e9718831e06ac8e [file] [log] [blame]
#!/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 bazel-tulsi-includes for indexing."""
from __future__ import print_function
import json
import os
import shutil
import sys
class Installer(object):
"""Symlinks generated files into bazel-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, 'bazel-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:])