1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0-only 3# Generate tags or cscope files 4# Usage tags.sh <mode> 5# 6# mode may be any of: tags, gtags, TAGS, cscope 7# 8# Uses the following environment variables: 9# SUBARCH, SRCARCH, srctree 10 11if [[ "$KBUILD_VERBOSE" =~ 1 ]]; then 12 set -x 13fi 14 15# RCS_FIND_IGNORE has escaped ()s -- remove them. 16ignore="$(echo "$RCS_FIND_IGNORE" | sed 's|\\||g' )" 17# tags and cscope files should also ignore MODVERSION *.mod.c files 18ignore="$ignore ( -name *.mod.c ) -prune -o" 19 20# ignore arbitrary directories 21if [ -n "${IGNORE_DIRS}" ]; then 22 for i in ${IGNORE_DIRS}; do 23 ignore="${ignore} ( -path $i ) -prune -o" 24 done 25fi 26 27# Use make KBUILD_ABS_SRCTREE=1 {tags|cscope} 28# to force full paths for a non-O= build 29if [ "${srctree}" = "." -o -z "${srctree}" ]; then 30 tree= 31else 32 tree=${srctree}/ 33fi 34 35# gtags(1) refuses to index any file outside of its current working dir. 36# If gtags indexing is requested and the build output directory is not 37# the kernel source tree, index all files in absolute-path form. 38if [[ "$1" == "gtags" && -n "${tree}" ]]; then 39 tree=$(realpath "$tree")/ 40fi 41 42# Detect if ALLSOURCE_ARCHS is set. If not, we assume SRCARCH 43if [ "${ALLSOURCE_ARCHS}" = "" ]; then 44 ALLSOURCE_ARCHS=${SRCARCH} 45elif [ "${ALLSOURCE_ARCHS}" = "all" ]; then 46 ALLSOURCE_ARCHS=$(find ${tree}arch/ -mindepth 1 -maxdepth 1 -type d -printf '%f ') 47fi 48 49setup_name_pattern() 50{ 51 pattern=() 52 for ext; do 53 if [ ${#pattern[@]} -gt 0 ]; then 54 pattern+=("-o" "-name" "$ext") 55 else 56 pattern+=("(" "-name" "$ext") 57 fi 58 done 59 if [ ${#pattern[@]} -gt 0 ]; then 60 pattern+=(")") 61 fi 62} 63 64# find sources in arch/$1 65find_arch_sources() 66{ 67 for i in $archincludedir; do 68 local prune="$prune ( -path $i ) -prune -o" 69 done 70 local src=${tree}arch/$1 71 shift 72 setup_name_pattern "$@" 73 find $src $ignore $prune "${pattern[@]}" -not -type l -print; 74} 75 76# find sources in arch/$1/include 77find_arch_include_sources() 78{ 79 local include=$(find ${tree}arch/$1/ -name include -type d -print); 80 if [ -n "$include" ]; then 81 archincludedir="$archincludedir $include" 82 shift 83 setup_name_pattern "$@" 84 find $include $ignore "${pattern[@]}" -not -type l -print; 85 fi 86} 87 88# find sources in include/ 89find_include_sources() 90{ 91 setup_name_pattern "$@" 92 find ${tree}include $ignore -name config -prune -o "${pattern[@]}" \ 93 -not -type l -print; 94} 95 96# find sources in rest of tree 97# we could benefit from a list of dirs to search in here 98find_other_sources() 99{ 100 setup_name_pattern "$@" 101 find ${tree}* $ignore \ 102 \( -path ${tree}include -o -path ${tree}arch -o -name '.tmp_*' \) -prune -o \ 103 "${pattern[@]}" -not -type l -print; 104} 105 106all_sources() 107{ 108 find_arch_include_sources ${SRCARCH} '*.[chS]' '*.rs' 109 if [ -n "$archinclude" ]; then 110 find_arch_include_sources $archinclude '*.[chS]' '*.rs' 111 fi 112 find_include_sources '*.[chS]' '*.rs' 113 for arch in $ALLSOURCE_ARCHS 114 do 115 find_arch_sources $arch '*.[chS]' '*.rs' 116 done 117 find_other_sources '*.[chS]' '*.rs' 118} 119 120all_compiled_sources() 121{ 122 { 123 echo include/generated/autoconf.h 124 find $ignore -name "*.cmd" -exec \ 125 grep -Poh '(?<=^ )\S+\.([chS]|rs)(?=\s)|(?<== )\S+\.(?1)(?=$)' {} \+ | 126 awk '!a[$0]++' 127 } | xargs realpath -esq $([ -z "$KBUILD_ABS_SRCTREE" ] && echo --relative-to=.) | 128 sort -u 129} 130 131all_target_sources() 132{ 133 if [ -n "$COMPILED_SOURCE" ]; then 134 all_compiled_sources 135 else 136 all_sources 137 fi 138} 139 140all_kconfigs() 141{ 142 find ${tree}arch/ -maxdepth 1 $ignore \ 143 -name "Kconfig*" -not -type l -print; 144 for arch in $ALLSOURCE_ARCHS; do 145 find_arch_sources $arch 'Kconfig*' 146 done 147 find_other_sources 'Kconfig*' 148} 149 150docscope() 151{ 152 (echo \-k; echo \-q; all_target_sources) > cscope.files 153 cscope -b -f cscope.out 154} 155 156dogtags() 157{ 158 all_target_sources | gtags -i -C "${tree:-.}" -f - "$PWD" 159} 160 161# Basic regular expressions with an optional /kind-spec/ for ctags and 162# the following limitations: 163# - No regex modifiers 164# - Use \{0,1\} instead of \?, because etags expects an unescaped ? 165# - \s is not working with etags, use a space or [ \t] 166# - \w works, but does not match underscores in etags 167# - etags regular expressions have to match at the start of a line; 168# a ^[^#] is prepended by setup_regex unless an anchor is already present 169regex_asm=( 170 '/^\(ENTRY\|_GLOBAL\)([[:space:]]*\([[:alnum:]_\\]*\)).*/\2/' 171 '/^SYM_[[:alnum:]_]*START[[:alnum:]_]*([[:space:]]*\([[:alnum:]_\\]*\)).*/\1/' 172) 173regex_c=( 174 '/^SYSCALL_DEFINE[0-9]([[:space:]]*\([[:alnum:]_]*\).*/sys_\1/' 175 '/^BPF_CALL_[0-9]([[:space:]]*\([[:alnum:]_]*\).*/\1/' 176 '/^COMPAT_SYSCALL_DEFINE[0-9]([[:space:]]*\([[:alnum:]_]*\).*/compat_sys_\1/' 177 '/^TRACE_EVENT([[:space:]]*\([[:alnum:]_]*\).*/trace_\1/' 178 '/^DEFINE_EVENT([^,)]*,[[:space:]]*\([[:alnum:]_]*\).*/trace_\1/' 179 '/^DEFINE_INSN_CACHE_OPS([[:space:]]*\([[:alnum:]_]*\).*/get_\1_slot/' 180 '/^DEFINE_INSN_CACHE_OPS([[:space:]]*\([[:alnum:]_]*\).*/free_\1_slot/' 181 '/^PAGEFLAG([[:space:]]*\([[:alnum:]_]*\).*/Page\1/' 182 '/^PAGEFLAG([[:space:]]*\([[:alnum:]_]*\).*/SetPage\1/' 183 '/^PAGEFLAG([[:space:]]*\([[:alnum:]_]*\).*/ClearPage\1/' 184 '/^TESTSETFLAG([[:space:]]*\([[:alnum:]_]*\).*/TestSetPage\1/' 185 '/^TESTPAGEFLAG([[:space:]]*\([[:alnum:]_]*\).*/Page\1/' 186 '/^SETPAGEFLAG([[:space:]]*\([[:alnum:]_]*\).*/SetPage\1/' 187 '/\<__SETPAGEFLAG([[:space:]]*\([[:alnum:]_]*\).*/__SetPage\1/' 188 '/\<TESTCLEARFLAG([[:space:]]*\([[:alnum:]_]*\).*/TestClearPage\1/' 189 '/\<__TESTCLEARFLAG([[:space:]]*\([[:alnum:]_]*\).*/TestClearPage\1/' 190 '/\<CLEARPAGEFLAG([[:space:]]*\([[:alnum:]_]*\).*/ClearPage\1/' 191 '/\<__CLEARPAGEFLAG([[:space:]]*\([[:alnum:]_]*\).*/__ClearPage\1/' 192 '/^__PAGEFLAG([[:space:]]*\([[:alnum:]_]*\).*/__SetPage\1/' 193 '/^__PAGEFLAG([[:space:]]*\([[:alnum:]_]*\).*/__ClearPage\1/' 194 '/^PAGEFLAG_FALSE([[:space:]]*\([[:alnum:]_]*\).*/Page\1/' 195 '/\<TESTSCFLAG([[:space:]]*\([[:alnum:]_]*\).*/TestSetPage\1/' 196 '/\<TESTSCFLAG([[:space:]]*\([[:alnum:]_]*\).*/TestClearPage\1/' 197 '/\<SETPAGEFLAG_NOOP([[:space:]]*\([[:alnum:]_]*\).*/SetPage\1/' 198 '/\<CLEARPAGEFLAG_NOOP([[:space:]]*\([[:alnum:]_]*\).*/ClearPage\1/' 199 '/\<__CLEARPAGEFLAG_NOOP([[:space:]]*\([[:alnum:]_]*\).*/__ClearPage\1/' 200 '/\<TESTCLEARFLAG_FALSE([[:space:]]*\([[:alnum:]_]*\).*/TestClearPage\1/' 201 '/^PAGE_TYPE_OPS([[:space:]]*\([[:alnum:]_]*\).*/Page\1/' 202 '/^PAGE_TYPE_OPS([[:space:]]*\([[:alnum:]_]*\).*/__SetPage\1/' 203 '/^PAGE_TYPE_OPS([[:space:]]*\([[:alnum:]_]*\).*/__ClearPage\1/' 204 '/^TASK_PFA_TEST([^,]*,[[:space:]]*\([[:alnum:]_]*\))/task_\1/' 205 '/^TASK_PFA_SET([^,]*,[[:space:]]*\([[:alnum:]_]*\))/task_set_\1/' 206 '/^TASK_PFA_CLEAR([^,]*,[[:space:]]*\([[:alnum:]_]*\))/task_clear_\1/' 207 '/^DEF_MMIO_\(IN\|OUT\)_[XD]([[:space:]]*\([[:alnum:]_]*\),[^)]*)/\2/' 208 '/^DEBUGGER_BOILERPLATE([[:space:]]*\([[:alnum:]_]*\))/\1/' 209 '/^DEF_PCI_AC_\(\|NO\)RET([[:space:]]*\([[:alnum:]_]*\).*/\2/' 210 '/^PCI_OP_READ([[:space:]]*\(\w*\).*[1-4])/pci_bus_read_config_\1/' 211 '/^PCI_OP_WRITE([[:space:]]*\(\w*\).*[1-4])/pci_bus_write_config_\1/' 212 '/\<DEFINE_\(RT_MUTEX\|MUTEX\|SEMAPHORE\|SPINLOCK\)([[:space:]]*\([[:alnum:]_]*\)/\2/v/' 213 '/\<DEFINE_\(RAW_SPINLOCK\|RWLOCK\|SEQLOCK\)([[:space:]]*\([[:alnum:]_]*\)/\2/v/' 214 '/\<DEFINE_TIMER(\([^,)]*\),/\1/' 215 '/\<DECLARE_\(RWSEM\|COMPLETION\)([[:space:]]*\([[:alnum:]_]\+\)/\2/v/' 216 '/\<DECLARE_BITMAP([[:space:]]*\([[:alnum:]_]\+\)/\1/v/' 217 '/\(^\|\s\)\(\|L\|H\)LIST_HEAD([[:space:]]*\([[:alnum:]_]*\)/\3/v/' 218 '/\(^\|\s\)RADIX_TREE([[:space:]]*\([[:alnum:]_]*\)/\2/v/' 219 '/\<DEFINE_PER_CPU([^,]*,[[:space:]]*\([[:alnum:]_]*\)/\1/v/' 220 '/\<DEFINE_PER_CPU_SHARED_ALIGNED([^,]*,[[:space:]]*\([[:alnum:]_]*\)/\1/v/' 221 '/\<DECLARE_WAIT_QUEUE_HEAD([[:space:]]*\([[:alnum:]_]*\)/\1/v/' 222 '/\<DECLARE_\(TASKLET\|WORK\|DELAYED_WORK\)([[:space:]]*\([[:alnum:]_]*\)/\2/v/' 223 '/\(^\s\)OFFSET([[:space:]]*\([[:alnum:]_]*\)/\2/v/' 224 '/\(^\s\)DEFINE([[:space:]]*\([[:alnum:]_]*\)/\2/v/' 225 '/\<\(DEFINE\|DECLARE\)_HASHTABLE([[:space:]]*\([[:alnum:]_]*\)/\2/v/' 226 '/\<DEFINE_ID\(R\|A\)([[:space:]]*\([[:alnum:]_]\+\)/\2/' 227 '/\<DEFINE_WD_CLASS([[:space:]]*\([[:alnum:]_]\+\)/\1/' 228 '/\<ATOMIC_NOTIFIER_HEAD([[:space:]]*\([[:alnum:]_]\+\)/\1/' 229 '/\<RAW_NOTIFIER_HEAD([[:space:]]*\([[:alnum:]_]\+\)/\1/' 230 '/\<DECLARE_FAULT_ATTR([[:space:]]*\([[:alnum:]_]\+\)/\1/' 231 '/\<BLOCKING_NOTIFIER_HEAD([[:space:]]*\([[:alnum:]_]\+\)/\1/' 232 '/\<DEVICE_ATTR_\(RW\|RO\|WO\)([[:space:]]*\([[:alnum:]_]\+\)/dev_attr_\2/' 233 '/\<DRIVER_ATTR_\(RW\|RO\|WO\)([[:space:]]*\([[:alnum:]_]\+\)/driver_attr_\2/' 234 '/\<\(DEFINE\|DECLARE\)_STATIC_KEY_\(TRUE\|FALSE\)\(\|_RO\)([[:space:]]*\([[:alnum:]_]\+\)/\4/' 235 '/^SEQCOUNT_LOCKTYPE(\([^,]*\),[[:space:]]*\([^,]*\),[^)]*)/seqcount_\2_t/' 236 '/^SEQCOUNT_LOCKTYPE(\([^,]*\),[[:space:]]*\([^,]*\),[^)]*)/seqcount_\2_init/' 237 '/^\<DECLARE_IDTENTRY[[:alnum:]_]*([^,)]*,[[:space:]]*\([[:alnum:]_]\+\)/\1/' 238 '/^\<DEFINE_IDTENTRY[[:alnum:]_]*([[:space:]]*\([[:alnum:]_]\+\)/\1/' 239 '/^\<DEFINE_FREE(\([[:alnum:]_]\+\)/cleanup_\1/' 240 '/^\<DEFINE_CLASS(\([[:alnum:]_]\+\)/class_\1/' 241 '/^\<EXTEND_CLASS(\([[:alnum:]_]\+\),[[:space:]]*\([[:alnum:]_]\+\)/class_\1\2/' 242 '/^\<DEFINE_GUARD(\([[:alnum:]_]\+\)/class_\1/' 243 '/^\<DEFINE_GUARD_COND(\([[:alnum:]_]\+\),[[:space:]]*\([[:alnum:]_]\+\)/class_\1\2/' 244 '/^\<DEFINE_LOCK_GUARD_[[:digit:]](\([[:alnum:]_]\+\)/class_\1/' 245 '/^\<DEFINE_LOCK_GUARD_[[:digit:]]_COND(\([[:alnum:]_]\+\),[[:space:]]*\([[:alnum:]_]\+\)/class_\1\2/' 246 '/^context_lock_struct(\([^,)]*\)[^)]*)/struct \1/' 247) 248regex_kconfig=( 249 '/^[[:blank:]]*\(menu\|\)config[[:blank:]]\+\([[:alnum:]_]\+\)/\2/' 250 '/^[[:blank:]]*\(menu\|\)config[[:blank:]]\+\([[:alnum:]_]\+\)/CONFIG_\2/' 251) 252setup_regex() 253{ 254 local mode=$1 lang tmp=() r 255 shift 256 257 regex=() 258 for lang; do 259 case "$lang" in 260 asm) tmp=("${regex_asm[@]}") ;; 261 c) tmp=("${regex_c[@]}") ;; 262 kconfig) tmp=("${regex_kconfig[@]}") ;; 263 esac 264 for r in "${tmp[@]}"; do 265 if test "$mode" = "exuberant"; then 266 regex[${#regex[@]}]="--regex-$lang=${r}b" 267 else 268 # Remove ctags /kind-spec/ 269 case "$r" in 270 /*/*/?/) 271 r=${r%?/} 272 esac 273 # Prepend ^[^#] unless already anchored 274 case "$r" in 275 /^*) ;; 276 *) 277 r="/^[^#]*${r#/}" 278 esac 279 regex[${#regex[@]}]="--regex=$r" 280 fi 281 done 282 done 283} 284 285exuberant() 286{ 287 CTAGS_EXTRA="extra" 288 if $1 --version 2>&1 | grep -iq universal; then 289 CTAGS_EXTRA="extras" 290 fi 291 setup_regex exuberant asm c 292 # identifiers to ignore by ctags 293 local ign=( 294 ACPI_EXPORT_SYMBOL 295 DECLARE_BITMAP 296 DEFINE_{TRACE,MUTEX,TIMER} 297 EXPORT_SYMBOL EXPORT_SYMBOL_GPL 298 EXPORT_TRACEPOINT_SYMBOL EXPORT_TRACEPOINT_SYMBOL_GPL 299 ____cacheline_aligned ____cacheline_aligned_in_smp 300 ____cacheline_internodealigned_in_smp 301 __acquires __aligned __always_inline __always_unused 302 __attribute 303 __cacheline_aligned __cacheline_aligned_in_smp 304 __deprecated 305 __exitdata 306 __initconst __initdata __initdata_memblock 307 __maybe_unused __must_check __must_hold 308 __packed __packed2__ 309 __read_mostly __refdata __releases __ro_after_init 310 __used 311 const 312 static 313 ) 314 all_target_sources | \ 315 xargs $1 -a -I "$(IFS=','; echo "${ign[*]}")" \ 316 --$CTAGS_EXTRA=+fq --c-kinds=+px --fields=+iaS --langmap=c:+.h \ 317 "${regex[@]}" 318 319 KCONFIG_ARGS=() 320 if ! $1 --list-languages | grep -iq kconfig; then 321 setup_regex exuberant kconfig 322 KCONFIG_ARGS=(--langdef=kconfig --language-force=kconfig "${regex[@]}") 323 fi 324 all_kconfigs | xargs $1 -a "${KCONFIG_ARGS[@]}" 325} 326 327emacs() 328{ 329 setup_regex emacs asm c 330 all_target_sources | xargs $1 -a "${regex[@]}" 331 332 setup_regex emacs kconfig 333 all_kconfigs | xargs $1 -a "${regex[@]}" 334} 335 336xtags() 337{ 338 if $1 --version 2>&1 | grep -iq exuberant; then 339 exuberant $1 340 elif $1 --version 2>&1 | grep -iq emacs; then 341 emacs $1 342 else 343 all_target_sources | xargs $1 -a 344 fi 345} 346 347# Support um (which uses SUBARCH) 348if [ "${ARCH}" = "um" ]; then 349 if [ "$SUBARCH" = "i386" ]; then 350 archinclude=x86 351 elif [ "$SUBARCH" = "x86_64" ]; then 352 archinclude=x86 353 else 354 archinclude=${SUBARCH} 355 fi 356fi 357 358remove_structs= 359case "$1" in 360 "cscope") 361 docscope 362 ;; 363 364 "gtags") 365 dogtags 366 ;; 367 368 "tags") 369 rm -f tags 370 xtags ${CTAGS:-ctags} 371 remove_structs=y 372 ;; 373 374 "TAGS") 375 rm -f TAGS 376 xtags etags 377 remove_structs=y 378 ;; 379esac 380 381# Remove structure forward declarations. 382if [ -n "$remove_structs" ]; then 383 LC_ALL=C sed -i -e '/^\([a-zA-Z_][a-zA-Z0-9_]*\)\t.*\t\/\^struct \1;.*\$\/;"\tx$/d' $1 384fi 385