X-Git-Url: http://nitlanguage.org diff --git a/src/toolcontext.nit b/src/toolcontext.nit index e78ec77..5b9d020 100644 --- a/src/toolcontext.nit +++ b/src/toolcontext.nit @@ -17,18 +17,20 @@ # limitations under the License. # Common command-line tool infractructure than handle options and error messages -package toolcontext +module toolcontext import opts import location +import version class Message super Comparable redef type OTHER: Message - readable var _location: nullable Location - readable var _text: String + var location: nullable Location + var text: String + # Comparisons are made on message locations. redef fun <(other: OTHER): Bool do if location == null then return true if other.location == null then return false @@ -69,32 +71,32 @@ end # Global context for tools class ToolContext # Number of errors - readable var _error_count: Int = 0 + var error_count: Int = 0 # Number of warnings - readable var _warning_count: Int = 0 + var warning_count: Int = 0 # Directory where to generate log files - readable var _log_directory: String = "logs" + var log_directory: String = "logs" # Messages - var _messages: Array[Message] = new Array[Message] - var _message_sorter: ComparableSorter[Message] = new ComparableSorter[Message] + private var messages: Array[Message] = new Array[Message] + private var message_sorter: ComparableSorter[Message] = new ComparableSorter[Message] fun check_errors do - if _messages.length > 0 then - _message_sorter.sort(_messages) + if messages.length > 0 then + message_sorter.sort(messages) - for m in _messages do + 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 - _messages.clear + messages.clear end if error_count > 0 then exit(1) @@ -103,8 +105,8 @@ class ToolContext # Display an error fun error(l: nullable Location, s: String) do - _messages.add(new Message(l,s)) - _error_count = _error_count + 1 + messages.add(new Message(l,s)) + error_count = error_count + 1 if opt_stop_on_first_error.value then check_errors end @@ -118,9 +120,9 @@ class ToolContext # Display a warning fun warning(l: nullable Location, s: String) do - if _opt_warn.value == 0 then return - _messages.add(new Message(l,s)) - _warning_count = _warning_count + 1 + if opt_warn.value == 0 then return + messages.add(new Message(l,s)) + warning_count = warning_count + 1 if opt_stop_on_first_error.value then check_errors end @@ -132,61 +134,162 @@ 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 - readable var _option_context: OptionContext = new OptionContext + var option_context: OptionContext = new OptionContext # Option --warn - readable var _opt_warn: OptionCount = new OptionCount("Show warnings", "-W", "--warn") + var opt_warn: OptionCount = new OptionCount("Show warnings", "-W", "--warn") # Option --quiet - readable var _opt_quiet: OptionBool = new OptionBool("Do not show warnings", "-q", "--quiet") + var opt_quiet: OptionBool = new OptionBool("Do not show warnings", "-q", "--quiet") # Option --log - readable var _opt_log: OptionBool = new OptionBool("Generate various log files", "--log") + var opt_log: OptionBool = new OptionBool("Generate various log files", "--log") # Option --log-dir - readable var _opt_log_dir: OptionString = new OptionString("Directory where to generate log files", "--log-dir") + var opt_log_dir: OptionString = new OptionString("Directory where to generate log files", "--log-dir") # Option --help - readable var _opt_help: OptionBool = new OptionBool("Show Help (This screen)", "-h", "-?", "--help") + var opt_help: OptionBool = new OptionBool("Show Help (This screen)", "-h", "-?", "--help") # Option --version - readable var _opt_version: OptionBool = new OptionBool("Show version and exit", "--version") + var opt_version: OptionBool = new OptionBool("Show version and exit", "--version") # Option --verbose - readable var _opt_verbose: OptionCount = new OptionCount("Verbose", "-v", "--verbose") + var opt_verbose: OptionCount = new OptionCount("Verbose", "-v", "--verbose") # Option --stop-on-first-error - readable var _opt_stop_on_first_error: OptionBool = new OptionBool("Stop on first error", "--stop-on-first-error") + var opt_stop_on_first_error: OptionBool = new OptionBool("Stop on first error", "--stop-on-first-error") # Option --no-color - readable var _opt_no_color: OptionBool = new OptionBool("Do not use color to display errors and warnings", "--no-color") + var opt_no_color: OptionBool = new OptionBool("Do not use color to display errors and warnings", "--no-color") # Verbose level - readable var _verbose_level: Int = 0 + 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) end + # Name, usage and synopsis of the tool. + # It is mainly used in `usage`. + # Should be correctly set by the client before calling `process_options` + # A multi-line string is recommmended. + # + # eg. `"Usage: tool [OPTION]... [FILE]...\nDo some things."` + var tooldescription: String writable = "Usage: [OPTION]... [ARG]..." + + # 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 + + # print the full usage of the tool. + # Is called by `process_option` on `--help`. + # It also could be called by the client. + fun usage + do + print tooldescription + option_context.usage + 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 # init options option_context.parse(args) + if opt_help.value then + usage + exit 0 + end + + if opt_version.value then + print nit_version + exit 0 + end + + var errors = option_context.get_errors + if not errors.is_empty then + for e in errors do print "Error: {e}" + print tooldescription + print "Use --help for help" + exit 1 + end + + if option_context.rest.is_empty and not accept_no_arguments then + print tooldescription + print "Use --help for help" + exit 1 + end + # Set verbose level - _verbose_level = opt_verbose.value + verbose_level = opt_verbose.value if self.opt_quiet.value then self.opt_warn.value = 0 - if opt_log_dir.value != null then _log_directory = opt_log_dir.value.as(not null) - if _opt_log.value then + if opt_log_dir.value != null then log_directory = opt_log_dir.value.as(not null) + if opt_log.value then # Make sure the output directory exists log_directory.mkdir end + + nit_dir = compute_nit_dir + end + + # The identified root directory of the Nit project + var nit_dir: nullable String + + 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