1#compdef make gmake pmake dmake freebsd-make bmake 2 3# TODO: Based on targets given on the command line, show only variables that 4# are used in those targets and their dependencies. 5 6_make-expandVars() { 7 local open close var val front ret tmp=$1 8 9 front=${tmp%%\$*} 10 case $tmp in 11 (\(*) # Variable of the form $(foobar) 12 open='(' 13 close=')' 14 ;; 15 16 ({*) # ${foobar} 17 open='{' 18 close='}' 19 ;; 20 21 ([[:alpha:]]*) # $foobar. This is exactly $(f)oobar. 22 open='' 23 close='' 24 var=${(s::)var[1]} 25 ;; 26 27 (\$*) # Escaped $. 28 print -- "${front}\$$(_make-expandVars ${tmp#\$})" 29 return 30 ;; 31 32 (*) # Nothing left to substitute. 33 print -- $tmp 34 return 35 ;; 36 esac 37 38 if [[ -n $open ]] 39 then 40 var=${tmp#$open} 41 var=${var%%$close*} 42 fi 43 44 case $var in 45 ([[:alnum:]_]#) 46 val=${VARIABLES[$var]} 47 ret=${ret//\$$open$var$close/$val} 48 ;; 49 50 (*) 51 # Improper variable name. No replacement. 52 # I'm not sure if this is desired behavior. 53 front+="\$$open$var$close" 54 ret=${ret/\$$open$var$close/} 55 ;; 56 esac 57 58 print -- "${front}$(_make-expandVars ${ret})" 59} 60 61_make-parseMakefile () { 62 local input var val target dep TAB=$'\t' dir=$1 tmp IFS= 63 64 while read input 65 do 66 case "$input " in 67 # VARIABLE = value 68 ([[:alnum:]][[:alnum:]_]#[ $TAB]#=*) 69 var=${input%%[ $TAB]#=*} 70 val=${input#*=} 71 val=${val##[ $TAB]#} 72 VARIABLES[$var]=$val 73 ;; 74 75 # VARIABLE := value 76 # Evaluated immediately 77 ([[:alnum:]][[:alnum:]_]#[ $TAB]#:=*) 78 var=${input%%[ $TAB]#:=*} 79 val=${input#*=} 80 val=${val##[ $TAB]#} 81 val=$(_make-expandVars $val) 82 VARIABLES[$var]=$val 83 ;; 84 85 # TARGET: dependencies 86 # TARGET1 TARGET2 TARGET3: dependencies 87 ([[:alnum:]][^$TAB:=]#:[^=]*) 88 input=$(_make-expandVars $input) 89 target=${input%%:*} 90 dep=${input#*:} 91 dep=${(z)dep} 92 dep="$dep" 93 for tmp in ${(z)target} 94 do 95 TARGETS[$tmp]=$dep 96 done 97 ;; 98 99 # Include another makefile 100 (${~incl} *) 101 local f=${input##${~incl} ##} 102 if [[ $incl == '.include' ]] 103 then 104 f=${f#[\"<]} 105 f=${f%[\">]} 106 fi 107 f=$(_make-expandVars $f) 108 case $f in 109 (/*) ;; 110 (*) f=$dir/$f ;; 111 esac 112 113 if [[ -r $f ]] 114 then 115 _make-parseMakefile ${f%%/[^/]##} < $f 116 fi 117 ;; 118 esac 119 done 120} 121 122_make-findBasedir () { 123 local file index basedir 124 basedir=$PWD 125 for (( index=0; index < $#@; index++ )) 126 do 127 if [[ $@[index] == -C ]] 128 then 129 file=${~@[index+1]}; 130 if [[ -z $file ]] 131 then 132 # make returns with an error if an empty arg is given 133 # even if the concatenated path is a valid directory 134 return 135 elif [[ $file == /* ]] 136 then 137 # Absolute path, replace base directory 138 basedir=$file 139 else 140 # Relative, concatenate path 141 basedir=$basedir/$file 142 fi 143 fi 144 done 145 print -- $basedir 146} 147 148_make() { 149 150 local prev="$words[CURRENT-1]" file expl tmp is_gnu dir incl match 151 local context state state_descr line 152 local -a option_specs 153 local -A TARGETS VARIABLES opt_args 154 local ret=1 155 156 _pick_variant -r is_gnu gnu=GNU unix -v -f 157 158 if [[ $is_gnu == gnu ]] 159 then 160 incl="(-|)include" 161 option_specs=( 162 '(-B --always-make)'{-B,--always-make}'[unconditionally make all targets]' 163 '*'{-C,--directory=}'[change directory first]:change to directory:->dir' 164 '-d[print lots of debug information]' 165 '--debug=-[print various types of debug information]:debug options:->debug' 166 '(-e --environment-overrides)'{-e,--environment-overrides}'[environment variables override makefiles]' 167 '--eval=-[evaluate STRING as a makefile statement]:STRING' 168 '(-f --file --makefile)'{-f,--file=,--makefile=}'[read FILE as a makefile]:makefile:->file' 169 '(- *)'{-h,--help}'[print help message and exit]' 170 '(-i --ignore-errors)'{-i,--ignore-errors}'[ignore errors from recipes]' 171 '*'{-I,--include-dir=}'[search DIRECTORY for included makefiles]:search path for included makefile:->dir' 172 '(-j --jobs)'{-j,--jobs=}'[allow N jobs at once; infinite jobs with no arg]:number of jobs' 173 '(-k --keep-going)'{-k,--keep-going}"[keep going when some targets can't be made]" 174 '(-l --load-average --max-load)'{-l,--load-average=,--max-load}"[don't start multiple jobs unless load is below N]:load" 175 '(-L --check-symlik-times)'{-L,--check-symlink-times}'[use the latest mtime between symlinks and target]' 176 '(-n --just-print --dry-run --recon)'{-n,--just-print,--dry-run,--recon}"[don't actually run any recipe; just print them]" 177 '*'{-o,--old-file=,--assume-old=}"[consider FILE to be very old and don't remake it]:file not to remake:->file" 178 '(-p --print-data-base)'{-p,--print-data-base}'[print makes internal database]' 179 '(-q --question)'{-q,--question}'[run no recipe; exit status says if up to date]' 180 '(-r --no-builtin-rules)'{-r,--no-builtin-rules}'[disable the built-in implicit rules]' 181 '(-R --no-builtin-variables)'{-R,--no-builtin-variables}'[disable the built-in variable settings]' 182 '(-s --silent --quiet)'{-s,--silent,--quiet}"[don't echo recipes]" 183 '(-S --no-keep-going --stop)'{-S,--no-keep-going,--stop}'[turns off -k]' 184 '(-t --touch)'{-t,--touch}'[touch targets instead of remaking them]' 185 '(- *)'{-v,--version}'[print the version number of make and exit]' 186 '(-w --print-directory)'{-w,--print-directory}'[print the current directory]' 187 '--no-print-directory[turn off -w, even if it was turned on implicitly]' 188 '*'{-W,--what-if=,--new-file=,--assume-new=}'[consider FILE to be infinitely new]:file to treat as modified:->file' 189 '--warn-undefined-variables[warn when an undefined variable is referenced]' 190 '--warn-undefined-functions[warn when an undefined user function is called]' 191 ) 192 else 193 # Basic make options only. 194 incl=.include 195 option_specs=( 196 '-C[change directory first]:directory:->dir' 197 '-I[include directory for makefiles]:directory:->dir' 198 '-f[specify makefile]:makefile:->file' 199 '-o[specify file not to remake]:file not to remake:->file' 200 '-W[pretend file was modified]:file to treat as modified:->file' 201 ) 202 fi 203 204 _arguments -s $option_specs \ 205 '*:make target:->target' && ret=0 206 207 case $state in 208 (dir) 209 _description directories expl "$state_descr" 210 _files "$expl[@]" -W ${(q)$(_make-findBasedir ${words[1,CURRENT-1]})} -/ && ret=0 211 ;; 212 213 (file) 214 _description files expl "$state_descr" 215 _files "$expl[@]" -W ${(q)$(_make-findBasedir $words)} && ret=0 216 ;; 217 218 (debug) 219 _values -s , 'debug options' \ 220 '(b v i j m)a[all debugging output]' \ 221 'b[basic debugging output]' \ 222 '(b)v[one level above basic]' \ 223 '(b)i[describe implicit rule searches (implies b)]' \ 224 'j[show details on invocation of subcommands]' \ 225 'm[enable debugging while remaking makefiles]' && ret=0 226 ;; 227 228 (target) 229 file=${(v)opt_args[(I)(-f|--file|--makefile)]} 230 if [[ -n $file ]] 231 then 232 [[ $file == [^/]* ]] && file=${(q)$(_make-findBasedir $words)}/$file 233 [[ -r $file ]] || file= 234 else 235 local basedir 236 basedir=${$(_make-findBasedir $words)} 237 if [[ $is_gnu == gnu && -r $basedir/GNUmakefile ]] 238 then 239 file=$basedir/GNUmakefile 240 elif [[ -r $basedir/makefile ]] 241 then 242 file=$basedir/makefile 243 elif [[ -r $basedir/Makefile ]] 244 then 245 file=$basedir/Makefile 246 else 247 file='' 248 fi 249 fi 250 251 if [[ -n "$file" ]] 252 then 253 if [[ $is_gnu == gnu ]] && zstyle -t ":completion:${curcontext}:targets" call-command 254 then 255 _make-parseMakefile $PWD < <(_call_program targets "$words[1]" -nsp --no-print-directory -f "$file" .PHONY 2> /dev/null) 256 else 257 case "$OSTYPE" in 258 freebsd*) 259 _make-parseMakefile $PWD < <(_call_program targets "$words[1]" -nsp -f "$file" .PHONY 2> /dev/null) 260 ;; 261 *) 262 _make-parseMakefile $PWD < $file 263 esac 264 fi 265 fi 266 267 if [[ $PREFIX == *'='* ]] 268 then 269 # Complete make variable as if shell variable 270 compstate[parameter]="${PREFIX%%\=*}" 271 compset -P 1 '*=' 272 _value "$@" && ret=0 273 else 274 _tags targets variables 275 while _tags 276 do 277 _requested targets expl 'make targets' \ 278 compadd -- ${(k)TARGETS} && ret=0 279 _requested variables expl 'make variables' \ 280 compadd -S '=' -- ${(k)VARIABLES} && ret=0 281 done 282 fi 283 esac 284 285 return ret 286} 287 288_make "$@" 289