Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions utils/YouCompleteMe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#!/usr/bin/env python
#
# Copyright (C) 2015 Universite Pierre et marie Curie (UPMC)
#
# You should have received a copy of the GNU General Public License
# along with ns3. If not, see <http://www.gnu.org/licenses/>.
#
# Author: Matthieu Coudron <[email protected]>
#

import os
import ycm_core
import logging
import subprocess

# This script allows to use YouCompleteMe to provide completion in (neo)vim for the ns3
# source files.
# To enable it, hard symlink (symbolic won't work) or copy this file into the ns3 root folder and rename it to
# .ycm_extra_conf.py
# The script needs ns3 to be compiled with clang, in order to produce
# the "build/compile_commands.json"
# The script should work out of the box, otherwise try changing the
# compilation_database_folder variable

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

# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
compilation_database_folder = os.path.join(DirectoryOfThisScript(),'build/')

# uncomment the file handler a few lines below to create the logfile
LOG_FILENAME = '/tmp/ns_ycm.log'


# Set up a specific logger with our desired output level
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

# Add the log message handler to the logger
handler = logging.FileHandler(LOG_FILENAME)
log.addHandler(handler)


def GetClangStandardHeaders():
"""
Without this, YCM triggers errors on standards <iostream> files etc...
It parses the output of
"""
system_flags = []
# https://github.com/Valloric/YouCompleteMe/issues/1478
# echo | clang -stdlib=libc++ -v -E -x c++ -
cmd = "echo | clang -stdlib=libc++ -v -E -x c++ -- "
cmd += "|sed -n '/#include <...> search starts here/,/End of search list/{//!p}'"
log.info("Running command:\n%s" % cmd)
res = subprocess.check_output(cmd, shell=True)
log.debug("Result:\n%s" % res)
res = res.decode()
system_headers = res.splitlines()

for include in system_headers:
system_flags.append('-isystem')
log.info("Appends include: %s" % include)
system_flags.append(include.strip())

return system_flags


if os.path.exists( compilation_database_folder ):
log.info("Loading database")
database = ycm_core.CompilationDatabase( compilation_database_folder )
log.info("database should be loaded: %r" % database)
else:
database = None

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



def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
log.info("Make flags absolute with directory [%s]" % 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' ]



def MatchHeaderToSource( filename ):
"""
NS3 headers are copied into a specific folder thus stock
YCM functions don't fit the bill
Return the .cc associated with the .h
"""
log.debug("MatchHeaderToSource: %s" % filename)
filename = os.path.basename(filename)
filename = os.path.splitext( filename )[ 0 ]
filename += ".cc"
log.debug("Looking for implementation of %s" % filename)
# be careful it is the python 2.7 version and as such does not return bytes
try:
match = subprocess.check_output("find src -name %s" % (filename), shell=True)
match=match.strip()
except Exception as e:
log.error("Could not map header to implementation: %s:" % e)

match = os.path.abspath(match)
return match


def GetCompilationInfoForFile( filename ):
# The compilation_commands.json file generated by waf does not have entries
# for header files. So we do our best by asking the db for flags for a
# corresponding source file, if any. If one exists, the flags for that file
# should be good enough.
log.debug("GetCompilationInfoForFile called with parameter=%s" % filename)
final_name = filename
if IsHeaderFile( filename ):
final_name = MatchHeaderToSource( filename)

log.info("compilation_info for =%s" % final_name)

return database.GetCompilationInfoForFile( final_name )


# This is the entry point; this function is called by ycmd to produce flags for
# a file.
def FlagsForFile( filename, **kwargs ):
log.debug("== Getting flags for file %s" % filename)
if database:
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object
compilation_info = GetCompilationInfoForFile( filename )
if not compilation_info:
log.warn("Could not find info for this one")
return None

log.debug(" flags before being made relative %s" % compilation_info.compiler_flags_)

# this should not be necessary, I leave it just in case
flags = MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_ )

flags += GetClangStandardHeaders();

log.debug("Open filename '%s': found flags=%s\n" % (filename, flags))
return {
'flags': flags,
'do_cache': True
}