about 7 years ago

今天腦子發熱,想來用vim達成類似XCode的自動補完功能(auto-completion).
先從國外網友提供的.ycm_extra_conf.py開始折騰。

import os
import ycm_core

flags = [
'-resource-dir',
'/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/6.0',
'-isysroot',
'/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk',
'-I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include',
'-F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/System/Library/Frameworks',
'-fblocks',
'-fobjc-runtime=macosx-10.10.0',
'-fencode-extended-block-signature',
'-fobjc-arc',
'-fobjc-exceptions',
'-fexceptions',
'-x',
'objective-c'
]

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

def FlagsForFile( filename, **kwargs ):
  return {
    'flags': flags,
    'do_cache': True
  }

先把YouCompleteMe安裝好,再把這個檔案放在專案的根目錄下面
用VIM打開.m檔的時候,就會出現一個對話框,問要不要執行.ycm_extra_conf.py這個程式,這時候選Load就能載入了

如果沒意外的話,配合國外網友提供的測試檔案

#import <Foundation/Foundation.h>

int main(int argc, char *argv[]) {
  int retVal;
  @autoreleasepool {
    NSString *string = @"hihi";
    NSLog(@"%@", string);
    retVal = 0;
  }
  return retVal;
}

就能看到auto completion的功能了

等等,UIKit去那了…

國外網友提供的是Mac app的設定,如果是iOS的話
要改成iOS的SDK

import os
import ycm_core

flags = [
'-arch i386',
'-fblocks',
'-fobjc-runtime=ios-8.0.0',
'-fencode-extended-block-signature',
'-fobjc-arc',
'-fobjc-exceptions',
'-fexceptions',
'-x',
'objective-c',
]

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

def FlagsForFile( filename, **kwargs ):
  return {
    'flags': flags,
    'do_cache': True
  }

在VIM輸入:YcmDiag,就能看到

'UIKit/UIKit.h' file not found

找不到UIKit是吧?
flags加入
'-mios-simulator-version-min=7.0'
指定deployment target是ios simulator 7.0

再試一次:YcmDiag

/System/Library/Frameworks/Security.framework/Versions/A/Headers/cssmtype.h|142 col 5 error| 'CSSM_GUID' is unavailable: not available on iOS
/System/Library/Frameworks/Security.framework/Versions/A/Headers/cssmtype.h|143 col 5 error| 'CSSM_VERSION' is unavailable: not available on iOS

出現了一大堆看不懂的東西
沒關係,再繼續trial-and-error
在flags裡加入

'-isysroot',
'/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk',

指定system root directory (這啥?)

繼續:YcmDiag
看不懂的東西不見了,但是出現其它更看不懂的東西

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFNumberFormatter.h|29 col 32 error| missing ',' between enumerators (FixIt available)
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFNumberFormatter.h|30 col 40 error| missing ',' between enumerators (FixIt available)
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFNumberFormatter.h|30 col 41 error| redefinition of enumerator '__AVAILABILITY_INTERNAL__IPHONE_9_0'

沒關係,再繼續trial-and-error
在flags裡加入

'-MMD',

這個-MMD是什麼呢?
看一下說明:

-MMD
    Like -MD except mention only user header files, not system header files. 

說明上寫和-MD很像
-MD又是什麼東東?

-MD
    -MD is equivalent to -M -MF file, except that -E is not implied. The driver determines file based on whether an -o option is given. If it is, the driver uses its argument but with a suffix of .d, otherwise it takes the name of the input file, removes any directory components and suffix, and applies a .d suffix. 

說明上寫和-M是幾乎一樣的東西
-M又是什麼東東?

-M
    Instead of outputting the result of preprocessing, output a rule suitable for make describing the dependencies of the main source file. The preprocessor outputs one make rule containing the object file name for that source file, a colon, and the names of all the included files, including those coming from -include or -imacros command-line options.

    Unless specified explicitly (with -MT or -MQ), the object file name consists of the name of the source file with any suffix replaced with object file suffix and with any leading directory parts removed. If there are many included files then the rule is split into several lines using ‘\’-newline. The rule has no commands. 

好…還是看不懂

自己動手試試吧
找一個.m
執行

clang -M hello.m  

出現了

hello.o: hello.m \
  /System/Library/Frameworks/Foundation.framework/Headers/Foundation.h \
  /System/Library/Frameworks/CoreFoundation.framework/Headers/CoreFoundation.h \
  /usr/include/sys/types.h /usr/include/sys/appleapiopts.h \
  /usr/include/sys/cdefs.h /usr/include/sys/_symbol_aliasing.h \
  /usr/include/sys/_posix_availability.h /usr/include/machine/types.h \
  /usr/include/i386/types.h /usr/include/i386/_types.h \
  /usr/include/sys/_types/_int8_t.h /usr/include/sys/_types/_int16_t.h \
  /usr/include/sys/_types/_int32_t.h /usr/include/sys/_types/_int64_t.h \
  /usr/include/sys/_types/_intptr_t.h \
...

從結果判斷,-M的功能是把所有這個.m檔出現的header files都列出來
-MD-MMD呢?
執行

clang -MD hello.m  

出現

Undefined symbols for architecture x86_64:
  "___CFConstantStringClassReference", referenced from:
      CFString in hello-eb80c0.o
  "_objc_autoreleasePoolPop", referenced from:
      _main in hello-eb80c0.o
  "_objc_autoreleasePoolPush", referenced from:
      _main in hello-eb80c0.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

出現link error
搞不懂,放棄

繼續:YcmDiag
之前的看不懂的東西都不見了,但是出來一個新的stdarg.h找不到

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CoreFoundation.h|12 col 10 error| 'stdarg.h' file not found

這怎麼辦呢?

去問Google大神

I get errors about some headers being missing (stddef.h, stdarg.h)

Some header files (stddef.h, stdarg.h, and others) are shipped with Clang — these are called builtin includes. Clang searches for them in a directory relative to the location of the clang binary. If you moved the clang binary, you need to move the builtin headers, too.

More information can be found in the Builtin includes section.

上面說Clang有自己的stdarg.h
所以加入在flag裡加入

'-I/Library/Developer/CommandLineTools/usr/bin/../lib/clang/7.0.0/include',

再來:YcmDiags,結果stdarg.h找不到的問題解決了,但是之前更看不懂的問題又出現了…

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFNumberFormatter.h|29 col 32 error| missing ',' between enumerators (FixIt available)
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFNumberFormatter.h|30 col 40 error| missing ',' between enumerators (FixIt available)

看起來是clang看不懂ios9特有的macro
那就不用YouCompleteMe的libclang,改用XCode的clang好了

執行

cd ~/.vim/bundle/YouCompleteMe
./install.py --clang-completer --system-libclang

繼續:YcmDiag
之前的錯誤訊息幾乎不見了,只剩下最後一個

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CoreFoundation.h|92 col 10 error| 'CoreFoundation/CFUserNotification.h' file not found

訊息說問題出在CoreFoundation.h第92行
就去那邊看看

#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
#include <CoreFoundation/CFUserNotification.h>
#include <CoreFoundation/CFXMLNode.h>
#include <CoreFoundation/CFXMLParser.h>
#endif

原來是include了CFUserNotification.h
但是iOS的framework裡面根本沒有這個檔案呀

問題可能是出在TARGET_OS_MAC有被定義,也可能是TARGET_OS_IPHONE沒有被定義

來寫一個簡單的測試程式,來看看那些MACRO有被定義

#import <Foundation/Foundation.h>

int foo() {
  return TARGET_OS_MAC;
  return TARGET_OS_EMBEDDED;
  return TARGET_OS_IPHONE;
}

執行:w z.m
回到command line,執行

clang -E z.m

出現

int foo() {
  return 1;
  return 0;
  return 0;
}

所以TARGET_OS_MAC的值是1,其它的是0

那我們直接把TARGET_OS_MAC設成0就好了

執行

clang -DTARGET_OS_MAC=0 -E z.m 

出現

int foo() {
  return 1;
  return 0;
  return 0;
}


還是沒變
所以是有別的地方改了這個MACRO的值

用silver searcher來找一下元兇是誰

cd /Applications/Xcode.app/Contents
ag 'define TARGET_OS_MAC'

出現

Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/TargetConditionals.h
92:    #define TARGET_OS_MAC               1
213:    #define TARGET_OS_MAC               1
339:    #define TARGET_OS_MAC                1
Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/TargetConditionals.h
92:    #define TARGET_OS_MAC               1
213:    #define TARGET_OS_MAC               1
339:    #define TARGET_OS_MAC                1

不對呀,有好幾個SDK,該用那一個?
應該來指定要使用的SDK

執行

clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -E z.m

出現

int foo() {
  return 1;
  return 0;
  return 1;
}

TARGET_OS_IPHONE變成1了
問題解決

回去:YcmDiag,看看是不是行了

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CoreFoundation.h|92 col 10 error| 'CoreFoundation/CFUserNotification.h' file not found

...
問題還在
來Debug一下,輸入:YcmDebugInfo

Printing YouCompleteMe debug information...
-- Server has Clang support compiled in: True
-- Clang version: Apple LLVM version 7.0.0 (clang-700.0.72)
-- Flags for /Users/Lono/tmp/a/z.m loaded from /Users/Lono/tmp/a/.ycm_extra_conf.py:
-- ['-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/7.0.0/include', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk', '-isystem', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1', '-isystem', '/usr/local/include', '-isystem', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include', '-isystem', '/System/Library/Frameworks', '-isystem', '/usr/include','-isystem', '/Library/Frameworks']

咦! 出現一些在.ycm_extra_conf.py裡沒有的flags
這可能是YouCompleteMe自己設的flags
再來ag一下

cd ~/.vim/bundle/YouCompleteMe
ag /usr/local/include

找到可疑檔案

~/.vim/bundle/YouCompleteMe/third_party/ycmd/ycmd/completers/cpp/flags.py

原來YouCompleteMe自己有放一些default的flags進去
把這些flags移掉

繼續:YcmDiag

出現

Forcing compilation, this will block Vim until done.
No warnings or errors detected


終於設定完成(淚~~)

最後的.ycm_extra.conf.py

import os
import ycm_core

flags = [
'-D__IPHONE_OS_VERSION_MIN_REQUIRED=70000',
'-resource-dir',
'/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/7.0.0',
'-mios-simulator-version-min=7.0',
'-arch i386',
'-fblocks',
'-fobjc-runtime=ios-7.0.0',
'-fencode-extended-block-signature',
'-fobjc-arc',
'-fobjc-exceptions',
'-fexceptions',
'-x',
'objective-c',
'-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks',
'-I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/Foundation.framework/Headers',
'-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include',
'-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/7.0.0/include',
'-I/Library/Developer/CommandLineTools/usr/include',
'-isysroot',
'/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk',
'-MMD',
]

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

def FlagsForFile( filename, **kwargs ):
  return {
    'flags': flags,
    'do_cache': True
  }

2015/11/08
今天:YcmDiag竟然出現

unknown argument: '-fencode-extended-block-signature'

真奇怪,之前都沒問題
可能和clang的版本有關(clang-700.0.72)
只好把這個argument移掉了(???)

← 在DigitalOcean上自建個人專屬Heroku libClang初體驗 →
 
comments powered by Disqus