Initial commit.

This commit is contained in:
2016-02-18 14:53:30 +01:00
commit 8e93ca7a95
2215 changed files with 341269 additions and 0 deletions

View File

@@ -0,0 +1,383 @@
# -*- mode: cmake ; coding: utf-8-unix -*-
cmake_minimum_required(VERSION 2.8)
cmake_policy(SET CMP0015 NEW)
cmake_policy(SET CMP0022 NEW)
include(CheckCXXCompilerFlag)
# ----------------------------------------
# before common setting
# ----------------------------------------
set(project_name clang-server)
# set(platform_name x86_64)
# set(platform_name x86_32)
# set(project_elf_name ${project_name}-${platform_name})
set(project_elf_name ${project_name})
# CMAKE_INSTALL_PREFIX check designated value before function Project()
if(CMAKE_INSTALL_PREFIX)
# message("user_install_prefix TRUE")
set(user_install_prefix TRUE)
else()
# message("user_install_prefix FALSE")
endif()
# The set before project declaration.
set(CMAKE_CONFIGURATION_TYPES Release RelWithDebInfo Debug CACHE TYPE INTERNAL FORCE)
# setup library per configuration
string(TOUPPER "${CMAKE_CONFIGURATION_TYPES}" project_configurations)
Project(${project_name})
# The set after project declaration.
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# CMAKE_INSTALL_PREFIX
# Windows (exe,dll)
# Default: C:/Program Files/clang-server/
# prefix : <user designate directory>
#
# Linux (elf)
# Default: /usr/local/bin/
# prefix : <user designate directory>
# Linux (so)
# Default: /usr/local/lib/clang-server/
# prefix : <user designate directory>
set(CMAKE_SKIP_RPATH TRUE)
# set(CMAKE_SKIP_BUILD_RPATH FALSE)
# set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
# set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib)
set(project_sources main.cpp ClangServer.cpp ClangServer.hpp ClangSession.cpp ClangSession.hpp Common.cpp Common.hpp CommandLine.hpp)
add_library(libclang SHARED IMPORTED)
set(dependency_libraries libclang)
list(APPEND CMAKE_FIND_LIBRARY_PREFIXES lib)
list(REMOVE_DUPLICATES CMAKE_FIND_LIBRARY_PREFIXES)
# set(libclang_names clang-${platform_name} clang)
set(libclang_names clang)
function(display_vars vars prefix)
message("${prefix}")
foreach(IT ${vars})
message("${prefix}${IT} = ${${IT}}")
endforeach()
endfunction()
set(echo_vars)
list(APPEND echo_vars CMAKE_CXX_COMPILER_ID CMAKE_CXX_STANDARD CYGWIN WIN32 UNIX MSVC MSVC_VERSION CMAKE_GNUtoMS CMAKE_SYSTEM_NAME CMAKE_COMPILER_IS_GNUC CMAKE_COMPILER_IS_GNUCXX CMAKE_COMPILER_IS_MINGW CMAKE_COMPILER_IS_CYGWIN)
display_vars("${echo_vars}" "env: ")
# ----------------------------------------
# platform dependency setting
# ----------------------------------------
if(MSVC)
# ----------------------------------------
# Microsoft Visual Studio
# ----------------------------------------
message("environment: MSVC")
set(echo_vars)
list(APPEND echo_vars CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE)
list(APPEND echo_vars CMAKE_EXE_LINKER_FLAGS CMAKE_EXE_LINKER_FLAGS_DEBUG CMAKE_EXE_LINKER_FLAGS_RELEASE)
list(APPEND echo_vars IMPORTED_IMPLIB IMPORTED_IMPLIB_DEBUG IMPORTED_IMPLIB_RELEASE)
display_vars("${echo_vars}" "msvc: ")
# set_target_properties(TARGET)
# setup compile & link flags
set(project_defs -D_CONSOLE -DUNICODE -D_UNICODE -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
set(CMAKE_CXX_FLAGS "/GR /EHsc /W3 /Zi")
set(CMAKE_CXX_FLAGS_DEBUG "/Gm /Od")
set(CMAKE_CXX_FLAGS_RELEASE "/GL /Gy /Oi")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /DEBUG /OPT:ICF /OPT:REF")
set(dependency_libraries odbc32 odbccp32 ${dependency_libraries})
# search & setup link library properties
list(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 .imp)
list(REMOVE_DUPLICATES CMAKE_FIND_LIBRARY_SUFFIXES)
# search priority EXTERNAL > CURRENT
set(libclang_search_paths ${LIBRARY_PATHS} ${CMAKE_SOURCE_DIR})
foreach(IT_SEARCH_PATH ${libclang_search_paths})
message("")
message("IT_SEARCH_PATH : ${IT_SEARCH_PATH}")
# collect absolute file paths
file(GLOB_RECURSE collected_library_apath ${IT_SEARCH_PATH} ${IT_SEARCH_PATH}/*.imp ${IT_SEARCH_PATH}/*.lib ${IT_SEARCH_PATH}/*.dll)
if(collected_library_apath)
# generate absolute directory paths
set(library_apaths ${IT_SEARCH_PATH})
foreach(IT_PATH ${collected_library_apath})
get_filename_component(library_dir_path ${IT_PATH} DIRECTORY)
list(APPEND library_apaths ${library_dir_path})
endforeach()
list(REMOVE_DUPLICATES library_apaths)
# message("collected_library_apath : ${collected_library_apath}")
message("library_apaths : ${library_apaths}")
# find library > generate relative path > set property per configuration.
foreach(IT_CONFIG ${project_configurations})
if(NOT matched_${IT_CONFIG})
# collect a absolute path that includes the same configuration name.
# IT_SEARCH_PATH is always accept.
# set(filtered_library_apaths ${IT_SEARCH_PATH})
# foreach(IT_PATH ${library_apaths})
# string(TOUPPER ${IT_PATH} CHECK_PATH_STR)
# if(${CHECK_PATH_STR} MATCHES /${IT_CONFIG})
# list(APPEND filtered_library_apaths ${IT_PATH})
# endif()
# endforeach()
# set(filtered_library_apaths ${library_apaths})
set(CMAKE_FIND_LIBRARY_SUFFIXES .imp .lib)
find_library(libclang_${IT_CONFIG}_found_lib_apath NAMES ${libclang_names} PATHS ${library_apaths} NO_DEFAULT_PATH)
set(CMAKE_FIND_LIBRARY_SUFFIXES .dll)
find_library(libclang_${IT_CONFIG}_found_dll_apath NAMES ${libclang_names} PATHS ${library_apaths} NO_DEFAULT_PATH)
display_vars("library_apaths;libclang_${IT_CONFIG}_found_lib_apath;libclang_${IT_CONFIG}_found_dll_apath" "${IT_CONFIG}: ")
if(libclang_${IT_CONFIG}_found_lib_apath AND libclang_${IT_CONFIG}_found_dll_apath)
file(RELATIVE_PATH libclang_${IT_CONFIG}_rpath ${IT_SEARCH_PATH} ${libclang_${IT_CONFIG}_found_lib_apath})
set_property(TARGET libclang PROPERTY IMPORTED_IMPLIB_${IT_CONFIG} ${libclang_${IT_CONFIG}_rpath})
set_property(TARGET libclang PROPERTY INTERFACE_LINK_LIBRARIES_${IT_CONFIG} ${libclang_${IT_CONFIG}_rpath})
set_property(TARGET libclang PROPERTY IMPORTED_LOCATION_${IT_CONFIG} ${libclang_${IT_CONFIG}_found_lib_apath})
link_directories(${IT_SEARCH_PATH})
set(libclang_${IT_CONFIG}_found_apath ${libclang_${IT_CONFIG}_found_dll_apath})
message("${IT_CONFIG}: found library at ${IT_SEARCH_PATH}")
set(matched_${IT_CONFIG} TRUE)
endif()
endif()
endforeach()
endif()
endforeach()
# install info
set(binary_destination_apath ${CMAKE_INSTALL_PREFIX})
set(library_destination_apath ${CMAKE_INSTALL_PREFIX})
if(NOT (matched_DEBUG OR matched_RELEASE OR matched_MINSIZEREL OR matched_RELWITHDEBINFO))
message("not found library")
endif()
elseif(UNIX)
# ----------------------------------------
# UNIX, Linux
# ----------------------------------------
message("environment: UNIX")
# setup compile & link flags
check_cxx_compiler_flag(-std=c++11 COMPILER_SUPPORTS_CXX11)
check_cxx_compiler_flag(-std=c++0x COMPILER_SUPPORTS_CXX0X)
# setup compile & link flags
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
message(FATAL_ERROR "The compiler has no C++11 support.")
endif()
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wunused-result")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused-result")
# search priority EXTERNAL > CURRENT
if(CYGWIN)
message("CYGWIN")
# set(dependency_libraries clang.dll)
# set(dependency_libraries libclang.a)
# find_library(CYG_LIBCLANG_PATH NAMES clang.dll PATHS ${library_apaths})
# find_library(CYG_LIBCLANG_PATH NAMES libclang.dll clang.dll clang PATHS "/lib" "c:/cygwin-x86_64/lib" )
# find_library(CYG_LIBCLANG_PATH NAMES libclang.dll PATHS "/lib" "c:/cygwin-x86_64/lib" )
# find_library(CYG_LIBCLANG_PATH NAMES libclang libclang.dll PATHS "c:/cygwin-x86_64/lib" )
message("CYG_LIBCLANG_PATH : ${CYG_LIBCLANG_PATH}")
else()
message("non CYGWIN")
endif()
# search priority EXTERNAL > CURRENT
set(libclang_search_paths ${LIBRARY_PATHS} ${CMAKE_SOURCE_DIR})
foreach(IT_SEARCH_PATH ${libclang_search_paths})
message("IT_SEARCH_PATH : ${IT_SEARCH_PATH}")
# collect absolute file paths
file(GLOB_RECURSE collected_library_apath ${IT_SEARCH_PATH} ${IT_SEARCH_PATH}/*.so ${IT_SEARCH_PATH}/*.a)
if(collected_library_apath)
# generate absolute directory paths
set(library_apaths ${IT_SEARCH_PATH})
foreach(IT_PATH ${collected_library_apath})
get_filename_component(library_dir_path ${IT_PATH} DIRECTORY)
list(APPEND library_apaths ${library_dir_path})
endforeach()
list(REMOVE_DUPLICATES library_apaths)
# message("collected_library_apath : ${collected_library_apath}")
message("library_apaths : ${library_apaths}")
# find library > generate relative path > set property per configuration.
foreach(IT_CONFIG ${project_configurations})
if(NOT matched_${IT_CONFIG})
# find library > generate relative path
find_library(libclang_${IT_CONFIG}_found_apath NAMES ${libclang_names} PATHS ${library_apaths} NO_DEFAULT_PATH)
display_vars("libclang_${IT_CONFIG}_found_apath" "${IT_CONFIG}: ")
if(libclang_${IT_CONFIG}_found_apath)
# message("found library at ${libclang_${IT_CONFIG}_found_apath}")
get_filename_component(libclang_link_apath ${libclang_${IT_CONFIG}_found_apath} DIRECTORY)
link_directories(${libclang_link_apath})
get_filename_component(libclang_found_name ${libclang_${IT_CONFIG}_found_apath} NAME_WE)
string(REGEX REPLACE "^lib" "" libclang_found_name ${libclang_found_name})
# message("libclang_found_name at ${libclang_found_name}")
set_property(TARGET libclang PROPERTY INTERFACE_LINK_LIBRARIES_${IT_CONFIG} ${libclang_found_name})
set_property(TARGET libclang PROPERTY IMPORTED_LOCATION_${IT_CONFIG} ${libclang_${IT_CONFIG}_found_apath})
message("${IT_CONFIG}: found library at ${libclang_link_apath}")
set(matched_${IT_CONFIG} TRUE)
endif()
endif()
endforeach()
endif()
endforeach()
# install info
if(user_install_prefix)
string(REGEX REPLACE "^~" "$ENV{HOME}" CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
get_filename_component(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} ABSOLUTE)
set(binary_destination_apath ${CMAKE_INSTALL_PREFIX})
set(library_destination_apath ${CMAKE_INSTALL_PREFIX})
else()
set(binary_destination_apath ${CMAKE_INSTALL_PREFIX}/bin)
set(library_destination_apath ${CMAKE_INSTALL_PREFIX}/lib/${project_name})
endif()
# The rpath is runtime library search path. The rpath specify path where you want to install a shared object.
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-rpath,${library_destination_apath}")
if(NOT (matched_DEBUG OR matched_RELEASE OR matched_MINSIZEREL OR matched_RELWITHDEBINFO))
message("not found library")
endif()
else()
message("sorry, not support environment")
endif()
# ----------------------------------------
# after common setting
# ----------------------------------------
add_executable(${project_name} ${project_sources})
# set_property(TARGET ${project_name} PROPERTY CXX_STANDARD 11)
set_property(TARGET ${project_name} PROPERTY OUTPUT_NAME_DEBUG "${project_elf_name}-debug")
set_property(TARGET ${project_name} PROPERTY OUTPUT_NAME_RELEASE "${project_elf_name}")
# set_property(TARGET ${project_name} PROPERTY OUTPUT_NAME_MINSIZEREL "${project_elf_name}")
set_property(TARGET ${project_name} PROPERTY OUTPUT_NAME_RELWITHDEBINFO "${project_elf_name}-reldbg")
list(APPEND project_defs -DCMAKE_GENERATOR="${CMAKE_GENERATOR}" -DCMAKE_HOST_SYSTEM_PROCESSOR="${CMAKE_HOST_SYSTEM_PROCESSOR}")
# list(APPEND project_defs CMAKE_GENERATOR="${CMAKE_GENERATOR}")
# set_property(TARGET ${project_name} PROPERTY COMPILE_DEFINITIONS ${project_defs})
# set_property(TARGET ${project_name} PROPERTY COMPILE_DEFINITIONS_DEBUG ${project_defs})
# set_property(TARGET ${project_name} PROPERTY COMPILE_DEFINITIONS_RELEASE ${project_defs})
add_definitions(${project_defs})
include_directories(./)
target_link_libraries(${project_name} ${dependency_libraries})
# ----------------------------------------
# install setting
# ----------------------------------------
set(CMAKE_INSTALL_RPATH ${library_destination_apath})
install(TARGETS ${project_name} RUNTIME DESTINATION ${binary_destination_apath})
foreach(IT_CONFIG ${project_configurations})
install(FILES ${libclang_${IT_CONFIG}_found_apath} DESTINATION ${library_destination_apath} CONFIGURATIONS ${IT_CONFIG})
endforeach()
# ----------------------------------------
# debug display for variables
# ----------------------------------------
# get_directory_property(DirDefs DIRECTORY ${CMAKE_SOURCE_DIR} COMPILE_DEFINITIONS)
# foreach(IT_DEF ${DirDefs})
# message(STATUS "Found Define: " ${IT_DEF})
# endforeach()
# message( "DirDefs: " ${DirDefs} )
set(echo_vars)
list(APPEND echo_vars CMAKE_CONFIGURATION_TYPES project_configurations CMAKE_GENERATOR CMAKE_HOST_SYSTEM_PROCESSOR CMAKE_CURRENT_SOURCE_DIR collected_library_apath library_apaths)
list(APPEND echo_vars CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE project_defs)
list(APPEND echo_vars CMAKE_EXE_LINKER_FLAGS CMAKE_EXE_LINKER_FLAGS_DEBUG CMAKE_EXE_LINKER_FLAGS_RELEASE)
list(APPEND echo_vars dependency_libraries binary_destination_apath library_destination_apath)
list(APPEND echo_vars CMAKE_FIND_LIBRARY_PREFIXES CMAKE_FIND_LIBRARY_SUFFIXES)
# list(APPEND echo_vars CMAKE_IMPORT_LIBRARY_SUFFIXES CMAKE_LINK_LIBRARY_SUFFIX CMAKE_SHARED_LIBRARY_SUFFIX CMAKE_SHARED_MODULE_SUFFIX CMAKE_STATIC_LIBRARY_SUFFIX)
# list(APPEND echo_vars PROJECT_SOURCE_DIR PROJECT_BINARY_DIR CMAKE_SOURCE_DIR CMAKE_BINARY_DIR CMAKE_RUNTIME_OUTPUT_DIRECTORY CMAKE_LIBRARY_OUTPUT_DIRECTORY CMAKE_INSTALL_NAME_DIR CMAKE_INCLUDE_CURRENT_DIR CMAKE_HOME_DIRECTORY CMAKE_CURRENT_LIST_DIR CMAKE_CURRENT_BINARY_DIR CMAKE_BINARY_DIR CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${project_name}_SOURCE_DIR ${project_name}_BINARY_DIR)
list(APPEND echo_vars CMAKE_INSTALL_PREFIX CMAKE_INSTALL_RPATH CMAKE_SKIP_BUILD_RPATH CMAKE_BUILD_WITH_INSTALL_RPATH CMAKE_SKIP_RPATH CMAKE_SKIP_INSTALL_RPATH)
# list(APPEND echo_vars CMAKE_PREFIX_PATH CMAKE_LIBRARY_PATH CMAKE_FRAMEWORK_PATH CMAKE_SYSTEM_PREFIX_PATH CMAKE_SYSTEM_LIBRARY_PATH CMAKE_SYSTEM_FRAMEWORK_PATH)
display_vars("${echo_vars}" "final: ")

View File

@@ -0,0 +1,439 @@
/* -*- mode: c++ ; coding: utf-8-unix -*- */
/* last updated : 2015/07/25.03:24:29 */
/*
* Copyright (c) 2013-2015 yaruopooner [https://github.com/yaruopooner]
*
* This file is part of ac-clang.
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*================================================================================================*/
/* Comment */
/*================================================================================================*/
/*================================================================================================*/
/* Include Files */
/*================================================================================================*/
#include "ClangServer.hpp"
using namespace std;
/*================================================================================================*/
/* Internal Function Definitions Section */
/*================================================================================================*/
namespace
{
std::string GetClangVersion( void )
{
CXString version_text = clang_getClangVersion();
const std::string clang_version = clang_getCString( version_text );
clang_disposeString( version_text );
return clang_version;
}
}
/*================================================================================================*/
/* Global Class Method Definitions Section */
/*================================================================================================*/
/*
---- SERVER MESSAGE ----
- GET_SPECIFIC_PROPERTIES: return clang-server specific properties
Message format:
command_type:Server
command_name:GET_SPECIFIC_PROPERTIES
- GET_CLANG_VERSION: return libclang version (libclang.lib/a)
Message format:
command_type:Server
command_name:GET_CLANG_VERSION
- SET_CLANG_PARAMETERS: setup libclang behavior parameters
Message format:
command_type:Server
command_name:SET_CLANG_PARAMETERS
translation_unit_flags:[#flag_name0|flag_name1|...#]
complete_at_flags:[#flag_name0|flag_name1|...#]
- CREATE_SESSION: create session.
Message format:
command_type:Server
command_name:CREATE_SESSION
session_name::[#session_name#]
- DELETE_SESSION: delete session.
Message format:
command_type:Server
command_name:DELETE_SESSION
session_name::[#session_name#]
- RESET: reset the clang server(all session delete. context reallocate)
Message format:
command_type:Server
command_name:RESET
- SHUTDOWN: shutdown the clang server (this program)
Message format:
command_type:Server
command_name:SHUTDOWN
---- SESSION MESSAGE ----
- SUSPEND: delete CXTranslationUnit
Message format:
command_type:Session
command_name:SUSPEND
session_name:[#session_name#]
- RESUME: realloc CXTranslationUnit
Message format:
command_type:Session
command_name:RESUME
session_name:[#session_name#]
- SET_CFLAGS: Specify CFLAGS argument passing to clang parser.
Message format:
command_type:Session
command_name:SET_CFLAGS
session_name:[#session_name#]
num_cflags:[#num_cflags#]
arg1 arg2 ...... (there should be num_cflags items here)
CFLAGS's white space must be accept.
a separator is '\n'.
source_length:[#source_length#]
<# SOURCE CODE #>
- SET_SOURCECODE: Update the source code in the source buffer
Message format:
command_type:Session
command_name:SET_SOURCECODE
session_name:[#session_name#]
source_length:[#source_length#]
<# SOURCE CODE #>
- REPARSE: Reparse the source code
command_type:Session
command_name:REPARSE
session_name:[#session_name#]
- COMPLETION: Do code completion at a specified point.
Message format:
command_type:Session
command_name:COMPLETION
session_name:[#session_name#]
line:[#line#]
column:[#column#]
source_length:[#source_length#]
<# SOURCE CODE #>
- SYNTAXCHECK: Retrieve diagnostic messages
Message format:
command_type:Session
command_name:SYNTAXCHECK
session_name:[#session_name#]
source_length:[#source_length#]
<# SOURCE CODE #>
- INCLUSION: Get location of inclusion file at point
Message format:
command_type:Session
command_name:INCLUSION
session_name:[#session_name#]
line:[#line#]
column:[#column#]
source_length:[#source_length#]
<# SOURCE CODE #>
- DEFINITION: Get location of definition at point
Message format:
command_type:Session
command_name:DEFINITION
session_name:[#session_name#]
line:[#line#]
column:[#column#]
source_length:[#source_length#]
<# SOURCE CODE #>
- DECLARATION: Get location of declaration at point
Message format:
command_type:Session
command_name:DECLARATION
session_name:[#session_name#]
line:[#line#]
column:[#column#]
source_length:[#source_length#]
<# SOURCE CODE #>
- SMARTJUMP: Get location of inclusion, definition, declaration at point. If that fails, it will search following.
inclusion -> finish, definition -> declaration -> finish.
Message format:
command_type:Session
command_name:SMARTJUMP
session_name:[#session_name#]
line:[#line#]
column:[#column#]
source_length:[#source_length#]
<# SOURCE CODE #>
*/
ClangServer::ClangServer( const Specification& specification )
:
m_Status( kStatus_Running )
, m_Specification( specification )
{
// setup stream buffer size
m_Specification.m_StdinBufferSize = std::max( m_Specification.m_StdinBufferSize, static_cast< size_t >( Specification::kStreamBuffer_UnitSize ) );
m_Specification.m_StdoutBufferSize = std::max( m_Specification.m_StdoutBufferSize, static_cast< size_t >( Specification::kStreamBuffer_UnitSize ) );
::setvbuf( stdin, nullptr, _IOFBF, m_Specification.m_StdinBufferSize );
::setvbuf( stdout, nullptr, _IOFBF, m_Specification.m_StdoutBufferSize );
// server command
m_ServerCommands.insert( ServerHandleMap::value_type( "GET_SPECIFICATION", std::mem_fn( &ClangServer::commandGetSpecification ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "GET_CLANG_VERSION", std::mem_fn( &ClangServer::commandGetClangVersion ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "SET_CLANG_PARAMETERS", std::mem_fn( &ClangServer::commandSetClangParameters ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "CREATE_SESSION", std::mem_fn( &ClangServer::commandCreateSession ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "DELETE_SESSION", std::mem_fn( &ClangServer::commandDeleteSession ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "RESET", std::mem_fn( &ClangServer::commandReset ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "SHUTDOWN", std::mem_fn( &ClangServer::commandShutdown ) ) );
// session command
m_SessionCommands.insert( SessionHandleMap::value_type( "SUSPEND", std::mem_fn( &ClangSession::commandSuspend ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "RESUME", std::mem_fn( &ClangSession::commandResume ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "SET_CFLAGS", std::mem_fn( &ClangSession::commandSetCFlags ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "SET_SOURCECODE", std::mem_fn( &ClangSession::commandSetSourceCode ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "REPARSE", std::mem_fn( &ClangSession::commandReparse ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "COMPLETION", std::mem_fn( &ClangSession::commandCompletion ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "SYNTAXCHECK", std::mem_fn( &ClangSession::commandDiagnostics ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "INCLUSION", std::mem_fn( &ClangSession::commandInclusion ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "DEFINITION", std::mem_fn( &ClangSession::commandDefinition ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "DECLARATION", std::mem_fn( &ClangSession::commandDeclaration ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "SMARTJUMP", std::mem_fn( &ClangSession::commandSmartJump ) ) );
// display initial specification
commandGetSpecification();
}
ClangServer::~ClangServer( void )
{
// m_Sessions must be destruction early than m_Context.
// Because m_Sessions depend to m_Context.
m_Sessions.clear();
}
void ClangServer::commandGetSpecification( void )
{
const std::string server_version = CLANG_SERVER_VERSION;
const std::string clang_version = ::GetClangVersion();
const std::string generate = CMAKE_GENERATOR "/" CMAKE_HOST_SYSTEM_PROCESSOR;
m_Writer.Write( "-------- Clang-Server Specification --------\n" );
m_Writer.Write( "Server Version : %s\n", server_version.c_str() );
m_Writer.Write( "Clang Version : %s\n", clang_version.c_str() );
m_Writer.Write( "Generate : %s\n", generate.c_str() );
// m_Writer.Write( "Log File : %s\n", m_Specification.m_LogFile.c_str() );
m_Writer.Write( "STDIN Buffer Size : %d bytes\n", m_Specification.m_StdinBufferSize );
m_Writer.Write( "STDOUT Buffer Size : %d bytes\n", m_Specification.m_StdoutBufferSize );
m_Writer.Flush();
}
void ClangServer::commandGetClangVersion( void )
{
const std::string clang_version = ::GetClangVersion();
m_Writer.Write( "%s ", clang_version.c_str() );
m_Writer.Flush();
}
void ClangServer::commandSetClangParameters( void )
{
const string translation_unit_flags = m_Reader.ReadToken( "translation_unit_flags:%s" );
const string complete_at_flags = m_Reader.ReadToken( "complete_at_flags:%s" );
const uint32_t translation_unit_flags_value = ClangFlagConverters::GetCXTranslationUnitFlags().GetValue( translation_unit_flags );
const uint32_t complete_at_flags_value = ClangFlagConverters::GetCXCodeCompleteFlags().GetValue( complete_at_flags );
uint32_t complete_results_limit;
m_Reader.ReadToken( "complete_results_limit:%d", complete_results_limit );
m_Context.SetTranslationUnitFlags( translation_unit_flags_value );
m_Context.SetCompleteAtFlags( complete_at_flags_value );
m_Context.SetCompleteResultsLimit( complete_results_limit );
}
void ClangServer::commandCreateSession( void )
{
const string session_name = m_Reader.ReadToken( "session_name:%s" );
// search session
Dictionary::iterator session_it = m_Sessions.find( session_name );
if ( session_it == m_Sessions.end() )
{
// not found session
// allocate & setup new session
std::shared_ptr< ClangSession > new_session( std::make_shared< ClangSession >( session_name, m_Context, m_Reader, m_Writer ) );
std::pair< Dictionary::iterator, bool > result = m_Sessions.insert( Dictionary::value_type( session_name, new_session ) );
if ( result.second )
{
// success
new_session->Allocate();
}
}
else
{
// already exist
}
}
void ClangServer::commandDeleteSession( void )
{
const string session_name = m_Reader.ReadToken( "session_name:%s" );
// search session
Dictionary::iterator session_it = m_Sessions.find( session_name );
if ( session_it != m_Sessions.end() )
{
// session_it->second->Deallocate();
m_Sessions.erase( session_it );
}
}
void ClangServer::commandReset( void )
{
m_Sessions.clear();
m_Context.Deallocate();
m_Context.Allocate();
}
void ClangServer::commandShutdown( void )
{
m_Status = kStatus_Exit;
}
void ClangServer::ParseServerCommand( void )
{
const string command_name = m_Reader.ReadToken( "command_name:%s" );
ServerHandleMap::iterator command_it = m_ServerCommands.find( command_name );
// execute command handler
if ( command_it != m_ServerCommands.end() )
{
command_it->second( *this );
}
else
{
// unknown command
}
}
void ClangServer::ParseSessionCommand( void )
{
const string command_name = m_Reader.ReadToken( "command_name:%s" );
const string session_name = m_Reader.ReadToken( "session_name:%s" );
if ( session_name.empty() )
{
return;
}
// search session
Dictionary::iterator session_it = m_Sessions.find( session_name );
// execute command handler
if ( session_it != m_Sessions.end() )
{
SessionHandleMap::iterator command_it = m_SessionCommands.find( command_name );
if ( command_it != m_SessionCommands.end() )
{
command_it->second( *session_it->second );
}
else
{
// session not found
}
}
else
{
// unknown command
}
}
void ClangServer::ParseCommand( void )
{
do
{
const string command_type = m_Reader.ReadToken( "command_type:%s" );
if ( command_type == "Server" )
{
ParseServerCommand();
}
else if ( command_type == "Session" )
{
ParseSessionCommand();
}
else
{
// unknown command type
}
} while ( m_Status != kStatus_Exit );
}
/*================================================================================================*/
/* EOF */
/*================================================================================================*/

View File

@@ -0,0 +1,132 @@
/* -*- mode: c++ ; coding: utf-8-unix -*- */
/* last updated : 2015/09/05.04:13:59 */
/*
* Copyright (c) 2013-2015 yaruopooner [https://github.com/yaruopooner]
*
* This file is part of ac-clang.
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __CLANG_SERVER_HPP__
#define __CLANG_SERVER_HPP__
#define CLANG_SERVER_VERSION "server version 1.4.0"
/*================================================================================================*/
/* Comment */
/*================================================================================================*/
/*================================================================================================*/
/* Include Files */
/*================================================================================================*/
#include <memory>
#include <functional>
#include <unordered_map>
#include "ClangSession.hpp"
/*================================================================================================*/
/* Class */
/*================================================================================================*/
class ClangServer
{
public:
enum Status
{
kStatus_Running,
kStatus_Exit,
};
struct Specification
{
enum
{
kStreamBuffer_UnitSize = 1 * 1024 * 1024,
};
Specification( size_t StdinBufferSize = kStreamBuffer_UnitSize,
size_t StdoutBufferSize = kStreamBuffer_UnitSize,
const std::string& LogFile = std::string() ) :
m_StdinBufferSize( StdinBufferSize )
, m_StdoutBufferSize( StdoutBufferSize )
, m_LogFile( LogFile )
{
}
size_t m_StdinBufferSize;
size_t m_StdoutBufferSize;
std::string m_LogFile;
};
ClangServer( const Specification& specification = Specification() );
~ClangServer( void );
void ParseCommand( void );
// void SetLogFile( const std::string& LogFile );
private:
void ParseServerCommand( void );
void ParseSessionCommand( void );
// commands
void commandGetSpecification( void );
void commandGetClangVersion( void );
void commandSetClangParameters( void );
void commandCreateSession( void );
void commandDeleteSession( void );
void commandReset( void );
void commandShutdown( void );
private:
typedef std::unordered_map< std::string, std::function< void (ClangServer&) > > ServerHandleMap;
typedef std::unordered_map< std::string, std::function< void (ClangSession&) > > SessionHandleMap;
typedef std::unordered_map< std::string, std::shared_ptr< ClangSession > > Dictionary;
ClangContext m_Context;
ServerHandleMap m_ServerCommands;
SessionHandleMap m_SessionCommands;
Dictionary m_Sessions;
StreamReader m_Reader;
StreamWriter m_Writer;
uint32_t m_Status;
Specification m_Specification;
};
#endif // __CLANG_SERVER_HPP__
/*================================================================================================*/
/* EOF */
/*================================================================================================*/

View File

@@ -0,0 +1,739 @@
/* -*- mode: c++ ; coding: utf-8-unix -*- */
/* last updated : 2015/07/25.03:32:02 */
/*
* Copyright (c) 2013-2015 yaruopooner [https://github.com/yaruopooner]
*
* This file is part of ac-clang.
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*================================================================================================*/
/* Comment */
/*================================================================================================*/
/*================================================================================================*/
/* Include Files */
/*================================================================================================*/
#include <regex>
#include "ClangSession.hpp"
using namespace std;
/*================================================================================================*/
/* Internal Printer Class Definitions Section */
/*================================================================================================*/
namespace
{
string GetNormalizePath( CXFile file )
{
CXString filename = clang_getFileName( file );
const string path = clang_getCString( filename );
const regex expression( "[\\\\]+" );
const string replace( "/" );
const string normalize_path = regex_replace( path, expression, replace );
clang_disposeString( filename );
return normalize_path;
}
}
class ClangSession::Completion
{
public:
Completion( ClangSession& Session ) :
m_Session( Session )
{
}
void PrintCompleteCandidates( void );
private:
bool PrintCompletionHeadTerm( CXCompletionString CompletionString );
void PrintAllCompletionTerms( CXCompletionString CompletionString );
void PrintCompletionLine( CXCompletionString CompletionString );
void PrintCompletionResults( CXCodeCompleteResults* CompleteResults );
private:
ClangSession& m_Session;
};
class ClangSession::Diagnostics
{
public:
Diagnostics( ClangSession& Session ) :
m_Session( Session )
{
}
void PrintDiagnosticsResult( void );
private:
ClangSession& m_Session;
};
class ClangSession::Jump
{
public:
Jump( ClangSession& Session ) :
m_Session( Session )
{
}
void PrintInclusionFileLocation( void );
void PrintDefinitionLocation( void );
void PrintDeclarationLocation( void );
void PrintSmartJumpLocation( void );
private:
struct Location
{
Location( void ) :
m_Line( 0 )
, m_Column( 0 )
{
}
string m_NormalizePath;
uint32_t m_Line;
uint32_t m_Column;
};
CXCursor GetCursor( const uint32_t Line, const uint32_t Column ) const;
void PrepareTransaction( uint32_t& Line, uint32_t& Column );
bool EvaluateCursorLocation( const CXCursor& Cursor );
bool EvaluateInclusionFileLocation( void );
bool EvaluateDefinitionLocation( void );
bool EvaluateDeclarationLocation( void );
bool EvaluateSmartJumpLocation( void );
void PrintLocation( void );
private:
ClangSession& m_Session;
Location m_Location;
};
bool ClangSession::Completion::PrintCompletionHeadTerm( CXCompletionString CompletionString )
{
// check accessibility of candidate. (access specifier of member : public/protected/private)
if ( clang_getCompletionAvailability( CompletionString ) == CXAvailability_NotAccessible )
{
return ( false );
}
const uint32_t n_chunks = clang_getNumCompletionChunks( CompletionString );
// inspect all chunks only to find the TypedText chunk
for ( uint32_t i_chunk = 0; i_chunk < n_chunks; ++i_chunk )
{
if ( clang_getCompletionChunkKind( CompletionString, i_chunk ) == CXCompletionChunk_TypedText )
{
// We got it, just dump it to fp
CXString ac_string = clang_getCompletionChunkText( CompletionString, i_chunk );
m_Session.m_Writer.Write( "COMPLETION: %s", clang_getCString( ac_string ) );
clang_disposeString( ac_string );
// care package on the way
return ( true );
}
}
// We haven't found TypedText chunk in CompletionString
return ( false );
}
void ClangSession::Completion::PrintAllCompletionTerms( CXCompletionString CompletionString )
{
const uint32_t n_chunks = clang_getNumCompletionChunks( CompletionString );
for ( uint32_t i_chunk = 0; i_chunk < n_chunks; ++i_chunk )
{
// get the type and completion text of this chunk
CXCompletionChunkKind chk_kind = clang_getCompletionChunkKind( CompletionString, i_chunk );
CXString chk_text = clang_getCompletionChunkText( CompletionString, i_chunk );
// differenct kinds of chunks has various output formats
switch ( chk_kind )
{
case CXCompletionChunk_Placeholder:
m_Session.m_Writer.Write( "<#%s#>", clang_getCString( chk_text ) );
break;
case CXCompletionChunk_ResultType:
m_Session.m_Writer.Write( "[#%s#]", clang_getCString( chk_text ) );
break;
case CXCompletionChunk_Optional:
// print optional term in a recursive way
m_Session.m_Writer.Write( "{#" );
PrintAllCompletionTerms( clang_getCompletionChunkCompletionString( CompletionString, i_chunk ) );
m_Session.m_Writer.Write( "#}" );
break;
default:
m_Session.m_Writer.Write( "%s", clang_getCString( chk_text ) );
}
clang_disposeString( chk_text );
}
}
void ClangSession::Completion::PrintCompletionLine( CXCompletionString CompletionString )
{
// print completion item head: COMPLETION: typed_string
if ( PrintCompletionHeadTerm( CompletionString ) )
{
// If there's not only one TypedText chunk in this completion string,
// * we still have a lot of info to dump:
// *
// * COMPLETION: typed_text : ##infos## \n
m_Session.m_Writer.Write( " : " );
PrintAllCompletionTerms( CompletionString );
m_Session.m_Writer.Write( "\n" );
}
}
void ClangSession::Completion::PrintCompletionResults( CXCodeCompleteResults* CompleteResults )
{
const uint32_t results_limit = m_Session.m_Context.GetCompleteResultsLimit();
const bool is_accept = results_limit ? ( CompleteResults->NumResults < results_limit ) : true;
if ( !is_accept )
{
m_Session.m_Writer.Write( "A number of completion results(%d) is threshold value(%d) over!!\n", CompleteResults->NumResults, results_limit );
return;
}
for ( uint32_t i = 0; i < CompleteResults->NumResults; ++i )
{
PrintCompletionLine( CompleteResults->Results[ i ].CompletionString );
}
}
void ClangSession::Completion::PrintCompleteCandidates( void )
{
uint32_t line;
uint32_t column;
m_Session.m_Reader.ReadToken( "line:%d", line );
m_Session.m_Reader.ReadToken( "column:%d", column );
m_Session.ReadSourceCode();
CXUnsavedFile unsaved_file = m_Session.GetCXUnsavedFile();
// necessary call?
// clang_reparseTranslationUnit( m_Session.m_CxTU, 1, &unsaved_file, m_Session.m_TranslationUnitFlags );
CXCodeCompleteResults* results = clang_codeCompleteAt( m_Session.m_CxTU, m_Session.m_SessionName.c_str(), line, column, &unsaved_file, 1, m_Session.m_CompleteAtFlags );
if ( results )
{
clang_sortCodeCompletionResults( results->Results, results->NumResults );
PrintCompletionResults( results );
clang_disposeCodeCompleteResults( results );
}
m_Session.m_Writer.Flush();
}
void ClangSession::Diagnostics::PrintDiagnosticsResult( void )
{
m_Session.ReadSourceCode();
CXUnsavedFile unsaved_file = m_Session.GetCXUnsavedFile();
clang_reparseTranslationUnit( m_Session.m_CxTU, 1, &unsaved_file, m_Session.m_TranslationUnitFlags );
const uint32_t n_diagnostics = clang_getNumDiagnostics( m_Session.m_CxTU );
for ( uint32_t i = 0; i < n_diagnostics; ++i )
{
CXDiagnostic diagnostic = clang_getDiagnostic( m_Session.m_CxTU, i );
CXString message = clang_formatDiagnostic( diagnostic, clang_defaultDiagnosticDisplayOptions() );
m_Session.m_Writer.Write( "%s\n", clang_getCString( message ) );
clang_disposeString( message );
clang_disposeDiagnostic( diagnostic );
}
m_Session.m_Writer.Flush();
}
CXCursor ClangSession::Jump::GetCursor( const uint32_t Line, const uint32_t Column ) const
{
const CXFile file = clang_getFile( m_Session.m_CxTU, m_Session.m_SessionName.c_str() );
const CXSourceLocation location = clang_getLocation( m_Session.m_CxTU, file, Line, Column );
const CXCursor cursor = clang_getCursor( m_Session.m_CxTU, location );
return cursor;
}
void ClangSession::Jump::PrepareTransaction( uint32_t& Line, uint32_t& Column )
{
m_Session.m_Reader.ReadToken( "line:%d", Line );
m_Session.m_Reader.ReadToken( "column:%d", Column );
m_Session.ReadSourceCode();
CXUnsavedFile unsaved_file = m_Session.GetCXUnsavedFile();
clang_reparseTranslationUnit( m_Session.m_CxTU, 1, &unsaved_file, m_Session.m_TranslationUnitFlags );
}
bool ClangSession::Jump::EvaluateCursorLocation( const CXCursor& Cursor )
{
if ( clang_isInvalid( Cursor.kind ) )
{
return ( false );
}
const CXSourceLocation dest_location = clang_getCursorLocation( Cursor );
CXFile dest_file;
uint32_t dest_line;
uint32_t dest_column;
uint32_t dest_offset;
clang_getExpansionLocation( dest_location, &dest_file, &dest_line, &dest_column, &dest_offset );
if ( !dest_file )
{
return ( false );
}
const string normalize_path = ::GetNormalizePath( dest_file );
m_Location.m_NormalizePath = normalize_path;
m_Location.m_Line = dest_line;
m_Location.m_Column = dest_column;
return ( true );
}
bool ClangSession::Jump::EvaluateInclusionFileLocation( void )
{
uint32_t line;
uint32_t column;
PrepareTransaction( line, column );
const CXCursor source_cursor = GetCursor( line, column );
if ( !clang_isInvalid( source_cursor.kind ) )
{
if ( source_cursor.kind == CXCursor_InclusionDirective )
{
const CXFile file = clang_getIncludedFile( source_cursor );
if ( file )
{
const uint32_t file_line = 1;
const uint32_t file_column = 1;
const string normalize_path = ::GetNormalizePath( file );
m_Location.m_NormalizePath = normalize_path;
m_Location.m_Line = file_line;
m_Location.m_Column = file_column;
return ( true );
}
}
}
return ( false );
}
bool ClangSession::Jump::EvaluateDefinitionLocation( void )
{
uint32_t line;
uint32_t column;
PrepareTransaction( line, column );
const CXCursor source_cursor = GetCursor( line, column );
if ( !clang_isInvalid( source_cursor.kind ) )
{
return EvaluateCursorLocation( clang_getCursorDefinition( source_cursor ) );
}
return ( false );
}
bool ClangSession::Jump::EvaluateDeclarationLocation( void )
{
uint32_t line;
uint32_t column;
PrepareTransaction( line, column );
const CXCursor source_cursor = GetCursor( line, column );
if ( !clang_isInvalid( source_cursor.kind ) )
{
return EvaluateCursorLocation( clang_getCursorReferenced( source_cursor ) );
}
return ( false );
}
bool ClangSession::Jump::EvaluateSmartJumpLocation( void )
{
uint32_t line;
uint32_t column;
PrepareTransaction( line, column );
const CXCursor source_cursor = GetCursor( line, column );
if ( !clang_isInvalid( source_cursor.kind ) )
{
if ( source_cursor.kind == CXCursor_InclusionDirective )
{
const CXFile file = clang_getIncludedFile( source_cursor );
if ( file )
{
const uint32_t file_line = 1;
const uint32_t file_column = 1;
const string normalize_path = ::GetNormalizePath( file );
m_Location.m_NormalizePath = normalize_path;
m_Location.m_Line = file_line;
m_Location.m_Column = file_column;
return ( true );
}
}
else
{
return ( EvaluateCursorLocation( clang_getCursorDefinition( source_cursor ) )
|| EvaluateCursorLocation( clang_getCursorReferenced( source_cursor ) ) );
}
}
return ( false );
}
void ClangSession::Jump::PrintLocation( void )
{
m_Session.m_Writer.Write( "\"%s\" %d %d ", m_Location.m_NormalizePath.c_str(), m_Location.m_Line, m_Location.m_Column );
}
void ClangSession::Jump::PrintInclusionFileLocation( void )
{
if ( EvaluateInclusionFileLocation() )
{
PrintLocation();
}
m_Session.m_Writer.Flush();
}
void ClangSession::Jump::PrintDefinitionLocation( void )
{
if ( EvaluateDefinitionLocation() )
{
PrintLocation();
}
m_Session.m_Writer.Flush();
}
void ClangSession::Jump::PrintDeclarationLocation( void )
{
if ( EvaluateDeclarationLocation() )
{
PrintLocation();
}
m_Session.m_Writer.Flush();
}
void ClangSession::Jump::PrintSmartJumpLocation( void )
{
if ( EvaluateSmartJumpLocation() )
{
PrintLocation();
}
m_Session.m_Writer.Flush();
}
/*================================================================================================*/
/* Global Class Method Definitions Section */
/*================================================================================================*/
ClangSession::ClangSession( const std::string& SessionName, const ClangContext& Context, StreamReader& Reader, StreamWriter& Writer )
:
m_SessionName( SessionName )
, m_Context( Context )
, m_Reader( Reader )
, m_Writer( Writer )
, m_CxTU( nullptr )
, m_TranslationUnitFlags( Context.GetTranslationUnitFlags() )
, m_CompleteAtFlags( Context.GetCompleteAtFlags() )
{
}
ClangSession::~ClangSession( void )
{
Deallocate();
}
void ClangSession::ReadCFlags( void )
{
int32_t num_cflags;
m_Reader.ReadToken( "num_cflags:%d", num_cflags );
vector< string > cflags;
for ( int32_t i = 0; i < num_cflags; ++i )
{
// CFLAGS's white space must be accept.
// a separator is '\n'.
cflags.push_back( m_Reader.ReadToken( "%[^\n]" ) );
}
m_CFlagsBuffer.Allocate( cflags );
}
void ClangSession::ReadSourceCode( void )
{
int32_t src_length;
m_Reader.ReadToken( "source_length:%d", src_length );
m_CSourceCodeBuffer.Allocate( src_length );
m_Reader.Read( m_CSourceCodeBuffer.GetBuffer(), m_CSourceCodeBuffer.GetSize() );
}
void ClangSession::CreateTranslationUnit( void )
{
if ( m_CxTU )
{
// clang parser already exist
return;
}
CXUnsavedFile unsaved_file = GetCXUnsavedFile();
m_CxTU = clang_parseTranslationUnit( m_Context.GetCXIndex(), m_SessionName.c_str(),
static_cast< const char * const *>( m_CFlagsBuffer.GetCFlags() ), m_CFlagsBuffer.GetNumberOfCFlags(),
&unsaved_file, 1, m_TranslationUnitFlags );
clang_reparseTranslationUnit( m_CxTU, 1, &unsaved_file, m_TranslationUnitFlags );
}
void ClangSession::DeleteTranslationUnit( void )
{
if ( !m_CxTU )
{
// clang parser not exist
return;
}
clang_disposeTranslationUnit( m_CxTU );
m_CxTU = nullptr;
}
void ClangSession::Allocate( void )
{
ReadCFlags();
ReadSourceCode();
CreateTranslationUnit();
}
void ClangSession::Deallocate( void )
{
DeleteTranslationUnit();
}
void ClangSession::commandSuspend( void )
{
DeleteTranslationUnit();
}
void ClangSession::commandResume( void )
{
CreateTranslationUnit();
}
void ClangSession::commandSetCFlags( void )
{
DeleteTranslationUnit();
ReadCFlags();
ReadSourceCode();
CreateTranslationUnit();
}
void ClangSession::commandSetSourceCode( void )
{
ReadSourceCode();
}
void ClangSession::commandReparse( void )
{
if ( !m_CxTU )
{
// clang parser not exist
return;
}
CXUnsavedFile unsaved_file = GetCXUnsavedFile();
clang_reparseTranslationUnit( m_CxTU, 1, &unsaved_file, m_TranslationUnitFlags );
}
void ClangSession::commandCompletion( void )
{
if ( !m_CxTU )
{
// clang parser not exist
return;
}
Completion printer( *this );
printer.PrintCompleteCandidates();
}
void ClangSession::commandDiagnostics( void )
{
if ( !m_CxTU )
{
// clang parser not exist
return;
}
Diagnostics printer( *this );
printer.PrintDiagnosticsResult();
}
void ClangSession::commandInclusion( void )
{
if ( !m_CxTU )
{
// clang parser not exist
return;
}
Jump printer( *this );
printer.PrintInclusionFileLocation();
}
void ClangSession::commandDeclaration( void )
{
if ( !m_CxTU )
{
// clang parser not exist
return;
}
Jump printer( *this );
printer.PrintDeclarationLocation();
}
void ClangSession::commandDefinition( void )
{
if ( !m_CxTU )
{
// clang parser not exist
return;
}
Jump printer( *this );
printer.PrintDefinitionLocation();
}
void ClangSession::commandSmartJump( void )
{
if ( !m_CxTU )
{
// clang parser not exist
return;
}
Jump printer( *this );
printer.PrintSmartJumpLocation();
}
/*================================================================================================*/
/* EOF */
/*================================================================================================*/

View File

@@ -0,0 +1,133 @@
/* -*- mode: c++ ; coding: utf-8-unix -*- */
/* last updated : 2015/07/25.00:55:41 */
/*
* Copyright (c) 2013-2015 yaruopooner [https://github.com/yaruopooner]
*
* This file is part of ac-clang.
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __CLANG_SESSION_HPP__
#define __CLANG_SESSION_HPP__
/*================================================================================================*/
/* Comment */
/*================================================================================================*/
/*================================================================================================*/
/* Include Files */
/*================================================================================================*/
#include "Common.hpp"
/*================================================================================================*/
/* Global Class Method Definitions Section */
/*================================================================================================*/
class ClangSession
{
public:
ClangSession( const std::string& SessionName, const ClangContext& Context, StreamReader& Reader, StreamWriter& Writer );
virtual ~ClangSession( void );
void Allocate( void );
void Deallocate( void );
// const CFlagsBuffer& GetCFlagsBuffer( void ) const
// {
// return ( m_CFlagsBuffer );
// }
// CFlagsBuffer& GetCFlagsBuffer( void )
// {
// return ( m_CFlagsBuffer );
// }
// commands
void commandSuspend( void );
void commandResume( void );
void commandSetCFlags( void );
void commandSetSourceCode( void );
void commandReparse( void );
void commandCompletion( void );
void commandDiagnostics( void );
void commandInclusion( void );
void commandDeclaration( void );
void commandDefinition( void );
void commandSmartJump( void );
private:
CXUnsavedFile GetCXUnsavedFile( void ) const
{
CXUnsavedFile unsaved_file;
unsaved_file.Filename = m_SessionName.c_str();
unsaved_file.Contents = m_CSourceCodeBuffer.GetBuffer();
unsaved_file.Length = m_CSourceCodeBuffer.GetSize();
return ( unsaved_file );
}
void ReadCFlags( void );
void ReadSourceCode( void );
void CreateTranslationUnit( void );
void DeleteTranslationUnit( void );
// internal printer classes
class Completion;
class Diagnostics;
class Jump;
private:
const std::string m_SessionName;
const ClangContext& m_Context;
StreamReader& m_Reader;
StreamWriter& m_Writer;
// clang parser object
CXTranslationUnit m_CxTU;
// clang parser options
uint32_t m_TranslationUnitFlags;
uint32_t m_CompleteAtFlags;
CFlagsBuffer m_CFlagsBuffer;
CSourceCodeBuffer m_CSourceCodeBuffer;
};
#endif // __CLANG_SESSION_HPP__
/*================================================================================================*/
/* EOF */
/*================================================================================================*/

View File

@@ -0,0 +1,607 @@
/* -*- mode: c++ ; coding: utf-8-unix -*- */
/* last updated : 2015/05/23.23:17:32 */
/*
* Copyright (c) 2013-2015 yaruopooner [https://github.com/yaruopooner]
*
* This file is part of ac-clang.
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __COMMAND_LINE_HPP__
#define __COMMAND_LINE_HPP__
/*================================================================================================*/
/* Include Files */
/*================================================================================================*/
#include <string>
#include <vector>
#include <set>
#include <memory>
#include <iostream>
#include <sstream>
/*================================================================================================*/
/* Class */
/*================================================================================================*/
namespace CommandLine
{
template< typename Target, typename Source >
struct lexical_cast_imp
{
static Target cast( const Source& value )
{
Target result;
std::stringstream interpreter;
if ( !( interpreter << value ) || !( interpreter >> result ) || !( interpreter >> std::ws ).eof() )
{
throw std::invalid_argument( "value cast failure." );
}
return result;
}
};
template< typename Source >
struct lexical_cast_imp< Source, Source >
{
static Source cast( const Source& value )
{
return value;
}
};
template< typename Target, typename Source >
static Target lexical_cast( const Source& value )
{
return lexical_cast_imp< Target, Source >::cast( value );
}
template< typename T >
struct DefaultReader
{
virtual ~DefaultReader( void )
{
}
virtual T operator ()( const std::string& argument ) const
{
return lexical_cast< T >( argument );
}
};
template< typename T >
class RangeReader : public DefaultReader< T >
{
public:
RangeReader( T min, T max ) :
m_Min( min )
, m_Max( max )
{
}
virtual T operator ()( const std::string& argument ) const override
{
const T value = lexical_cast< T >( argument );
if ( ( value < m_Min ) || ( m_Max < value ) )
{
throw std::domain_error( "value is out of range : " + argument );
}
return value;
}
T m_Min;
T m_Max;
};
class IOptionWithValue;
template< typename T, typename Reader >
class OptionWithValueWithReader;
class IOptionDetail
{
protected:
virtual ~IOptionDetail()
{
}
public:
enum Flag
{
kFlag_Once = 1 << 0,
kFlag_HasValue = 1 << 1,
kFlag_RequireValue = 1 << 2,
};
virtual int32_t GetId( void ) const = 0;
virtual const std::string& GetName( void ) const = 0;
virtual const std::string& GetShortName( void ) const = 0;
virtual const std::string& GetDescription( void ) const = 0;
virtual bool HasFlag( uint32_t flag ) const = 0;
virtual const std::string& GetValueDescription( void ) const = 0;
virtual bool IsSameName( const std::string& name ) const = 0;
virtual std::shared_ptr< IOptionWithValue > CreateEvaluator( const std::string& argument ) const = 0;
};
template< typename T, typename Reader = DefaultReader< T > >
class OptionDetail : public IOptionDetail
{
public:
OptionDetail( int32_t id, const std::string& name, const std::string& shortName, const std::string& description, uint32_t flags = 0, const std::string& valueDescription = std::string(), const Reader& reader = Reader() ) :
m_Id( id )
, m_Name( "--" + name )
, m_ShortName( "-" + shortName )
, m_Description( description )
, m_Flags( flags )
, m_ValueDescription( valueDescription )
, m_Reader( reader )
{
}
virtual ~OptionDetail()
{
}
int32_t GetId( void ) const final
{
return m_Id;
}
const std::string& GetName( void ) const final
{
return m_Name;
}
const std::string& GetShortName( void ) const final
{
return m_ShortName;
}
const std::string& GetDescription( void ) const final
{
return m_Description;
}
bool HasFlag( uint32_t flag ) const final
{
return ( m_Flags & flag ) ? true : false;
}
const std::string& GetValueDescription( void ) const final
{
return m_ValueDescription;
}
bool IsSameName( const std::string& name ) const final
{
return ( ( m_Name == name ) || ( m_ShortName == name ) );
}
std::shared_ptr< IOptionWithValue > CreateEvaluator( const std::string& argument ) const override
{
return std::make_shared< OptionWithValueWithReader< T, Reader > >( this, argument );
}
T GetValue( const std::string& argument ) const
{
return m_Reader( argument );
}
protected:
const int32_t m_Id;
const std::string m_Name;
const std::string m_ShortName;
const std::string m_Description;
const uint32_t m_Flags;
const std::string m_ValueDescription;
const Reader m_Reader;
};
class IOptionWithValue
{
protected:
virtual ~IOptionWithValue( void )
{
}
public:
virtual const IOptionDetail* GetDetail( void ) const = 0;
virtual uint32_t GetId( void ) const = 0;
virtual const std::string& GetOptionName( void ) const = 0;
virtual bool IsValid( void ) const = 0;
virtual bool Evaluate( std::string& message ) = 0;
};
template< typename T >
class OptionWithValue : public IOptionWithValue
{
protected:
OptionWithValue( const IOptionDetail* detail, const std::string& argument ) :
m_Detail( detail )
, m_Argument( argument )
, m_ValidValue( false )
{
}
public:
virtual ~OptionWithValue( void )
{
}
const IOptionDetail* GetDetail( void ) const final
{
return m_Detail;
}
uint32_t GetId( void ) const final
{
return m_Detail->GetId();
}
const std::string& GetOptionName( void ) const final
{
return m_Detail->GetName();
}
bool IsValid( void ) const final
{
return m_ValidValue;
}
virtual bool Evaluate( std::string& message ) override
{
return true;
}
const T& GetValue( void ) const
{
return m_Value;
}
protected:
const IOptionDetail* m_Detail;
const std::string m_Argument;
bool m_ValidValue;
T m_Value;
};
template< typename T, typename Reader >
class OptionWithValueWithReader : public OptionWithValue< T >
{
public:
OptionWithValueWithReader( const IOptionDetail* detail, const std::string& argument ) :
OptionWithValue< T >( detail, argument )
{
}
virtual ~OptionWithValueWithReader( void )
{
}
bool Evaluate( std::string& message ) override
{
try
{
if ( !this->m_Argument.empty() )
{
const auto* detail = dynamic_cast< const OptionDetail< T, Reader >* >( this->m_Detail );
this->m_Value = detail->GetValue( this->m_Argument );
this->m_ValidValue = true;
}
return true;
}
catch ( const std::exception& exception )
{
std::stringstream ss;
ss << this->m_Detail->GetName() << " : " << exception.what() << std::endl;
ss << this->m_Detail->GetDescription();
message = ss.str();
return false;
}
}
};
typedef std::vector< std::shared_ptr< IOptionDetail > > OptionDetailArray;
typedef std::vector< std::shared_ptr< IOptionWithValue > > OptionWithValueArray;
class Parser
{
public:
void AddOption( int32_t id, const std::string& name, const std::string& shortName, const std::string& description, uint32_t flags = 0, const std::string& valueDescription = std::string() )
{
m_Details.push_back( std::make_shared< OptionDetail< std::string > >( id, name, shortName, description, flags, valueDescription ) );
}
template< typename T, typename Reader = DefaultReader< T > >
void AddOption( int32_t id, const std::string& name, const std::string& shortName, const std::string& description, uint32_t flags = 0, const std::string& valueDescription = std::string(), const Reader& reader = Reader() )
{
m_Details.push_back( std::make_shared< OptionDetail< T, Reader > >( id, name, shortName, description, flags, valueDescription, reader ) );
}
size_t GetNumberOfOptionValues( void ) const
{
return m_OptionValues.size();
}
size_t GetNumberOfArguments( void ) const
{
return m_Arguments.size();
}
static const std::string& GetOptionName( const IOptionWithValue* value )
{
return value->GetDetail()->GetName();
}
template< typename T >
static const T& GetValue( const std::shared_ptr< IOptionWithValue >& value )
{
const auto* casted_value = dynamic_cast< const OptionWithValue< T >* >( value.get() );
return casted_value->GetValue();
}
const OptionDetailArray& GetOptionDetailArray( void ) const
{
return m_Details;
}
const OptionWithValueArray& GetOptionWithValueArray( void ) const
{
return m_OptionValues;
}
bool Parse( int argc, char* argv[] )
{
// clear & store
m_Arguments.clear();
for ( int i = 1; i < argc; ++i )
{
m_Arguments.push_back( argv[ i ] );
}
// parse
const size_t n_args = m_Arguments.size();
for ( size_t i = 0; i < n_args; ++i )
{
const std::string& option_name = m_Arguments[ i ];
if ( !HasOptionPrefix( option_name ) )
{
// error, argument is not option format
// ignore
m_Errors.push_back( "option syntax error : argument is not option format : " + option_name );
continue;
}
bool is_match = false;
bool is_valid_format = false;
std::string value;
for ( const auto detail : m_Details )
{
if ( !detail->IsSameName( option_name ) )
{
continue;
}
is_match = true;
// found
if ( detail->HasFlag( IOptionDetail::kFlag_HasValue ) )
{
const size_t next_i = i + 1;
if ( detail->HasFlag( IOptionDetail::kFlag_RequireValue ) )
{
if ( n_args <= next_i )
{
// error, argument locator over
m_Errors.push_back( "option syntax error : not enough argument : " + option_name );
break;
}
const std::string& next_value = m_Arguments[ next_i ];
if ( HasOptionPrefix( next_value ) )
{
// option have not value
m_Errors.push_back( "option syntax error : value not found : " + option_name );
break;
}
value = m_Arguments[ next_i ];
i = next_i;
}
else
{
if ( next_i < n_args )
{
const std::string& next_value = m_Arguments[ next_i ];
if ( !HasOptionPrefix( next_value ) )
{
// option have not value
value = m_Arguments[ next_i ];
i = next_i;
}
}
else
{
// not value, option only
}
}
}
// store found detal & value
m_OptionValues.push_back( detail->CreateEvaluator( value ) );
is_valid_format = true;
break;
}
if ( !is_match )
{
// unknown option
m_Warnings.push_back( "unknown option : " + option_name );
// value check
const size_t next_i = i + 1;
if ( next_i < n_args )
{
const std::string& next_value = m_Arguments[ next_i ];
if ( !HasOptionPrefix( next_value ) )
{
// discard unknown option value
i = next_i;
}
}
}
else if ( !is_valid_format )
{
// illegal option format
m_Errors.push_back( "option syntax error : illegal option format : " + option_name );
}
}
std::set< const IOptionDetail* > used_options;
for ( auto& option_value : m_OptionValues )
{
const IOptionDetail* detal = option_value->GetDetail();
const bool found = ( used_options.find( detal ) != used_options.end() );
if ( !found )
{
// first use
used_options.insert( detal );
}
if ( found && detal->HasFlag( IOptionDetail::kFlag_Once ) )
{
// duplicate use
m_Errors.push_back( "option syntax error : duplicate use : " + detal->GetName() );
continue;
}
std::string message;
if ( !option_value->Evaluate( message ) )
{
m_Errors.push_back( message );
// bad value
}
}
return ( m_Errors.size() == 0 );
}
void PrintUsage( const std::string& format ) const
{
std::cout << "Usage: " << format <<std::endl;
std::cout << std::endl;
std::cout << "OPTIONS:" << std::endl;
for ( const auto& detail : m_Details )
{
std::cout << " " << detail->GetName();
if ( !detail->GetShortName().empty() )
{
std::cout << ", " << detail->GetShortName();
}
if ( !detail->GetValueDescription().empty() )
{
std::cout << " <" << detail->GetValueDescription() << ">";
}
std::cout << std::endl;
std::cout << " " << detail->GetDescription() << std::endl;
}
}
void PrintErrors( void ) const
{
if ( !m_Errors.empty() )
{
std::cout << "Error:" << std::endl;
for ( const auto& error : m_Errors )
{
std::cout << error << std::endl;
}
}
}
void PrintWarnings( void ) const
{
if ( !m_Warnings.empty() )
{
std::cout << "Warning:" << std::endl;
for ( const auto& warning : m_Warnings )
{
std::cout << warning << std::endl;
}
}
}
private:
static bool HasOptionPrefix( const std::string& name )
{
return ( ( name.compare( 0, 2, "--" ) == 0 ) || ( name.compare( 0, 1, "-" ) == 0 ) );
}
private:
OptionDetailArray m_Details;
std::vector< std::string > m_Arguments;
std::vector< std::string > m_Errors;
std::vector< std::string > m_Warnings;
OptionWithValueArray m_OptionValues;
};
};
#endif
/*================================================================================================*/
/* EOF */
/*================================================================================================*/

View File

@@ -0,0 +1,284 @@
/* -*- mode: c++ ; coding: utf-8-unix -*- */
/* last updated : 2015/03/25.01:38:16 */
/*
* Copyright (c) 2013-2015 yaruopooner [https://github.com/yaruopooner]
*
* This file is part of ac-clang.
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*================================================================================================*/
/* Comment */
/*================================================================================================*/
/*================================================================================================*/
/* Include Files */
/*================================================================================================*/
#include <cstring>
#include <cstdarg>
#include "Common.hpp"
using namespace std;
FlagConverter ClangFlagConverters::sm_CXTranslationUnitFlags;
FlagConverter ClangFlagConverters::sm_CXCodeCompleteFlags;
/*================================================================================================*/
/* Global Class Method Definitions Section */
/*================================================================================================*/
StreamReader::StreamReader( void )
:
m_File( stdin )
{
ClearLine();
}
StreamReader::~StreamReader( void )
{
}
void StreamReader::ClearLine( void )
{
::memset( m_Line, 0, kLineMax );
}
void StreamReader::StepNextLine( void )
{
char crlf[ kLineMax ];
::fgets( crlf, kLineMax, m_File );
}
const char* StreamReader::ReadToken( const char* Format, bool bStepNextLine )
{
ClearLine();
const int32_t result = ::fscanf( m_File, Format, m_Line );
(void) result;
if ( bStepNextLine )
{
StepNextLine();
}
return ( m_Line );
}
void StreamReader::Read( char* Buffer, size_t ReadSize )
{
const size_t stored_size = ::fread( Buffer, 1, ReadSize, m_File );
if ( (stored_size < ReadSize) || ::feof( m_File ) )
{
// error
}
}
StreamWriter::StreamWriter( void )
:
m_File( stdout )
{
}
StreamWriter::~StreamWriter( void )
{
}
void StreamWriter::Write( const char* Format, ... )
{
va_list args;
va_start( args, Format );
::vfprintf( m_File, Format, args );
va_end( args );
}
void StreamWriter::Flush( void )
{
::fprintf( m_File, "$" );
::fflush( m_File );
}
CFlagsBuffer::CFlagsBuffer( void )
:
m_NumberOfCFlags( 0 )
, m_CFlags( nullptr )
{
}
CFlagsBuffer::~CFlagsBuffer( void )
{
Deallocate();
}
void CFlagsBuffer::Allocate( const std::vector< std::string >& Args )
{
Deallocate();
m_NumberOfCFlags = static_cast< int32_t >( Args.size() );
m_CFlags = reinterpret_cast< char** >( ::calloc( sizeof( char* ), m_NumberOfCFlags ) );
for ( int32_t i = 0; i < m_NumberOfCFlags; ++i )
{
m_CFlags[ i ] = reinterpret_cast< char* >( ::calloc( sizeof( char ), Args[ i ].length() + 1 ) );
::strcpy( m_CFlags[ i ], Args[ i ].c_str() );
}
}
void CFlagsBuffer::Deallocate( void )
{
if ( !m_CFlags )
{
return;
}
for ( int32_t i = 0; i < m_NumberOfCFlags; ++i )
{
::free( m_CFlags[ i ] );
}
::free( m_CFlags );
m_CFlags = nullptr;
m_NumberOfCFlags = 0;
}
CSourceCodeBuffer::CSourceCodeBuffer( void )
:
m_Size( 0 )
, m_BufferCapacity( 0 )
, m_Buffer( nullptr )
{
}
CSourceCodeBuffer::~CSourceCodeBuffer( void )
{
Deallocate();
}
void CSourceCodeBuffer::Allocate( int32_t Size )
{
m_Size = Size;
if ( m_Size >= m_BufferCapacity )
{
const int32_t extend_size = std::max( m_Size * 2, static_cast< int32_t >( kInitialSrcBufferSize ) );
char* extend_buffer = reinterpret_cast< char* >( ::realloc( m_Buffer, extend_size ) );
if ( extend_buffer )
{
m_BufferCapacity = extend_size;
m_Buffer = extend_buffer;
}
else
{
// error
}
}
}
void CSourceCodeBuffer::Deallocate( void )
{
if ( m_Buffer )
{
::free( m_Buffer );
m_Buffer = nullptr;
}
m_Size = 0;
}
ClangContext::ClangContext( bool excludeDeclarationsFromPCH )
:
m_CxIndex( nullptr )
, m_ExcludeDeclarationsFromPCH( excludeDeclarationsFromPCH )
, m_TranslationUnitFlags( CXTranslationUnit_PrecompiledPreamble )
, m_CompleteAtFlags( CXCodeComplete_IncludeMacros )
, m_CompleteResultsLimit( 0 )
{
Allocate();
}
ClangContext::~ClangContext( void )
{
Deallocate();
}
void ClangContext::Allocate( void )
{
m_CxIndex = clang_createIndex( m_ExcludeDeclarationsFromPCH, 0 );
}
void ClangContext::Deallocate( void )
{
if ( m_CxIndex )
{
clang_disposeIndex( m_CxIndex );
m_CxIndex = nullptr;
}
}
ClangFlagConverters::ClangFlagConverters( void )
{
sm_CXTranslationUnitFlags.Add( FLAG_DETAILS( CXTranslationUnit_DetailedPreprocessingRecord ) );
sm_CXTranslationUnitFlags.Add( FLAG_DETAILS( CXTranslationUnit_Incomplete ) );
sm_CXTranslationUnitFlags.Add( FLAG_DETAILS( CXTranslationUnit_PrecompiledPreamble ) );
sm_CXTranslationUnitFlags.Add( FLAG_DETAILS( CXTranslationUnit_CacheCompletionResults ) );
sm_CXTranslationUnitFlags.Add( FLAG_DETAILS( CXTranslationUnit_ForSerialization ) );
sm_CXTranslationUnitFlags.Add( FLAG_DETAILS( CXTranslationUnit_CXXChainedPCH ) );
sm_CXTranslationUnitFlags.Add( FLAG_DETAILS( CXTranslationUnit_SkipFunctionBodies ) );
sm_CXTranslationUnitFlags.Add( FLAG_DETAILS( CXTranslationUnit_IncludeBriefCommentsInCodeCompletion ) );
sm_CXCodeCompleteFlags.Add( FLAG_DETAILS( CXCodeComplete_IncludeMacros ) );
sm_CXCodeCompleteFlags.Add( FLAG_DETAILS( CXCodeComplete_IncludeCodePatterns ) );
sm_CXCodeCompleteFlags.Add( FLAG_DETAILS( CXCodeComplete_IncludeBriefComments ) );
}
/*================================================================================================*/
/* EOF */
/*================================================================================================*/

View File

@@ -0,0 +1,371 @@
/* -*- mode: c++ ; coding: utf-8-unix -*- */
/* last updated : 2015/03/25.01:38:00 */
/*
* Copyright (c) 2013-2015 yaruopooner [https://github.com/yaruopooner]
*
* This file is part of ac-clang.
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __COMMON_HPP__
#define __COMMON_HPP__
/*================================================================================================*/
/* Comment */
/*================================================================================================*/
/*================================================================================================*/
/* Include Files */
/*================================================================================================*/
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <algorithm>
#include <tuple>
#include <string>
#include <vector>
#include "clang-c/Index.h"
/*================================================================================================*/
/* Class */
/*================================================================================================*/
class StreamReader
{
public:
StreamReader( void );
virtual ~StreamReader( void );
template< typename T >
void ReadToken( const char* Format, T& Value, bool bStepNextLine = true )
{
ClearLine();
::fscanf( m_File, Format, &Value );
if ( bStepNextLine )
{
StepNextLine();
}
}
const char* ReadToken( const char* Format, bool bStepNextLine = true );
void Read( char* Buffer, size_t ReadSize );
private:
void ClearLine( void );
void StepNextLine( void );
private:
enum
{
kLineMax = 2048,
};
FILE* m_File;
char m_Line[ kLineMax ];
};
class StreamWriter
{
public:
StreamWriter( void );
virtual ~StreamWriter( void );
void Write( const char* Format, ... );
void Flush( void );
private:
FILE* m_File;
};
class CFlagsBuffer
{
public:
CFlagsBuffer( void );
virtual ~CFlagsBuffer( void );
void Allocate( const std::vector< std::string >& CFlags );
void Deallocate( void );
int32_t GetNumberOfCFlags( void ) const
{
return ( m_NumberOfCFlags );
}
char** GetCFlags( void ) const
{
return ( m_CFlags );
}
private:
int32_t m_NumberOfCFlags;
char** m_CFlags;
};
class CSourceCodeBuffer
{
public:
CSourceCodeBuffer( void );
virtual ~CSourceCodeBuffer( void );
void Allocate( int32_t Size );
void Deallocate( void );
int32_t GetSize( void ) const
{
return ( m_Size );
}
char* GetBuffer( void ) const
{
return ( m_Buffer );
}
private:
enum
{
kInitialSrcBufferSize = 4096,
};
int32_t m_Size;
int32_t m_BufferCapacity;
char* m_Buffer;
};
class ClangContext
{
public:
ClangContext( bool excludeDeclarationsFromPCH = false );
virtual ~ClangContext( void );
void Allocate( void );
void Deallocate( void );
const CXIndex GetCXIndex( void ) const
{
return ( m_CxIndex );
}
CXIndex GetCXIndex( void )
{
return ( m_CxIndex );
}
void SetTranslationUnitFlags( uint32_t Flags )
{
m_TranslationUnitFlags = Flags;
}
uint32_t GetTranslationUnitFlags( void ) const
{
return ( m_TranslationUnitFlags );
}
void SetCompleteAtFlags( uint32_t Flags )
{
m_CompleteAtFlags = Flags;
}
uint32_t GetCompleteAtFlags( void ) const
{
return ( m_CompleteAtFlags );
}
void SetCompleteResultsLimit( uint32_t NumberOfLimit )
{
m_CompleteResultsLimit = NumberOfLimit;
}
uint32_t GetCompleteResultsLimit( void ) const
{
return ( m_CompleteResultsLimit );
}
private:
CXIndex m_CxIndex;
bool m_ExcludeDeclarationsFromPCH;
uint32_t m_TranslationUnitFlags;
uint32_t m_CompleteAtFlags;
uint32_t m_CompleteResultsLimit;
};
template< uint32_t Value >
struct BitField
{
enum
{
kValue = Value,
kIndex = BitField< (Value >> 1) >::kIndex + 1,
};
};
template<>
struct BitField< 0 >
{
enum
{
kValue = 0,
kIndex = -1,
};
};
class FlagConverter
{
public:
typedef std::tuple< const char*, uint32_t > Details;
enum
{
kMaxValues = 32,
};
FlagConverter( void ) :
m_MaxValue( 0 )
{
}
virtual ~FlagConverter( void )
{
}
void Clear( void )
{
m_MaxValue = 0;
}
void Add( const Details& Values )
{
Add( std::get< 0 >( Values ), std::get< 1 >( Values ) );
}
void Add( const char* Name, uint32_t BitIndex )
{
assert( Name );
assert( BitIndex < kMaxValues );
m_FlagNames[ BitIndex ] = Name;
m_MaxValue = std::max( m_MaxValue, (BitIndex + 1) );
}
uint32_t GetValue( const std::string& Names ) const
{
return ( GetValue( Names.c_str() ) );
}
uint32_t GetValue( const char* Names ) const
{
if ( !Names )
{
return ( 0 );
}
std::string names( Names );
const char* delimit = "|";
if ( *(names.rbegin()) != *delimit )
{
names += delimit;
}
uint32_t value = 0;
size_t begin = 0;
size_t end = names.find_first_of( delimit );
while ( end != std::string::npos )
{
const size_t length = end - begin;
const std::string name = names.substr( begin, length );
for ( size_t i = 0; i < m_MaxValue; ++i )
{
if ( m_FlagNames[ i ] == name )
{
value |= (1 << i);
break;
}
}
begin = end + 1;
end = names.find_first_of( delimit, begin );
}
return ( value );
}
private:
std::string m_FlagNames[ kMaxValues ];
uint32_t m_MaxValue;
};
#define FLAG_DETAILS( _FLAG_NAME ) FlagConverter::Details( #_FLAG_NAME, BitField< _FLAG_NAME >::kIndex )
class ClangFlagConverters
{
public:
ClangFlagConverters( void );
static const FlagConverter& GetCXTranslationUnitFlags( void )
{
return ( sm_CXTranslationUnitFlags );
}
static const FlagConverter& GetCXCodeCompleteFlags( void )
{
return ( sm_CXCodeCompleteFlags );
}
private:
static FlagConverter sm_CXTranslationUnitFlags;
static FlagConverter sm_CXCodeCompleteFlags;
};
#endif // __COMMON_HPP__
/*================================================================================================*/
/* EOF */
/*================================================================================================*/

View File

@@ -0,0 +1,32 @@
# -*- mode: org ; coding: utf-8-unix -*-
# last updated : 2015/09/05.04:13:32
#+TITLE: clang-server Changes Log
#+AUTHOR: yaruopooner
#+EMAIL: [https://github.com/yaruopooner]
#+OPTIONS: author:nil timestamp:t |:t \n:t ^:nil
* 1.4.0
- Clang-3.7.0 support
* 1.3.0
- Added new feature. Jump to inclusion file. return from jumped location.
- Bugfix
* 1.2.1
- Fix compile error in clang.
* 1.2.0
- Access specifier of completion candidate support.
* 1.1.1
- Clang-3.6.1 support
- Bugfix
* 1.1.0
- Clang-3.6.0 support
* 1.0.0
- Clang-3.5.0 support

View File

@@ -0,0 +1,156 @@
/*==-- clang-c/BuildSystem.h - Utilities for use by build systems -*- C -*-===*\
|* *|
|* The LLVM Compiler Infrastructure *|
|* *|
|* This file is distributed under the University of Illinois Open Source *|
|* License. See LICENSE.TXT for details. *|
|* *|
|*===----------------------------------------------------------------------===*|
|* *|
|* This header provides various utilities for use by build systems. *|
|* *|
\*===----------------------------------------------------------------------===*/
#ifndef LLVM_CLANG_C_BUILDSYSTEM_H
#define LLVM_CLANG_C_BUILDSYSTEM_H
#include "clang-c/Platform.h"
#include "clang-c/CXErrorCode.h"
#include "clang-c/CXString.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* \defgroup BUILD_SYSTEM Build system utilities
* @{
*/
/**
* \brief Return the timestamp for use with Clang's
* \c -fbuild-session-timestamp= option.
*/
CINDEX_LINKAGE unsigned long long clang_getBuildSessionTimestamp(void);
/**
* \brief Object encapsulating information about overlaying virtual
* file/directories over the real file system.
*/
typedef struct CXVirtualFileOverlayImpl *CXVirtualFileOverlay;
/**
* \brief Create a \c CXVirtualFileOverlay object.
* Must be disposed with \c clang_VirtualFileOverlay_dispose().
*
* \param options is reserved, always pass 0.
*/
CINDEX_LINKAGE CXVirtualFileOverlay
clang_VirtualFileOverlay_create(unsigned options);
/**
* \brief Map an absolute virtual file path to an absolute real one.
* The virtual path must be canonicalized (not contain "."/"..").
* \returns 0 for success, non-zero to indicate an error.
*/
CINDEX_LINKAGE enum CXErrorCode
clang_VirtualFileOverlay_addFileMapping(CXVirtualFileOverlay,
const char *virtualPath,
const char *realPath);
/**
* \brief Set the case sensitivity for the \c CXVirtualFileOverlay object.
* The \c CXVirtualFileOverlay object is case-sensitive by default, this
* option can be used to override the default.
* \returns 0 for success, non-zero to indicate an error.
*/
CINDEX_LINKAGE enum CXErrorCode
clang_VirtualFileOverlay_setCaseSensitivity(CXVirtualFileOverlay,
int caseSensitive);
/**
* \brief Write out the \c CXVirtualFileOverlay object to a char buffer.
*
* \param options is reserved, always pass 0.
* \param out_buffer_ptr pointer to receive the buffer pointer, which should be
* disposed using \c clang_free().
* \param out_buffer_size pointer to receive the buffer size.
* \returns 0 for success, non-zero to indicate an error.
*/
CINDEX_LINKAGE enum CXErrorCode
clang_VirtualFileOverlay_writeToBuffer(CXVirtualFileOverlay, unsigned options,
char **out_buffer_ptr,
unsigned *out_buffer_size);
/**
* \brief free memory allocated by libclang, such as the buffer returned by
* \c CXVirtualFileOverlay() or \c clang_ModuleMapDescriptor_writeToBuffer().
*
* \param buffer memory pointer to free.
*/
CINDEX_LINKAGE void clang_free(void *buffer);
/**
* \brief Dispose a \c CXVirtualFileOverlay object.
*/
CINDEX_LINKAGE void clang_VirtualFileOverlay_dispose(CXVirtualFileOverlay);
/**
* \brief Object encapsulating information about a module.map file.
*/
typedef struct CXModuleMapDescriptorImpl *CXModuleMapDescriptor;
/**
* \brief Create a \c CXModuleMapDescriptor object.
* Must be disposed with \c clang_ModuleMapDescriptor_dispose().
*
* \param options is reserved, always pass 0.
*/
CINDEX_LINKAGE CXModuleMapDescriptor
clang_ModuleMapDescriptor_create(unsigned options);
/**
* \brief Sets the framework module name that the module.map describes.
* \returns 0 for success, non-zero to indicate an error.
*/
CINDEX_LINKAGE enum CXErrorCode
clang_ModuleMapDescriptor_setFrameworkModuleName(CXModuleMapDescriptor,
const char *name);
/**
* \brief Sets the umbrealla header name that the module.map describes.
* \returns 0 for success, non-zero to indicate an error.
*/
CINDEX_LINKAGE enum CXErrorCode
clang_ModuleMapDescriptor_setUmbrellaHeader(CXModuleMapDescriptor,
const char *name);
/**
* \brief Write out the \c CXModuleMapDescriptor object to a char buffer.
*
* \param options is reserved, always pass 0.
* \param out_buffer_ptr pointer to receive the buffer pointer, which should be
* disposed using \c clang_free().
* \param out_buffer_size pointer to receive the buffer size.
* \returns 0 for success, non-zero to indicate an error.
*/
CINDEX_LINKAGE enum CXErrorCode
clang_ModuleMapDescriptor_writeToBuffer(CXModuleMapDescriptor, unsigned options,
char **out_buffer_ptr,
unsigned *out_buffer_size);
/**
* \brief Dispose a \c CXModuleMapDescriptor object.
*/
CINDEX_LINKAGE void clang_ModuleMapDescriptor_dispose(CXModuleMapDescriptor);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* CLANG_C_BUILD_SYSTEM_H */

View File

@@ -0,0 +1,170 @@
/*===-- clang-c/CXCompilationDatabase.h - Compilation database ---*- C -*-===*\
|* *|
|* The LLVM Compiler Infrastructure *|
|* *|
|* This file is distributed under the University of Illinois Open Source *|
|* License. See LICENSE.TXT for details. *|
|* *|
|*===----------------------------------------------------------------------===*|
|* *|
|* This header provides a public inferface to use CompilationDatabase without *|
|* the full Clang C++ API. *|
|* *|
\*===----------------------------------------------------------------------===*/
#ifndef LLVM_CLANG_C_CXCOMPILATIONDATABASE_H
#define LLVM_CLANG_C_CXCOMPILATIONDATABASE_H
#include "clang-c/Platform.h"
#include "clang-c/CXString.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \defgroup COMPILATIONDB CompilationDatabase functions
* \ingroup CINDEX
*
* @{
*/
/**
* A compilation database holds all information used to compile files in a
* project. For each file in the database, it can be queried for the working
* directory or the command line used for the compiler invocation.
*
* Must be freed by \c clang_CompilationDatabase_dispose
*/
typedef void * CXCompilationDatabase;
/**
* \brief Contains the results of a search in the compilation database
*
* When searching for the compile command for a file, the compilation db can
* return several commands, as the file may have been compiled with
* different options in different places of the project. This choice of compile
* commands is wrapped in this opaque data structure. It must be freed by
* \c clang_CompileCommands_dispose.
*/
typedef void * CXCompileCommands;
/**
* \brief Represents the command line invocation to compile a specific file.
*/
typedef void * CXCompileCommand;
/**
* \brief Error codes for Compilation Database
*/
typedef enum {
/*
* \brief No error occurred
*/
CXCompilationDatabase_NoError = 0,
/*
* \brief Database can not be loaded
*/
CXCompilationDatabase_CanNotLoadDatabase = 1
} CXCompilationDatabase_Error;
/**
* \brief Creates a compilation database from the database found in directory
* buildDir. For example, CMake can output a compile_commands.json which can
* be used to build the database.
*
* It must be freed by \c clang_CompilationDatabase_dispose.
*/
CINDEX_LINKAGE CXCompilationDatabase
clang_CompilationDatabase_fromDirectory(const char *BuildDir,
CXCompilationDatabase_Error *ErrorCode);
/**
* \brief Free the given compilation database
*/
CINDEX_LINKAGE void
clang_CompilationDatabase_dispose(CXCompilationDatabase);
/**
* \brief Find the compile commands used for a file. The compile commands
* must be freed by \c clang_CompileCommands_dispose.
*/
CINDEX_LINKAGE CXCompileCommands
clang_CompilationDatabase_getCompileCommands(CXCompilationDatabase,
const char *CompleteFileName);
/**
* \brief Get all the compile commands in the given compilation database.
*/
CINDEX_LINKAGE CXCompileCommands
clang_CompilationDatabase_getAllCompileCommands(CXCompilationDatabase);
/**
* \brief Free the given CompileCommands
*/
CINDEX_LINKAGE void clang_CompileCommands_dispose(CXCompileCommands);
/**
* \brief Get the number of CompileCommand we have for a file
*/
CINDEX_LINKAGE unsigned
clang_CompileCommands_getSize(CXCompileCommands);
/**
* \brief Get the I'th CompileCommand for a file
*
* Note : 0 <= i < clang_CompileCommands_getSize(CXCompileCommands)
*/
CINDEX_LINKAGE CXCompileCommand
clang_CompileCommands_getCommand(CXCompileCommands, unsigned I);
/**
* \brief Get the working directory where the CompileCommand was executed from
*/
CINDEX_LINKAGE CXString
clang_CompileCommand_getDirectory(CXCompileCommand);
/**
* \brief Get the number of arguments in the compiler invocation.
*
*/
CINDEX_LINKAGE unsigned
clang_CompileCommand_getNumArgs(CXCompileCommand);
/**
* \brief Get the I'th argument value in the compiler invocations
*
* Invariant :
* - argument 0 is the compiler executable
*/
CINDEX_LINKAGE CXString
clang_CompileCommand_getArg(CXCompileCommand, unsigned I);
/**
* \brief Get the number of source mappings for the compiler invocation.
*/
CINDEX_LINKAGE unsigned
clang_CompileCommand_getNumMappedSources(CXCompileCommand);
/**
* \brief Get the I'th mapped source path for the compiler invocation.
*/
CINDEX_LINKAGE CXString
clang_CompileCommand_getMappedSourcePath(CXCompileCommand, unsigned I);
/**
* \brief Get the I'th mapped source content for the compiler invocation.
*/
CINDEX_LINKAGE CXString
clang_CompileCommand_getMappedSourceContent(CXCompileCommand, unsigned I);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,64 @@
/*===-- clang-c/CXErrorCode.h - C Index Error Codes --------------*- C -*-===*\
|* *|
|* The LLVM Compiler Infrastructure *|
|* *|
|* This file is distributed under the University of Illinois Open Source *|
|* License. See LICENSE.TXT for details. *|
|* *|
|*===----------------------------------------------------------------------===*|
|* *|
|* This header provides the CXErrorCode enumerators. *|
|* *|
\*===----------------------------------------------------------------------===*/
#ifndef LLVM_CLANG_C_CXERRORCODE_H
#define LLVM_CLANG_C_CXERRORCODE_H
#include "clang-c/Platform.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Error codes returned by libclang routines.
*
* Zero (\c CXError_Success) is the only error code indicating success. Other
* error codes, including not yet assigned non-zero values, indicate errors.
*/
enum CXErrorCode {
/**
* \brief No error.
*/
CXError_Success = 0,
/**
* \brief A generic error code, no further details are available.
*
* Errors of this kind can get their own specific error codes in future
* libclang versions.
*/
CXError_Failure = 1,
/**
* \brief libclang crashed while performing the requested operation.
*/
CXError_Crashed = 2,
/**
* \brief The function detected that the arguments violate the function
* contract.
*/
CXError_InvalidArguments = 3,
/**
* \brief An AST deserialization error has occurred.
*/
CXError_ASTReadError = 4
};
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,61 @@
/*===-- clang-c/CXString.h - C Index strings --------------------*- C -*-===*\
|* *|
|* The LLVM Compiler Infrastructure *|
|* *|
|* This file is distributed under the University of Illinois Open Source *|
|* License. See LICENSE.TXT for details. *|
|* *|
|*===----------------------------------------------------------------------===*|
|* *|
|* This header provides the interface to C Index strings. *|
|* *|
\*===----------------------------------------------------------------------===*/
#ifndef LLVM_CLANG_C_CXSTRING_H
#define LLVM_CLANG_C_CXSTRING_H
#include "clang-c/Platform.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* \defgroup CINDEX_STRING String manipulation routines
* \ingroup CINDEX
*
* @{
*/
/**
* \brief A character string.
*
* The \c CXString type is used to return strings from the interface when
* the ownership of that string might differ from one call to the next.
* Use \c clang_getCString() to retrieve the string data and, once finished
* with the string data, call \c clang_disposeString() to free the string.
*/
typedef struct {
const void *data;
unsigned private_flags;
} CXString;
/**
* \brief Retrieve the character data associated with the given string.
*/
CINDEX_LINKAGE const char *clang_getCString(CXString string);
/**
* \brief Free the given string.
*/
CINDEX_LINKAGE void clang_disposeString(CXString string);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,554 @@
/*==-- clang-c/Documentation.h - Utilities for comment processing -*- C -*-===*\
|* *|
|* The LLVM Compiler Infrastructure *|
|* *|
|* This file is distributed under the University of Illinois Open Source *|
|* License. See LICENSE.TXT for details. *|
|* *|
|*===----------------------------------------------------------------------===*|
|* *|
|* This header provides a supplementary interface for inspecting *|
|* documentation comments. *|
|* *|
\*===----------------------------------------------------------------------===*/
#ifndef LLVM_CLANG_C_DOCUMENTATION_H
#define LLVM_CLANG_C_DOCUMENTATION_H
#include "clang-c/Index.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* \defgroup CINDEX_COMMENT Comment introspection
*
* The routines in this group provide access to information in documentation
* comments. These facilities are distinct from the core and may be subject to
* their own schedule of stability and deprecation.
*
* @{
*/
/**
* \brief A parsed comment.
*/
typedef struct {
const void *ASTNode;
CXTranslationUnit TranslationUnit;
} CXComment;
/**
* \brief Given a cursor that represents a documentable entity (e.g.,
* declaration), return the associated parsed comment as a
* \c CXComment_FullComment AST node.
*/
CINDEX_LINKAGE CXComment clang_Cursor_getParsedComment(CXCursor C);
/**
* \brief Describes the type of the comment AST node (\c CXComment). A comment
* node can be considered block content (e. g., paragraph), inline content
* (plain text) or neither (the root AST node).
*/
enum CXCommentKind {
/**
* \brief Null comment. No AST node is constructed at the requested location
* because there is no text or a syntax error.
*/
CXComment_Null = 0,
/**
* \brief Plain text. Inline content.
*/
CXComment_Text = 1,
/**
* \brief A command with word-like arguments that is considered inline content.
*
* For example: \\c command.
*/
CXComment_InlineCommand = 2,
/**
* \brief HTML start tag with attributes (name-value pairs). Considered
* inline content.
*
* For example:
* \verbatim
* <br> <br /> <a href="http://example.org/">
* \endverbatim
*/
CXComment_HTMLStartTag = 3,
/**
* \brief HTML end tag. Considered inline content.
*
* For example:
* \verbatim
* </a>
* \endverbatim
*/
CXComment_HTMLEndTag = 4,
/**
* \brief A paragraph, contains inline comment. The paragraph itself is
* block content.
*/
CXComment_Paragraph = 5,
/**
* \brief A command that has zero or more word-like arguments (number of
* word-like arguments depends on command name) and a paragraph as an
* argument. Block command is block content.
*
* Paragraph argument is also a child of the block command.
*
* For example: \\brief has 0 word-like arguments and a paragraph argument.
*
* AST nodes of special kinds that parser knows about (e. g., \\param
* command) have their own node kinds.
*/
CXComment_BlockCommand = 6,
/**
* \brief A \\param or \\arg command that describes the function parameter
* (name, passing direction, description).
*
* For example: \\param [in] ParamName description.
*/
CXComment_ParamCommand = 7,
/**
* \brief A \\tparam command that describes a template parameter (name and
* description).
*
* For example: \\tparam T description.
*/
CXComment_TParamCommand = 8,
/**
* \brief A verbatim block command (e. g., preformatted code). Verbatim
* block has an opening and a closing command and contains multiple lines of
* text (\c CXComment_VerbatimBlockLine child nodes).
*
* For example:
* \\verbatim
* aaa
* \\endverbatim
*/
CXComment_VerbatimBlockCommand = 9,
/**
* \brief A line of text that is contained within a
* CXComment_VerbatimBlockCommand node.
*/
CXComment_VerbatimBlockLine = 10,
/**
* \brief A verbatim line command. Verbatim line has an opening command,
* a single line of text (up to the newline after the opening command) and
* has no closing command.
*/
CXComment_VerbatimLine = 11,
/**
* \brief A full comment attached to a declaration, contains block content.
*/
CXComment_FullComment = 12
};
/**
* \brief The most appropriate rendering mode for an inline command, chosen on
* command semantics in Doxygen.
*/
enum CXCommentInlineCommandRenderKind {
/**
* \brief Command argument should be rendered in a normal font.
*/
CXCommentInlineCommandRenderKind_Normal,
/**
* \brief Command argument should be rendered in a bold font.
*/
CXCommentInlineCommandRenderKind_Bold,
/**
* \brief Command argument should be rendered in a monospaced font.
*/
CXCommentInlineCommandRenderKind_Monospaced,
/**
* \brief Command argument should be rendered emphasized (typically italic
* font).
*/
CXCommentInlineCommandRenderKind_Emphasized
};
/**
* \brief Describes parameter passing direction for \\param or \\arg command.
*/
enum CXCommentParamPassDirection {
/**
* \brief The parameter is an input parameter.
*/
CXCommentParamPassDirection_In,
/**
* \brief The parameter is an output parameter.
*/
CXCommentParamPassDirection_Out,
/**
* \brief The parameter is an input and output parameter.
*/
CXCommentParamPassDirection_InOut
};
/**
* \param Comment AST node of any kind.
*
* \returns the type of the AST node.
*/
CINDEX_LINKAGE enum CXCommentKind clang_Comment_getKind(CXComment Comment);
/**
* \param Comment AST node of any kind.
*
* \returns number of children of the AST node.
*/
CINDEX_LINKAGE unsigned clang_Comment_getNumChildren(CXComment Comment);
/**
* \param Comment AST node of any kind.
*
* \param ChildIdx child index (zero-based).
*
* \returns the specified child of the AST node.
*/
CINDEX_LINKAGE
CXComment clang_Comment_getChild(CXComment Comment, unsigned ChildIdx);
/**
* \brief A \c CXComment_Paragraph node is considered whitespace if it contains
* only \c CXComment_Text nodes that are empty or whitespace.
*
* Other AST nodes (except \c CXComment_Paragraph and \c CXComment_Text) are
* never considered whitespace.
*
* \returns non-zero if \c Comment is whitespace.
*/
CINDEX_LINKAGE unsigned clang_Comment_isWhitespace(CXComment Comment);
/**
* \returns non-zero if \c Comment is inline content and has a newline
* immediately following it in the comment text. Newlines between paragraphs
* do not count.
*/
CINDEX_LINKAGE
unsigned clang_InlineContentComment_hasTrailingNewline(CXComment Comment);
/**
* \param Comment a \c CXComment_Text AST node.
*
* \returns text contained in the AST node.
*/
CINDEX_LINKAGE CXString clang_TextComment_getText(CXComment Comment);
/**
* \param Comment a \c CXComment_InlineCommand AST node.
*
* \returns name of the inline command.
*/
CINDEX_LINKAGE
CXString clang_InlineCommandComment_getCommandName(CXComment Comment);
/**
* \param Comment a \c CXComment_InlineCommand AST node.
*
* \returns the most appropriate rendering mode, chosen on command
* semantics in Doxygen.
*/
CINDEX_LINKAGE enum CXCommentInlineCommandRenderKind
clang_InlineCommandComment_getRenderKind(CXComment Comment);
/**
* \param Comment a \c CXComment_InlineCommand AST node.
*
* \returns number of command arguments.
*/
CINDEX_LINKAGE
unsigned clang_InlineCommandComment_getNumArgs(CXComment Comment);
/**
* \param Comment a \c CXComment_InlineCommand AST node.
*
* \param ArgIdx argument index (zero-based).
*
* \returns text of the specified argument.
*/
CINDEX_LINKAGE
CXString clang_InlineCommandComment_getArgText(CXComment Comment,
unsigned ArgIdx);
/**
* \param Comment a \c CXComment_HTMLStartTag or \c CXComment_HTMLEndTag AST
* node.
*
* \returns HTML tag name.
*/
CINDEX_LINKAGE CXString clang_HTMLTagComment_getTagName(CXComment Comment);
/**
* \param Comment a \c CXComment_HTMLStartTag AST node.
*
* \returns non-zero if tag is self-closing (for example, &lt;br /&gt;).
*/
CINDEX_LINKAGE
unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment Comment);
/**
* \param Comment a \c CXComment_HTMLStartTag AST node.
*
* \returns number of attributes (name-value pairs) attached to the start tag.
*/
CINDEX_LINKAGE unsigned clang_HTMLStartTag_getNumAttrs(CXComment Comment);
/**
* \param Comment a \c CXComment_HTMLStartTag AST node.
*
* \param AttrIdx attribute index (zero-based).
*
* \returns name of the specified attribute.
*/
CINDEX_LINKAGE
CXString clang_HTMLStartTag_getAttrName(CXComment Comment, unsigned AttrIdx);
/**
* \param Comment a \c CXComment_HTMLStartTag AST node.
*
* \param AttrIdx attribute index (zero-based).
*
* \returns value of the specified attribute.
*/
CINDEX_LINKAGE
CXString clang_HTMLStartTag_getAttrValue(CXComment Comment, unsigned AttrIdx);
/**
* \param Comment a \c CXComment_BlockCommand AST node.
*
* \returns name of the block command.
*/
CINDEX_LINKAGE
CXString clang_BlockCommandComment_getCommandName(CXComment Comment);
/**
* \param Comment a \c CXComment_BlockCommand AST node.
*
* \returns number of word-like arguments.
*/
CINDEX_LINKAGE
unsigned clang_BlockCommandComment_getNumArgs(CXComment Comment);
/**
* \param Comment a \c CXComment_BlockCommand AST node.
*
* \param ArgIdx argument index (zero-based).
*
* \returns text of the specified word-like argument.
*/
CINDEX_LINKAGE
CXString clang_BlockCommandComment_getArgText(CXComment Comment,
unsigned ArgIdx);
/**
* \param Comment a \c CXComment_BlockCommand or
* \c CXComment_VerbatimBlockCommand AST node.
*
* \returns paragraph argument of the block command.
*/
CINDEX_LINKAGE
CXComment clang_BlockCommandComment_getParagraph(CXComment Comment);
/**
* \param Comment a \c CXComment_ParamCommand AST node.
*
* \returns parameter name.
*/
CINDEX_LINKAGE
CXString clang_ParamCommandComment_getParamName(CXComment Comment);
/**
* \param Comment a \c CXComment_ParamCommand AST node.
*
* \returns non-zero if the parameter that this AST node represents was found
* in the function prototype and \c clang_ParamCommandComment_getParamIndex
* function will return a meaningful value.
*/
CINDEX_LINKAGE
unsigned clang_ParamCommandComment_isParamIndexValid(CXComment Comment);
/**
* \param Comment a \c CXComment_ParamCommand AST node.
*
* \returns zero-based parameter index in function prototype.
*/
CINDEX_LINKAGE
unsigned clang_ParamCommandComment_getParamIndex(CXComment Comment);
/**
* \param Comment a \c CXComment_ParamCommand AST node.
*
* \returns non-zero if parameter passing direction was specified explicitly in
* the comment.
*/
CINDEX_LINKAGE
unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment Comment);
/**
* \param Comment a \c CXComment_ParamCommand AST node.
*
* \returns parameter passing direction.
*/
CINDEX_LINKAGE
enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
CXComment Comment);
/**
* \param Comment a \c CXComment_TParamCommand AST node.
*
* \returns template parameter name.
*/
CINDEX_LINKAGE
CXString clang_TParamCommandComment_getParamName(CXComment Comment);
/**
* \param Comment a \c CXComment_TParamCommand AST node.
*
* \returns non-zero if the parameter that this AST node represents was found
* in the template parameter list and
* \c clang_TParamCommandComment_getDepth and
* \c clang_TParamCommandComment_getIndex functions will return a meaningful
* value.
*/
CINDEX_LINKAGE
unsigned clang_TParamCommandComment_isParamPositionValid(CXComment Comment);
/**
* \param Comment a \c CXComment_TParamCommand AST node.
*
* \returns zero-based nesting depth of this parameter in the template parameter list.
*
* For example,
* \verbatim
* template<typename C, template<typename T> class TT>
* void test(TT<int> aaa);
* \endverbatim
* for C and TT nesting depth is 0,
* for T nesting depth is 1.
*/
CINDEX_LINKAGE
unsigned clang_TParamCommandComment_getDepth(CXComment Comment);
/**
* \param Comment a \c CXComment_TParamCommand AST node.
*
* \returns zero-based parameter index in the template parameter list at a
* given nesting depth.
*
* For example,
* \verbatim
* template<typename C, template<typename T> class TT>
* void test(TT<int> aaa);
* \endverbatim
* for C and TT nesting depth is 0, so we can ask for index at depth 0:
* at depth 0 C's index is 0, TT's index is 1.
*
* For T nesting depth is 1, so we can ask for index at depth 0 and 1:
* at depth 0 T's index is 1 (same as TT's),
* at depth 1 T's index is 0.
*/
CINDEX_LINKAGE
unsigned clang_TParamCommandComment_getIndex(CXComment Comment, unsigned Depth);
/**
* \param Comment a \c CXComment_VerbatimBlockLine AST node.
*
* \returns text contained in the AST node.
*/
CINDEX_LINKAGE
CXString clang_VerbatimBlockLineComment_getText(CXComment Comment);
/**
* \param Comment a \c CXComment_VerbatimLine AST node.
*
* \returns text contained in the AST node.
*/
CINDEX_LINKAGE CXString clang_VerbatimLineComment_getText(CXComment Comment);
/**
* \brief Convert an HTML tag AST node to string.
*
* \param Comment a \c CXComment_HTMLStartTag or \c CXComment_HTMLEndTag AST
* node.
*
* \returns string containing an HTML tag.
*/
CINDEX_LINKAGE CXString clang_HTMLTagComment_getAsString(CXComment Comment);
/**
* \brief Convert a given full parsed comment to an HTML fragment.
*
* Specific details of HTML layout are subject to change. Don't try to parse
* this HTML back into an AST, use other APIs instead.
*
* Currently the following CSS classes are used:
* \li "para-brief" for \\brief paragraph and equivalent commands;
* \li "para-returns" for \\returns paragraph and equivalent commands;
* \li "word-returns" for the "Returns" word in \\returns paragraph.
*
* Function argument documentation is rendered as a \<dl\> list with arguments
* sorted in function prototype order. CSS classes used:
* \li "param-name-index-NUMBER" for parameter name (\<dt\>);
* \li "param-descr-index-NUMBER" for parameter description (\<dd\>);
* \li "param-name-index-invalid" and "param-descr-index-invalid" are used if
* parameter index is invalid.
*
* Template parameter documentation is rendered as a \<dl\> list with
* parameters sorted in template parameter list order. CSS classes used:
* \li "tparam-name-index-NUMBER" for parameter name (\<dt\>);
* \li "tparam-descr-index-NUMBER" for parameter description (\<dd\>);
* \li "tparam-name-index-other" and "tparam-descr-index-other" are used for
* names inside template template parameters;
* \li "tparam-name-index-invalid" and "tparam-descr-index-invalid" are used if
* parameter position is invalid.
*
* \param Comment a \c CXComment_FullComment AST node.
*
* \returns string containing an HTML fragment.
*/
CINDEX_LINKAGE CXString clang_FullComment_getAsHTML(CXComment Comment);
/**
* \brief Convert a given full parsed comment to an XML document.
*
* A Relax NG schema for the XML can be found in comment-xml-schema.rng file
* inside clang source tree.
*
* \param Comment a \c CXComment_FullComment AST node.
*
* \returns string containing an XML document.
*/
CINDEX_LINKAGE CXString clang_FullComment_getAsXML(CXComment Comment);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* CLANG_C_DOCUMENTATION_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
CLANG_LEVEL := ../..
DIRS :=
include $(CLANG_LEVEL)/Makefile
IntIncludeDir = $(DESTDIR)$(PROJ_internal_prefix)/include
install-local::
$(Echo) Installing Clang C API include files
$(Verb) $(MKDIR) $(IntIncludeDir)
$(Verb) if test -d "$(PROJ_SRC_DIR)" ; then \
cd $(PROJ_SRC_DIR)/.. && \
for hdr in `find clang-c -type f '!' '(' -name '*~' \
-o -name '.#*' -o -name '*.in' -o -name '*.txt' \
-o -name 'Makefile' -o -name '*.td' ')' -print \
| grep -v CVS | grep -v .svn | grep -v .dir` ; do \
instdir=`dirname "$(IntIncludeDir)/$$hdr"` ; \
if test \! -d "$$instdir" ; then \
$(EchoCmd) Making install directory $$instdir ; \
$(MKDIR) $$instdir ;\
fi ; \
$(DataInstall) $$hdr $(IntIncludeDir)/$$hdr ; \
done ; \
fi
ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT))
$(Verb) if test -d "$(PROJ_OBJ_ROOT)/tools/clang/include/clang-c" ; then \
cd $(PROJ_OBJ_ROOT)/tools/clang/include && \
for hdr in `find clang-c -type f '!' '(' -name 'Makefile' ')' -print \
| grep -v CVS | grep -v .tmp | grep -v .dir` ; do \
instdir=`dirname "$(IntIncludeDir)/$$hdr"` ; \
if test \! -d "$$instdir" ; then \
$(EchoCmd) Making install directory $$instdir ; \
$(MKDIR) $$instdir ;\
fi ; \
$(DataInstall) $$hdr $(IntIncludeDir)/$$hdr ; \
done ; \
fi
endif

View File

@@ -0,0 +1,45 @@
/*===-- clang-c/Platform.h - C Index platform decls -------------*- C -*-===*\
|* *|
|* The LLVM Compiler Infrastructure *|
|* *|
|* This file is distributed under the University of Illinois Open Source *|
|* License. See LICENSE.TXT for details. *|
|* *|
|*===----------------------------------------------------------------------===*|
|* *|
|* This header provides platform specific macros (dllimport, deprecated, ...) *|
|* *|
\*===----------------------------------------------------------------------===*/
#ifndef LLVM_CLANG_C_PLATFORM_H
#define LLVM_CLANG_C_PLATFORM_H
#ifdef __cplusplus
extern "C" {
#endif
/* MSVC DLL import/export. */
#ifdef _MSC_VER
#ifdef _CINDEX_LIB_
#define CINDEX_LINKAGE __declspec(dllexport)
#else
#define CINDEX_LINKAGE __declspec(dllimport)
#endif
#else
#define CINDEX_LINKAGE
#endif
#ifdef __GNUC__
#define CINDEX_DEPRECATED __attribute__((deprecated))
#else
#ifdef _MSC_VER
#define CINDEX_DEPRECATED __declspec(deprecated)
#else
#define CINDEX_DEPRECATED
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,4 @@
module Clang_C {
umbrella "."
module * { export * }
}

View File

@@ -0,0 +1,176 @@
/* -*- mode: c++ ; coding: utf-8-unix -*- */
/* last updated : 2015/05/25.01:59:31 */
/*
* Copyright (c) 2013-2015 yaruopooner [https://github.com/yaruopooner]
*
* This file is part of ac-clang.
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*================================================================================================*/
/* Include Files */
/*================================================================================================*/
#include "CommandLine.hpp"
#include "ClangServer.hpp"
/*================================================================================================*/
/* Global Function Definitions Section */
/*================================================================================================*/
enum
{
kStreamBuffer_UnitSize = 1 * 1024 * 1024,
kStreamBuffer_MinMB = 1,
kStreamBuffer_MaxMB = 5,
};
enum
{
kOption_Help,
kOption_Version,
kOption_LogFile,
kOption_STDIN_BufferSize,
kOption_STDOUT_BufferSize,
};
namespace
{
std::string GetClangVersion( void )
{
CXString version_text = clang_getClangVersion();
const std::string clang_version = clang_getCString( version_text );
clang_disposeString( version_text );
return clang_version;
}
}
int main( int argc, char *argv[] )
{
std::ios_base::sync_with_stdio( false );
// parse options
const std::string server_version = CLANG_SERVER_VERSION;
const std::string clang_version = ::GetClangVersion();
const std::string generate = CMAKE_GENERATOR "/" CMAKE_HOST_SYSTEM_PROCESSOR;
std::string logfile;
size_t stdin_buffer_size = kStreamBuffer_MinMB;
size_t stdout_buffer_size = kStreamBuffer_MinMB;
{
CommandLine::Parser declare_options;
declare_options.AddOption( kOption_Help, "help", "h", "Display available options.",
CommandLine::IOptionDetail::kFlag_Once );
declare_options.AddOption( kOption_Version, "version", "v", "Display current version.",
CommandLine::IOptionDetail::kFlag_Once );
// declare_options.AddOption< std::string >( kOption_LogFile, "logfile", "l", "Enable IPC records output.(for debug)",
// ( CommandLine::IOptionDetail::kFlag_Once | CommandLine::IOptionDetail::kFlag_HasValue ), "file path" );
declare_options.AddOption< uint32_t >( kOption_STDIN_BufferSize, "stdin-buffer-size", "sibs", "STDIN buffer size. <size> is 1 - 5 MB",
( CommandLine::IOptionDetail::kFlag_Once | CommandLine::IOptionDetail::kFlag_HasValue ), "size",
CommandLine::RangeReader< uint32_t >( kStreamBuffer_MinMB, kStreamBuffer_MaxMB ) );
declare_options.AddOption< uint32_t >( kOption_STDOUT_BufferSize, "stdout-buffer-size", "sobs", "STDOUT buffer size. <size> is 1 - 5 MB",
( CommandLine::IOptionDetail::kFlag_Once | CommandLine::IOptionDetail::kFlag_HasValue ), "size",
CommandLine::RangeReader< uint32_t >( kStreamBuffer_MinMB, kStreamBuffer_MaxMB ) );
if ( declare_options.Parse( argc, argv ) )
{
for ( const auto& option_value : declare_options.GetOptionWithValueArray() )
{
switch ( option_value->GetId() )
{
case kOption_Help:
declare_options.PrintUsage( "clang-server [options] <values>" );
return 0;
case kOption_Version:
std::cout << server_version << " (" << generate << ")" << std::endl;
std::cout << clang_version << std::endl;
return 0;
case kOption_LogFile:
if ( option_value->IsValid() )
{
const std::string result = declare_options.GetValue< std::string >( option_value );
logfile = result;
}
break;
case kOption_STDIN_BufferSize:
if ( option_value->IsValid() )
{
const uint32_t result = declare_options.GetValue< uint32_t >( option_value );
stdin_buffer_size = result;
}
break;
case kOption_STDOUT_BufferSize:
if ( option_value->IsValid() )
{
const uint32_t result = declare_options.GetValue< uint32_t >( option_value );
stdout_buffer_size = result;
}
break;
}
}
declare_options.PrintWarnings();
}
else
{
declare_options.PrintErrors();
return 1;
}
}
// stream buffer expand
// std::shared_ptr< char > stdin_buffer( new char[ kStreamBuffer_UnitSize ], std::default_delete< char[] >() );
// std::shared_ptr< char > stdout_buffer( new char[ kStreamBuffer_UnitSize ], std::default_delete< char[] >() );
stdin_buffer_size *= kStreamBuffer_UnitSize;
stdout_buffer_size *= kStreamBuffer_UnitSize;
// server instance
ClangFlagConverters flag_converter;
ClangServer::Specification initial_spec( stdin_buffer_size, stdout_buffer_size, logfile );
ClangServer server( initial_spec );
server.ParseCommand();
return 0;
}
/*================================================================================================*/
/* EOF */
/*================================================================================================*/

View File

@@ -0,0 +1,22 @@
Index: lib/Support/MemoryBuffer.cpp
===================================================================
--- lib/Support/MemoryBuffer.cpp (revision 226551)
+++ lib/Support/MemoryBuffer.cpp (working copy)
@@ -267,6 +267,9 @@
bool RequiresNullTerminator,
int PageSize,
bool IsVolatileSize) {
+#if 1
+ return false;
+#else
// mmap may leave the buffer without null terminator if the file size changed
// by the time the last page is mapped in, so avoid it if the file size is
// likely to change.
@@ -306,6 +309,7 @@
return false;
return true;
+#endif
}
static ErrorOr<std::unique_ptr<MemoryBuffer>>

View File

@@ -0,0 +1,29 @@
Index: tools/libclang/CMakeLists.txt
===================================================================
--- tools/libclang/CMakeLists.txt (revision 226551)
+++ tools/libclang/CMakeLists.txt (working copy)
@@ -72,9 +72,9 @@
endif()
if(WIN32)
- set(output_name "libclang")
+ set(output_name "libclang-x86_64")
else()
- set(output_name "clang")
+ set(output_name "clang-x86_64")
endif()
add_clang_library(libclang ${ENABLE_SHARED} ${ENABLE_STATIC}
Index: tools/libclang/Makefile
===================================================================
--- tools/libclang/Makefile (revision 226551)
+++ tools/libclang/Makefile (working copy)
@@ -8,7 +8,7 @@
##===----------------------------------------------------------------------===##
CLANG_LEVEL := ../..
-LIBRARYNAME = clang
+LIBRARYNAME = clang-x86_64
EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/libclang.exports

View File

@@ -0,0 +1,381 @@
<div id="table-of-contents">
<h2>Table of Contents</h2>
<div id="text-table-of-contents">
<ul>
<li><a href="#sec-1">1. このドキュメントについて</a></li>
<li><a href="#sec-2">2. セルフビルド手順</a>
<ul>
<li><a href="#sec-2-1">2.1. LLVMセルフビルド</a></li>
<li><a href="#sec-2-2">2.2. clang-serverセルフビルド</a></li>
</ul>
</li>
<li><a href="#sec-3">3. セルフビルドに必要なソフトウェア</a>
<ul>
<li><a href="#sec-3-1">3.1. Windows</a>
<ul>
<li><a href="#sec-3-1-1">3.1.1. LLVM</a></li>
<li><a href="#sec-3-1-2">3.1.2. Visual Studio 2015/2013/2012/2010</a></li>
<li><a href="#sec-3-1-3">3.1.3. CMake</a></li>
</ul>
</li>
<li><a href="#sec-3-2">3.2. Linux</a>
<ul>
<li><a href="#sec-3-2-1">3.2.1. LLVM</a></li>
<li><a href="#sec-3-2-2">3.2.2. CMake</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#sec-4">4. セルフビルド</a>
<ul>
<li><a href="#sec-4-1">4.1. Windows</a>
<ul>
<li><a href="#sec-4-1-1">4.1.1. LLVM</a></li>
<li><a href="#sec-4-1-2">4.1.2. clang-server</a></li>
</ul>
</li>
<li><a href="#sec-4-2">4.2. Linux</a>
<ul>
<li><a href="#sec-4-2-1">4.2.1. LLVM</a></li>
<li><a href="#sec-4-2-2">4.2.2. clang-server</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#sec-5">5. パッチ適用済みバイナリ(Windows Only)</a></li>
<li><a href="#sec-6">6. パッチを適用せずLLVMオフィシャルのlibclangを使用する場合の制限事項</a>
<ul>
<li><a href="#sec-6-1">6.1. 特定ファイルがロックされセーブできなくなる</a>
<ul>
<li><a href="#sec-6-1-1">6.1.1. emacs側での対処方法</a></li>
<li><a href="#sec-6-1-2">6.1.2. 原因(実装上の問題説明、解決案求む)</a></li>
</ul>
</li>
<li><a href="#sec-6-2">6.2. その他</a></li>
</ul>
</li>
<li><a href="#sec-7">7. パッチ解説</a>
<ul>
<li><a href="#sec-7-1">7.1. パッチ</a></li>
<li><a href="#sec-7-2">7.2. パッチ(invalidate-mmap.patch)で行っている事</a></li>
<li><a href="#sec-7-3">7.3. LLVM3.5の追加仕様</a></li>
</ul>
</li>
</ul>
</div>
</div>
[English Manual](./readme.md)
# このドキュメントについて<a id="sec-1" name="sec-1"></a>
clang-serverのセルフビルドについて説明します。
※Windows環境で付属の実行ファイルを利用する場合は読まなくても問題ありません。
# セルフビルド手順<a id="sec-2" name="sec-2"></a>
clang-serverのビルドにはLLVMのlibclangが必要になります。
ですのでLLVM libclangをセルフビルドしてからclang-serverをセルフビルドします。
## LLVMセルフビルド<a id="sec-2-1" name="sec-2-1"></a>
以下の4つを行います。
この作業を簡略化するスクリプトもあります。
- LLVMのチェックアウト
- パッチの適用
- CMake or configureによるプロジェクトファイル生成
- ビルド
## clang-serverセルフビルド<a id="sec-2-2" name="sec-2-2"></a>
LLVMセルフビルドで生成したパッチ適用済みのライブラリ libclang を使用します。
- CMakeによるプロジェクトファイル生成
- ビルド
- インストール
# セルフビルドに必要なソフトウェア<a id="sec-3" name="sec-3"></a>
## Windows<a id="sec-3-1" name="sec-3-1"></a>
以下が必要になります。
### LLVM<a id="sec-3-1-1" name="sec-3-1-1"></a>
ビルド済みライブラリ
libclang.lib or libclang.imp
libclang.dll
が必要です。
### Visual Studio 2015/2013/2012/2010<a id="sec-3-1-2" name="sec-3-1-2"></a>
どれでもOK
### CMake<a id="sec-3-1-3" name="sec-3-1-3"></a>
<http://www.cmake.org/>
Windows ZIPをダウンロードして何処かへ展開。
Visual Studio ソリューションプロジェクトファイル生成と、ビルドインストールのmsbuild呼び出しで使用されます。
## Linux<a id="sec-3-2" name="sec-3-2"></a>
以下が必要になります。
### LLVM<a id="sec-3-2-1" name="sec-3-2-1"></a>
ビルド済みライブラリ
libclang.so
が必要です。
### CMake<a id="sec-3-2-2" name="sec-3-2-2"></a>
$ sudo apt-get install cmake
最新版の場合は↓からダウンロード
<http://www.cmake.org/>
cmake-3.1.3.tar.gzをダウンロードし解凍、ビルド、インストールを行う。
$ tar -xf cmake-3.1.3.tar.gz .
$ cd cmake-3.1.3
$ ./configure && make
$ make install
# セルフビルド<a id="sec-4" name="sec-4"></a>
## Windows<a id="sec-4-1" name="sec-4-1"></a>
### LLVM<a id="sec-4-1-1" name="sec-4-1-1"></a>
LLVMのセルフビルドが必要になります。
またセルフビルド時にパッチを適用する必要があります。
セルフビルド後のパッケージはインストールする必要はありません。
ビルド後に生成されたバイナリを指すパスを
CMakeによるプロジェクト生成時に設定すればビルド可能です。
LLVMがインストール済みであればインストールされているディレクトリを指定します。
LLVMセルフビルドを行う場合は
自前でチェックアウトし、CMakeでLLVMソリューションファイルを生成するか、以下のshell scriptを使用してください。
<https://github.com/yaruopooner/llvm-build-shells>
1. スクリプトでLLVMパッチを適用する方法
builderShell の引数に -tasks を指定し、-tasks パラメーターに PATCH を追加、
パッチを適用するパスとパッチファイルを記述したテーブルを -patchInfos パラメーターとして与えます。
詳しくはllvm-build-shellsのsample.ps1を参考にしてください。
2. LLVMパッチの内容
mmapの使用が常時無効化されます。
### clang-server<a id="sec-4-1-2" name="sec-4-1-2"></a>
ac-clang/build/builder\_sample.bat
を使用します。
必要に応じてbuilder\_sample.batを編集してください。
コマンドラインかエクスプローラーから実行します。
- example
cmake -G "Visual Studio 12 2013 Win64" ../clang-server -DLIBRARY_PATHS="c:/cygwin-x86_64/tmp/llvm-build-shells/ps1/clang-360/build/msvc-64/" -DCMAKE_INSTALL_PREFIX="c:/cygwin-x86_64/usr/local/bin/"
- オプション解説
- `-DLIBRARY_PATHS`
セルフビルドしたLLVMが配置されているディレクトリを指定します。
LLVMのトップディレクトリである必要があります。
省略した場合は ac-clang/clang-server が使われます。
- `-DCMAKE_INSTALL_PREFIX`
clang-serverのインストールパスを指定します。
省略した場合は
`C:/Program Files/clang-server`
になります。
## Linux<a id="sec-4-2" name="sec-4-2"></a>
### LLVM<a id="sec-4-2-1" name="sec-4-2-1"></a>
LLVMのセルフビルドが必要になります。
またセルフビルド時にパッチを適用する必要があります。
セルフビルド後のパッケージはインストールする必要はありません。
ビルド後に生成されたバイナリを指すパスを
CMakeによるプロジェクト生成時に設定すればビルド可能です。
LLVMがインストール済みであればインストールされているディレクトリを指定します。
LLVMセルフビルドを行う場合は
自前でチェックアウトし、CMakeでLLVMソリューションファイルを生成するか、以下のshell scriptを使用してください。
<https://github.com/yaruopooner/llvm-build-shells>
1. スクリプトでLLVMパッチを適用する方法
executeBuilder の引数に -patch を追加し、
パッチを適用するパスを-patchApplyLocation、
パッチファイルを-patchPathに記述して引数として与えます。
-patchApplyLocation,-patchPathはペアになっており、複数回指定可能です。
詳しくはllvm-build-shellsのsample.shを参考にしてください。
2. LLVMパッチの内容
mmapの使用が常時無効化されます。
### clang-server<a id="sec-4-2-2" name="sec-4-2-2"></a>
ac-clang/build/builder\_sample.sh
を使用します。
必要に応じてbuilder\_sample.shを編集してください。
builder\_sample.shを実行します。
- example
cmake -G "Unix Makefiles" ../clang-server -DLIBRARY_PATHS="/home/yaruopooner/work/llvm-build-shells/sh/clang-350/build" -DCMAKE_INSTALL_PREFIX="~/work/clang-server"
- オプション解説
- `-DLIBRARY_PATHS`
セルフビルドしたLLVMが配置されているディレクトリを指定します。
LLVMのトップディレクトリである必要があります。
省略した場合は ac-clang/clang-server が使われます。
- `-DCMAKE_INSTALL_PREFIX`
clang-serverのインストールパスを指定します。
省略した場合は
`/usr/local/bin`
になります。
# パッチ適用済みバイナリ(Windows Only)<a id="sec-5" name="sec-5"></a>
<https://github.com/yaruopooner/ac-clang/releases>
上記に置いてあるclang-server-X.X.X.zipは
パッチ適用済みのバイナリとライブラリファイル
- clang-server.exe
- libclang.dll
- libclang.lib or libclang.imp
の3ファイルが格納されています。
LLVMはセルフビルドせずにclang-serverのみをセルフビルドする場合は
clang-server-X.X.X.zipをac-clangに解凍します。
すると以下のように配置されます。
ac-clang/clang-server/binary/clang-server.exe
ac-clang/clang-server/library/x86\_64/release/libclang.dll
ac-clang/clang-server/library/x86\_64/release/libclang.lib
# パッチを適用せずLLVMオフィシャルのlibclangを使用する場合の制限事項<a id="sec-6" name="sec-6"></a>
## 特定ファイルがロックされセーブできなくなる<a id="sec-6-1" name="sec-6-1"></a>
編集したヘッダファイルをセーブしようとすると "basic-save-buffer-2: Opening output file: invalid argument \`HEADER-FILE-NAME\`" となりセーブできない。
必ず発生するわけではなく特定の条件を満たしたファイルサイズが16kBを越えるヘッダファイルで発生する。
16kB以下のヘッダファイルではまったく発生しない。
libclang の TranslationUnit(以下TU) の問題。
libclang の TU がinclude対象のファイルをロックしている。
ac-clang側で暫定対処パッチを施してあるので多少は緩和されているが完全に回避はできない。
発生した場合はマニュアル対処する以外ない。
### emacs側での対処方法<a id="sec-6-1-1" name="sec-6-1-1"></a>
include対象なので大抵は foo.cpp/foo.hpp という構成だとおもわれます。
foo.hpp(modified)がセーブできない場合、大抵foo.cppが(modified)になっているのでfoo.cppをセーブしましょう。
これによりfoo.hppはセーブ可能になるはずです。
これでもセーブできない場合は、foo.cpp以外のソースでfoo.hppをインクルードしており(modified)になっているバッファがあるはずなので
それもセーブしましょう。
また、定義へのジャンプ機能で該当ソースがアクティブ化されている場合は、未編集バッファであってもアクティブ化されています。
該当バッファを削除してみるか、そのバッファへスイッチして (ac-clang-deactivate) を実行してください。
これ以外でも16kBを越えるヘッダを編集しようとした際に、そのファイルのcppはオープンしてもいないのにセーブできない場合、
該当ヘッダファイルを何処か遠いモジュールでインクルードしている場合なども同様の症状になります。
ライブラリモジュールやフレームワークなどを開発している場合は発生しやすいかもしれません。
※ライブラリ・フレームワークはアプリ側からよくincludeされるため。
### 原因(実装上の問題説明、解決案求む)<a id="sec-6-1-2" name="sec-6-1-2"></a>
foo.cpp(modified)のとき foo.cppのセッションで
TUが foo.cpp パース後もincludeされているファイルのロックを保持しつづけている。
この状態で foo.hpp を編集してセーブしようとするとロックでエラーになる。
ロックを解除するには、 foo.cpp のTUをリリースする。
なので foo.cpp セーブ時にセッションは保持した状態で TU だけをリリースして、
foo.cpp が再び modified になったときに TU を生成するように修正。
これにより foo.cpp セーブ後であればincludeロックでが全解除されるので foo.hpp がセーブ可能になる。
当然 foo.cpp 以外に foo.hpp をinclude しているソースでかつ、編集中のバッファがある場合は、
それら全てを保存しないとロックでは解除されない。
Windows環境において、
このロックはI/Oのopen関数によるロックはではなくWindowsAPIのCreateFileMappingによるロックである。
libclang FileManagerは16kB以上のファイルをメモリマップドファイルとしてアロケーションする。
TUがリリースされるとUnmapViewOfFileによりメモリマップドファイルがリリースされるようになりファイルに対して書き込み可能になる。
Linux環境においても発現する不具合はWindows環境と若干異なるものの mmap/munmapによる問題は発生する。
foo.cppのTUを保持している状態でfoo.hppにおいてclass fooのメソッドを追加・削除し保存する。
foo.hpp更新後にfoo.cppにおいてclass fooのメソッドを補間しようとするとTUがクラッシュする。
libclangがSTDOUTに "libclang: crash detected in code completion" を出力する。
clang-serverのプロセスは生きており、セッションを破棄して再生成すれば補間続行は可能。
## その他<a id="sec-6-2" name="sec-6-2"></a>
上記の問題はlibclangにパッチを適用して改善している。
パッチを適用したリリースバイナリのlibclang-x86\_XX.(dll or so)を使用している場合は発生しない。
パッチを適用していないLLVMセルフビルドおよび、LLVMオフィシャルバイナリを使用する場合にのみ問題が発生します。
clang側の仕様バグなので現在LLVM bugzilla に報告済み。対応待ち中。
<http://llvm.org/bugs/show_bug.cgi?id=20880>
# パッチ解説<a id="sec-7" name="sec-7"></a>
## パッチ<a id="sec-7-1" name="sec-7-1"></a>
ac-clang/clang-server/patch/invalidate-mmap.patch
を使用。
cd llvm/
svn patch ac-clang/clang-server/patch/invalidate-mmap.patch
## パッチ(invalidate-mmap.patch)で行っている事<a id="sec-7-2" name="sec-7-2"></a>
mmapを使わないようにパッチを適用している
適用するのは以下のソース
clang-trunk/llvm/lib/Support/MemoryBuffer.cpp
static error_code getOpenFileImpl(int FD, const char *Filename,
OwningPtr<MemoryBuffer> &result,
uint64_t FileSize, uint64_t MapSize,
int64_t Offset, bool RequiresNullTerminator) {
↑の関数内で呼ばれる shouldUseMmap によりファイルに対するmmapの使用可否が判断される
static bool shouldUseMmap(int FD,
size_t FileSize,
size_t MapSize,
off_t Offset,
bool RequiresNullTerminator,
int PageSize) {
この関数のresultが常時falseであればmmapは恒久的に使用されない。
よってこの関数の先頭で
return false;
とすればよい。
以降のコードは#if 0 end するなりすればよい。
## LLVM3.5の追加仕様<a id="sec-7-3" name="sec-7-3"></a>
shouldUseMmap,getOpenFileImplに引数IsVolatileSizeが追加された。
これはshouldUseMmapまで加工なしでパスされ、
shouldUseMmap先頭において、
if (IsVolatileSize)
return false;
される。
コメントがついていた
// mmap may leave the buffer without null terminator if the file size changed
// by the time the last page is mapped in, so avoid it if the file size is
// likely to change.
mmapはファイルサイズが最後のページがマップされたされた時点で変更された場合はnull終端せずにバッファを残すので、ファイルサイズが変更される可能性がある場合は、それを回避することができる。
とは言っているものの、想定されていない事態がいろいろあるようで仕様抜けの模様。
またバッファ確保系関数の上流で IsVolatileSize が指定されていなかったりコンストラクタのデフォルト値のまま運用されている箇所が何箇所か見受けられた。
そういった箇所を自前で修正してみたところ従来よりマシになったものの、他にも問題があるようで想定通りにmmapを制御は出来なかった。
LLVMのファイルシステム・メモリ周りの仕様を完全に把握していないと、ここら辺の修正は厳しいのかもしれない。
よって現時点においては上記パッチ適用が一番無難なやり方となる。

View File

@@ -0,0 +1,297 @@
# -*- mode: org ; coding: utf-8-unix -*-
# last updated : 2015/07/21.00:46:00
#+TITLE: Clang Server Manual
#+AUTHOR: yaruopooner
#+EMAIL: [https://github.com/yaruopooner]
#+OPTIONS: author:nil timestamp:t |:t \n:t ^:nil
[[./readme.org][English Manual]]
* このドキュメントについて
clang-serverのセルフビルドについて説明します。
※Windows環境で付属の実行ファイルを利用する場合は読まなくても問題ありません。
* セルフビルド手順
clang-serverのビルドにはLLVMのlibclangが必要になります。
ですのでLLVM libclangをセルフビルドしてからclang-serverをセルフビルドします。
** LLVMセルフビルド
以下の4つを行います。
この作業を簡略化するスクリプトもあります。
- LLVMのチェックアウト
- パッチの適用
- CMake or configureによるプロジェクトファイル生成
- ビルド
** clang-serverセルフビルド
LLVMセルフビルドで生成したパッチ適用済みのライブラリ libclang を使用します。
- CMakeによるプロジェクトファイル生成
- ビルド
- インストール
* セルフビルドに必要なソフトウェア
** Windows
以下が必要になります。
*** LLVM
ビルド済みライブラリ
libclang.lib or libclang.imp
libclang.dll
が必要です。
*** Visual Studio 2015/2013/2012/2010
どれでもOK
*** CMake
http://www.cmake.org/
Windows ZIPをダウンロードして何処かへ展開。
Visual Studio ソリューションプロジェクトファイル生成と、ビルドインストールのmsbuild呼び出しで使用されます。
** Linux
以下が必要になります。
*** LLVM
ビルド済みライブラリ
libclang.so
が必要です。
*** CMake
#+begin_src shell
$ sudo apt-get install cmake
#+end_src
最新版の場合は↓からダウンロード
http://www.cmake.org/
cmake-3.1.3.tar.gzをダウンロードし解凍、ビルド、インストールを行う。
#+begin_src shell
$ tar -xf cmake-3.1.3.tar.gz .
$ cd cmake-3.1.3
$ ./configure && make
$ make install
#+end_src
* セルフビルド
** Windows
*** LLVM
LLVMのセルフビルドが必要になります。
またセルフビルド時にパッチを適用する必要があります。
セルフビルド後のパッケージはインストールする必要はありません。
ビルド後に生成されたバイナリを指すパスを
CMakeによるプロジェクト生成時に設定すればビルド可能です。
LLVMがインストール済みであればインストールされているディレクトリを指定します。
LLVMセルフビルドを行う場合は
自前でチェックアウトし、CMakeでLLVMソリューションファイルを生成するか、以下のshell scriptを使用してください。
https://github.com/yaruopooner/llvm-build-shells
**** スクリプトでLLVMパッチを適用する方法
builderShell の引数に -tasks を指定し、-tasks パラメーターに PATCH を追加、
パッチを適用するパスとパッチファイルを記述したテーブルを -patchInfos パラメーターとして与えます。
詳しくはllvm-build-shellsのsample.ps1を参考にしてください。
**** LLVMパッチの内容
mmapの使用が常時無効化されます。
*** clang-server
ac-clang/build/builder_sample.bat
を使用します。
必要に応じてbuilder_sample.batを編集してください。
コマンドラインかエクスプローラーから実行します。
- example
#+begin_src bat
cmake -G "Visual Studio 12 2013 Win64" ../clang-server -DLIBRARY_PATHS="c:/cygwin-x86_64/tmp/llvm-build-shells/ps1/clang-360/build/msvc-64/" -DCMAKE_INSTALL_PREFIX="c:/cygwin-x86_64/usr/local/bin/"
#+end_src
- オプション解説
- =-DLIBRARY_PATHS=
セルフビルドしたLLVMが配置されているディレクトリを指定します。
LLVMのトップディレクトリである必要があります。
省略した場合は ac-clang/clang-server が使われます。
- =-DCMAKE_INSTALL_PREFIX=
clang-serverのインストールパスを指定します。
省略した場合は
=C:/Program Files/clang-server=
になります。
** Linux
*** LLVM
LLVMのセルフビルドが必要になります。
またセルフビルド時にパッチを適用する必要があります。
セルフビルド後のパッケージはインストールする必要はありません。
ビルド後に生成されたバイナリを指すパスを
CMakeによるプロジェクト生成時に設定すればビルド可能です。
LLVMがインストール済みであればインストールされているディレクトリを指定します。
LLVMセルフビルドを行う場合は
自前でチェックアウトし、CMakeでLLVMソリューションファイルを生成するか、以下のshell scriptを使用してください。
https://github.com/yaruopooner/llvm-build-shells
**** スクリプトでLLVMパッチを適用する方法
executeBuilder の引数に -patch を追加し、
パッチを適用するパスを-patchApplyLocation、
パッチファイルを-patchPathに記述して引数として与えます。
-patchApplyLocation,-patchPathはペアになっており、複数回指定可能です。
詳しくはllvm-build-shellsのsample.shを参考にしてください。
**** LLVMパッチの内容
mmapの使用が常時無効化されます。
*** clang-server
ac-clang/build/builder_sample.sh
を使用します。
必要に応じてbuilder_sample.shを編集してください。
builder_sample.shを実行します。
- example
#+begin_src shell-script
cmake -G "Unix Makefiles" ../clang-server -DLIBRARY_PATHS="/home/yaruopooner/work/llvm-build-shells/sh/clang-350/build" -DCMAKE_INSTALL_PREFIX="~/work/clang-server"
#+end_src
- オプション解説
- =-DLIBRARY_PATHS=
セルフビルドしたLLVMが配置されているディレクトリを指定します。
LLVMのトップディレクトリである必要があります。
省略した場合は ac-clang/clang-server が使われます。
- =-DCMAKE_INSTALL_PREFIX=
clang-serverのインストールパスを指定します。
省略した場合は
=/usr/local/bin=
になります。
* パッチ適用済みバイナリ(Windows Only)
https://github.com/yaruopooner/ac-clang/releases
上記に置いてあるclang-server-X.X.X.zipは
パッチ適用済みのバイナリとライブラリファイル
- clang-server.exe
- libclang.dll
- libclang.lib or libclang.imp
の3ファイルが格納されています。
LLVMはセルフビルドせずにclang-serverのみをセルフビルドする場合は
clang-server-X.X.X.zipをac-clangに解凍します。
すると以下のように配置されます。
ac-clang/clang-server/binary/clang-server.exe
ac-clang/clang-server/library/x86_64/release/libclang.dll
ac-clang/clang-server/library/x86_64/release/libclang.lib
* パッチを適用せずLLVMオフィシャルのlibclangを使用する場合の制限事項
** 特定ファイルがロックされセーブできなくなる
編集したヘッダファイルをセーブしようとすると "basic-save-buffer-2: Opening output file: invalid argument `HEADER-FILE-NAME`" となりセーブできない。
必ず発生するわけではなく特定の条件を満たしたファイルサイズが16kBを越えるヘッダファイルで発生する。
16kB以下のヘッダファイルではまったく発生しない。
libclang の TranslationUnit(以下TU) の問題。
libclang の TU がinclude対象のファイルをロックしている。
ac-clang側で暫定対処パッチを施してあるので多少は緩和されているが完全に回避はできない。
発生した場合はマニュアル対処する以外ない。
*** emacs側での対処方法
include対象なので大抵は foo.cpp/foo.hpp という構成だとおもわれます。
foo.hpp(modified)がセーブできない場合、大抵foo.cppが(modified)になっているのでfoo.cppをセーブしましょう。
これによりfoo.hppはセーブ可能になるはずです。
これでもセーブできない場合は、foo.cpp以外のソースでfoo.hppをインクルードしており(modified)になっているバッファがあるはずなので
それもセーブしましょう。
また、定義へのジャンプ機能で該当ソースがアクティブ化されている場合は、未編集バッファであってもアクティブ化されています。
該当バッファを削除してみるか、そのバッファへスイッチして (ac-clang-deactivate) を実行してください。
これ以外でも16kBを越えるヘッダを編集しようとした際に、そのファイルのcppはオープンしてもいないのにセーブできない場合、
該当ヘッダファイルを何処か遠いモジュールでインクルードしている場合なども同様の症状になります。
ライブラリモジュールやフレームワークなどを開発している場合は発生しやすいかもしれません。
※ライブラリ・フレームワークはアプリ側からよくincludeされるため。
*** 原因(実装上の問題説明、解決案求む)
foo.cpp(modified)のとき foo.cppのセッションで
TUが foo.cpp パース後もincludeされているファイルのロックを保持しつづけている。
この状態で foo.hpp を編集してセーブしようとするとロックでエラーになる。
ロックを解除するには、 foo.cpp のTUをリリースする。
なので foo.cpp セーブ時にセッションは保持した状態で TU だけをリリースして、
foo.cpp が再び modified になったときに TU を生成するように修正。
これにより foo.cpp セーブ後であればincludeロックでが全解除されるので foo.hpp がセーブ可能になる。
当然 foo.cpp 以外に foo.hpp をinclude しているソースでかつ、編集中のバッファがある場合は、
それら全てを保存しないとロックでは解除されない。
Windows環境において、
このロックはI/Oのopen関数によるロックはではなくWindowsAPIのCreateFileMappingによるロックである。
libclang FileManagerは16kB以上のファイルをメモリマップドファイルとしてアロケーションする。
TUがリリースされるとUnmapViewOfFileによりメモリマップドファイルがリリースされるようになりファイルに対して書き込み可能になる。
Linux環境においても発現する不具合はWindows環境と若干異なるものの mmap/munmapによる問題は発生する。
foo.cppのTUを保持している状態でfoo.hppにおいてclass fooのメソッドを追加・削除し保存する。
foo.hpp更新後にfoo.cppにおいてclass fooのメソッドを補間しようとするとTUがクラッシュする。
libclangがSTDOUTに "libclang: crash detected in code completion" を出力する。
clang-serverのプロセスは生きており、セッションを破棄して再生成すれば補間続行は可能。
** その他
上記の問題はlibclangにパッチを適用して改善している。
パッチを適用したリリースバイナリのlibclang-x86_XX.(dll or so)を使用している場合は発生しない。
パッチを適用していないLLVMセルフビルドおよび、LLVMオフィシャルバイナリを使用する場合にのみ問題が発生します。
clang側の仕様バグなので現在LLVM bugzilla に報告済み。対応待ち中。
http://llvm.org/bugs/show_bug.cgi?id=20880
* パッチ解説
** パッチ
ac-clang/clang-server/patch/invalidate-mmap.patch
を使用。
#+begin_src shell-script
cd llvm/
svn patch ac-clang/clang-server/patch/invalidate-mmap.patch
#+end_src
** パッチ(invalidate-mmap.patch)で行っている事
mmapを使わないようにパッチを適用している
適用するのは以下のソース
clang-trunk/llvm/lib/Support/MemoryBuffer.cpp
#+begin_src C++
static error_code getOpenFileImpl(int FD, const char *Filename,
OwningPtr<MemoryBuffer> &result,
uint64_t FileSize, uint64_t MapSize,
int64_t Offset, bool RequiresNullTerminator) {
#+end_src
↑の関数内で呼ばれる shouldUseMmap によりファイルに対するmmapの使用可否が判断される
#+begin_src C++
static bool shouldUseMmap(int FD,
size_t FileSize,
size_t MapSize,
off_t Offset,
bool RequiresNullTerminator,
int PageSize) {
#+end_src
この関数のresultが常時falseであればmmapは恒久的に使用されない。
よってこの関数の先頭で
#+begin_src C++
return false;
#+end_src
とすればよい。
以降のコードは#if 0 end するなりすればよい。
** LLVM3.5の追加仕様
shouldUseMmap,getOpenFileImplに引数IsVolatileSizeが追加された。
これはshouldUseMmapまで加工なしでパスされ、
shouldUseMmap先頭において、
#+begin_src C++
if (IsVolatileSize)
return false;
#+end_src
される。
コメントがついていた
#+begin_src C++
// mmap may leave the buffer without null terminator if the file size changed
// by the time the last page is mapped in, so avoid it if the file size is
// likely to change.
#+end_src
mmapはファイルサイズが最後のページがマップされたされた時点で変更された場合はnull終端せずにバッファを残すので、ファイルサイズが変更される可能性がある場合は、それを回避することができる。
とは言っているものの、想定されていない事態がいろいろあるようで仕様抜けの模様。
またバッファ確保系関数の上流で IsVolatileSize が指定されていなかったりコンストラクタのデフォルト値のまま運用されている箇所が何箇所か見受けられた。
そういった箇所を自前で修正してみたところ従来よりマシになったものの、他にも問題があるようで想定通りにmmapを制御は出来なかった。
LLVMのファイルシステム・メモリ周りの仕様を完全に把握していないと、ここら辺の修正は厳しいのかもしれない。
よって現時点においては上記パッチ適用が一番無難なやり方となる。

View File

@@ -0,0 +1,392 @@
<div id="table-of-contents">
<h2>Table of Contents</h2>
<div id="text-table-of-contents">
<ul>
<li><a href="#sec-1">1. About this document</a></li>
<li><a href="#sec-2">2. Step of self-build</a>
<ul>
<li><a href="#sec-2-1">2.1. LLVM self-build</a></li>
<li><a href="#sec-2-2">2.2. clang-server self-build</a></li>
</ul>
</li>
<li><a href="#sec-3">3. Software required for self-build</a>
<ul>
<li><a href="#sec-3-1">3.1. Windows</a>
<ul>
<li><a href="#sec-3-1-1">3.1.1. LLVM</a></li>
<li><a href="#sec-3-1-2">3.1.2. Visual Studio 2015/2013/2012/2010</a></li>
<li><a href="#sec-3-1-3">3.1.3. CMake</a></li>
</ul>
</li>
<li><a href="#sec-3-2">3.2. Linux</a>
<ul>
<li><a href="#sec-3-2-1">3.2.1. LLVM</a></li>
<li><a href="#sec-3-2-2">3.2.2. CMake</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#sec-4">4. Self-Build</a>
<ul>
<li><a href="#sec-4-1">4.1. Windows</a>
<ul>
<li><a href="#sec-4-1-1">4.1.1. LLVM</a></li>
<li><a href="#sec-4-1-2">4.1.2. clang-server</a></li>
</ul>
</li>
<li><a href="#sec-4-2">4.2. Linux</a>
<ul>
<li><a href="#sec-4-2-1">4.2.1. LLVM</a></li>
<li><a href="#sec-4-2-2">4.2.2. clang-server</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#sec-5">5. Patch was applied binary(Windows Only)</a></li>
<li><a href="#sec-6">6. Restrictions when you use LLVM official libclang without applying a patch</a>
<ul>
<li><a href="#sec-6-1">6.1. A specific file is locked and cannot save it</a>
<ul>
<li><a href="#sec-6-1-1">6.1.1. Solution in Emacs side</a></li>
<li><a href="#sec-6-1-2">6.1.2. Issue(Implementation issues explanation, it wanted suggested solutions)</a></li>
</ul>
</li>
<li><a href="#sec-6-2">6.2. Miscellaneous</a></li>
</ul>
</li>
<li><a href="#sec-7">7. Patch commentary</a>
<ul>
<li><a href="#sec-7-1">7.1. Patch</a></li>
<li><a href="#sec-7-2">7.2. The contents of the LLVM patch(invalidate-mmap.patch)</a></li>
<li><a href="#sec-7-3">7.3. <span class="todo TODO">TODO</span> Additional Specification of LLVM3.5</a></li>
</ul>
</li>
</ul>
</div>
</div>
[Japanese Manual](./readme.ja.md)
# About this document<a id="sec-1" name="sec-1"></a>
This is explained about self-build of clang-server.
When you use clang-server binary for distribution on Windows environment, it is not necessary to read this document.
# Step of self-build<a id="sec-2" name="sec-2"></a>
A build of clang-server requires libclang of LLVM.
Therefore, first self-build is LLVM libclang, next self-build is clang-server.
## LLVM self-build<a id="sec-2-1" name="sec-2-1"></a>
You have to do four step of following.
There is also a script which simplify this work.
- Checkout LLVM
- Apply patch to LLVM for libclang
- Project file generation by CMAKE or configure
- Build
## clang-server self-build<a id="sec-2-2" name="sec-2-2"></a>
Patch applied libclang library use by LLVM self-build
Use the patch applied libclang library
- Project file generation by CMAKE
- Build
- Installation
# Software required for self-build<a id="sec-3" name="sec-3"></a>
## Windows<a id="sec-3-1" name="sec-3-1"></a>
The following is required.
### LLVM<a id="sec-3-1-1" name="sec-3-1-1"></a>
The following built library is required.
libclang.lib or libclang.imp
libclang.dll
### Visual Studio 2015/2013/2012/2010<a id="sec-3-1-2" name="sec-3-1-2"></a>
any OK
### CMake<a id="sec-3-1-3" name="sec-3-1-3"></a>
<http://www.cmake.org/>
Download cmake archive, and decompress to any location.
CMake is used for Visual Studio Solution File generation and build and installation.
## Linux<a id="sec-3-2" name="sec-3-2"></a>
The following is required.
### LLVM<a id="sec-3-2-1" name="sec-3-2-1"></a>
The following built library is required.
libclang.so
### CMake<a id="sec-3-2-2" name="sec-3-2-2"></a>
$ sudo apt-get install cmake
If you want to use latest version, download from following
<http://www.cmake.org/>
e.g.
Download cmake-3.1.3.tar.gz, and decompress to work directory.
You perform a build and installation.
$ tar -xf cmake-3.1.3.tar.gz .
$ cd cmake-3.1.3
$ ./configure && make
$ make install
# Self-Build<a id="sec-4" name="sec-4"></a>
## Windows<a id="sec-4-1" name="sec-4-1"></a>
### LLVM<a id="sec-4-1-1" name="sec-4-1-1"></a>
Required process for LLVM self-build.
And you must apply patch to LLVM before build.
It isn't necessary install a package after self-build.
You must designate generated binary PATH after LLVM self-build at CMake project generation argument.
Therefore clang-server is able to build.
When LLVM is already installed, you must designate installed directory of LLVM.
When you want to self-build,
Checkout from SVN by yourself, and perform the LLVM Solution File generation and build by cmake.
Or, use following script.
<https://github.com/yaruopooner/llvm-build-shells>
1. How to designate the LLVM patch in the script
You will designate -tasks to the argument of builderShell,
and designate PATCH to the parameter of -tasks,
It will gives a table that describes the path to apply the patch and patch file to parameter of -patchInfos.
Please refer to the sample.ps1 of llvm-build-shells for details.
2. The contents of the LLVM patch
Use of mmap always invalidation.
### clang-server<a id="sec-4-1-2" name="sec-4-1-2"></a>
Use the ac-clang/build/builder\_sample.bat
Please edit the builder\_sample.bat as necessary.
It's necessary to execute in the command line or Windows Explorer.
- example
cmake -G "Visual Studio 12 2013 Win64" ../clang-server -DLIBRARY_PATHS="c:/cygwin-x86_64/tmp/llvm-build-shells/ps1/clang-360/build/msvc-64/" -DCMAKE_INSTALL_PREFIX="c:/cygwin-x86_64/usr/local/bin/"
- Option commentary
- `-DLIBRARY_PATHS`
You have to designate location of LLVM self-build completed directory.
It is necessary to designate the directory that a binary was generated.(e.g. {LLVM output path}/Release/)
If you omit this option, value will be use `ac-clang/clang-server` .
- `-DCMAKE_INSTALL_PREFIX`
You have to designate installation location of clang-server.
If you omit this option, value will be use `C:/Program Files/clang-server` .
## Linux<a id="sec-4-2" name="sec-4-2"></a>
### LLVM<a id="sec-4-2-1" name="sec-4-2-1"></a>
Required process for LLVM self-build.
And you must apply patch to LLVM before build.
It isn't necessary install a package after self-build.
You must designate generated binary PATH after LLVM self-build at CMake project generation argument.
Therefore clang-server is able to build.
When LLVM is already installed, you must designate installed directory of LLVM.
When you want to self-build,
Checkout from SVN by yourself, and perform the LLVM Solution File generation and build by cmake.
Or, use following script.
<https://github.com/yaruopooner/llvm-build-shells>
1. How to designate the LLVM patch in the script
You will designate -patch to the argument of executeBuilder.
Add to -patchApplyLocation the path where you want to apply the patch.
You write the patch file to -patchPath gives as an parameter.
-patchApplyLocation,-patchPath becomes the pair, it is possible to multiple times designate.
Please refer to the sample.sh of llvm-build-shells for details.
2. The contents of the LLVM patch
Use of mmap always invalidation.
### clang-server<a id="sec-4-2-2" name="sec-4-2-2"></a>
Use the ac-clang/build/builder\_sample.sh
Please edit the builder\_sample.sh as necessary.
Execute the builder\_sample.sh
- example
cmake -G "Unix Makefiles" ../clang-server -DLIBRARY_PATHS="/home/yaruopooner/work/llvm-build-shells/sh/clang-350/build" -DCMAKE_INSTALL_PREFIX="~/work/clang-server"
- Option commentary
- `-DLIBRARY_PATHS`
You have to designate location of LLVM self-build completed directory.
It is necessary to designate the directory that a binary was generated.(e.g. {LLVM output path}/Release/)
If you omit this option, value will be use `ac-clang/clang-server` .
- `-DCMAKE_INSTALL_PREFIX`
You have to designate installation location of clang-server.
If you omit this option, value will be use `/usr/local/bin` .
# Patch was applied binary(Windows Only)<a id="sec-5" name="sec-5"></a>
<https://github.com/yaruopooner/ac-clang/releases>
clang-server-X.X.X.zip is you can download from the above
The archive is 3 files contain, these file applied patch.
- clang-server.exe
- libclang.dll
- libclang.lib or libclang.imp
When you want to self-build only clang-server without LLVM,
clang-server-X.X.X.zip decompress to ac-clang directory.
Then, it will be placed in the following.
ac-clang/clang-server/binary/clang-server.exe
ac-clang/clang-server/library/x86\_64/release/libclang.dll
ac-clang/clang-server/library/x86\_64/release/libclang.lib
# Restrictions when you use LLVM official libclang without applying a patch<a id="sec-6" name="sec-6"></a>
## A specific file is locked and cannot save it<a id="sec-6-1" name="sec-6-1"></a>
When you try to save the edited header file,
it will be "basic-save-buffer-2: Opening output file: invalid argument \`HEADER-FILE-NAME\`",
and you can't save.
This occur if it meets certain conditions.
This condition is met when the header file size is larger than 16kB.
It is not at all occur when header file size is smaller than 16kB.
This issue belong to TranslationUnit(TU) of libclang.
The inclusion target file is locked by TU of libclang.
By performing a provisional transaction in ac-clang side, the more or less is erased, but it can't be avoided perfectly.
When this issue is occurring, only manual handle can be avoided.
### Solution in Emacs side<a id="sec-6-1-1" name="sec-6-1-1"></a>
I suppose that combination of source file is foo.cpp/foo.hpp.
When foo.hpp(modified) can't save, foo.cpp is (modified) often, so foo.cpp have to saved.
Therefore, foo.hpp should be possible to save.
When this can't save,
foo.hpp is included by source files besides foo.cpp, and it has (modified) status.
You have to save those.
And, when corresponding source is activated by definition jump feature, even if buffer don't modified that buffer is activated.
You try remove corresponding buffer, or (ac-clang-deactivate) must be execute in buffer.
In other cases, when you try save header file that file size larger than 16kB
When you save a header file of larger than 16kB, if it fails.
And that header file does not opened.
In this case, header file is included by a far module from current source file.
When you having developed a library module framework, it may be easy to occur.
because library and framework is included from application side.
### Issue(Implementation issues explanation, it wanted suggested solutions)<a id="sec-6-1-2" name="sec-6-1-2"></a>
When session of "foo.cpp" is edited in the buffer,
TU continue locking to included header file after parsed "foo.cpp".
When you edit and save to "foo.hpp" in this state, it occur error, because file is locked by mmap.
Therefore I modified a server as follows.
So while maintaining the session when "foo.cpp" saving,
TU is generated when "foo.cpp" is edited after TU released.
Therefore "foo.hpp" is possible to save that the included header file is unlocked after "foo.cpp" saved.
When a "foo.hpp" is included buffer where exist in buffer editing group without buffer of "foo.cpp",
the lock is not released when you does not save all them.
In the Windows environment,
This lock is not open function of I/O, is a lock by CreateFileMapping of WindowsAPI.
libclang FileManager does allocation to memory mapped file for the files larger than 16kB.
When TU is released, memory mapped file is released by UnmapViewOfFile, these becomes writable to file.
In the Linux environment,
problems with mmap/munmap bug differ slightly from the Windows environment, but also occurred in Linux environment.
The method add to "class Foo" in "foo.hpp" in the state that holds TU of "foo.cpp", and save to file.
After "foo.hpp" update, when you try complete method of "class Foo" in the "foo.cpp", TU will crash.
in this case, libclang output to STDOUT that "libclang: crash detected in code completion"
libclang output "libclang: crash detected in code completion" to STDOUT.
The process of clang-server is living in this situation.
Completion is possible after deletion of session and creation of session.
## Miscellaneous<a id="sec-6-2" name="sec-6-2"></a>
The above problems are solved by patching for libclang.
When you use the patch applied release binary(libclang.dll or so) it is not occur.
When you use the patch does not applied to LLVM self-build and LLVM official binary, this problem is occur.
I think specification bug of clang side. This problem has been reported to LLVM bugzilla. in the corresponding waitting.
<http://llvm.org/bugs/show_bug.cgi?id=20880>
# Patch commentary<a id="sec-7" name="sec-7"></a>
## Patch<a id="sec-7-1" name="sec-7-1"></a>
Use the ac-clang/clang-server/patch/invalidate-mmap.patch
cd llvm/
svn patch ac-clang/clang-server/patch/invalidate-mmap.patch
## The contents of the LLVM patch(invalidate-mmap.patch)<a id="sec-7-2" name="sec-7-2"></a>
Patch is applied so as not to use mmap.
Apply to the following source code to
`clang-trunk/llvm/lib/Support/MemoryBuffer.cpp`
static error_code getOpenFileImpl(int FD, const char *Filename,
OwningPtr<MemoryBuffer> &result,
uint64_t FileSize, uint64_t MapSize,
int64_t Offset, bool RequiresNullTerminator) {
It is determined availability of mmap for file by shouldUseMmap call from the above function.
static bool shouldUseMmap(int FD,
size_t FileSize,
size_t MapSize,
off_t Offset,
bool RequiresNullTerminator,
int PageSize) {
When the result of function is always false, mmap is not never used.
Therefore, the following modify has been applied to the top of this function.
#if 1
return false;
#else
/* original codes */
#endif
## TODO Additional Specification of LLVM3.5<a id="sec-7-3" name="sec-7-3"></a>
IsVolatileSize has been added to arguments of shouldUseMmap and getOpenFileImpl.
This will be passed unchanged to shouldUseMmap.
It is executed as follows in the shouldUseMmap top.
if (IsVolatileSize)
return false;
Following comments had been attached
// mmap may leave the buffer without null terminator if the file size changed
// by the time the last page is mapped in, so avoid it if the file size is
// likely to change.
Although that said, there is a situation which isn't assumed Variously, I'm supposing that mistake of specification.
Moreover, upstream function of buffer association function
I was found some place where is not designated value of IsVolatileSize and is used default value of constructor argument.
I tried modified it.
Result was become more better than conventional.
But it seem to have a problem, because I was not able to control mmap like a assumption.
I'm not enough understand the specification of around the file system and memory of LLVM.
For that reason that the rightly correction is difficult.
Therefore, the above patching becomes the most safe way at present.

View File

@@ -0,0 +1,314 @@
# -*- mode: org ; coding: utf-8-unix -*-
# last updated : 2015/07/21.00:46:31
#+TITLE: Clang Server Manual
#+AUTHOR: yaruopooner
#+EMAIL: [https://github.com/yaruopooner]
#+OPTIONS: author:nil timestamp:t |:t \n:t ^:nil
[[./readme.ja.org][Japanese Manual]]
* About this document
This is explained about self-build of clang-server.
When you use clang-server binary for distribution on Windows environment, it is not necessary to read this document.
* Step of self-build
A build of clang-server requires libclang of LLVM.
Therefore, first self-build is LLVM libclang, next self-build is clang-server.
** LLVM self-build
You have to do four step of following.
There is also a script which simplify this work.
- Checkout LLVM
- Apply patch to LLVM for libclang
- Project file generation by CMAKE or configure
- Build
** clang-server self-build
Patch applied libclang library use by LLVM self-build
Use the patch applied libclang library
- Project file generation by CMAKE
- Build
- Installation
* Software required for self-build
** Windows
The following is required.
*** LLVM
The following built library is required.
libclang.lib or libclang.imp
libclang.dll
*** Visual Studio 2015/2013/2012/2010
any OK
*** CMake
http://www.cmake.org/
Download cmake archive, and decompress to any location.
CMake is used for Visual Studio Solution File generation and build and installation.
** Linux
The following is required.
*** LLVM
The following built library is required.
libclang.so
*** CMake
#+begin_src shell
$ sudo apt-get install cmake
#+end_src
If you want to use latest version, download from following
http://www.cmake.org/
e.g.
Download cmake-3.1.3.tar.gz, and decompress to work directory.
You perform a build and installation.
#+begin_src shell
$ tar -xf cmake-3.1.3.tar.gz .
$ cd cmake-3.1.3
$ ./configure && make
$ make install
#+end_src
* Self-Build
** Windows
*** LLVM
Required process for LLVM self-build.
And you must apply patch to LLVM before build.
It isn't necessary install a package after self-build.
You must designate generated binary PATH after LLVM self-build at CMake project generation argument.
Therefore clang-server is able to build.
When LLVM is already installed, you must designate installed directory of LLVM.
When you want to self-build,
Checkout from SVN by yourself, and perform the LLVM Solution File generation and build by cmake.
Or, use following script.
https://github.com/yaruopooner/llvm-build-shells
**** How to designate the LLVM patch in the script
You will designate -tasks to the argument of builderShell,
and designate PATCH to the parameter of -tasks,
It will gives a table that describes the path to apply the patch and patch file to parameter of -patchInfos.
Please refer to the sample.ps1 of llvm-build-shells for details.
**** The contents of the LLVM patch
Use of mmap always invalidation.
*** clang-server
Use the ac-clang/build/builder_sample.bat
Please edit the builder_sample.bat as necessary.
It's necessary to execute in the command line or Windows Explorer.
- example
#+begin_src bat
cmake -G "Visual Studio 12 2013 Win64" ../clang-server -DLIBRARY_PATHS="c:/cygwin-x86_64/tmp/llvm-build-shells/ps1/clang-360/build/msvc-64/" -DCMAKE_INSTALL_PREFIX="c:/cygwin-x86_64/usr/local/bin/"
#+end_src
- Option commentary
- =-DLIBRARY_PATHS=
You have to designate location of LLVM self-build completed directory.
It is necessary to designate the directory that a binary was generated.(e.g. {LLVM output path}/Release/)
If you omit this option, value will be use =ac-clang/clang-server= .
- =-DCMAKE_INSTALL_PREFIX=
You have to designate installation location of clang-server.
If you omit this option, value will be use =C:/Program Files/clang-server= .
** Linux
*** LLVM
Required process for LLVM self-build.
And you must apply patch to LLVM before build.
It isn't necessary install a package after self-build.
You must designate generated binary PATH after LLVM self-build at CMake project generation argument.
Therefore clang-server is able to build.
When LLVM is already installed, you must designate installed directory of LLVM.
When you want to self-build,
Checkout from SVN by yourself, and perform the LLVM Solution File generation and build by cmake.
Or, use following script.
https://github.com/yaruopooner/llvm-build-shells
**** How to designate the LLVM patch in the script
You will designate -patch to the argument of executeBuilder.
Add to -patchApplyLocation the path where you want to apply the patch.
You write the patch file to -patchPath gives as an parameter.
-patchApplyLocation,-patchPath becomes the pair, it is possible to multiple times designate.
Please refer to the sample.sh of llvm-build-shells for details.
**** The contents of the LLVM patch
Use of mmap always invalidation.
*** clang-server
Use the ac-clang/build/builder_sample.sh
Please edit the builder_sample.sh as necessary.
Execute the builder_sample.sh
- example
#+begin_src shell-script
cmake -G "Unix Makefiles" ../clang-server -DLIBRARY_PATHS="/home/yaruopooner/work/llvm-build-shells/sh/clang-350/build" -DCMAKE_INSTALL_PREFIX="~/work/clang-server"
#+end_src
- Option commentary
- =-DLIBRARY_PATHS=
You have to designate location of LLVM self-build completed directory.
It is necessary to designate the directory that a binary was generated.(e.g. {LLVM output path}/Release/)
If you omit this option, value will be use =ac-clang/clang-server= .
- =-DCMAKE_INSTALL_PREFIX=
You have to designate installation location of clang-server.
If you omit this option, value will be use =/usr/local/bin= .
* Patch was applied binary(Windows Only)
https://github.com/yaruopooner/ac-clang/releases
clang-server-X.X.X.zip is you can download from the above
The archive is 3 files contain, these file applied patch.
- clang-server.exe
- libclang.dll
- libclang.lib or libclang.imp
When you want to self-build only clang-server without LLVM,
clang-server-X.X.X.zip decompress to ac-clang directory.
Then, it will be placed in the following.
ac-clang/clang-server/binary/clang-server.exe
ac-clang/clang-server/library/x86_64/release/libclang.dll
ac-clang/clang-server/library/x86_64/release/libclang.lib
* Restrictions when you use LLVM official libclang without applying a patch
** A specific file is locked and cannot save it
When you try to save the edited header file,
it will be "basic-save-buffer-2: Opening output file: invalid argument `HEADER-FILE-NAME`",
and you can't save.
This occur if it meets certain conditions.
This condition is met when the header file size is larger than 16kB.
It is not at all occur when header file size is smaller than 16kB.
This issue belong to TranslationUnit(TU) of libclang.
The inclusion target file is locked by TU of libclang.
By performing a provisional transaction in ac-clang side, the more or less is erased, but it can't be avoided perfectly.
When this issue is occurring, only manual handle can be avoided.
*** Solution in Emacs side
I suppose that combination of source file is foo.cpp/foo.hpp.
When foo.hpp(modified) can't save, foo.cpp is (modified) often, so foo.cpp have to saved.
Therefore, foo.hpp should be possible to save.
When this can't save,
foo.hpp is included by source files besides foo.cpp, and it has (modified) status.
You have to save those.
And, when corresponding source is activated by definition jump feature, even if buffer don't modified that buffer is activated.
You try remove corresponding buffer, or (ac-clang-deactivate) must be execute in buffer.
In other cases, when you try save header file that file size larger than 16kB
When you save a header file of larger than 16kB, if it fails.
And that header file does not opened.
In this case, header file is included by a far module from current source file.
When you having developed a library module framework, it may be easy to occur.
because library and framework is included from application side.
*** Issue(Implementation issues explanation, it wanted suggested solutions)
When session of "foo.cpp" is edited in the buffer,
TU continue locking to included header file after parsed "foo.cpp".
When you edit and save to "foo.hpp" in this state, it occur error, because file is locked by mmap.
Therefore I modified a server as follows.
So while maintaining the session when "foo.cpp" saving,
TU is generated when "foo.cpp" is edited after TU released.
Therefore "foo.hpp" is possible to save that the included header file is unlocked after "foo.cpp" saved.
When a "foo.hpp" is included buffer where exist in buffer editing group without buffer of "foo.cpp",
the lock is not released when you does not save all them.
In the Windows environment,
This lock is not open function of I/O, is a lock by CreateFileMapping of WindowsAPI.
libclang FileManager does allocation to memory mapped file for the files larger than 16kB.
When TU is released, memory mapped file is released by UnmapViewOfFile, these becomes writable to file.
In the Linux environment,
problems with mmap/munmap bug differ slightly from the Windows environment, but also occurred in Linux environment.
The method add to "class Foo" in "foo.hpp" in the state that holds TU of "foo.cpp", and save to file.
After "foo.hpp" update, when you try complete method of "class Foo" in the "foo.cpp", TU will crash.
in this case, libclang output to STDOUT that "libclang: crash detected in code completion"
libclang output "libclang: crash detected in code completion" to STDOUT.
The process of clang-server is living in this situation.
Completion is possible after deletion of session and creation of session.
** Miscellaneous
The above problems are solved by patching for libclang.
When you use the patch applied release binary(libclang.dll or so) it is not occur.
When you use the patch does not applied to LLVM self-build and LLVM official binary, this problem is occur.
I think specification bug of clang side. This problem has been reported to LLVM bugzilla. in the corresponding waitting.
http://llvm.org/bugs/show_bug.cgi?id=20880
* Patch commentary
** Patch
Use the ac-clang/clang-server/patch/invalidate-mmap.patch
#+begin_src shell-script
cd llvm/
svn patch ac-clang/clang-server/patch/invalidate-mmap.patch
#+end_src
** The contents of the LLVM patch(invalidate-mmap.patch)
Patch is applied so as not to use mmap.
Apply to the following source code to
=clang-trunk/llvm/lib/Support/MemoryBuffer.cpp=
#+begin_src C++
static error_code getOpenFileImpl(int FD, const char *Filename,
OwningPtr<MemoryBuffer> &result,
uint64_t FileSize, uint64_t MapSize,
int64_t Offset, bool RequiresNullTerminator) {
#+end_src
It is determined availability of mmap for file by shouldUseMmap call from the above function.
#+begin_src C++
static bool shouldUseMmap(int FD,
size_t FileSize,
size_t MapSize,
off_t Offset,
bool RequiresNullTerminator,
int PageSize) {
#+end_src
When the result of function is always false, mmap is not never used.
Therefore, the following modify has been applied to the top of this function.
#+begin_src C++
#if 1
return false;
#else
/* original codes */
#endif
#+end_src
** TODO Additional Specification of LLVM3.5
IsVolatileSize has been added to arguments of shouldUseMmap and getOpenFileImpl.
This will be passed unchanged to shouldUseMmap.
It is executed as follows in the shouldUseMmap top.
#+begin_src C++
if (IsVolatileSize)
return false;
#+end_src
Following comments had been attached
#+begin_src C++
// mmap may leave the buffer without null terminator if the file size changed
// by the time the last page is mapped in, so avoid it if the file size is
// likely to change.
#+end_src
Although that said, there is a situation which isn't assumed Variously, I'm supposing that mistake of specification.
Moreover, upstream function of buffer association function
I was found some place where is not designated value of IsVolatileSize and is used default value of constructor argument.
I tried modified it.
Result was become more better than conventional.
But it seem to have a problem, because I was not able to control mmap like a assumption.
I'm not enough understand the specification of around the file system and memory of LLVM.
For that reason that the rightly correction is difficult.
Therefore, the above patching becomes the most safe way at present.