Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[READY] Add checks to ycm_core library when starting ycmd #467

Merged
merged 1 commit into from May 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Expand Up @@ -153,6 +153,17 @@ keep-alive background thread that periodically pings ycmd (just call the
You can also turn this off by passing `--idle_suicide_seconds=0`, although that
isn't recommended.

### Exit codes

During startup, ycmd attempts to load the `ycm_core` library and exits with one
of the following return codes if unsuccessful:

- 3: unexpected error while loading the library;
- 4: the `ycm_core` library is missing;
- 5: the `ycm_core` library is compiled for Python 3 but loaded in Python 2;
- 6: the `ycm_core` library is compiled for Python 2 but loaded in Python 3;
- 7: the version of the `ycm_core` library is outdated.

User-level customization
-----------------------

Expand Down
44 changes: 0 additions & 44 deletions check_core_version.py

This file was deleted.

8 changes: 4 additions & 4 deletions ycmd/__main__.py
Expand Up @@ -25,7 +25,7 @@
import os

sys.path.insert( 0, os.path.dirname( os.path.abspath( __file__ ) ) )
from server_utils import SetUpPythonPath, CompatibleWithCurrentCoreVersion
from server_utils import SetUpPythonPath, CompatibleWithCurrentCore
SetUpPythonPath()

from future import standard_library
Expand Down Expand Up @@ -157,9 +157,9 @@ def Main():
YcmCoreSanityCheck()
extra_conf_store.CallGlobalExtraConfYcmCorePreloadIfExists()

if not CompatibleWithCurrentCoreVersion():
# ycm_core.[so|dll|dylib] is too old and needs to be recompiled.
sys.exit( 2 )
code = CompatibleWithCurrentCore()
if code:
sys.exit( code )

PossiblyDetachFromTerminal()

Expand Down
23 changes: 5 additions & 18 deletions ycmd/handlers.py
Expand Up @@ -23,30 +23,17 @@
standard_library.install_aliases()
from builtins import * # noqa

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
import bottle
import http.client
import json
import logging
import traceback
from bottle import request
from . import server_state
from ycmd import user_options_store

import ycm_core
from ycmd import extra_conf_store, hmac_plugin, server_state, user_options_store
from ycmd.responses import BuildExceptionResponse, BuildCompletionResponse
from ycmd import hmac_plugin
from ycmd import extra_conf_store
from ycmd.request_wrap import RequestWrap
from ycmd.bottle_utils import SetResponseHeader
from ycmd.completers.completer_utils import FilterAndSortCandidatesWrap
Expand Down
97 changes: 83 additions & 14 deletions ycmd/server_utils.py
Expand Up @@ -22,24 +22,57 @@
# No other imports from `future` because this module is loaded before we have
# put our submodules in sys.path

import sys
import os
import io
import logging
import os
import re
import sys

CORE_MISSING_ERROR_REGEX = re.compile( "No module named '?ycm_core'?" )
CORE_PYTHON2_ERROR_REGEX = re.compile(
'dynamic module does not define (?:init|module export) '
'function \(PyInit_ycm_core\)|'
'Module use of python2[0-9].dll conflicts with this version of Python\.$' )
CORE_PYTHON3_ERROR_REGEX = re.compile(
'dynamic module does not define init function \(initycm_core\)|'
'Module use of python3[0-9].dll conflicts with this version of Python\.$' )

CORE_MISSING_MESSAGE = (
'ycm_core library not detected; you need to compile it by running the '
'build.py script. See the documentation for more details.' )
CORE_PYTHON2_MESSAGE = (
'ycm_core library compiled for Python 2 but loaded in Python 3.' )
CORE_PYTHON3_MESSAGE = (
'ycm_core library compiled for Python 3 but loaded in Python 2.' )
CORE_OUTDATED_MESSAGE = (
'ycm_core library too old; PLEASE RECOMPILE by running the build.py script. '
'See the documentation for more details.' )

# Exit statuses returned by the CompatibleWithCurrentCore function:
# - CORE_COMPATIBLE_STATUS: ycm_core is compatible;
# - CORE_UNEXPECTED_STATUS: unexpected error while loading ycm_core;
# - CORE_MISSING_STATUS : ycm_core is missing;
# - CORE_PYTHON2_STATUS : ycm_core is compiled with Python 2 but loaded with
# Python 3;
# - CORE_PYTHON3_STATUS : ycm_core is compiled with Python 3 but loaded with
# Python 2;
# - CORE_OUTDATED_STATUS : ycm_core version is outdated.
# Values 1 and 2 are not used because 1 is for general errors and 2 has often a
# special meaning for Unix programs. See
# https://docs.python.org/2/library/sys.html#sys.exit
CORE_COMPATIBLE_STATUS = 0
CORE_UNEXPECTED_STATUS = 3
CORE_MISSING_STATUS = 4
CORE_PYTHON2_STATUS = 5
CORE_PYTHON3_STATUS = 6
CORE_OUTDATED_STATUS = 7

VERSION_FILENAME = 'CORE_VERSION'
CORE_NOT_COMPATIBLE_MESSAGE = (
'ycmd can\'t run: ycm_core lib too old, PLEASE RECOMPILE'
)

DIR_OF_CURRENT_SCRIPT = os.path.dirname( os.path.abspath( __file__ ) )
DIR_PACKAGES_REGEX = re.compile( '(site|dist)-packages$' )


def SetUpPythonPath():
sys.path.insert( 0, os.path.join( DIR_OF_CURRENT_SCRIPT, '..' ) )

AddNearestThirdPartyFoldersToSysPath( __file__ )
_logger = logging.getLogger( __name__ )


def ExpectedCoreVersion():
Expand All @@ -48,13 +81,49 @@ def ExpectedCoreVersion():
return int( f.read() )


def CompatibleWithCurrentCoreVersion():
import ycm_core
def ImportCore():
"""Imports and returns the ycm_core module. This function exists for easily
mocking this import in tests."""
import ycm_core as ycm_core
return ycm_core


def CompatibleWithCurrentCore():
"""Checks if ycm_core library is compatible and returns with an exit
status."""
try:
ycm_core = ImportCore()
except ImportError as error:
message = str( error )
if CORE_MISSING_ERROR_REGEX.match( message ):
_logger.exception( CORE_MISSING_MESSAGE )
return CORE_MISSING_STATUS
if CORE_PYTHON2_ERROR_REGEX.match( message ):
_logger.exception( CORE_PYTHON2_MESSAGE )
return CORE_PYTHON2_STATUS
if CORE_PYTHON3_ERROR_REGEX.match( message ):
_logger.exception( CORE_PYTHON3_MESSAGE )
return CORE_PYTHON3_STATUS
_logger.exception( message )
return CORE_UNEXPECTED_STATUS

try:
current_core_version = ycm_core.YcmCoreVersion()
except AttributeError:
return False
return ExpectedCoreVersion() == current_core_version
_logger.exception( CORE_OUTDATED_MESSAGE )
return CORE_OUTDATED_STATUS

if ExpectedCoreVersion() != current_core_version:
_logger.error( CORE_OUTDATED_MESSAGE )
return CORE_OUTDATED_STATUS

return CORE_COMPATIBLE_STATUS


def SetUpPythonPath():
sys.path.insert( 0, os.path.join( DIR_OF_CURRENT_SCRIPT, '..' ) )

AddNearestThirdPartyFoldersToSysPath( __file__ )


def AncestorFolders( path ):
Expand Down
31 changes: 0 additions & 31 deletions ycmd/tests/check_core_version_test.py

This file was deleted.