Skip to content

Instantly share code, notes, and snippets.

@scue
Last active December 13, 2021 01:50
Show Gist options
  • Save scue/e1a84e9677d788fbbe2d to your computer and use it in GitHub Desktop.
Save scue/e1a84e9677d788fbbe2d to your computer and use it in GitHub Desktop.
A script generate cscope index and .ycm_extra_conf.py, find parse all Android.mk files under current directory to get all include paths.-- 根据当前目录下所有的Android.mk文件,来生成cscope索引和.ycm_extra_conf.py文件。
#!/bin/bash -
#===============================================================================
#
# FILE: cscope-android.sh
#
# USAGE:
# . build/envsetup.sh
# lunch
# cd /path/to/workdir
# # first setup, find c include path by mmm command:
# source ~/bin/cscope-ycm-android.sh 1 # must run by source!
# # second setup, generate cscope index and .ycm_extra_conf.py
# ~/bin/cscope-ycm-android.sh 2
#
# DESCRIPTION: A script generate cscope index and .ycm_extra_conf.py, find
# parse all Android.mk files under current directory to get all
# include paths.
# 根据当前目录下所有的Android.mk文件,来生成cscope索引和
# .ycm_extra_conf.py文件。
#
# OPTIONS: ---
# REQUIREMENTS:
# cp cscope-ycm-android.sh ~/bin/
# chmod +x ~/bin/cscope-ycm-android.sh
# BUGS: ---
# NOTES: ---
# AUTHOR: linkscue(scue)
# ORGANIZATION:
# CREATED: 2015年09月10日 15:39
# REVISION: ---
#===============================================================================
#-------------------------------------------------------------------------------
# 背景故事:
# 现在正式做Android的底层开发,第一个任务就是视频流的硬解码工作;
# 但是在过程中发现,VIM编辑Android这些底层的源码,跳转得不是很灵活,尝试过
# 1)使用Eclipse和Android Studio,把整个Android工程导入进行代码浏览 -- 太大
# 2)使用Source Insight进入源码索引,这个方法按理说是最好的 -- 需要Windows
# 3)使用OpenGrok进入源码浏览,这个也是非常好的 -- 但它基于搜索的不是很精确
#
# 所以最后还是选择了VIM + cscope来操作 -- 因为我现在只在一个不起眼的目录下工作
# 我只要把这个目录下,所依赖的头文件都整理出来,交给cscope来建立一个完善的索引
# 或者,让 youcomleteme 正确的找到头文件,ycm最强大的功能莫过于可以帮助检查错误
#
# 于是乎,最后写了一这个这样子的简单脚本,实现我以上的需求
#
#-------------------------------------------------------------------------------
MYSRCTOP=/source/VDI4.5-Android-OS4C
# 用于存储C/C++头文件索引路径
include_path_file=LOCAL_C_INCLUDES.txt
#-------------------------------------------------------------------------------
# 运行方法:
# . build/envsetup.sh
# lunch
# cd /path/to/workdir
# source ~/bin/cscope-ycm-android.sh 1
# 因为调用mmm命令解析 LOCAL_C_INCLUDE 变量,所以必须使用 source 来执行
#
# 输出结果:
# 文件: LOCAL_C_INCLUDES.txt
# 包含从Android.mk中解决出来的 LOCAL_C_INCLUDE 变量,保存形式 $MYSRCTOP/具体路径
#
# 之所以定义一个 $MYSRCTOP,是因为某些童鞋,Android编译环境可能是Docker容器里边的
# 路径,与外边的Ubuntu真实路径有所不一致,若你没有这个问题,大可以设定MYSRCTOP为$T
#-------------------------------------------------------------------------------
find_c_include(){
(
# 错误检查
if [[ ! -f Android.mk ]]; then
echo "Error: can't find an Android.mk file in currect path"
return 1
fi
# 备份旧的Android.mk文件
find . -name Android.mk -exec cp -f {} {}_ \;
# 直接修改Android.mk文件,添加输出信息
find . -name Android.mk -exec \
sed -i '/include \$(BUILD_/ i\$(info LOCAL_C_INCLUDES=$(LOCAL_PATH))' {} \;
# 逐个Android.mk文件进行分析
echo -e "\nFind all Android.mk under currect directory ..\n"
while read line; do
include_path=$(echo $line | awk -F'=' '{print $2}')
for path in $include_path ; do
echo -e "$MYSRCTOP/$path" | tee -a $include_path_file
done
done < <(mmm . -n | grep '^LOCAL_C_INCLUDES')
# 恢复原始的Android.mk文件
echo -e "\nRestore the origin Android.mk files .."
find . -name Android.mk_ | while read mkfile; do
mv -f $mkfile ${mkfile%_}
done
)
}
#-------------------------------------------------------------------------------
# 运行方法:
# cd /path/to/workdir
# ~/bin/cscope-ycm-android.sh 2
# 输出结果:
# cscope.files -- cscope 索引时自动读取这个文件
# cscope.out -- cscope 索引输出文件,我们主要目的是要这个文件
# .ycm_extra_conf.py -- YouCompleteMe 额外配置文件,有了它可以检查Android编程
# 错误,以及实际跳转(cscope也可以跳转,两者不冲突)
#-------------------------------------------------------------------------------
generate_cscope_ycmconf(){
(
# 获取 cscope 搜索路径
cscsope_paths="."
while read line; do
cscsope_paths="$cscsope_paths $line"
done < <(cat $include_path_file)
# 保存至 cscope.files
find $cscsope_paths \
-iname '*.java' -print -o \
-iname '*.aidl' -print -o \
-iname '*.hpp' -print -o \
-iname '*.cpp' -print -o \
-iname '*.xml' -print -o \
-iname '*.mk' -print -o \
-iname '*.[chxsS]' -print > cscope.files
# 生成索引
cscope -Rbqk && echo -e "\nGenerate cscope index done!"
# Youcompleteme
ycm_extra_conf_temp=/tmp/.ycm_extra_conf.py_android.tmp
for path in $cscsope_paths ; do
echo -e "'-isystem',\n'$path',"
done > $ycm_extra_conf_temp
test -s $ycm_extra_conf_temp && \
test -f .ycm_extra_conf.py && \
mv .ycm_extra_conf.py .ycm_extra_conf.py.bak
cat <<-EOF > .ycm_extra_conf.py
#!/usr/bin/env python
#
# Copyright (C) 2014 Google Inc.
#
# This file is part of YouCompleteMe.
#
# YouCompleteMe is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# YouCompleteMe is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import os
import ycm_core
# These are the compilation flags that will be used in case there's no
# compilation database set (by default, one is not set).
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
flags = [
'-Wall',
'-Wextra',
'-Werror',
'-fexceptions',
'-DNDEBUG',
# THIS IS IMPORTANT! Without a "-std=<something>" flag, clang won't know which
# language to use when compiling headers. So it will guess. Badly. So C++
# headers will be compiled as C headers. You don't want that so ALWAYS specify
# a "-std=<something>".
# For a C project, you would set this to something like 'c99' instead of
# 'c++11'.
'-std=c++11',
# ...and the same thing goes for the magic -x option which specifies the
# language that the files to be compiled are written in. This is mostly
# relevant for c++ headers.
# For a C project, you would set this to 'c' instead of 'c++'.
'-x',
'c++',
$(cat $ycm_extra_conf_temp)
''
]
# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags.
compilation_database_folder = ''
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 DirectoryOfThisScript():
return os.path.dirname( os.path.abspath( __file__ ) )
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 ):
# The compilation_commands.json file generated by CMake does not have entries
# for header files. So we do our best by asking the db for flags for a
# corresponding source file, if any. If one exists, the flags for that file
# should be good enough.
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 )
# This is the entry point; this function is called by ycmd to produce flags for
# a file.
def FlagsForFile( filename, **kwargs ):
if database:
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object
compilation_info = GetCompilationInfoForFile( filename )
if not compilation_info:
return None
final_flags = MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_ )
else:
relative_to = DirectoryOfThisScript()
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
return {
'flags': final_flags,
'do_cache': True
}
EOF
echo -e "\nGenerate .ycm_extra_conf.py done!"
)
}
case $1 in
1|f|find )
find_c_include
;;
2|g|gen )
generate_cscope_ycmconf
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment