[PATCH, doc RFA] New option -B: simplify running gdb from build directory

Doug Evans
2014-03-06T21:41:28Z

Doug Evans writes:
> Hi.
>
> The "make run" Makefile rule simplifies running gdb from the shell,
> but it doesn't simplify running gdb from gdb: I'm always typing
> "--data-directory=$(pwd)/data-directory".
> And since we can't agree on a way to let gdb auto-detect being run
> from the build directory, how about this?
>
> This patch provides a new option, -B, which if provided tells gdb
> it *may* have been run from the build directory. If -B was provided
> and --data-directory was not provided, use a heuristic to determine
> if gdb was run from the build directory and if so set gdb_datadir
> appropriately.
>
> We can certainly discuss what's the best heuristic to use here.
> We don't, IMO, have to employ too complicated a heuristic because
> the user has explicitly passed -B.
> OTOH, I'm sure one can improve a bit on the heuristic that's here.
> One thought I have is to have the Makefile create a file with a name that
> is reasonably obscure (maybe even pseudo-cryptographically secure and encode
> the name in gdb?), and then simplify the test by just checking for that file.
>
> If we can agree on this approach, I'll add NEWS and docs.

I believe I've addressed Eli's comments,
[ref: https://sourceware.org/ml/gdb-patches/2013-12/msg00969.html]
and I didn't see any other discussion, so here's the patch.

2014-03-06 Doug Evans <dje@google.com>

New option -B.
* NEWS: Mention it.
* main.c (maybe_run_from_builddir): New static global.
(get_build_data_directory): New function.
(captured_main): New enum value OPT_BATCH_SILENT. Use it instead of
'B' for --batch-silent. Recognize and handle -B option.

doc/
* gdb.texinfo (Mode Options): Document -B.

testsuite/
* gdb.base/dash-b.exp: New file.

diff --git a/gdb/NEWS b/gdb/NEWS
index 2a384ba..a1561e7 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,11 @@

*** Changes since GDB 7.7

+* New command line options:
+
+-B Indicates GDB may have been run from its build directory, and GDB
+ should first look for its ancillary files there.
+
* Guile scripting

GDB now has support for scripting using Guile. Whether this is
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index de5ac63..ab6a643 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -1169,6 +1169,18 @@ Run @value{GDBN} using @var{directory} as its data directory.
The data directory is where @value{GDBN} searches for its
auxiliary files. @xref{Data Files}.

+@item -B
+@cindex @code{-B}
+This option tells @value{GDBN} it may have been run from its build directory,
+and it should first look for its auxiliary files there. @xref{Data Files}.
+If @value{GDBN} cannot determine it has been run from its build directory,
+then it will expect to find its auxiliary files in their installed
+location as specified by the @code{--prefix} option when @value{GDBN} was
+configured.
+
+This option is ignored if the data directory has been explicitly specified
+with @code{--data-directory}.
+
@item -fullname
@itemx -f
@cindex @code{--fullname}
diff --git a/gdb/main.c b/gdb/main.c
index 59015f5..536f9b7 100644
--- a/gdb/main.c
+++ b/gdb/main.c
@@ -57,6 +57,9 @@ int xdb_commands = 0;
/* Whether dbx commands will be handled. */
int dbx_commands = 0;

+/* Whether GDB may have been run from the build directory. */
+static int maybe_run_from_builddir = 0;
+
/* System root path, used to find libraries etc. */
char *gdb_sysroot = 0;

@@ -160,6 +163,60 @@ relocate_gdb_directory (const char *initial, int flag)
return dir;
}

+/* Return the path of the data directory in the build tree if we can
+ reasonably determine that gdb was run from the build directory.
+ Otherwise return NULL.
+ Space for the result is obtained with malloc, caller must free.
+
+ We say "reasonably" because gdb *could* be run from a place that *looks*
+ like the build directory, and we want to avoid false positives as much as
+ is reasonably possible. This code is only used if the user passes -B to
+ gdb so our tests needn't be *perfect*.
+
+ The test is done by seeing if various files that we expect to find in a
+ build directory exist. There are a few constraints on our choices:
+
+ 1) We don't test for object files so that we don't have to deal with the
+ complexity of different suffixes (e.g., .o vs .obj).
+ 2) We can't (easily) test for files that only exist for a particular
+ configuration.
+
+ In the end we choose three files from the data-directory since it's the
+ data-directory that we need to find:
+ 1) Makefile - only exists in build tree,
+ 2) stamp-syscalls - only exists in build tree that has been built,
+ 3) syscalls/amd64-linux.xml - gdb needs this
+ (installed regardless of platform so it doesn't matter if the current
+ platform is not amd64-linux). */
+
+static char *
+get_build_data_directory (void)
+{
+ char *gdb_dirname = ldirname (gdb_program_name);
+ char *full_gdb_dirname = gdb_realpath (gdb_dirname);
+ char *datadir = concat (full_gdb_dirname, SLASH_STRING "data-directory",
+ NULL);
+ char *test_file1 = concat (datadir, SLASH_STRING, "Makefile", NULL);
+ char *test_file2 = concat (datadir, SLASH_STRING, "stamp-syscalls", NULL);
+ char *test_file3 = concat (datadir, SLASH_STRING, "syscalls",
+ SLASH_STRING, "amd64-linux.xml", NULL);
+ int builddir_found;
+
+ builddir_found = (access (test_file1, R_OK) == 0
+ && access (test_file2, R_OK) == 0
+ && access (test_file3, R_OK) == 0);
+ xfree (gdb_dirname);
+ xfree (full_gdb_dirname);
+ xfree (test_file1);
+ xfree (test_file2);
+ xfree (test_file3);
+
+ if (builddir_found)
+ return datadir;
+ xfree (datadir);
+ return NULL;
+}
+
/* Compute the locations of init files that GDB should source and
return them in SYSTEM_GDBINIT, HOME_GDBINIT, LOCAL_GDBINIT. If
there is no system gdbinit (resp. home gdbinit and local gdbinit)
@@ -461,7 +518,8 @@ captured_main (void *data)
OPT_NOWINDOWS,
OPT_WINDOWS,
OPT_IX,
- OPT_IEX
+ OPT_IEX,
+ OPT_BATCH_SILENT
};
static struct option long_options[] =
{
@@ -476,7 +534,7 @@ captured_main (void *data)
{"nh", no_argument, &inhibit_home_gdbinit, 1},
{"nx", no_argument, &inhibit_gdbinit, 1},
{"n", no_argument, &inhibit_gdbinit, 1},
- {"batch-silent", no_argument, 0, 'B'},
+ {"batch-silent", no_argument, 0, OPT_BATCH_SILENT},
{"batch", no_argument, &batch_flag, 1},

/* This is a synonym for "--annotate=1". --annotate is now
@@ -530,6 +588,7 @@ captured_main (void *data)
{"args", no_argument, &set_args, 1},
{"l", required_argument, 0, 'l'},
{"return-child-result", no_argument, &return_child_result, 1},
+ {"B", no_argument, &maybe_run_from_builddir, 1},
{0, no_argument, 0, 0}
};

@@ -636,7 +695,7 @@ captured_main (void *data)
VEC_safe_push (cmdarg_s, cmdarg_vec, &cmdarg);
}
break;
- case 'B':
+ case OPT_BATCH_SILENT:
batch_flag = batch_silent = 1;
gdb_stdout = ui_file_new();
break;
@@ -742,6 +801,20 @@ captured_main (void *data)
quiet = 1;
}

+ /* If -B was provided and --data-directory was not, then see if we have been
+ run from the build directory and if so use the data-directory there. */
+ if (maybe_run_from_builddir && !gdb_datadir_provided)
+ {
+ char *builddir = get_build_data_directory ();
+
+ if (builddir != NULL)
+ {
+ xfree (gdb_datadir);
+ gdb_datadir = builddir;
+ gdb_datadir_provided = 1;
+ }
+ }
+
/* Initialize all files. Give the interpreter a chance to take
control of the console via the deprecated_init_ui_hook (). */
gdb_init (gdb_program_name);
diff --git a/gdb/testsuite/gdb.base/dash-b.exp b/gdb/testsuite/gdb.base/dash-b.exp
new file mode 100644
index 0000000..24ac6f9
--- /dev/null
+++ b/gdb/testsuite/gdb.base/dash-b.exp
@@ -0,0 +1,34 @@
+# Copyright 2014 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+# Test the -B option.
+# The test here needs to handle the case where gdb has already been installed,
+# in which case if -B isn't working probably the test might still pass whereas
+# it should fail. The way we test this is to verify the value of the
+# "data-directory" parameter after gdb has started.
+
+set save_INTERNAL_GDBFLAGS $INTERNAL_GDBFLAGS
+set INTERNAL_GDBFLAGS "-nw -nx -B"
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+
+set normalized_objdir [file normalize $objdir]
+verbose -log "normalized objdir: $normalized_objdir"
+gdb_test "show data-directory" \
+ "GDB's data directory is \"[file dirname $normalized_objdir]/data-directory\"\."
+
+set INTERNAL_GDBFLAGS $save_INTERNAL_GDBFLAGS