X-Git-Url: http://nitlanguage.org diff --git a/src/toolcontext.nit b/src/toolcontext.nit index dd5014f..5a3e3cb 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 @@ -90,6 +91,24 @@ class Message end end +redef class Location + # Errors and warnings associated to this location. + var messages: nullable Array[Message] + + # Add a message to `self` + # + # See `messages` + private fun add_message(m: Message) + do + var ms = messages + if ms == null then + ms = new Array[Message] + messages = ms + end + ms.add m + end +end + # Global context for tools class ToolContext # Number of errors @@ -101,13 +120,46 @@ class ToolContext # Directory where to generate log files var log_directory: String = "logs" + # Stream in `log_directory` where all info messages are written + var log_info: nullable Writer = null + # Messages private var messages = new Array[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 + # + # 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 total error informations is displayed and the program exits. + # Else, false is returned. + fun check_errors: Bool do if messages.length > 0 then message_sorter.sort(messages) @@ -124,30 +176,39 @@ class ToolContext end if error_count > 0 then - errors_info - exit(1) + if not keep_going then + errors_info + exit(1) + end + return false end + return true end - # Display (and reset) total error informations + # Display 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 - fun error(l: nullable Location, s: String) + # + # Return the message (to add information) + fun error(l: nullable Location, s: String): Message do - messages.add(new Message(l,null,s)) + var m = new Message(l,null,s) + if l != null then l.add_message m + messages.add m error_count = error_count + 1 if opt_stop_on_first_error.value then check_errors + return m end # Add an error, show errors and quit + # + # Because the program will quit, nothing is returned. fun fatal_error(l: nullable Location, s: String) do error(l,s) @@ -164,13 +225,19 @@ class ToolContext # * They always are real issues (no false positive) # # First-level warnings are displayed by default (except if option `-q` is given). - fun warning(l: nullable Location, tag: String, text: String) + # + # Return the message (to add information) or null if the warning is disabled + fun warning(l: nullable Location, tag: String, text: String): nullable Message do - if opt_warning.value.has("no-{tag}") then return - if not opt_warning.value.has(tag) and opt_warn.value == 0 then return - messages.add(new Message(l, tag, text)) + if opt_warning.value.has("no-{tag}") then return null + if not opt_warning.value.has(tag) and opt_warn.value == 0 then return null + if is_warning_blacklisted(l, tag) then return null + var m = new Message(l, tag, text) + if l != null then l.add_message m + messages.add m warning_count = warning_count + 1 if opt_stop_on_first_error.value then check_errors + return m end # Display a second-level warning. @@ -186,13 +253,19 @@ class ToolContext # # In order to prevent warning inflation à la Java, second-level warnings are not displayed by # default and require an additional option `-W`. - fun advice(l: nullable Location, tag: String, text: String) + # + # Return the message (to add information) or null if the warning is disabled + fun advice(l: nullable Location, tag: String, text: String): nullable Message do - if opt_warning.value.has("no-{tag}") then return - if not opt_warning.value.has(tag) and opt_warn.value <= 1 then return - messages.add(new Message(l, tag, text)) + if opt_warning.value.has("no-{tag}") then return null + if not opt_warning.value.has(tag) and opt_warn.value <= 1 then return null + if is_warning_blacklisted(l, tag) then return null + var m = new Message(l, tag, text) + if l != null then l.add_message m + messages.add m warning_count = warning_count + 1 if opt_stop_on_first_error.value then check_errors + return m end # Display an info @@ -201,34 +274,40 @@ class ToolContext if level <= verbose_level then print "{s}" end + if log_info != null then + log_info.write s + log_info.write "\n" + 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 + 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 @@ -392,8 +471,10 @@ The Nit language documentation and the source code of its tools and libraries ma if opt_log.value then # Make sure the output directory exists log_directory.mkdir - end + # 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.