1

我在尝试使用带有 Unix 风格 Makefiles 的 CMake 和带有 youcompleteme 插件的 vim 在我的 Mac(带有 Xcode 6 的 Yosemite)上设置 C++ 项目时遇到了问题(我是 Linux 的老手和 Mac 新手,所以我更喜欢这个设置Xcode)。代码构建并运行,但 youcompleteme 抛出了一些虚假错误,我认为归结为它无法找到 <cstdint> 标头。

我也刚刚在Linux上尝试过,并且遇到了同样的问题。

我已将 .ycm_extra_conf.py 配置为使用 cake 生成的 compile_commands.json。compile_commands.json 中的“命令”行使用这些标志:

  "command": "/usr/bin/c++     -std=c++11 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk -F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/System/Library/Frameworks -I/usr/local/include -I/Users/tony/Dev/cow/jni -I/Users/tony/Library/Frameworks/SDL2.framework/Headers    -Wall -Wextra -Wunused -F/Users/tony/Library/Frameworks  -o ...

那里似乎没有明确引用包含 stdint 作为直接父级的任何目录。

有没有一种方法可以让 youcompleteme 用 libclang 完成它的工作,以便它可以隐式找到目录,这在命令行上运行 c++ 时似乎有效?或者,让 cmake 添加适当的系统头文件路径而不进行硬连线的最佳方法是什么?我希望我的 CMakeLists.txt 是可移植的并且能够应对工具链升级。

我的 .ycm_extra_conf.py 几乎是所提供示例的副本,稍作修改以找到我放置的 compile_commands.json。

4

3 回答 3

3

正如@ladislas 所说,YCM 需要明确指向所有相关的包含目录,因为 libclang 不会使用普通编译器驱动程序调用(即命令行中的 clang++)将使用的相同隐式位置。

我通常在 OSX 上做的是让 YCM 了解 Xcode 的 libc++ 头文件,例如(在 .ycm_extra_conf.py 中):

import os
import ycm_core
import subprocess
xcode_cpp11headers = subprocess.Popen("xcode-select -p", stdout = subprocess.PIPE, shell=True).communicate()[0].rstrip('\n') + '/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1'
.
.
flags = [
.
.
'-isystem',
xcode_cpp11headers,
.
.
]

“xcode_cpp11headers”变量根据您当前 Xcode 的安装位置填充了正确的路径,如果您想使用 libc++ 的命令行工具版本(即包含在 /Library/Developer 中),您可以相应地更改它/CommandLineTools/usr/include/c++/v1) 或 libc++ 的源代码分发,如果您自己编译的话。

当然,这取决于平台,您可以在项目旁边提供特定于平台的 .ycm_extra_conf.py,或者您可以根据当前平台使用一些额外的 python 代码以不同方式填充该变量。

于 2015-01-08T11:13:02.380 回答
2

您必须添加 YCM 需要查找源、库等的所有路径。

它不能递归工作,所以一开始有点麻烦,但一旦为您的项目设置就不应更改。

例如,这是我的一个 Arduino 项目:

https://github.com/ladislas/Bare-Arduino-Project/blob/master/.ycm_extra_conf.py#L21

希望这可以帮助!

编辑 - 2015/01/08

@abigagli 的解决方案非常优雅!我也使用类似的东西来解析我的lib目录并查找.h文件以将其路径添加到flags.

如果它有用的话就在这里:) http://git.io/IiR1JA

于 2015-01-08T08:14:05.067 回答
0

正如我从上述答案中发现的那样,需要告诉 YCM 编译器的系统包含通常隐含在使用编译器的其他方式中的路径。GetSystemIncludePaths()我在 .ycm_extra_conf.py 中添加了一个函数,以可移植地发现和缓存这些路径。这是完整的文件,其中包含注释和标记列表的无关内容。原件版权所有 (C) 2014 Google Inc,拥有 GPL2+ 许可:

import subprocess, os
import ycm_core

flags = []


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

compilation_database_folder = os.path.abspath(
        os.path.join(DirectoryOfThisScript(), 'build-make'))

if os.path.exists( compilation_database_folder ):
  database = ycm_core.CompilationDatabase( compilation_database_folder )
else:
  database = None

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

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

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

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

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

    if new_flag:
      new_flags.append( new_flag )
  return new_flags


def IsHeaderFile( filename ):
  extension = os.path.splitext( filename )[ 1 ]
  return extension in [ '.h', '.hxx', '.hpp', '.hh' ]


def GetCompilationInfoForFile( filename ):
  if IsHeaderFile( filename ):
    basename = os.path.splitext( filename )[ 0 ]
    for extension in SOURCE_EXTENSIONS:
      replacement_file = basename + extension
      if os.path.exists( replacement_file ):
        compilation_info = database.GetCompilationInfoForFile(
          replacement_file )
        if compilation_info.compiler_flags_:
          return compilation_info
    return None
  return database.GetCompilationInfoForFile( filename )


def GetSystemIncludePaths():
  cache = os.path.join(DirectoryOfThisScript(), ".ycm_sys_incs")
  if os.path.exists(cache):
    fp = open(cache, 'r')
    flags = fp.readlines()
    fp.close()
    flags = [s.strip() for s in flags]
  else:
    devnull = open(os.devnull, 'r')
    child = subprocess.Popen(["/usr/bin/cpp", "-xc++", "-v"],
        stdin = devnull, stderr = subprocess.PIPE)
    output = child.communicate()[1].split('\n')
    devnull.close()
    flags = []
    status = 0
    for l in output:
      l = l.strip()
      if l == '#include "..." search starts here:':
        status = 1
      elif l == '#include <...> search starts here:':
        status = 2
      elif status:
        if l == 'End of search list.':
          break
        elif l.endswith('(framework directory)'):
          continue
        elif status == 1:
          flags.append('-I')
        elif status == 2:
          flags.append('-isystem')
        flags.append(os.path.normpath(l))
    fp = open(cache, 'w')
    fp.write('\n'.join(flags))
    fp.close()
  return flags


def FlagsForFile( filename, **kwargs ):
  if database:
    compilation_info = GetCompilationInfoForFile( filename )
    if not compilation_info:
      return None

    final_flags = MakeRelativePathsInFlagsAbsolute(
      compilation_info.compiler_flags_,
      compilation_info.compiler_working_dir_ )
    sys_incs = GetSystemIncludePaths()
    if sys_incs:
        final_flags += sys_incs
  else:
    relative_to = DirectoryOfThisScript()
    final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )

  return {
    'flags': final_flags,
    'do_cache': True
  }
于 2015-01-10T15:07:07.473 回答