Skip to content

Commit

Permalink
Make relative paths in flags from extra conf absolute
Browse files Browse the repository at this point in the history
  • Loading branch information
micbou committed Jul 17, 2017
1 parent ebf04ba commit 92991db
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 90 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ The return value must be one of the following:

- `flags`: (mandatory) a list of compiler flags.

- `working_directory': (optional) the directory used to convert relative paths
in the list of flags to absolute. Defaults to the directory containing the
`.ycm_extra_conf.py` file.

- `do_cache`: (optional) a boolean indicating whether or not the result of
this call (i.e. the list of flags) should be cached for this file name.
Defaults to `True`. If unsure, the default is almost always correct.
Expand Down
47 changes: 6 additions & 41 deletions cpp/ycm/.ycm_extra_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,39 +102,6 @@

SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]

def DirectoryOfThisScript():
return os.path.dirname( os.path.abspath( __file__ ) )


def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
if not working_directory:
return list( flags )
new_flags = []
make_next_absolute = False
path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
for flag in flags:
new_flag = flag

if make_next_absolute:
make_next_absolute = False
if not flag.startswith( '/' ):
new_flag = os.path.join( working_directory, flag )

for path_flag in path_flags:
if flag == path_flag:
make_next_absolute = True
break

if flag.startswith( path_flag ):
path = flag[ len( path_flag ): ]
new_flag = path_flag + os.path.join( working_directory, path )
break

if new_flag:
new_flags.append( new_flag )
return new_flags


def IsHeaderFile( filename ):
extension = os.path.splitext( filename )[ 1 ]
return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
Expand Down Expand Up @@ -166,19 +133,17 @@ def FlagsForFile( filename, **kwargs ):
if not compilation_info:
return None

final_flags = MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_ )

# NOTE: This is just for YouCompleteMe; it's highly likely that your project
# does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR
# ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT.
try:
final_flags.remove( '-stdlib=libc++' )
except ValueError:
pass
else:
relative_to = DirectoryOfThisScript()
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )

return { 'flags': final_flags }
return {
'flags': compilation_info.compiler_flags_,
'working_directory': compilation_info.compiler_working_dir_
}

return { 'flags': flags }
12 changes: 10 additions & 2 deletions ycmd/completers/cpp/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,17 @@ def _CallExtraConfFlagsForFile( module, filename, client_data ):
# For the sake of backwards compatibility, we need to first check whether the
# FlagsForFile function in the extra conf module even allows keyword args.
if inspect.getargspec( module.FlagsForFile ).keywords:
return module.FlagsForFile( filename, client_data = client_data )
flags = module.FlagsForFile( filename, client_data = client_data )
else:
return module.FlagsForFile( filename )
flags = module.FlagsForFile( filename )

# If no working directory is specified, consider the paths to be relative to
# the directory containing the .ycm_extra_conf.py file.
working_directory = flags.get( 'working_directory',
os.path.dirname( inspect.getfile( module ) ) )
flags[ 'flags' ] = _MakeRelativePathsInFlagsAbsolute( flags[ 'flags' ],
working_directory )
return flags


def _SysRootSpecifedIn( flags ):
Expand Down
152 changes: 105 additions & 47 deletions ycmd/tests/clang/flags_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,102 +22,160 @@
# Not installing aliases from python-future; it's unreliable and slow.
from builtins import * # noqa

import contextlib
import os

from nose.tools import eq_, ok_
from ycmd.completers.cpp import flags
from mock import patch, Mock
from mock import patch, MagicMock, Mock
from types import ModuleType
from ycmd.tests.test_utils import MacOnly
from ycmd.responses import NoExtraConfDetected
from ycmd.tests.clang import TemporaryClangProject, TemporaryClangTestDir

from hamcrest import assert_that, calling, contains, has_item, not_, raises


@patch( 'ycmd.extra_conf_store.ModuleForSourceFile', return_value = Mock() )
def FlagsForFile_FlagsNotReady_test( *args ):
fake_flags = {
'flags': [ ],
'flags_ready': False
}
@contextlib.contextmanager
def MockExtraConfModule( flags_for_file_function,
pathname = '.ycm_extra_conf.py' ):
module = MagicMock( spec = ModuleType )
module.__file__ = pathname
module.FlagsForFile = flags_for_file_function
with patch( 'ycmd.extra_conf_store.ModuleForSourceFile',
return_value = module ):
yield


def FlagsForFile_FlagsNotReady_test():
flags_object = flags.Flags()

def FlagsForFile( filename ):
return {
'flags': [],
'flags_ready': False
}

with patch( 'ycmd.completers.cpp.flags._CallExtraConfFlagsForFile',
return_value = fake_flags ):
flags_object = flags.Flags()
with MockExtraConfModule( FlagsForFile ):
flags_list = flags_object.FlagsForFile( '/foo', False )
eq_( list( flags_list ), [ ] )
eq_( list( flags_list ), [] )


@patch( 'ycmd.extra_conf_store.ModuleForSourceFile', return_value = Mock() )
def FlagsForFile_BadNonUnicodeFlagsAreAlsoRemoved_test( *args ):
fake_flags = {
'flags': [ bytes( b'-c' ), '-c', bytes( b'-foo' ), '-bar' ]
}
flags_object = flags.Flags()

with patch( 'ycmd.completers.cpp.flags._CallExtraConfFlagsForFile',
return_value = fake_flags ):
flags_object = flags.Flags()
def FlagsForFile( filename ):
return {
'flags': [ bytes( b'-c' ), '-c', bytes( b'-foo' ), '-bar' ]
}

with MockExtraConfModule( FlagsForFile ):
flags_list = flags_object.FlagsForFile( '/foo', False )
eq_( list( flags_list ), [ '-foo', '-bar' ] )


@patch( 'ycmd.extra_conf_store.ModuleForSourceFile', return_value = Mock() )
def FlagsForFile_FlagsCachedByDefault_test( *args ):
def FlagsForFile_FlagsCachedByDefault_test():
flags_object = flags.Flags()

results = { 'flags': [ '-x', 'c' ] }
with patch( 'ycmd.completers.cpp.flags._CallExtraConfFlagsForFile',
return_value = results ):
def FlagsForFile( filename ):
return {
'flags': [ '-x', 'c' ]
}

with MockExtraConfModule( FlagsForFile ):
flags_list = flags_object.FlagsForFile( '/foo', False )
assert_that( flags_list, contains( '-x', 'c' ) )

results[ 'flags' ] = [ '-x', 'c++' ]
with patch( 'ycmd.completers.cpp.flags._CallExtraConfFlagsForFile',
return_value = results ):
def FlagsForFile( filename ):
return {
'flags': [ '-x', 'c++' ]
}

with MockExtraConfModule( FlagsForFile ):
flags_list = flags_object.FlagsForFile( '/foo', False )
assert_that( flags_list, contains( '-x', 'c' ) )


@patch( 'ycmd.extra_conf_store.ModuleForSourceFile', return_value = Mock() )
def FlagsForFile_FlagsNotCachedWhenDoCacheIsFalse_test( *args ):
def FlagsForFile_FlagsNotCachedWhenDoCacheIsFalse_test():
flags_object = flags.Flags()

results = {
'flags': [ '-x', 'c' ],
'do_cache': False
}
with patch( 'ycmd.completers.cpp.flags._CallExtraConfFlagsForFile',
return_value = results ):
def FlagsForFile( filename ):
return {
'flags': [ '-x', 'c' ],
'do_cache': False
}

with MockExtraConfModule( FlagsForFile ):
flags_list = flags_object.FlagsForFile( '/foo', False )
assert_that( flags_list, contains( '-x', 'c' ) )

results[ 'flags' ] = [ '-x', 'c++' ]
with patch( 'ycmd.completers.cpp.flags._CallExtraConfFlagsForFile',
return_value = results ):
def FlagsForFile( filename ):
return {
'flags': [ '-x', 'c++' ]
}

with MockExtraConfModule( FlagsForFile ):
flags_list = flags_object.FlagsForFile( '/foo', False )
assert_that( flags_list, contains( '-x', 'c++' ) )


@patch( 'ycmd.extra_conf_store.ModuleForSourceFile', return_value = Mock() )
def FlagsForFile_FlagsCachedWhenDoCacheIsTrue_test( *args ):
def FlagsForFile_FlagsCachedWhenDoCacheIsTrue_test():
flags_object = flags.Flags()

results = {
'flags': [ '-x', 'c' ],
'do_cache': True
}
with patch( 'ycmd.completers.cpp.flags._CallExtraConfFlagsForFile',
return_value = results ):
def FlagsForFile( filename ):
return {
'flags': [ '-x', 'c' ],
'do_cache': True
}

with MockExtraConfModule( FlagsForFile ):
flags_list = flags_object.FlagsForFile( '/foo', False )
assert_that( flags_list, contains( '-x', 'c' ) )

results[ 'flags' ] = [ '-x', 'c++' ]
with patch( 'ycmd.completers.cpp.flags._CallExtraConfFlagsForFile',
return_value = results ):
def FlagsForFile( filename ):
return {
'flags': [ '-x', 'c++' ]
}

with MockExtraConfModule( FlagsForFile ):
flags_list = flags_object.FlagsForFile( '/foo', False )
assert_that( flags_list, contains( '-x', 'c' ) )


def FlagsForFile_FlagsRelativeToExtraConfDirectory_test():
flags_object = flags.Flags()

def FlagsForFile( filename ):
return {
'flags': [ '-x', 'c', '-I', 'header' ]
}

with MockExtraConfModule( FlagsForFile,
pathname = '/project_root/.ycm_extra_conf.py' ):
flags_list = flags_object.FlagsForFile( '/foo', False )
assert_that( flags_list,
contains( '-x', 'c',
'-I', os.path.normpath( '/project_root/header' ) ) )


def FlagsForFile_FlagsRelativeToWorkingDirectory_test():
flags_object = flags.Flags()

def FlagsForFile( filename ):
return {
'flags': [ '-x', 'c', '-I', 'header' ],
'working_directory': '/working_dir/'
}

with MockExtraConfModule( FlagsForFile,
pathname = '/project_root/.ycm_extra_conf.py' ):
flags_list = flags_object.FlagsForFile( '/foo', False )
assert_that( flags_list,
contains( '-x', 'c',
'-I', os.path.normpath( '/working_dir/header' ) ) )


def RemoveUnusedFlags_Passthrough_test():
eq_( [ '-foo', '-bar' ],
flags._RemoveUnusedFlags( [ '-foo', '-bar' ], 'file' ) )
Expand Down

0 comments on commit 92991db

Please sign in to comment.