From: David Malcolm Date: Mon, 2 Feb 2026 23:14:11 +0000 (-0500) Subject: diagnostics: respect dumpdir when outputting SARIF and HTML file [PR110522] X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6fe28020cae53e3703921aedb9f267115a5262d3;p=thirdparty%2Fgcc.git diagnostics: respect dumpdir when outputting SARIF and HTML file [PR110522] PR diagnostics/110522 notes that when generating the output path for diagnostics in SARIF or HTML form, the compiler ignores the path for the compiler output, leading to race conditions and overwritten SARIF output when compiling the same source file multiple times in a build. Given e.g. gcc \ -o build-dir/foo.o \ -fdiagnostics-add-output=sarif foo.c my intent was for the sarif sink to write to "build-dir/foo.c.sarif", but in GCC 13 - GCC 15 it writes to "foo.c.sarif" instead. The driver adds the option "-dumpdir build-dir" when invoking cc1, and opts.cc's finish_options has the logic to prepend the dump_dir_name to the dump_base_name, which in theory ought to have affected the name for SARIF output. The root cause is that finish_options is called *after* processing the options for creating the diagnostic sinks, and so the sarif_sink is created when dump_base_name is "foo.c", and thus writes to "foo.c.sarif", rather than "build-dir/foo.c.sarif". This patch introduces a new helper function for accessing the base name for diagnostic sinks and refactors the prepending logic in finish_options so that it is also used when creating diagnostic sinks, so that in the above example the SARIF is written to "build-dir/foo.c.sarif". The patch took the number of .sarif files generated by the analyzer integration tests from 5094 to 11309. The most extreme example I could find within them was where we previously got a single .sarif file: qemu-7.2.0/build/fpu_softfloat.c.c.sarif and *with* the patch we get 66 .sarif files: qemu-7.2.0/build/libqemu-aarch64-linux-user.fa.p/fpu_softfloat.c.c.sarif qemu-7.2.0/build/libqemu-aarch64-softmmu.fa.p/fpu_softfloat.c.c.sarif [...snip lots of different architectures...] qemu-7.2.0/build/libqemu-xtensaeb-linux-user.fa.p/fpu_softfloat.c.c.sarif qemu-7.2.0/build/libqemu-xtensaeb-softmmu.fa.p/fpu_softfloat.c.c.sarif which presumably were previously all being written to the same place during the build. Indeed, the old file is invalid JSON, and appears to have been truncated. gcc/ChangeLog: PR diagnostics/110522 * gcc.cc (driver_handle_option): Use get_diagnostic_file_output_basename for OPT_fdiagnostics_format_. * opts-diagnostic.cc (get_base_filename): Likewise. (get_diagnostic_file_output_basename): New. * opts-diagnostic.h (get_diagnostic_file_output_basename): New decl. * opts.cc (maybe_prepend_dump_dir_name): New, based on code in finish_options. (finish_options): Move code for determining prepended dump_base_name to maybe_prepend_dump_dir_name and call it. (common_handle_option): Use get_diagnostic_file_output_basename for OPT_fdiagnostics_format_. * opts.h (maybe_prepend_dump_dir_name): New decl. Signed-off-by: David Malcolm --- diff --git a/gcc/gcc.cc b/gcc/gcc.cc index 6b6f6f87c52..2fc743af3d9 100644 --- a/gcc/gcc.cc +++ b/gcc/gcc.cc @@ -4387,8 +4387,7 @@ driver_handle_option (struct gcc_options *opts, case OPT_fdiagnostics_format_: { - const char *basename = (opts->x_dump_base_name ? opts->x_dump_base_name - : opts->x_main_input_basename); + const char *basename = get_diagnostic_file_output_basename (*opts); gcc_assert (dc); diagnostics::output_format_init (*dc, opts->x_main_input_filename, basename, diff --git a/gcc/opts-diagnostic.cc b/gcc/opts-diagnostic.cc index f6062824448..594f735a8c6 100644 --- a/gcc/opts-diagnostic.cc +++ b/gcc/opts-diagnostic.cc @@ -94,9 +94,7 @@ public: const char * get_base_filename () const final override { - return (m_opts.x_dump_base_name - ? m_opts.x_dump_base_name - : m_opts.x_main_input_basename); + return get_diagnostic_file_output_basename (m_opts); } const gcc_options &m_opts; @@ -170,3 +168,24 @@ handle_OPT_fdiagnostics_set_output_ (const gcc_options &opts, if (auto sink = try_to_make_sink (opts, dc, option_name, unparsed_spec, loc)) dc.set_sink (std::move (sink)); } + +/* Return the base name to use when choosing names for output file for + diagnostic sinks (e.g. BASENAME.sarif or BASENAME.html). */ + +const char * +get_diagnostic_file_output_basename (const gcc_options &opts) +{ + /* This might have been called before finish_options, which prepends + the dump dir to the dump base name. If so, make a prepended copy + now and use it. */ + if (opts.x_dump_base_name + && ! opts.x_dump_base_name_prefixed) + if (const char *prepended_dump_base_name + = maybe_prepend_dump_dir_name (opts)) + /* Allocated in opts_obstack. */ + return prepended_dump_base_name; + + return (opts.x_dump_base_name + ? opts.x_dump_base_name + : opts.x_main_input_basename); +} diff --git a/gcc/opts-diagnostic.h b/gcc/opts-diagnostic.h index f496ef04912..d0a8beddd81 100644 --- a/gcc/opts-diagnostic.h +++ b/gcc/opts-diagnostic.h @@ -86,4 +86,8 @@ handle_OPT_fdiagnostics_set_output_ (const gcc_options &opts, diagnostics::context &dc, const char *arg, location_t loc); + +extern const char * +get_diagnostic_file_output_basename (const gcc_options &opts); + #endif diff --git a/gcc/opts.cc b/gcc/opts.cc index 6323fd58801..6658b6acd37 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -1070,6 +1070,37 @@ validate_ipa_reorder_locality_lto_partition (struct gcc_options *opts, validated_p = true; } +/* If OPTS.x_dump_base_name doesn't contain any directory separators + and has not had OPTS.x_dump_dir_name prepended to it, generate + a new string in opts_obstack that has the dump_dir_name prepended to + the dump_base_name. */ + +const char * +maybe_prepend_dump_dir_name (const gcc_options &opts) +{ + const char *sep = opts.x_dump_base_name; + + for (; *sep; sep++) + if (IS_DIR_SEPARATOR (*sep)) + break; + + if (*sep) + { + /* If dump_base_name contains subdirectories, don't prepend + anything. */ + return nullptr; + } + + if (opts.x_dump_dir_name) + { + /* We have a DUMP_DIR_NAME, prepend that. */ + return opts_concat (opts.x_dump_dir_name, + opts.x_dump_base_name, NULL); + } + + return nullptr; +} + /* After all options at LOC have been read into OPTS and OPTS_SET, finalize settings of those options and diagnose incompatible combinations. */ @@ -1080,19 +1111,9 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set, if (opts->x_dump_base_name && ! opts->x_dump_base_name_prefixed) { - const char *sep = opts->x_dump_base_name; - - for (; *sep; sep++) - if (IS_DIR_SEPARATOR (*sep)) - break; - - if (*sep) - /* If dump_base_path contains subdirectories, don't prepend - anything. */; - else if (opts->x_dump_dir_name) - /* We have a DUMP_DIR_NAME, prepend that. */ - opts->x_dump_base_name = opts_concat (opts->x_dump_dir_name, - opts->x_dump_base_name, NULL); + if (const char *prepended_dump_base_name + = maybe_prepend_dump_dir_name (*opts)) + opts->x_dump_base_name = prepended_dump_base_name; /* It is definitely prefixed now. */ opts->x_dump_base_name_prefixed = true; @@ -3023,8 +3044,7 @@ common_handle_option (struct gcc_options *opts, case OPT_fdiagnostics_format_: { - const char *basename = (opts->x_dump_base_name ? opts->x_dump_base_name - : opts->x_main_input_basename); + const char *basename = get_diagnostic_file_output_basename (*opts); gcc_assert (dc); diagnostics::output_format_init (*dc, opts->x_main_input_filename, basename, diff --git a/gcc/opts.h b/gcc/opts.h index a59475b1daf..2d28620edab 100644 --- a/gcc/opts.h +++ b/gcc/opts.h @@ -577,4 +577,7 @@ struct switchstr extern label_text get_option_url_suffix (int option_index, unsigned lang_mask); +extern const char * +maybe_prepend_dump_dir_name (const gcc_options &opts); + #endif