X-Git-Url: http://nitlanguage.org diff --git a/src/toolcontext.nit b/src/toolcontext.nit index 4e26321..dd1d2c1 100644 --- a/src/toolcontext.nit +++ b/src/toolcontext.nit @@ -3,6 +3,7 @@ # Copyright 2006-2008 Floréal Morandat # Copyright 2008-2012 Jean Privat # Copyright 2009 Jean-Sebastien Gelinas +# Copyright 2014 Alexandre Terrasa # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,6 +23,7 @@ module toolcontext import opts import location import version +import template class Message super Comparable @@ -90,9 +92,9 @@ class ToolContext for m in messages do if opt_no_color.value then - stderr.write("{m}\n") + sys.stderr.write("{m}\n") else - stderr.write("{m.to_color_string}\n") + sys.stderr.write("{m.to_color_string}\n") end end @@ -134,6 +136,33 @@ class ToolContext end end + # Executes a program while checking if it's available and if the execution ended correctly + # + # Stops execution and prints errors if the program isn't available or didn't end correctly + fun exec_and_check(args: Array[String], error: String) + do + var prog = args.first + args.remove_at 0 + + # Is the wanted program available? + var proc_which = new IProcess.from_a("which", [prog]) + proc_which.wait + var res = proc_which.status + if res != 0 then + print "{error}: executable \"{prog}\" not found" + exit 1 + end + + # Execute the wanted program + var proc = new Process.from_a(prog, args) + proc.wait + res = proc.status + if res != 0 then + print "{error}: execution of \"{prog} {args.join(" ")}\" failed" + exit 1 + end + end + # Global OptionContext var option_context: OptionContext = new OptionContext @@ -155,6 +184,9 @@ class ToolContext # Option --version var opt_version: OptionBool = new OptionBool("Show version and exit", "--version") + # Option --set-dummy-tool + var opt_set_dummy_tool: OptionBool = new OptionBool("Set toolname and version to DUMMY. Useful for testing", "--set-dummy-tool") + # Option --verbose var opt_verbose: OptionCount = new OptionCount("Verbose", "-v", "--verbose") @@ -164,12 +196,15 @@ class ToolContext # Option --no-color var opt_no_color: OptionBool = new OptionBool("Do not use color to display errors and warnings", "--no-color") + # Option --bash-completion + var opt_bash_completion: OptionBool = new OptionBool("Generate bash_completion file for this program", "--bash-completion") + # Verbose level var verbose_level: Int = 0 init do - option_context.add_option(opt_warn, opt_quiet, opt_stop_on_first_error, opt_no_color, opt_log, opt_log_dir, opt_help, opt_version, opt_verbose) + option_context.add_option(opt_warn, opt_quiet, opt_stop_on_first_error, opt_no_color, opt_log, opt_log_dir, opt_help, opt_version, opt_set_dummy_tool, opt_verbose, opt_bash_completion) end # Name, usage and synopsis of the tool. @@ -178,14 +213,14 @@ class ToolContext # A multi-line string is recommmended. # # eg. `"Usage: tool [OPTION]... [FILE]...\nDo some things."` - var tooldescription: String writable = "Usage: [OPTION]... [ARG]..." + var tooldescription: String = "Usage: [OPTION]... [ARG]..." is writable # Does `process_options` should accept an empty sequence of arguments. # ie. nothing except options. # Is `false` by default. # # If required, if should be set by the client before calling `process_options` - var accept_no_arguments writable = false + var accept_no_arguments = false is writable # print the full usage of the tool. # Is called by `process_option` on `--help`. @@ -197,7 +232,7 @@ class ToolContext end # Parse and process the options given on the command line - fun process_options + fun process_options(args: Sequence[String]) do self.opt_warn.value = 1 @@ -210,7 +245,13 @@ class ToolContext end if opt_version.value then - print nit_version + print version + exit 0 + end + + if opt_bash_completion.value then + var bash_completion = new BashCompletion(self) + bash_completion.write_to(sys.stdout) exit 0 end @@ -238,5 +279,105 @@ class ToolContext # Make sure the output directory exists log_directory.mkdir end + + nit_dir = compute_nit_dir + 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 project + var nit_dir: nullable String = null + + private fun compute_nit_dir: nullable String + do + # a environ variable has precedence + var res = "NIT_DIR".environ + if not res.is_empty then return res + + # find the runpath of the program from argv[0] + res = "{sys.program_name.dirname}/.." + if res.file_exists and "{res}/src/nit.nit".file_exists 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 res.file_exists and "{res}/src/nit.nit".file_exists then return res.simplify_path + end + + return null + 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. +# +# $ nitls [TAB][TAB] +# file1.nit file2.nit file3.nit +# +# $ nitls --[TAB][TAB] +# --bash-toolname --keep --path --tree +# --depends --log --project --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 + + init(toolcontext: ToolContext) do + self.toolcontext = toolcontext + end + + 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 option_names != null then + addn " opts=\"{option_names.join(" ")}\"" + addn " if [[ $\{cur\} == -* ]] ; then" + addn " COMPREPLY=( $(compgen -W \"$\{opts\}\" -- $\{cur\}) )" + addn " return 0" + addn " fi" + end + addn "\} &&" + addn "complete -o default -F _{name} {name}" end end