+
+ # Redirect the verbose messages
+ log_info = (log_directory/"info.txt").to_path.open_wo
+ end
+ end
+
+ # Get the current `nit_version` or "DUMMY_VERSION" if `--set-dummy-tool` is set.
+ fun version: String do
+ if opt_set_dummy_tool.value then
+ return "DUMMY_VERSION"
+ end
+ return nit_version
+ end
+
+ # Get the name of the tool or "DUMMY_TOOL" id `--set-dummy-tool` is set.
+ fun toolname: String do
+ if opt_set_dummy_tool.value then
+ return "DUMMY_TOOL"
+ end
+ return sys.program_name.basename
+ end
+
+ # The identified root directory of the Nit package
+ var nit_dir: String is noinit
+
+ # Shared files directory.
+ #
+ # Most often `nit/share/`.
+ var share_dir: String is lazy do
+ var sharedir = opt_share_dir.value
+ if sharedir == null then
+ sharedir = nit_dir / "share"
+ if not sharedir.file_exists then
+ fatal_error(null, "Fatal Error: cannot locate shared files directory in {sharedir}. Uses --share-dir to define it's location.")
+ end
+ end
+ return sharedir
+ end
+
+ private fun compute_nit_dir: String
+ do
+ # the option has precedence
+ var res = opt_nit_dir.value
+ if res != null then
+ if not check_nit_dir(res) then
+ fatal_error(null, "Fatal Error: the value of --nit-dir does not seem to be a valid base Nit directory: {res}.")
+ end
+ return res
+ end
+
+ # then the environ variable has precedence
+ res = "NIT_DIR".environ
+ if not res.is_empty then
+ if not check_nit_dir(res) then
+ fatal_error(null, "Fatal Error: the value of NIT_DIR does not seem to be a valid base Nit directory: {res}.")
+ end
+ return res
+ end
+
+ # find the runpath of the program from argv[0]
+ res = "{sys.program_name.dirname}/.."
+ if check_nit_dir(res) then return res.simplify_path
+
+ # find the runpath of the process from /proc
+ var exe = "/proc/self/exe"
+ if exe.file_exists then
+ res = exe.realpath
+ res = res.dirname.join_path("..")
+ if check_nit_dir(res) then return res.simplify_path
+ end
+
+ # search in the PATH
+ var ps = "PATH".environ.split(":")
+ for p in ps do
+ res = p/".."
+ if check_nit_dir(res) then return res.simplify_path
+ end
+
+ fatal_error(null, "Fatal Error: cannot locate a valid base Nit directory. It is quite unexpected. Try to set the environment variable `NIT_DIR` or to use the `--nit-dir` option.")
+ abort
+ end
+
+ private fun check_nit_dir(res: String): Bool
+ do
+ return res.file_exists and "{res}/src/nit.nit".file_exists
+ end
+end
+
+# This class generates a compatible `bash_completion` script file.
+#
+# On some Linux systems `bash_completion` allow the program to control command line behaviour.
+#
+# ~~~sh
+# $ nitls [TAB][TAB]
+# file1.nit file2.nit file3.nit
+#
+# $ nitls --[TAB][TAB]
+# --bash-toolname --keep --path --tree
+# --depends --log --package --verbose
+# --disable-phase --log-dir --quiet --version
+# --gen-bash-completion --no-color --recursive --warn
+# --help --only-metamodel --source
+# --ignore-visibility --only-parse --stop-on-first-error
+# ~~~
+#
+# Generated file can be placed in system bash_completion directory `/etc/bash_completion.d/`
+# or source it in `~/.bash_completion`.
+class BashCompletion
+ super Template
+
+ var toolcontext: ToolContext
+
+ private fun extract_options_names: Array[String] do
+ var names = new Array[String]
+ for option in toolcontext.option_context.options do
+ for name in option.names do
+ if name.has_prefix("--") then names.add name
+ end
+ end
+ return names
+ end
+
+ redef fun rendering do
+ var name = toolcontext.toolname
+ var option_names = extract_options_names
+ addn "# generated bash completion file for {name} {toolcontext.version}"
+ addn "_{name}()"
+ addn "\{"
+ addn " local cur prev opts"
+ addn " COMPREPLY=()"
+ addn " cur=\"$\{COMP_WORDS[COMP_CWORD]\}\""
+ addn " prev=\"$\{COMP_WORDS[COMP_CWORD-1]\}\""
+ if not option_names.is_empty then
+ addn " opts=\"{option_names.join(" ")}\""
+ addn " if [[ $\{cur\} == -* ]] ; then"
+ addn " COMPREPLY=( $(compgen -W \"$\{opts\}\" -- $\{cur\}) )"
+ addn " return 0"
+ addn " fi"