Skip to content

Commit

Permalink
CCDB component enhanced with CompileCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
rtbo committed May 23, 2023
1 parent 3286258 commit 7d1f57f
Showing 1 changed file with 117 additions and 65 deletions.
182 changes: 117 additions & 65 deletions workspace-d/source/workspaced/com/ccdb.d
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ module workspaced.com.ccdb;

import std.exception;
import std.json;
import std.path;
import fs = std.file;

import workspaced.api;
import workspaced.com.dcd;

import containers.hashset;
import workspaced.com.dub;

@component("ccdb")
class ClangCompilationDatabaseComponent : ComponentWrapper
Expand Down Expand Up @@ -72,6 +74,8 @@ class ClangCompilationDatabaseComponent : ComponentWrapper
HashSet!string versions;
HashSet!string debugVersions;

_compileCommands.clear();

{
string jsonString = cast(string) assumeUnique(fs.read(dbPath));
auto json = parseJSON(jsonString);
Expand All @@ -80,12 +84,15 @@ class ClangCompilationDatabaseComponent : ComponentWrapper
// two represention of the same data
jsonString = null;

json.array
auto ccRng = json.array
.map!(jv => CompileCommand.fromJson(jv))
.filter!(cc => cc.isValid)
.each!(cc =>
cc.feedOptions(imports, stringImports, fileImports, versions, debugVersions)
);
.filter!(cc => cc.isValid);

foreach (cc; ccRng)
{
cc.feedOptions(imports, stringImports, fileImports, versions, debugVersions);
_compileCommands[cc.getNormalizedFilePath()] = cc;
}
}

_importPaths = imports[].array;
Expand All @@ -102,6 +109,7 @@ class ClangCompilationDatabaseComponent : ComponentWrapper
_importFiles = null;
_versions = null;
_debugVersions = null;
_compileCommands.clear();
}

/// Lists all import paths
Expand Down Expand Up @@ -134,27 +142,41 @@ class ClangCompilationDatabaseComponent : ComponentWrapper
return _debugVersions;
}

/// Return the compile command for the given D source file, or null if this file is not
/// in the database.
CompileCommand getCompileCommand(string filename) @property
{
auto normalized = buildNormalizedPath(filename);
auto ccp = normalized in _compileCommands;
if (ccp)
return ccp.dup;
return CompileCommand.init;
}

private:

string[] _importPaths, _stringImportPaths, _importFiles, _versions, _debugVersions;
CompileCommand[string] _compileCommands;
}

private struct CompileCommand
public struct CompileCommand
{
string directory;
string file;
string[] args;
string output;

static CompileCommand fromJson(JSONValue json)
private static CompileCommand fromJson(JSONValue json)
{
import std.algorithm : map;
import std.array : array;

CompileCommand cc;

cc.directory = enforce("directory" in json, "'directory' missing from Clang compilation database entry").str;
cc.file = enforce("file" in json, "'file' missing from Clang compilation database entry").str;
cc.directory = enforce("directory" in json, "'directory' missing from Clang compilation database entry")
.str;
cc.file = enforce("file" in json, "'file' missing from Clang compilation database entry")
.str;

if (auto args = "arguments" in json)
{
Expand Down Expand Up @@ -189,71 +211,101 @@ private struct CompileCommand
return true;
}

void feedOptions(
ref HashSet!string imports,
ref HashSet!string stringImports,
ref HashSet!string fileImports,
ref HashSet!string versions,
ref HashSet!string debugVersions)
{
import std.algorithm : startsWith;

enum importMark = "-I"; // optional =
enum stringImportMark = "-J"; // optional =
enum fileImportMark = "-i=";
enum dmdVersionMark = "-version=";
enum ldcVersionMark = "--d-version=";
enum dmdDebugMark = "-debug=";
enum ldcDebugMark = "--d-debug=";
bool opCast(T:bool)() const {
return isValid;
}

foreach (arg; args)
{
const mark = arg.startsWith(
importMark, stringImportMark, fileImportMark, dmdVersionMark, ldcVersionMark, dmdDebugMark, ldcDebugMark,
);
@property CompileCommand dup() const {
return CompileCommand(directory, file, args.dup, output);
}

switch (mark)
{
case 0:
break;
case 1:
case 2:
if (arg.length == 2)
break; // ill-formed flag, we don't need to care here
const st = arg[2] == '=' ? 3 : 2;
const path = getPath(arg[st .. $]);
if (mark == 1)
imports.put(path);
else
stringImports.put(path);
break;
case 3:
fileImports.put(getPath(arg[fileImportMark.length .. $]));
break;
case 4:
versions.put(arg[dmdVersionMark.length .. $]);
break;
case 5:
versions.put(arg[ldcVersionMark.length .. $]);
break;
case 6:
debugVersions.put(arg[dmdDebugMark.length .. $]);
break;
case 7:
debugVersions.put(arg[ldcDebugMark.length .. $]);
break;
default:
break;
}
}
string getNormalizedFilePath() const
{
return getPath(file).buildNormalizedPath();
}

string getPath(string filename)
string getPath(string filename) const
{
import std.path : absolutePath;

return absolutePath(filename, directory);
}

Future!(BuildIssue[]) run() const {
import std.algorithm : canFind, remove;
import std.process : Config, execute;

return Future!(BuildIssue[]).async({
trace("stripping color from ", args);
string[] program = args.dup.remove!(a => a.canFind("-color=on") || a.canFind("-enable-color"));
trace("running ", program);
auto res = execute(program, null, Config.none, size_t.max, directory);
trace(res.status, " ", res.output);
auto issues = parseBuildIssues(res.output);
trace("found ", issues.length, " issue(s)!");
return issues;
});
}
}

void feedOptions(
in CompileCommand cc,
ref HashSet!string imports,
ref HashSet!string stringImports,
ref HashSet!string fileImports,
ref HashSet!string versions,
ref HashSet!string debugVersions)
{
import std.algorithm : startsWith;

enum importMark = "-I"; // optional =
enum stringImportMark = "-J"; // optional =
enum fileImportMark = "-i=";
enum dmdVersionMark = "-version=";
enum ldcVersionMark = "--d-version=";
enum dmdDebugMark = "-debug=";
enum ldcDebugMark = "--d-debug=";

foreach (arg; cc.args)
{
const mark = arg.startsWith(
importMark, stringImportMark, fileImportMark, dmdVersionMark, ldcVersionMark, dmdDebugMark, ldcDebugMark,
);

switch (mark)
{
case 0:
break;
case 1:
case 2:
if (arg.length == 2)
break; // ill-formed flag, we don't need to care here
const st = arg[2] == '=' ? 3 : 2;
const path = cc.getPath(arg[st .. $]);
if (mark == 1)
imports.put(path);
else
stringImports.put(path);
break;
case 3:
fileImports.put(cc.getPath(arg[fileImportMark.length .. $]));
break;
case 4:
versions.put(arg[dmdVersionMark.length .. $]);
break;
case 5:
versions.put(arg[ldcVersionMark.length .. $]);
break;
case 6:
debugVersions.put(arg[dmdDebugMark.length .. $]);
break;
case 7:
debugVersions.put(arg[ldcDebugMark.length .. $]);
break;
default:
break;
}
}
}

private string[] unescapeCommand(string cmd)
Expand Down

0 comments on commit 7d1f57f

Please sign in to comment.