From 344b620aa4775b84f0cf75a87c670da99a9df071 Mon Sep 17 00:00:00 2001 From: Yves Deweerdt Date: Thu, 2 May 2019 17:15:33 +0200 Subject: [PATCH] generate cscope.files based on a compile_commands.json file the compile_commands.json file is parsed to find all files that are compiled for the project and to find all include directories specified for compilation. The header files found in all these include directories are added to the cscope.files. --- README.md | 3 +- src/CscopeExecutor.ts | 86 ++++++++++++++++++++++++++++++++++++++++--- src/extension.ts | 23 ++++++++++-- 3 files changed, 103 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7f6626a..e6ffcc7 100644 --- a/README.md +++ b/README.md @@ -30,4 +30,5 @@ Visual Studio Code C/C++ extension already supported tag parsing and symbol sear * open_new_column - This flag controls how shall new .find window shall be opened. 'yes' means it shall be opened in a separate column while 'no' will open in a new tab. Default setting is no since v0.0.5. * engine_configurations.cscope.paths - This is an array of the paths where all source code files need to be parsed. It allows to include paths that outside of the vs code project. Default value is ${workspaceRoot}. * engine_configurations.cscope.database_path - The path indicates where the cscope database should be built/and found. Default value is ${workspaceRoot}/.vscode/cscope as this was the default path before. - * engine_configurations.cscope.build_command - specify a build command for generating the cscope database. Default value is "", to use the built-int algorithm + * engine_configurations.cscope.build_command - specify a build command for generating the cscope database. Default value is "", to use the built-int algorithm. + * engine_configurations.cscope.compile_commands_json - specify a compile_commands.json file to find used files and include files to generate the cscope.files from, if not empty, this overrules the standard algorithm to find the files to index. Default value is "". diff --git a/src/CscopeExecutor.ts b/src/CscopeExecutor.ts index 94108de..db44504 100644 --- a/src/CscopeExecutor.ts +++ b/src/CscopeExecutor.ts @@ -16,6 +16,8 @@ cscope find command: const spawnSync = require('child_process').spawnSync; const spawn = require('child_process').spawn; const fs = require('fs'); +const glob = require('glob'); + import SymbolLocation from './SymbolLocation'; import OutputInterface from './OutputInterface'; @@ -46,14 +48,16 @@ export default class CscopeExecutor { source_paths:string[]; database_path:string; build_command:string; + compile_commands_json_path:string; outInf:OutputInterface; executorBusy:boolean; - constructor(source_paths:string[], database_path:string, build_command:string, out:OutputInterface) + constructor(source_paths:string[], database_path:string, build_command:string, compile_commands_json_path: string, out:OutputInterface) { this.source_paths = source_paths; this.database_path = database_path; this.build_command = build_command; + this.compile_commands_json_path = compile_commands_json_path; this.outInf = out; this.executorBusy = false; } @@ -145,7 +149,7 @@ export default class CscopeExecutor { return true; } - private internal_buildDataBase() : any + private buildCscopeFilesWithFind() { let start = true; this.source_paths.forEach((path) => { @@ -160,6 +164,7 @@ export default class CscopeExecutor { '-o', '-type', 'f', '-name', '*.mm'], execConfig); if (ret.stderr.length > 0) { console.log(ret.stderr.toString()); + return ret; } else { if (start) { @@ -171,13 +176,82 @@ export default class CscopeExecutor { start = false; } }); - + return {}; + } + + private buildCscopeFilesFromCompileCommandsJson(compile_commands_json_path:string) + { + let compileCommandsText; + let compileCommands; + + try + { + compileCommandsText = fs.readFileSync(compile_commands_json_path); + } catch(err) { + return { 'stderr' : 'unable to open ' + compile_commands_json_path }; + } + try + { + compileCommands = JSON.parse(compileCommandsText); + } + catch(err) + { + return { 'stderr': 'unable to parse ' + compile_commands_json_path }; + } + const reInclude = /-I([^\s]*)/g; + + const cscopeFiles = this.database_path + '/cscope.files'; + let includeDirs = {}; + + fs.writeFileSync(cscopeFiles, ''); + + compileCommands.forEach((cu) => { + fs.appendFileSync(cscopeFiles, cu.file + '\n'); + + let match; + while (match = reInclude.exec(cu.command)) + { + includeDirs[match[1]] = 1; + } + }); + + let includeFiles = {}; + + for (var dir in includeDirs) + { + let files = glob.sync("**/*.{h,hpp}", {cwd : dir, realpath: true}); + files.forEach((file) => + { + includeFiles[file] = 1; + }); + } + + for (var file in includeFiles) + { + fs.appendFileSync(cscopeFiles, file + '\n'); + } + } + + private internal_buildDataBase() : any + { + let ret; + + if (this.compile_commands_json_path !== "") + { + ret = this.buildCscopeFilesFromCompileCommandsJson(this.compile_commands_json_path); + } + else + { + ret = this.buildCscopeFilesWithFind(); + } + if ((ret.stderr) && (ret.stderr.length > 0)) + return ret; + const cscopeExecConfig = { cwd: this.database_path, env: process.env}; - const ret = spawnSync("cscope", ['-b', '-q', '-k'], cscopeExecConfig); + return spawnSync("cscope", ['-b', '-q', '-k'], cscopeExecConfig); - return ret; } public buildDataBase():boolean{ @@ -204,9 +278,11 @@ export default class CscopeExecutor { if (value) { if ((ret.stderr) && (ret.stderr.length > 0)) { this.outInf.errorToUser(ret.stderr.toString()); + this.outInf.updateState("failed"); } else if (ret.stdout.toString().search("fail") === 0) { this.outInf.errorToUser(ret.stdout.toString()); + this.outInf.updateState("failed"); } else { this.outInf.notifyUser("database build finished."); diff --git a/src/extension.ts b/src/extension.ts index b27aa64..78fdc7a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -60,6 +60,16 @@ function getDatabasePath(database_path_config:string) return expanded_path; } +function getCompileCommandsJson(compile_commands_json_path:string) : string +{ + let expanded_path = compile_commands_json_path.replace('${workspaceRoot}', vscode.workspace.rootPath); + if (!path.isAbsolute(expanded_path)) + { + return path.join(vscode.workspace.rootPath, expanded_path); + } + return expanded_path; +} + // this method is called when your extension is activated // your extension is activated the very first time the command is executed export function activate(context: vscode.ExtensionContext) { @@ -75,8 +85,9 @@ export function activate(context: vscode.ExtensionContext) { // This line of code will only be executed once when your extension is activated const database_path = getDatabasePath(configurations.engine_configurations[0].cscope.database_path); const build_command = configurations.engine_configurations[0].cscope.build_command; + const compile_commands_json_path = getCompileCommandsJson(configurations.engine_configurations[0].cscope.compile_commands_json); - const executor = new CscopeExecutor(null, database_path, build_command, out); + const executor = new CscopeExecutor(null, database_path, build_command, compile_commands_json_path, out); const searchResult = new SearchResultProvider(executor); const providerRegistrations = vscode.Disposable.from( @@ -136,7 +147,8 @@ const defaultConfig = ' "${workspaceRoot}"\n' + ' ],\n' + ' "build_command" : "",\n' + -' "database_path" : "${workspaceRoot}/.vscode/cscope"\n' + +' "database_path" : "${workspaceRoot}/.vscode/cscope",\n' + +' "compile_commands_json" : "",\n' + ' }\n' + ' }\n' + ' ]\n' + @@ -151,6 +163,10 @@ function validateConfiguration(configuration:any) { if (!configuration.engine_configurations[0].cscope.build_command) { configuration.engine_configurations[0].cscope.build_command = ""; } + + if (!configuration.engine_configurations[0].cscope.compile_commands_json) { + configuration.engine_configurations[0].cscope.compile_commands_json = ""; + } } function loadConfiguration():string @@ -230,6 +246,7 @@ function buildDataBase() const database_path = getDatabasePath(newConfig.engine_configurations[0].cscope.database_path); const build_command = newConfig.engine_configurations[0].cscope.build_command; + const compile_commands_json_path = getCompileCommandsJson(newConfig.engine_configurations[0].cscope.compile_commands_json); let paths = []; sourcePaths.forEach((path) => { @@ -241,7 +258,7 @@ function buildDataBase() // to node api for file search.5 // Now we are building the database - const executor = new CscopeExecutor(paths, database_path, build_command, out); + const executor = new CscopeExecutor(paths, database_path, build_command, compile_commands_json_path, out); if (executor.checkTool()) { executor.buildDataBase();