Now using new ycm_client_support shared lib

This means we can now load just ycm_client_support (which is a much smaller
library) into Vim and ycm_core into ycmd. Since ycm_client_support never depends
on libclang.so, we never have to load that into Vim which makes things much,
much easier.
This commit is contained in:
Strahinja Val Markovic 2013-10-15 11:19:56 -07:00
parent 016434e846
commit 436017bd4d
16 changed files with 221 additions and 66 deletions

View File

@ -58,7 +58,7 @@ function! youcompleteme#Enable()
if !pyeval( 'base.CompatibleWithYcmCore()')
echohl WarningMsg |
\ echomsg "YouCompleteMe unavailable: ycm_core too old, PLEASE RECOMPILE ycm_core" |
\ echomsg "YouCompleteMe unavailable: YCM support libs too old, PLEASE RECOMPILE" |
\ echohl None
return
endif

View File

@ -26,8 +26,8 @@ cmake_minimum_required( VERSION 2.8 )
project( BoostParts )
set( Python_ADDITIONAL_VERSIONS 2.7 2.6 2.5 )
find_package( PythonLibs 2.5 REQUIRED )
set( Python_ADDITIONAL_VERSIONS 2.7 2.6 )
find_package( PythonLibs 2.6 REQUIRED )
if ( NOT PYTHONLIBS_VERSION_STRING VERSION_LESS "3.0.0" )
message( FATAL_ERROR

View File

@ -17,10 +17,12 @@
cmake_minimum_required( VERSION 2.8 )
project( ycm_core )
project( ycm_support_libs )
set( CLIENT_LIB "ycm_client_support" )
set( SERVER_LIB "ycm_core" )
set( Python_ADDITIONAL_VERSIONS 2.7 2.6 2.5 )
find_package( PythonLibs 2.5 REQUIRED )
set( Python_ADDITIONAL_VERSIONS 2.7 2.6 )
find_package( PythonLibs 2.6 REQUIRED )
if ( NOT PYTHONLIBS_VERSION_STRING VERSION_LESS "3.0.0" )
message( FATAL_ERROR
@ -138,15 +140,15 @@ include_directories(
${CLANG_INCLUDES_DIR}
)
file( GLOB_RECURSE SOURCES *.h *.cpp )
file( GLOB_RECURSE SERVER_SOURCES *.h *.cpp )
# The test sources are a part of a different target, so we remove them
# The CMakeFiles cpp file is picked up when the user creates an in-source build,
# and we don't want that.
file( GLOB_RECURSE to_remove tests/*.h tests/*.cpp CMakeFiles/*.cpp )
# and we don't want that. We also remove client-specific code
file( GLOB_RECURSE to_remove tests/*.h tests/*.cpp CMakeFiles/*.cpp *client* )
if( to_remove )
list( REMOVE_ITEM SOURCES ${to_remove} )
list( REMOVE_ITEM SERVER_SOURCES ${to_remove} )
endif()
if ( USE_CLANG_COMPLETER )
@ -158,7 +160,7 @@ else()
file( GLOB_RECURSE to_remove_clang ClangCompleter/*.h ClangCompleter/*.cpp )
if( to_remove_clang )
list( REMOVE_ITEM SOURCES ${to_remove_clang} )
list( REMOVE_ITEM SERVER_SOURCES ${to_remove_clang} )
endif()
endif()
@ -217,11 +219,33 @@ endif()
#############################################################################
add_library( ${PROJECT_NAME} SHARED
${SOURCES}
# We don't actually need all of the files this picks up, just the ones needed by
# PythonSupport.cpp. But this is easier to maintain and dead code elemination
# will remove unused code.
file( GLOB CLIENT_SOURCES *.h *.cpp )
file( GLOB SERVER_SPECIFIC *ycm_core* )
if( SERVER_SPECIFIC )
list( REMOVE_ITEM CLIENT_SOURCES ${SERVER_SPECIFIC} )
endif()
add_library( ${CLIENT_LIB} SHARED
${CLIENT_SOURCES}
)
target_link_libraries( ${PROJECT_NAME}
target_link_libraries( ${CLIENT_LIB}
BoostParts
${PYTHON_LIBRARIES}
${EXTRA_LIBS}
)
#############################################################################
add_library( ${SERVER_LIB} SHARED
${SERVER_SOURCES}
)
target_link_libraries( ${SERVER_LIB}
BoostParts
${PYTHON_LIBRARIES}
${LIBCLANG_TARGET}
@ -231,35 +255,43 @@ target_link_libraries( ${PROJECT_NAME}
if( LIBCLANG_TARGET )
if( NOT WIN32 )
add_custom_command(
TARGET ${PROJECT_NAME}
TARGET ${SERVER_LIB}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${LIBCLANG_TARGET}" "$<TARGET_FILE_DIR:${PROJECT_NAME}>"
COMMAND ${CMAKE_COMMAND} -E copy "${LIBCLANG_TARGET}" "$<TARGET_FILE_DIR:${SERVER_LIB}>"
)
else()
add_custom_command(
TARGET ${PROJECT_NAME}
TARGET ${SERVER_LIB}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${PATH_TO_LLVM_ROOT}/bin/libclang.dll" "$<TARGET_FILE_DIR:${PROJECT_NAME}>")
COMMAND ${CMAKE_COMMAND} -E copy "${PATH_TO_LLVM_ROOT}/bin/libclang.dll" "$<TARGET_FILE_DIR:${SERVER_LIB}>")
endif()
endif()
#############################################################################
# Convenience target that builds both support libs.
add_custom_target( ${PROJECT_NAME}
DEPENDS ${CLIENT_LIB} ${SERVER_LIB} )
#############################################################################
# Things are a bit different on Macs when using an external libclang.dylib; here
# we want to make sure we use @loader_path/libclang.dylib instead of
# @rpath/libclang.dylib in the final ycm_core.so. If we use the @rpath version,
# then it may load the system libclang which the user explicitely does not want
# (otherwise the user would specify USE_SYSTEM_LIBCLANG). With @loader_path, we
# make sure that only the libclang.dylib present in the same directory as our
# ycm_core.so is used.
# @rpath/libclang.dylib in the final ycm_core.so. If we use the
# @rpath version, then it may load the system libclang which the user
# explicitely does not want (otherwise the user would specify
# USE_SYSTEM_LIBCLANG). With @loader_path, we make sure that only the
# libclang.dylib present in the same directory as our ycm_core.so
# is used.
if ( EXTERNAL_LIBCLANG_PATH AND APPLE )
add_custom_command( TARGET ${PROJECT_NAME}
add_custom_command( TARGET ${SERVER_LIB}
POST_BUILD
COMMAND install_name_tool
"-change"
"@rpath/libclang.dylib"
"@loader_path/libclang.dylib"
"$<TARGET_FILE:${PROJECT_NAME}>"
"$<TARGET_FILE:${SERVER_LIB}>"
)
endif()
@ -268,19 +300,24 @@ endif()
# We don't want the "lib" prefix, it can screw up python when it tries to search
# for our module
set_target_properties( ${PROJECT_NAME} PROPERTIES PREFIX "")
set_target_properties( ${CLIENT_LIB} PROPERTIES PREFIX "")
set_target_properties( ${SERVER_LIB} PROPERTIES PREFIX "")
if ( WIN32 OR CYGWIN )
# This is the extension for compiled Python modules on Windows
set_target_properties( ${PROJECT_NAME} PROPERTIES SUFFIX ".pyd")
set_target_properties( ${CLIENT_LIB} PROPERTIES SUFFIX ".pyd")
set_target_properties( ${SERVER_LIB} PROPERTIES SUFFIX ".pyd")
else()
# Even on macs, we want a .so extension instead of a .dylib which is what
# cmake would give us by default. Python won't recognize a .dylib as a module,
# but it will recognize a .so
set_target_properties( ${PROJECT_NAME} PROPERTIES SUFFIX ".so")
set_target_properties( ${CLIENT_LIB} PROPERTIES SUFFIX ".so")
set_target_properties( ${SERVER_LIB} PROPERTIES SUFFIX ".so")
endif()
set_target_properties( ${PROJECT_NAME} PROPERTIES
set_target_properties( ${CLIENT_LIB} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../python )
set_target_properties( ${SERVER_LIB} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../../python )
#############################################################################

View File

@ -28,7 +28,7 @@ endif()
add_subdirectory( gmock )
include_directories(
${ycm_core_SOURCE_DIR}
${ycm_support_libs_SOURCE_DIR}
)
include_directories(
@ -67,7 +67,8 @@ add_executable( ${PROJECT_NAME}
)
target_link_libraries( ${PROJECT_NAME}
ycm_core
${SERVER_LIB}
${CLIENT_LIB}
gmock )

26
cpp/ycm/versioning.cpp Normal file
View File

@ -0,0 +1,26 @@
// Copyright (C) 2013 Strahinja Val Markovic <val@markovic.io>
//
// 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 <http://www.gnu.org/licenses/>.
namespace YouCompleteMe {
int YcmCoreVersion() {
// We increment this every time when we want to force users to recompile
// ycm_core.
return 7;
}
} // namespace YouCompleteMe

22
cpp/ycm/versioning.h Normal file
View File

@ -0,0 +1,22 @@
// Copyright (C) 2013 Strahinja Val Markovic <val@markovic.io>
//
// 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 <http://www.gnu.org/licenses/>.
namespace YouCompleteMe {
int YcmCoreVersion();
} // namespace YouCompleteMe

View File

@ -0,0 +1,44 @@
// Copyright (C) 2013 Strahinja Val Markovic <val@markovic.io>
//
// 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 <http://www.gnu.org/licenses/>.
#include "IdentifierCompleter.h"
#include "PythonSupport.h"
#include "versioning.h"
#include <boost/python.hpp>
#include <boost/utility.hpp>
BOOST_PYTHON_MODULE(ycm_client_support)
{
using namespace boost::python;
using namespace YouCompleteMe;
// Necessary because of usage of the ReleaseGil class
PyEval_InitThreads();
def( "FilterAndSortCandidates", FilterAndSortCandidates );
def( "YcmCoreVersion", YcmCoreVersion );
}
// Boost.Thread forces us to implement this.
// We don't use any thread-specific (local) storage so it's fine to implement
// this as an empty function.
namespace boost {
void tss_cleanup_implemented() {}
};

View File

@ -17,6 +17,7 @@
#include "IdentifierCompleter.h"
#include "PythonSupport.h"
#include "versioning.h"
#ifdef USE_CLANG_COMPLETER
# include "ClangCompleter.h"
@ -32,8 +33,7 @@
#include <boost/utility.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
bool HasClangSupport()
{
bool HasClangSupport() {
#ifdef USE_CLANG_COMPLETER
return true;
#else
@ -41,13 +41,6 @@ bool HasClangSupport()
#endif // USE_CLANG_COMPLETER
}
int YcmCoreVersion()
{
// We increment this every time when we want to force users to recompile
// ycm_core.
return 6;
}
BOOST_PYTHON_MODULE(ycm_core)
{

View File

@ -37,11 +37,14 @@ let s:script_folder_path = escape( expand( '<sfile>:p:h' ), '\' )
function! s:HasYcmCore()
let path_prefix = s:script_folder_path . '/../python/'
if filereadable(path_prefix . 'ycm_core.so')
if filereadable(path_prefix . 'ycm_client_support.so') &&
\ filereadable(path_prefix . 'ycm_core.so')
return 1
elseif filereadable(path_prefix . 'ycm_core.pyd')
elseif filereadable(path_prefix . 'ycm_client_support.pyd') &&
\ filereadable(path_prefix . 'ycm_core.pyd')
return 1
elseif filereadable(path_prefix . 'ycm_core.dll')
elseif filereadable(path_prefix . 'ycm_client_support.dll')
\ filereadable(path_prefix . 'ycm_core.dll')
return 1
endif
return 0
@ -52,7 +55,8 @@ let g:ycm_check_if_ycm_core_present =
if g:ycm_check_if_ycm_core_present && !s:HasYcmCore()
echohl WarningMsg |
\ echomsg "ycm_core.[so|pyd|dll] not detected; you need to compile " .
\ echomsg "ycm_client_support.[so|pyd|dll] and " .
\ "ycm_core.[so|pyd|dll] not detected; you need to compile " .
\ "YCM before using it. Read the docs!" |
\ echohl None
finish

View File

@ -17,25 +17,15 @@
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import os
import re
import vim
from ycm import vimsupport
from ycm import utils
from ycm import user_options_store
import ycm_client_support
YCM_VAR_PREFIX = 'ycm_'
try:
import ycm_core
except ImportError as e:
vimsupport.PostVimMessage(
'Error importing ycm_core. Are you sure you have placed a version 3.2+ '
'libclang.[so|dll|dylib] in folder "{0}"? See the Installation Guide in '
'the docs. Full error: {1}'.format(
os.path.dirname( os.path.dirname( os.path.abspath( __file__ ) ) ),
str( e ) ) )
def BuildServerConf():
"""Builds a dictionary mapping YCM Vim user options to values. Option names
@ -152,11 +142,11 @@ def AdjustCandidateInsertionText( candidates ):
return new_candidates
COMPATIBLE_WITH_CORE_VERSION = 6
COMPATIBLE_WITH_CORE_VERSION = 7
def CompatibleWithYcmCore():
try:
current_core_version = ycm_core.YcmCoreVersion()
current_core_version = ycm_client_support.YcmCoreVersion()
except AttributeError:
return False

View File

@ -18,7 +18,7 @@
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import abc
import ycm_core
import ycm_client_support
from ycm.utils import ToUtf8IfNeeded, ForceSemanticCompletion
from ycm.completers.completer_utils import TriggersForFiletype
@ -207,7 +207,7 @@ class Completer( object ):
elif 'insertion_text' in candidates[ 0 ]:
sort_property = 'insertion_text'
matches = ycm_core.FilterAndSortCandidates(
matches = ycm_client_support.FilterAndSortCandidates(
candidates,
sort_property,
ToUtf8IfNeeded( query ) )

View File

@ -17,6 +17,18 @@
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from os import path
try:
import ycm_core
except ImportError as e:
raise RuntimeError(
'Error importing ycm_core. Are you sure you have placed a '
'version 3.2+ libclang.[so|dll|dylib] in folder "{0}"? '
'See the Installation Guide in the docs. Full error: {1}'.format(
path.realpath( path.join( path.abspath( __file__ ), '../../..' ) ),
str( e ) ) )
import atexit
import logging
import json
@ -28,6 +40,7 @@ from ycm import user_options_store
from ycm.server.responses import BuildExceptionResponse
from ycm import extra_conf_store
# num bytes for the request body buffer; request.json only works if the request
# size is less than this
bottle.Request.MEMFILE_MAX = 300 * 1024
@ -135,8 +148,6 @@ def LoadExtraConfFile():
@app.post( '/debug_info' )
def DebugInfo():
# This can't be at the top level because of possible extra conf preload
import ycm_core
LOGGER.info( 'Received debug info request' )
output = []

View File

@ -59,13 +59,14 @@ def Main():
if args.options_file:
options = json.load( open( args.options_file, 'r' ) )
user_options_store.SetAll( options )
# This ensures that ycm_core is not loaded before extra conf preload
# was run.
# This ensures that ycm_core is not loaded before extra conf
# preload was run.
YcmCoreSanityCheck()
extra_conf_store.CallGlobalExtraConfYcmCorePreloadIfExists()
# This can't be a top-level import because it transitively imports ycm_core
# which we want to be imported ONLY after extra conf preload has executed.
# This can't be a top-level import because it transitively imports
# ycm_core which we want to be imported ONLY after extra conf
# preload has executed.
import handlers
handlers.UpdateUserOptions( options )
waitress.serve( handlers.app,

View File

@ -146,7 +146,14 @@ def NumLinesInBuffer( buffer_object ):
# time of writing, YCM only uses the GUI thread inside Vim (this used to not be
# the case).
def PostVimMessage( message ):
vim.command( "echohl WarningMsg | echomsg '{0}' | echohl None"
vim.command( "echohl WarningMsg | echom '{0}' | echohl None"
.format( EscapeForVim( str( message ) ) ) )
# Unlike PostVimMesasge, this supports messages with newlines in them because it
# uses 'echo' instead of 'echomsg'. This also means that the message will NOT
# appear in Vim's message log.
def PostMultiLineNotice( message ):
vim.command( "echohl WarningMsg | echo '{0}' | echohl None"
.format( EscapeForVim( str( message ) ) ) )

View File

@ -40,6 +40,11 @@ try:
except ImportError:
USE_ULTISNIPS_DATA = False
SERVER_CRASH_MESSAGE_STDERR_FILE = 'The ycmd server crashed with output:\n'
SERVER_CRASH_MESSAGE_SAME_STDERR = (
'The ycmd server crashed, check console output for logs!' )
class YouCompleteMe( object ):
def __init__( self, user_options ):
self._user_options = user_options
@ -83,6 +88,18 @@ class YouCompleteMe( object ):
self._server_popen = subprocess.Popen( args,
stdout = fstdout,
stderr = fstderr )
self._CheckIfServerCrashed()
def _CheckIfServerCrashed( self ):
server_crashed = self._server_popen.poll()
if server_crashed:
if self._server_stderr:
with open( self._server_stderr, 'r' ) as server_stderr_file:
vimsupport.PostMultiLineNotice( SERVER_CRASH_MESSAGE_STDERR_FILE +
server_stderr_file.read() )
else:
vimsupport.PostVimMessage( SERVER_CRASH_MESSAGE_SAME_STDERR )
def CreateCompletionRequest( self, force_semantic = False ):
@ -126,6 +143,7 @@ class YouCompleteMe( object ):
def OnFileReadyToParse( self ):
self._CheckIfServerCrashed()
self._omnicomp.OnFileReadyToParse( None )
extra_data = {}

View File

@ -19,6 +19,7 @@ astyle \
--recursive \
--exclude=gmock \
--exclude=testdata \
--exclude=ycm_client_support.cpp \
--exclude=ycm_core.cpp \
--exclude=CustomAssert.h \
--exclude=CustomAssert.cpp \