Created
April 24, 2026 20:52
-
-
Save valgur/bbc47efa1b64fbf86c63f4f161f8b1c7 to your computer and use it in GitHub Desktop.
Bash completions for Conan
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Bash completion for Conan 2.x CLI | |
| # Save under ~/.local/share/bash-completion/completions/conan | |
| # | |
| # Features: | |
| # - All subcommands and their options | |
| # - --version: versions from $PWD/conandata.yml | |
| # - -o/-o:a/-o:b/-o:h: options from $PWD/conanfile.py (binary choices | |
| # auto-filled with non-default value); -o:a/-o:b/-o:h also offer *:shared=True | |
| # - -pr/-pr:a/-pr:b/-pr:h: profiles from ~/.conan2/profiles/ | |
| # - -c/-c:a/-c:b/-c:h: config keys from `conan config list` | |
| # - -s/-s:a/-s:b/-s:h: common setting keys (os, arch, compiler.*, cuda.*) | |
| # - -r/--remote: remote names from `conan remote list` | |
| # - conan remove: package refs from local cache | |
| # | |
| # SPDX-License-Identifier: CC0-1.0 | |
| _conan_versions_from_conandata() { | |
| local f="$PWD/conandata.yml" | |
| [[ -f "$f" ]] || return | |
| sed -n '/^sources:/,/^[^ ]/{s/^ "\(.*\)":/\1/p}' "$f" 2>/dev/null | |
| } | |
| _conan_options_from_conanfile() { | |
| local f="$PWD/conanfile.py" | |
| [[ -f "$f" ]] || return | |
| python3 -c " | |
| import re, ast, sys | |
| with open('$f') as f: | |
| c = f.read() | |
| mo = re.search(r'options\s*=\s*(\{[^}]+\})', c) | |
| md = re.search(r'default_options\s*=\s*(\{[^}]+\})', c) | |
| if not mo: sys.exit() | |
| opts = ast.literal_eval(mo.group(1)) | |
| defs = ast.literal_eval(md.group(1)) if md else {} | |
| for name, vals in opts.items(): | |
| d = defs.get(name) | |
| if len(vals) == 2 and d is not None: | |
| other = vals[1] if vals[0] == d else vals[0] | |
| print(f'{name}={other}') | |
| else: | |
| print(f'{name}=') | |
| " 2>/dev/null | |
| } | |
| _conan_profiles() { | |
| local dir="${CONAN_HOME:-$HOME/.conan2}/profiles" | |
| [[ -d "$dir" ]] && command ls "$dir" 2>/dev/null | |
| } | |
| _conan_conf_keys() { | |
| conan config list 2>/dev/null | sed -n 's/^\([a-z][a-z.:_]*\):.*/\1/p' | |
| } | |
| _conan_remotes() { | |
| conan remote list 2>/dev/null | sed -n 's/:.*//p' | |
| } | |
| _conan_cached_refs() { | |
| conan list --format=compact 2>/dev/null | sed -n 's/^ //p' | |
| } | |
| _conan_complete_option() { | |
| local cur="$1" include_star="$2" | |
| local opts completions="" | |
| if [[ "$include_star" == "1" ]]; then | |
| completions="*:shared=True" | |
| fi | |
| opts=$(_conan_options_from_conanfile) | |
| if [[ -n "$opts" ]]; then | |
| [[ -n "$completions" ]] && completions+=$'\n' | |
| completions+="$opts" | |
| fi | |
| [[ -z "$completions" ]] && return | |
| if [[ "$cur" == *=* ]]; then | |
| return | |
| fi | |
| COMPREPLY=($(compgen -W "$completions" -- "$cur")) | |
| compopt -o nospace | |
| } | |
| _conan() { | |
| local cur prev words cword | |
| _init_completion || return | |
| local commands="cache config graph inspect install list lock pkglist profile remote remove require run search version workspace build create download editable export export-pkg new source test upload audit report" | |
| local common_build_opts="-h --help -f --format -v -b --build -r --remote -nr --no-remote -u --update -pr -pr:b -pr:h -pr:a -o -o:b -o:h -o:a -s -s:b -s:h -s:a -c -c:b -c:h -c:a --name --version --user --channel -l --lockfile" | |
| # Find the subcommand | |
| local subcmd="" | |
| local i | |
| for ((i = 1; i < cword; i++)); do | |
| case "${words[i]}" in | |
| -*) continue ;; | |
| *) | |
| if [[ " $commands " == *" ${words[i]} "* ]]; then | |
| subcmd="${words[i]}" | |
| break | |
| fi | |
| ;; | |
| esac | |
| done | |
| # Handle option arguments | |
| case "$prev" in | |
| --version) | |
| if [[ -n "$subcmd" ]]; then | |
| local versions | |
| versions=$(_conan_versions_from_conandata) | |
| if [[ -n "$versions" ]]; then | |
| COMPREPLY=($(compgen -W "$versions" -- "$cur")) | |
| return | |
| fi | |
| fi | |
| ;; | |
| -o) | |
| _conan_complete_option "$cur" 0 | |
| return | |
| ;; | |
| -o:b|-o:h|-o:a) | |
| _conan_complete_option "$cur" 1 | |
| return | |
| ;; | |
| -pr|-pr:b|-pr:h|-pr:a|--profile) | |
| local profiles | |
| profiles=$(_conan_profiles) | |
| if [[ -n "$profiles" ]]; then | |
| COMPREPLY=($(compgen -W "$profiles" -- "$cur")) | |
| fi | |
| return | |
| ;; | |
| -c|-c:b|-c:h|-c:a|--conf) | |
| local keys | |
| keys=$(_conan_conf_keys) | |
| if [[ -n "$keys" ]]; then | |
| COMPREPLY=($(compgen -W "$keys" -- "$cur")) | |
| compopt -o nospace | |
| fi | |
| return | |
| ;; | |
| -b|--build) | |
| COMPREPLY=($(compgen -W "missing" -- "$cur")) | |
| return | |
| ;; | |
| -r|--remote) | |
| local remotes | |
| remotes=$(_conan_remotes) | |
| [[ -n "$remotes" ]] && COMPREPLY=($(compgen -W "$remotes" -- "$cur")) | |
| return | |
| ;; | |
| -s|-s:b|-s:h|-s:a) | |
| COMPREPLY=($(compgen -W "os= arch= build_type= compiler= compiler.version= compiler.cppstd= compiler.cstd= compiler.libcxx= compiler.runtime= cuda= cuda.version= cuda.architectures=" -- "$cur")) | |
| compopt -o nospace | |
| return | |
| ;; | |
| -d|--deployer) | |
| COMPREPLY=($(compgen -W "full_deploy direct_deploy runtime_deploy" -- "$cur")) | |
| return | |
| ;; | |
| -g|--generator) | |
| COMPREPLY=($(compgen -W "CMakeDeps CMakeToolchain MSBuildDeps MSBuildToolchain MesonToolchain PkgConfigDeps" -- "$cur")) | |
| return | |
| ;; | |
| -v) | |
| COMPREPLY=($(compgen -W "quiet error warning notice status verbose debug trace" -- "$cur")) | |
| return | |
| ;; | |
| esac | |
| # Handle --build=, -b= style (equals-attached) | |
| if [[ "$cur" == --build=* ]]; then | |
| COMPREPLY=($(compgen -W "--build=missing" -- "$cur")) | |
| return | |
| fi | |
| if [[ -z "$subcmd" ]]; then | |
| if [[ "$cur" == -* ]]; then | |
| COMPREPLY=($(compgen -W "-h --help -v --version" -- "$cur")) | |
| else | |
| COMPREPLY=($(compgen -W "$commands" -- "$cur")) | |
| fi | |
| return | |
| fi | |
| # Per-subcommand completions | |
| case "$subcmd" in | |
| install) | |
| COMPREPLY=($(compgen -W "$common_build_opts -g --generator -of --output-folder -d --deployer --requires --tool-requires" -- "$cur")) | |
| ;; | |
| create) | |
| COMPREPLY=($(compgen -W "$common_build_opts -tf --test-folder -tm --test-missing -bt --build-test --build-require" -- "$cur")) | |
| ;; | |
| build) | |
| COMPREPLY=($(compgen -W "$common_build_opts -g --generator -of --output-folder -d --deployer" -- "$cur")) | |
| ;; | |
| export) | |
| COMPREPLY=($(compgen -W "-h --help -f --format -v --name --version --user --channel -r --remote -nr --no-remote -l --lockfile" -- "$cur")) | |
| ;; | |
| test) | |
| COMPREPLY=($(compgen -W "$common_build_opts" -- "$cur")) | |
| ;; | |
| source) | |
| COMPREPLY=($(compgen -W "-h --help -v --name --version --user --channel" -- "$cur")) | |
| ;; | |
| inspect) | |
| COMPREPLY=($(compgen -W "-h --help -f --format -v" -- "$cur")) | |
| ;; | |
| search) | |
| COMPREPLY=($(compgen -W "-h --help -f --format -v -r --remote" -- "$cur")) | |
| ;; | |
| list) | |
| if [[ "$cur" == -* ]]; then | |
| COMPREPLY=($(compgen -W "-h --help -f --format -v -r --remote -p --package-query" -- "$cur")) | |
| fi | |
| ;; | |
| upload) | |
| if [[ "$cur" == -* ]]; then | |
| COMPREPLY=($(compgen -W "-h --help -f --format -v -r --remote --check --confirm -p --package-query -l --lockfile --only-recipe" -- "$cur")) | |
| fi | |
| ;; | |
| download) | |
| if [[ "$cur" == -* ]]; then | |
| COMPREPLY=($(compgen -W "-h --help -f --format -v -r --remote -p --package-query -l --lockfile" -- "$cur")) | |
| fi | |
| ;; | |
| remove) | |
| if [[ "$cur" == -* ]]; then | |
| COMPREPLY=($(compgen -W "-h --help -f --format -v -r --remote --confirm -p --package-query -l --lockfile" -- "$cur")) | |
| else | |
| local refs | |
| refs=$(_conan_cached_refs) | |
| [[ -n "$refs" ]] && COMPREPLY=($(compgen -W "$refs" -- "$cur")) | |
| fi | |
| ;; | |
| graph) | |
| # Find graph subcommand | |
| local graphsub="" | |
| for ((i = 2; i < cword; i++)); do | |
| case "${words[i]}" in | |
| info|build-order|build-order-merge|explain|outdated) | |
| graphsub="${words[i]}" | |
| break ;; | |
| esac | |
| done | |
| if [[ -z "$graphsub" ]]; then | |
| COMPREPLY=($(compgen -W "info build-order build-order-merge explain outdated" -- "$cur")) | |
| elif [[ "$cur" == -* ]]; then | |
| COMPREPLY=($(compgen -W "$common_build_opts -f --format" -- "$cur")) | |
| fi | |
| ;; | |
| profile) | |
| local profsub="" | |
| for ((i = 2; i < cword; i++)); do | |
| case "${words[i]}" in | |
| detect|list|path|show) profsub="${words[i]}"; break ;; | |
| esac | |
| done | |
| if [[ -z "$profsub" ]]; then | |
| COMPREPLY=($(compgen -W "detect list path show" -- "$cur")) | |
| elif [[ "$profsub" == "show" || "$profsub" == "path" ]]; then | |
| local profiles | |
| profiles=$(_conan_profiles) | |
| [[ -n "$profiles" ]] && COMPREPLY=($(compgen -W "$profiles -h --help" -- "$cur")) | |
| else | |
| COMPREPLY=($(compgen -W "-h --help" -- "$cur")) | |
| fi | |
| ;; | |
| remote) | |
| local remsub="" | |
| for ((i = 2; i < cword; i++)); do | |
| case "${words[i]}" in | |
| add|disable|enable|list|login|logout|remove|rename|update) remsub="${words[i]}"; break ;; | |
| esac | |
| done | |
| if [[ -z "$remsub" ]]; then | |
| COMPREPLY=($(compgen -W "add disable enable list login logout remove rename update" -- "$cur")) | |
| else | |
| COMPREPLY=($(compgen -W "-h --help" -- "$cur")) | |
| fi | |
| ;; | |
| config) | |
| local confsub="" | |
| for ((i = 2; i < cword; i++)); do | |
| case "${words[i]}" in | |
| home|install|install-pkg|list|show) confsub="${words[i]}"; break ;; | |
| esac | |
| done | |
| if [[ -z "$confsub" ]]; then | |
| COMPREPLY=($(compgen -W "home install install-pkg list show" -- "$cur")) | |
| else | |
| COMPREPLY=($(compgen -W "-h --help" -- "$cur")) | |
| fi | |
| ;; | |
| cache) | |
| local cachesub="" | |
| for ((i = 2; i < cword; i++)); do | |
| case "${words[i]}" in | |
| check-integrity|clean|path|save|restore) cachesub="${words[i]}"; break ;; | |
| esac | |
| done | |
| if [[ -z "$cachesub" ]]; then | |
| COMPREPLY=($(compgen -W "check-integrity clean path save restore" -- "$cur")) | |
| else | |
| COMPREPLY=($(compgen -W "-h --help" -- "$cur")) | |
| fi | |
| ;; | |
| new) | |
| if [[ "$cur" == -* ]]; then | |
| COMPREPLY=($(compgen -W "-h --help -v -d --define -f --force" -- "$cur")) | |
| fi | |
| ;; | |
| editable) | |
| local edsub="" | |
| for ((i = 2; i < cword; i++)); do | |
| case "${words[i]}" in | |
| add|remove|list) edsub="${words[i]}"; break ;; | |
| esac | |
| done | |
| if [[ -z "$edsub" ]]; then | |
| COMPREPLY=($(compgen -W "add remove list" -- "$cur")) | |
| else | |
| COMPREPLY=($(compgen -W "-h --help" -- "$cur")) | |
| fi | |
| ;; | |
| lock) | |
| local locksub="" | |
| for ((i = 2; i < cword; i++)); do | |
| case "${words[i]}" in | |
| add|create|merge|remove|update) locksub="${words[i]}"; break ;; | |
| esac | |
| done | |
| if [[ -z "$locksub" ]]; then | |
| COMPREPLY=($(compgen -W "add create merge remove update" -- "$cur")) | |
| else | |
| COMPREPLY=($(compgen -W "-h --help" -- "$cur")) | |
| fi | |
| ;; | |
| *) | |
| COMPREPLY=($(compgen -W "-h --help" -- "$cur")) | |
| ;; | |
| esac | |
| } | |
| complete -F _conan conan |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment