blob: 9945425b8219eeef8d0cd9bb893e3e2d8478e7af [file] [log] [blame]
#!/usr/bin/python3
# Copyright 2017 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.
# ==============================================================================
# This .ycm_extra_conf will be picked up automatically for code completion using
# YouCompleteMe.
#
# See https://valloric.github.io/YouCompleteMe/ for instructions on setting up
# YouCompleteMe using Vim. This .ycm_extra_conf file also works with any other
# completion engine that uses YCMD (https://github.com/Valloric/ycmd).
#
# Code completion uses `bazel print_action` for the file and pulls out the
# included command line flags.
# ==============================================================================
import logging
import os
import pathlib
import re
import subprocess
BAZEL_PRINT_FLAGS = [
# Run bazel.
'bazel',
# Dump the text proto action on STDOUT.
'print_action',
# Be quiet.
'--noshow_progress',
'--noshow_loading_progress',
'--show_result=0',
# Don't build anything and keep going.
'--nobuild',
'--keep_going',
'--workspace_status_command=/bin/true',
# Execute a single arbitrary action for the file.
'--compile_one_dependency',
]
FLAG_PATTERN = re.compile(r'^\s+compiler_option: "([^"]*)"')
# Workspace path.
WORKSPACE_PATH = None
# Execution root.
EXECUTION_ROOT = None
def InitBazelConfig():
"""Initialize globals based on Bazel configuration.
Initialize COMPILATION_DATABASE_PATH, WORKSPACE_PATH, and
CANONICAL_SOURCE_FILE based on Bazel. These values are not expected to change
during the session.
"""
global WORKSPACE_PATH
global EXECUTION_ROOT
EXECUTION_ROOT = pathlib.Path(
os.fsdecode(
subprocess.check_output(['bazel', 'info', 'execution_root']).strip()))
WORKSPACE_PATH = pathlib.Path(
os.fsdecode(
subprocess.check_output(['bazel', 'info', 'workspace']).strip()))
def ExpandAndNormalizePath(filepath, basepath=None):
"""Resolves |filepath| relative to |basepath| and expands symlinks."""
if basepath is None:
basepath = WORKSPACE_PATH
if not filepath.is_absolute() and basepath:
filepath = basepath.joinpath(filepath)
return filepath.resolve()
def RelativePath(filename, root=None):
"""Resolves |filename| and returns a path relative to |root| if possible."""
if root is None:
root = WORKSPACE_PATH
path = ExpandAndNormalizePath(filename, basepath=root)
try:
return path.relative_to(root)
except ValueError:
return path
# Entrypoint for YouCompleteMe.
def Settings(**kwargs):
if kwargs['language'] != 'cfamily':
return {}
if EXECUTION_ROOT is None:
InitBazelConfig()
filename = RelativePath(pathlib.Path(kwargs.pop('filename')))
logging.info('Calling bazel print_action for %s', filename)
try:
action = subprocess.check_output(
BAZEL_PRINT_FLAGS + [str(filename)], stderr=subprocess.STDOUT).decode()
except subprocess.CalledProcessError as err:
logging.error('Error calling bazel %s (%s)', err, err.output)
return {}
flags = [
FLAG_PATTERN.match(line).group(1)
for line in action.split('\n')
if FLAG_PATTERN.match(line)
]
logging.info('Found flags %s', flags)
return {
# Always indicate C++.
'flags': ['-x', 'c++'] + flags,
'include_paths_relative_to_dir': EXECUTION_ROOT,
}
# For testing.
if __name__ == '__main__':
import sys
print(Settings(filename=sys.argv[1]))