aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/manual.cli48
-rw-r--r--libbuild2/cc/init.cxx112
2 files changed, 142 insertions, 18 deletions
diff --git a/doc/manual.cli b/doc/manual.cli
index 03fa04a..1e489e9 100644
--- a/doc/manual.cli
+++ b/doc/manual.cli
@@ -8165,12 +8165,12 @@ $ b libhello/
In the above setup it feels natural to call each database after the project
and place them into the output directory. However, some consumers, such as
-IDEs, may not handle this setup well. Specifically, they may only recognize
-the canonical \c{compile_commands.json} file as the compilation database,
-opening all other files as generic JSON. They may also assume the directory
-where this file resides to be the project source directory root. To accommodate
-these assumptions we can instead place each database into the project's
-source directory and call it \c{compile_commands.json}:
+IDEs and LSP servers, may not handle this setup well. Specifically, they may
+only recognize the canonical \c{compile_commands.json} file as the compilation
+database, opening all other files as generic JSON. They may also assume the
+directory where this file resides to be the project source directory root. To
+accommodate these assumptions we can instead place each database into the
+project's source directory and call it \c{compile_commands.json}:
\
$ bdep init @gcc config.cc.compiledb=libhello@./compile_commands.json
@@ -8178,13 +8178,39 @@ $ bdep init @gcc config.cc.compiledb=libhello@./compile_commands.json
$ bdep init @gcc config.cc.compiledb=hello@./compile_commands.json
\
-Note that in this case it will be your responsibility to remove the database
-files if and when necessary. \N{\l{bdep-new(1)} adds \c{compile_commands.json}
-to \c{.gitignore} it generates.}
+To facilitate this use-case, \c{config.cc.compiledb} supports another
+shortcut: if we specify just \ci{name} and it contains a directory component,
+then it is interpreted as \ci{path} rather than \ci{name}. In this case
+\ci{name} is taken to be the name of the last directory component in \ci{path}
+(which would typically be a project or package name). And if \ci{path} is a
+directory, then the database file name is taken to be
+\c{compile_commands.json}. Or, in other words, the following:
+
+\
+config.cc.compiledb=.../<dir>/
+\
+
+Is equivalent to:
+
+\
+config.cc.compiledb=<dir>@.../<dir>/compile_commands.json
+\
+
+This shortcut allows us to simplify the above \c{init} commands to read:
+
+\
+$ bdep init @gcc config.cc.compiledb=./
+
+$ bdep init @gcc config.cc.compiledb=./
+\
+
+Note also that in this case it will be your responsibility to remove the
+database files if and when necessary. \N{\l{bdep-new(1)} adds
+\c{compile_commands.json} to \c{.gitignore} it generates.}
If instead of having a separate database for each project we wanted to place
-all the entries into a single database, then the relevant commands would
-change as follows:
+all the entries into a single database (and in the output directory), then the
+relevant commands would change as follows:
\
$ bdep init @gcc config.cc.compiledb=compiledb
diff --git a/libbuild2/cc/init.cxx b/libbuild2/cc/init.cxx
index 1ddeca8..d691bc5 100644
--- a/libbuild2/cc/init.cxx
+++ b/libbuild2/cc/init.cxx
@@ -107,6 +107,36 @@ namespace build2
return r;
}
+ // Detect if just <name> in the <name>[@<path>] form is actually <path>.
+ // We assume it is <path> and not <name> if it contains a directory
+ // component or is the special directory name (`.`/`..`) . If that's the
+ // case, return canonicalized name representing <path>. See the call site
+ // in core_config_init() below for background.
+ //
+ static optional<name>
+ compiledb_name_to_path (const name& n)
+ {
+ if (n.directory ())
+ return n;
+
+ if (n.file ())
+ {
+ if (!n.dir.empty () ||
+ path_traits::find_separator (n.value) != string::npos)
+ {
+ name r (n);
+ r.canonicalize ();
+ return r;
+ }
+ else if (n.value == "." || n.value == "..")
+ {
+ return name (dir_path (n.value));
+ }
+ }
+
+ return nullopt;
+ }
+
// Custom save function that completes relative paths in the
// config.cc.compiledb and config.cc.compiledb.name values.
//
@@ -118,6 +148,26 @@ namespace build2
{
const names& ns (v.as<names> ()); // Value is untyped.
+ // Detect and handle the case where just <name> is actually <path>.
+ //
+ if (ns.size () == 1)
+ {
+ const name& n (ns.back ());
+
+ if (optional<name> otn = compiledb_name_to_path (n))
+ {
+ name& tn (*otn);
+
+ if (tn.dir.relative ())
+ tn.dir.complete ();
+
+ tn.dir.normalize ();
+
+ storage.push_back (move (tn));
+ return make_pair (names_view (storage), "=");
+ }
+ }
+
if (find_if (ns.begin (), ns.end (),
[] (const name& n) {return n.pair;}) == ns.end ())
{
@@ -188,11 +238,11 @@ namespace build2
//
// See the manual for the semantics.
//
- // config.cc.compiledb -- <name>[@<path>] (untyped)
- // config.cc.compiledb.name -- <name>[@<path>]... (untyped)
- // config.cc.compiledb.filter -- [<name>@]<bool>...
- // config.cc.compiledb.filter.input -- [<name>@]<target-type>...
- // config.cc.compiledb.filter.output -- [<name>@]<target-type>...
+ // config.cc.compiledb -- <name>[@<path>]|<path> (untyped)
+ // config.cc.compiledb.name -- <name>[@<path>]... (untyped)
+ // config.cc.compiledb.filter -- [<name>@]<bool>...
+ // config.cc.compiledb.filter.input -- [<name>@]<target-type>...
+ // config.cc.compiledb.filter.output -- [<name>@]<target-type>...
//
vp.insert ("config.cc.compiledb");
vp.insert ("config.cc.compiledb.name");
@@ -601,6 +651,12 @@ namespace build2
if (!i->simple () || i->empty ())
fail (loc) << "invalid compilation database name '" << *i << "'";
+ // Don't allow names that have (or are) directory components.
+ //
+ if (compiledb_name_to_path (*i))
+ fail (loc) << "directory component in compilation database name '"
+ << *i << "'";
+
string n (i->value);
path p;
@@ -760,9 +816,51 @@ namespace build2
// Make sure it's one name/path.
//
- if (ns.empty () || ns.size () != (ns.front ().pair ? 2 : 1))
+ size_t n (ns.size ());
+ if (n == 0 || n != (ns.front ().pair ? 2 : 1))
fail (loc) << "invalid compilation database name '" << ns << "'";
+ // Detect and translate just <name> which is actually <path> to the
+ // <name>@<path> form:
+ //
+ // - The <name> part is the name of the directory where the database
+ // file will reside (typically project/repository or package
+ // name).
+ //
+ // - If <path> is a directory, then the database name is
+ // compile_commands.json.
+ //
+ names tns;
+ if (n == 1)
+ {
+ const name& n (ns.front ());
+
+ if (optional<name> otn = compiledb_name_to_path (n))
+ {
+ name& tn (*otn);
+
+ // Note: the add_cdbs() call below completes and normalizes the
+ // path but we need to do it earlier in order to be able to
+ // derive the name (the last component can be `.`/`..`).
+ //
+ if (tn.dir.relative ())
+ tn.dir.complete ();
+
+ tn.dir.normalize ();
+
+ if (!exists (tn.dir))
+ fail (loc) << "compilation database directory " << tn.dir
+ << " does not exist";
+
+ if (tn.value.empty ())
+ tn.value = "compile_commands.json";
+
+ tns.push_back (name (tn.dir.leaf ().string ()));
+ tns.back ().pair = '@';
+ tns.push_back (move (tn));
+ }
+ }
+
// We inject the database directly into the outer amalgamation's
// module, as-if config.cc.compiledb.name was specified in its
// scope. Unless there isn't one, in which case it's us.
@@ -773,7 +871,7 @@ namespace build2
//
enable_filter = add_cdbs (
(p.second != nullptr ? *p.second : m).cdb_names_,
- ns,
+ tns.empty () ? ns : tns,
p.first->out_path ());
}