#!/usr/bin/env python # # Copyright (C) 2011, 2012 Strahinja Val Markovic # # This file is part of YouCompleteMe. # # YouCompleteMe is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # YouCompleteMe 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with YouCompleteMe. If not, see . import imp import os import ycm_core import random import string import sys import vimsupport YCM_EXTRA_CONF_FILENAME = '.ycm_extra_conf.py' NO_EXTRA_CONF_FILENAME_MESSAGE = ('No {0} file detected, so no compile flags ' 'are available. Thus no semantic support for C/C++/ObjC/ObjC++.').format( YCM_EXTRA_CONF_FILENAME ) GLOBAL_YCM_EXTRA_CONF_FILE = os.path.expanduser( vimsupport.GetVariableValue( "g:ycm_global_ycm_extra_conf" ) ) class Flags( object ): def __init__( self ): # It's caches all the way down... self.flags_for_file = {} self.flags_module_for_file = {} self.flags_module_for_flags_module_file = {} self.special_clang_flags = _SpecialClangIncludes() self.no_extra_conf_file_warning_posted = False def FlagsForFile( self, filename ): try: return self.flags_for_file[ filename ] except KeyError: flags_module = self._FlagsModuleForFile( filename ) if not flags_module: if not self.no_extra_conf_file_warning_posted: vimsupport.PostVimMessage( NO_EXTRA_CONF_FILENAME_MESSAGE ) self.no_extra_conf_file_warning_posted = True return None results = flags_module.FlagsForFile( filename ) if not results.get( 'flags_ready', True ): return None results[ 'flags' ] += self.special_clang_flags sanitized_flags = _SanitizeFlags( results[ 'flags' ] ) if results[ 'do_cache' ]: self.flags_for_file[ filename ] = sanitized_flags return sanitized_flags def _FlagsModuleForFile( self, filename ): try: return self.flags_module_for_file[ filename ] except KeyError: flags_module_file = _FlagsModuleSourceFileForFile( filename ) if not flags_module_file: return None try: flags_module = self.flags_module_for_flags_module_file[ flags_module_file ] except KeyError: sys.path.insert( 0, _DirectoryOfThisScript() ) flags_module = imp.load_source( _RandomName(), flags_module_file ) del sys.path[ 0 ] self.flags_module_for_flags_module_file[ flags_module_file ] = flags_module self.flags_module_for_file[ filename ] = flags_module return flags_module def _FlagsModuleSourceFileForFile( filename ): """For a given filename, finds its nearest YCM_EXTRA_CONF_FILENAME file that will compute the flags necessary to compile the file. If no YCM_EXTRA_CONF_FILENAME file could be found, try to use GLOBAL_YCM_EXTRA_CONF_FILE instead. If that also fails, return None. Uses the global ycm_extra_conf file if one is set.""" ycm_conf_file = None parent_folder = os.path.dirname( filename ) old_parent_folder = '' while True: current_file = os.path.join( parent_folder, YCM_EXTRA_CONF_FILENAME ) if os.path.exists( current_file ): ycm_conf_file = current_file break old_parent_folder = parent_folder parent_folder = os.path.dirname( parent_folder ) if parent_folder is old_parent_folder: break if ( not ycm_conf_file and GLOBAL_YCM_EXTRA_CONF_FILE and os.path.exists( GLOBAL_YCM_EXTRA_CONF_FILE ) ): ycm_conf_file = GLOBAL_YCM_EXTRA_CONF_FILE return ycm_conf_file def _RandomName(): """Generates a random module name.""" return ''.join( random.choice( string.ascii_lowercase ) for x in range( 15 ) ) def _SanitizeFlags( flags ): """Drops unsafe flags. Currently these are only -arch flags; they tend to crash libclang.""" sanitized_flags = [] saw_arch = False for i, flag in enumerate( flags ): if flag == '-arch': saw_arch = True continue elif flag.startswith( '-arch' ): continue elif saw_arch: saw_arch = False continue sanitized_flags.append( flag ) vector = ycm_core.StringVec() for flag in sanitized_flags: vector.append( flag ) return vector def _SpecialClangIncludes(): libclang_dir = os.path.dirname( ycm_core.__file__ ) path_to_includes = os.path.join( libclang_dir, 'clang_includes' ) return [ '-I', path_to_includes ] def _DirectoryOfThisScript(): return os.path.dirname( os.path.abspath( __file__ ) )