| /* A program for re-emitting diagnostics saved in SARIF form. |
| Copyright (C) 2022-2025 Free Software Foundation, Inc. |
| Contributed by David Malcolm <dmalcolm@redhat.com>. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #define INCLUDE_STRING |
| #define INCLUDE_VECTOR |
| #include "system.h" |
| #include "coretypes.h" |
| #include "version.h" |
| #include "intl.h" |
| #include "libgdiagnostics++.h" |
| #include "libsarifreplay.h" |
| |
| static const char *progname; |
| |
| static void |
| set_defaults (replay_options &replay_opts) |
| { |
| /* Defaults. */ |
| replay_opts.m_echo_file = false; |
| replay_opts.m_json_comments = false; |
| replay_opts.m_verbose = false; |
| replay_opts.m_diagnostics_colorize = DIAGNOSTIC_COLORIZE_IF_TTY; |
| } |
| |
| struct options |
| { |
| options () |
| { |
| set_defaults (m_replay_opts); |
| } |
| |
| replay_options m_replay_opts; |
| std::vector<const char *> m_sarif_filenames; |
| std::vector<std::string> m_extra_output_specs; |
| }; |
| |
| static void |
| print_version () |
| { |
| printf (_("%s %s%s\n"), progname, pkgversion_string, |
| version_string); |
| printf ("Copyright %s 2024-2025 Free Software Foundation, Inc.\n", |
| _("(C)")); |
| fputs (_("This is free software; see the source for copying conditions. There is NO\n\ |
| warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"), |
| stdout); |
| } |
| |
| static const char *const usage_msg = ( |
| "sarif-replay [OPTIONS] FILE+\n" |
| "\n" |
| " \"Replay\" results from one or more .sarif files as if they were\n" |
| " GCC diagnostics\n" |
| "\n" |
| "Options:\n" |
| "\n" |
| " -fdiagnostics-add-output=SCHEME\n" |
| " Add an additional output sink when replaying diagnostics, as\n" |
| " per the gcc option\n" |
| "\n" |
| " -fdiagnostics-color={never|always|auto}\n" |
| " Control colorization of diagnostics. Default: auto.\n" |
| "\n" |
| " -fjson-comments\n" |
| " Support C and C++ style comments in .sarif JSON files\n" |
| "\n" |
| " --verbose\n" |
| " Print notes about each .sarif file before and after replaying it.\n" |
| "\n" |
| " --echo-file\n" |
| " Print the filename and file contents to stderr before replaying it.\n" |
| "\n" |
| " -v, --version\n" |
| " Print version and exit.\n" |
| "\n" |
| " --usage\n" |
| " Print this message and exit.\n" |
| "\n" |
| "Options for maintainers:\n" |
| "\n" |
| " -fdebug-physical-locations\n" |
| " Dump debugging information about physical locations to stderr.\n" |
| "\n"); |
| |
| static void |
| print_usage () |
| { |
| fprintf (stderr, usage_msg); |
| } |
| |
| /* If STR starts with PREFIX, return the rest of STR. |
| Otherwise return nullptr. */ |
| |
| static const char * |
| str_starts_with (const char *str, const char *prefix) |
| { |
| size_t prefix_len = strlen (prefix); |
| if (0 == strncmp (str, prefix, prefix_len)) |
| return str + prefix_len; |
| else |
| return nullptr; |
| } |
| |
| static bool |
| parse_options (int argc, char **argv, |
| options &opts, |
| libgdiagnostics::text_sink control_text_sink) |
| { |
| libgdiagnostics::manager options_mgr; |
| options_mgr.set_tool_name ("sarif-replay"); |
| options_mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_NO/*IF_TTY*/); |
| |
| for (int i = 1; i < argc; ++i) |
| { |
| const char *option = argv[i]; |
| bool handled = false; |
| if (strcmp (option, "-fjson-comments") == 0) |
| { |
| opts.m_replay_opts.m_json_comments = true; |
| handled = true; |
| } |
| else if (strcmp (option, "--verbose") == 0) |
| { |
| opts.m_replay_opts.m_verbose = true; |
| handled = true; |
| } |
| else if (strcmp (option, "--usage") == 0) |
| { |
| print_usage (); |
| exit (0); |
| } |
| else if (strcmp (option, "--echo-file") == 0) |
| { |
| opts.m_replay_opts.m_echo_file = true; |
| handled = true; |
| } |
| #define ADD_OUTPUT_OPTION "-fdiagnostics-add-output=" |
| else if (const char *arg |
| = str_starts_with (option, ADD_OUTPUT_OPTION)) |
| { |
| opts.m_extra_output_specs.push_back (std::string (arg)); |
| handled = true; |
| } |
| else if (strcmp (option, "-fdiagnostics-color=never") == 0) |
| { |
| opts.m_replay_opts.m_diagnostics_colorize = DIAGNOSTIC_COLORIZE_NO; |
| control_text_sink.set_colorize |
| (opts.m_replay_opts.m_diagnostics_colorize); |
| handled = true; |
| } |
| else if (strcmp (option, "-fdiagnostics-color=always") == 0) |
| { |
| opts.m_replay_opts.m_diagnostics_colorize = DIAGNOSTIC_COLORIZE_YES; |
| control_text_sink.set_colorize |
| (opts.m_replay_opts.m_diagnostics_colorize); |
| handled = true; |
| } |
| else if (strcmp (option, "-fdiagnostics-color=auto") == 0) |
| { |
| opts.m_replay_opts.m_diagnostics_colorize |
| = DIAGNOSTIC_COLORIZE_IF_TTY; |
| control_text_sink.set_colorize |
| (opts.m_replay_opts.m_diagnostics_colorize); |
| handled = true; |
| } |
| else if (strcmp (option, "-v") == 0 |
| || strcmp (option, "--version") == 0) |
| { |
| print_version (); |
| exit (0); |
| } |
| else if (strcmp (option, "-fdebug-physical-locations") == 0) |
| { |
| opts.m_replay_opts.m_debug_physical_locations = true; |
| handled = true; |
| } |
| if (!handled) |
| { |
| if (option[0] == '-') |
| { |
| auto err = options_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR); |
| err.finish ("unrecognized option: %qs", option); |
| return false; |
| } |
| else |
| opts.m_sarif_filenames.push_back (option); |
| } |
| } |
| |
| if (opts.m_sarif_filenames.size () == 0) |
| { |
| auto err = options_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR); |
| err.finish ("need at least one .sarif file to dump"); |
| return false; |
| } |
| return true; |
| } |
| |
| static const char * |
| get_progname (const char *argv0) |
| { |
| const char *p = argv0 + strlen (argv0); |
| while (p != argv0 && !IS_DIR_SEPARATOR (p[-1])) |
| --p; |
| return p; |
| } |
| |
| /* Entrypoint to sarif-replay command-line tool. */ |
| |
| int |
| main (int argc, char **argv) |
| { |
| progname = get_progname (argv[0]); |
| xmalloc_set_program_name (progname); |
| |
| libgdiagnostics::manager control_mgr; |
| |
| control_mgr.set_tool_name (progname); |
| |
| libgdiagnostics::text_sink control_text_sink |
| = control_mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_IF_TTY); |
| |
| options opts; |
| if (!parse_options (argc, argv, opts, control_text_sink)) |
| { |
| print_usage (); |
| return -1; |
| } |
| |
| int failures = 0; |
| for (auto filename : opts.m_sarif_filenames) |
| { |
| if (opts.m_replay_opts.m_verbose) |
| { |
| auto note = control_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_NOTE); |
| note.finish ("about to replay %qs...", filename); |
| } |
| libgdiagnostics::manager playback_mgr; |
| playback_mgr.set_debug_physical_locations |
| (opts.m_replay_opts.m_debug_physical_locations); |
| playback_mgr.add_text_sink (stderr, |
| opts.m_replay_opts.m_diagnostics_colorize); |
| for (auto spec : opts.m_extra_output_specs) |
| if (playback_mgr.add_sink_from_spec |
| (ADD_OUTPUT_OPTION, |
| spec.c_str (), |
| libgdiagnostics::manager (control_mgr.m_inner, false))) |
| return -1; |
| |
| int result = sarif_replay_path (filename, |
| playback_mgr.m_inner, |
| control_mgr.m_inner, |
| &opts.m_replay_opts); |
| if (result) |
| ++failures; |
| |
| if (opts.m_replay_opts.m_verbose) |
| { |
| auto note = control_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_NOTE); |
| note.finish ("...finished replaying %qs", filename); |
| } |
| } |
| return failures; |
| } |