X-Git-Url: http://nitlanguage.org diff --git a/src/toolcontext.nit b/src/toolcontext.nit index fd9ce98..a5b7ceb 100644 --- a/src/toolcontext.nit +++ b/src/toolcontext.nit @@ -24,6 +24,7 @@ import opts import location import version import template +import more_collections # A warning or an error class Message @@ -103,11 +104,41 @@ class ToolContext # Messages private var messages = new Array[Message] - private var message_sorter = new ComparableSorter[Message] + private var message_sorter: Comparator = default_comparator - # Output all current stacked messages. - # If some errors occurred, exits the program. - fun check_errors + # Does an error prevent the program to stop at `check_errors`? + # + # Default to false. + # Set this value to `true` if you need to keep the program going in case of error. + var keep_going = false is writable + + # List of tags per source-file whose warnings are not displayed. + # + # Initially empty, it is up to the toll to fill it. + # The tag "all" means all warnings and advices. + var warning_blacklist = new MultiHashMap[SourceFile, String] + + # Is the source-file of `l` associated with `tag` in `warning_blacklist`? + # + # currently returns `false` if `l` is null or does not have a source-file. + fun is_warning_blacklisted(l: nullable Location, tag: String): Bool + do + if l == null then return false + var f = l.file + if f == null then return false + var tags = warning_blacklist.get_or_null(f) + if tags == null then return false + return tags.has("all") or tags.has(tag) + end + + # Output all current stacked messages and display total error informations + # + # Return true if no errors occurred. + # + # If some errors occurred, the behavior depends on the value of `keep_going`. + # If `keep_going` is false, then the program exits. + # Else, the error count and the warning count are reset and false is returned. + fun check_errors: Bool do if messages.length > 0 then message_sorter.sort(messages) @@ -125,16 +156,20 @@ class ToolContext if error_count > 0 then errors_info - exit(1) + if not keep_going then exit(1) + return false end + return true end - # Display total error informations + # Display (and reset) total error informations fun errors_info do if error_count == 0 and warning_count == 0 then return if opt_no_color.value then return sys.stderr.write "Errors: {error_count}. Warnings: {warning_count}.\n" + error_count = 0 + warning_count = 0 end # Display an error @@ -166,6 +201,7 @@ class ToolContext do if opt_warning.value.has("no-{tag}") then return if not opt_warning.value.has(tag) and opt_warn.value == 0 then return + if is_warning_blacklisted(l, tag) then return messages.add(new Message(l, tag, text)) warning_count = warning_count + 1 if opt_stop_on_first_error.value then check_errors @@ -188,6 +224,7 @@ class ToolContext do if opt_warning.value.has("no-{tag}") then return if not opt_warning.value.has(tag) and opt_warn.value <= 1 then return + if is_warning_blacklisted(l, tag) then return messages.add(new Message(l, tag, text)) warning_count = warning_count + 1 if opt_stop_on_first_error.value then check_errors @@ -205,28 +242,30 @@ class ToolContext # # 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 + do + info("+ {args.join(" ")}", 2) + + var prog = args.first + args.remove_at 0 + + # Is the wanted program available? + var proc_which = new ProcessReader.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 = new OptionContext @@ -246,6 +285,9 @@ class ToolContext # Option --log-dir var opt_log_dir = new OptionString("Directory where to generate log files", "--log-dir") + # Option --nit-dir + var opt_nit_dir = new OptionString("Base directory of the Nit installation", "--nit-dir") + # Option --help var opt_help = new OptionBool("Show Help (This screen)", "-h", "-?", "--help") @@ -275,7 +317,12 @@ class ToolContext init do - option_context.add_option(opt_warn, opt_warning, 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, opt_stub_man) + option_context.add_option(opt_warn, opt_warning, opt_quiet, opt_stop_on_first_error, opt_no_color, opt_log, opt_log_dir, opt_nit_dir, opt_help, opt_version, opt_set_dummy_tool, opt_verbose, opt_bash_completion, opt_stub_man) + + # Hide some internal options + opt_stub_man.hidden = true + opt_bash_completion.hidden = true + opt_set_dummy_tool.hidden = true end # Name, usage and synopsis of the tool. @@ -365,6 +412,8 @@ The Nit language documentation and the source code of its tools and libraries ma exit 1 end + nit_dir = compute_nit_dir + if option_context.rest.is_empty and not accept_no_arguments then print tooldescription print "Use --help for help" @@ -382,7 +431,6 @@ The Nit language documentation and the source code of its tools and libraries ma log_directory.mkdir end - nit_dir = compute_nit_dir end # Get the current `nit_version` or "DUMMY_VERSION" if `--set-dummy-tool` is set. @@ -402,27 +450,54 @@ The Nit language documentation and the source code of its tools and libraries ma end # The identified root directory of the Nit project - var nit_dir: nullable String = null + var nit_dir: String is noinit - private fun compute_nit_dir: nullable String + private fun compute_nit_dir: String do - # a environ variable has precedence - var res = "NIT_DIR".environ - if not res.is_empty then return res + # 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 res.file_exists and "{res}/src/nit.nit".file_exists then return res.simplify_path + 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 res.file_exists and "{res}/src/nit.nit".file_exists then return res.simplify_path + if check_nit_dir(res) then return res.simplify_path end - return null + # 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 @@ -448,10 +523,6 @@ class BashCompletion 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 @@ -472,7 +543,7 @@ class BashCompletion addn " COMPREPLY=()" addn " cur=\"$\{COMP_WORDS[COMP_CWORD]\}\"" addn " prev=\"$\{COMP_WORDS[COMP_CWORD-1]\}\"" - if option_names != null then + if not option_names.is_empty then addn " opts=\"{option_names.join(" ")}\"" addn " if [[ $\{cur\} == -* ]] ; then" addn " COMPREPLY=( $(compgen -W \"$\{opts\}\" -- $\{cur\}) )"