#!/usr/bin/env python # Copyright (C) 2013 Danilo Cesar Lemes de Paula # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import os import re import subprocess import sys # It's very likely that this script is a symlink somewhere in the WebKit directory, # so we try to find the actual script location so that we can locate the tools # directory. original_file = __file__[:-1] if __file__.endswith(".pyc") else __file__ if os.path.islink(original_file): parent_folder = os.path.abspath(os.path.dirname(original_file)) link_file = os.path.join(parent_folder, os.readlink(original_file)) __tools_directory = os.path.dirname(link_file) else: __tools_directory = os.path.dirname(original_file) sys.path.insert(0, os.path.abspath(__tools_directory)) import common MAKE_TRACE_FILE_NAME = "ycm-make-trace" FLAGS_PRECEDING_PATHS = ['-isystem', '-I', '-iquote', '--sysroot='] def transform_relative_paths_to_absolute_paths(arguments, build_path): result = [] make_next_absolute = False for argument in arguments: if make_next_absolute: make_next_absolute = False if not argument.startswith('/'): argument = os.path.join(build_path, argument) elif argument in FLAGS_PRECEDING_PATHS: # Some flags precede the path in the list. For those we make the # next argument absolute. if argument == flag: make_next_absolute = True else: # Some argument contain the flag and the path together. For these # we parse the argument out of the path. for flag in FLAGS_PRECEDING_PATHS: if argument.startswith(flag): argument = flag + os.path.join(build_path, argument[len(flag):]) break result.append(argument) return result def create_make_trace_file(build_path): print "Creating make trace file for YouCompleteMe, might take a few seconds" os.chdir(build_path) subprocess.call("make -n -s > %s" % MAKE_TRACE_FILE_NAME, shell=True) def make_trace_file_up_to_date(build_path): trace_file = os.path.join(build_path, MAKE_TRACE_FILE_NAME) if not os.path.isfile(trace_file): return False makefile = os.path.join(build_path, 'GNUmakefile') if os.path.getmtime(trace_file) < os.path.getctime(makefile): return False return True def ensure_make_trace_file(build_path): if make_trace_file_up_to_date(build_path): return True create_make_trace_file(build_path) return make_trace_file_up_to_date(build_path) def get_compilation_flags_from_build_commandline(command_line, build_path): flags = re.findall("-[I,D,p,O][\w,\.,/,-]*(?:=\"?\w*\"?)?", command_line) return transform_relative_paths_to_absolute_paths(flags, build_path) def get_compilation_flags_for_file(build_path, filename): trace_file_path = os.path.join(build_path, MAKE_TRACE_FILE_NAME) try: trace_file = open(trace_file_path) lines = trace_file.readlines() trace_file.close() except: print "==== Error reading %s file", trace_file_path return [] basename = os.path.basename(filename) for line in lines: if line.find(basename) != -1 and (line.find("CC") != -1 or line.find("CXX") != -1): return get_compilation_flags_from_build_commandline(line, build_path) print "Could not find flags for %s" % filename return [] def FlagsForFile(filename, **kwargs): """This is the main entry point for YCM. Its interface is fixed. Args: filename: (String) Path to source file being edited. Returns: (Dictionary) 'flags': (List of Strings) Command line flags. 'do_cache': (Boolean) True if the result should be cached. """ result = {'flags': ['-std=c++11', '-x', 'c++'], 'do_cache': True} # Headers can't be built, so we get the source file flags instead. if filename.endswith('.h'): filename = filename[:-2] + ".cpp" if not os.path.exists(filename): return result # Force config.h file inclusion, for GLib macros. result['flags'].append("-includeconfig.h") build_path = common.get_build_path(fatal=False) if not build_path: print "Could not find WebKit build path." return result if not ensure_make_trace_file(build_path): print "Could not create make trace file" return result result['flags'].extend(get_compilation_flags_for_file(build_path, filename)) return result if __name__ == "__main__": import sys if len(sys.argv) >= 2: print FlagsForFile(sys.argv[1])