Dev/andris/makefile tools/pr107 compile commands (#177)

* Add compile_commands.json generator

* Move compile commands parsing into parseCustomConfigProvider

* Support a non clean configuration

* Save the compilation database in the cache

* Remove redundant setting and change slightly where compile_commands is calculated.

* Implement PR feedback.

Co-authored-by: Giulio Girardi <giulio.girardi@protechgroup.it>
This commit is contained in:
Andreea Isac 2021-06-09 01:06:25 -07:00 коммит произвёл GitHub
Родитель 2df46a4a15
Коммит d1697e0e4c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 78 добавлений и 12 удалений

Просмотреть файл

@ -455,6 +455,12 @@
"default": true,
"description": "Clear the output channel at the beginning of a build",
"scope": "resource"
},
"makefile.compileCommandsPath": {
"type": "string",
"default": null,
"description": "The path to the compilation database file",
"scope": "resource"
}
}
},

Просмотреть файл

@ -295,6 +295,20 @@ export function readConfigurationCachePath(): void {
logger.message(`Configurations cached at ${configurationCachePath}`);
}
let compileCommandsPath: string | undefined;
export function getCompileCommandsPath(): string | undefined { return compileCommandsPath; }
export function setCompileCommandsPath(path: string): void { compileCommandsPath = path; }
export function readCompileCommandsPath(): void {
let workspaceConfiguration: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("makefile");
compileCommandsPath = workspaceConfiguration.get<string>("compileCommandsPath");
if (compileCommandsPath) {
compileCommandsPath = util.resolvePathToRoot(compileCommandsPath);
}
logger.message(`compile_commands.json path: ${compileCommandsPath}`);
}
let additionalCompilerNames: string[] | undefined;
export function getAdditionalCompilerNames(): string[] | undefined { return additionalCompilerNames; }
export function setAdditionalCompilerNames(compilerNames: string[]): void { additionalCompilerNames = compilerNames; }
@ -841,6 +855,7 @@ export async function initFromStateAndSettings(): Promise<void> {
readBuildBeforeLaunch();
readClearOutputBeforeBuild();
readIgnoreDirectoryCommands();
readCompileCommandsPath();
analyzeConfigureParams();
@ -1180,6 +1195,16 @@ export async function initFromStateAndSettings(): Promise<void> {
updatedSettingsSubkeys.push(subKey);
}
subKey = "compileCommandsPath";
let updatedCompileCommandsPath: string | undefined = workspaceConfiguration.get<string>(subKey);
if (updatedCompileCommandsPath) {
updatedCompileCommandsPath = util.resolvePathToRoot(updatedCompileCommandsPath);
}
if (updatedCompileCommandsPath !== compileCommandsPath) {
readCompileCommandsPath();
updatedSettingsSubkeys.push(subKey);
}
// Final updates in some constructs that depend on more than one of the above settings.
if (extension.getState().configureDirty) {
analyzeConfigureParams();

Просмотреть файл

@ -6,14 +6,19 @@
import * as configuration from './configuration';
import * as logger from './logger';
import * as make from './make';
import * as parser from './parser';
import * as path from 'path';
import * as util from './util';
import * as vscode from 'vscode';
import * as cpp from 'vscode-cpptools';
export interface SourceFileConfigurationItem extends cpp.SourceFileConfigurationItem {
readonly compileCommand: parser.CompileCommand;
}
export interface CustomConfigurationProvider {
workspaceBrowse: cpp.WorkspaceBrowseConfiguration;
fileIndex: Map<string, cpp.SourceFileConfigurationItem>;
fileIndex: Map<string, SourceFileConfigurationItem>;
}
export class CppConfigurationProvider implements cpp.CustomConfigurationProvider {
@ -22,13 +27,13 @@ export class CppConfigurationProvider implements cpp.CustomConfigurationProvider
private workspaceBrowseConfiguration: cpp.WorkspaceBrowseConfiguration = { browsePath: [] };
private getConfiguration(uri: vscode.Uri): cpp.SourceFileConfigurationItem | undefined {
private getConfiguration(uri: vscode.Uri): SourceFileConfigurationItem | undefined {
const norm_path: string = path.normalize(uri.fsPath);
// First look in the file index computed during the last configure.
// If nothing is found and there is a configure running right now,
// try also the temporary index of the current configure.
let sourceFileConfiguration: cpp.SourceFileConfigurationItem | undefined = this.fileIndex.get(norm_path);
let sourceFileConfiguration: SourceFileConfigurationItem | undefined = this.fileIndex.get(norm_path);
if (!sourceFileConfiguration && make.getIsConfiguring()) {
sourceFileConfiguration = make.getDeltaCustomConfigurationProvider().fileIndex.get(norm_path);
logger.message(`Configuration for file ${norm_path} was not found. Searching in the current configure temporary file index.`);
@ -80,7 +85,7 @@ export class CppConfigurationProvider implements cpp.CustomConfigurationProvider
};
}
let map: Map<string, cpp.SourceFileConfigurationItem> = this.fileIndex;
let map: Map<string, SourceFileConfigurationItem> = this.fileIndex;
provider.fileIndex.forEach(function(value, key): void {
map.set(key, value);
});
@ -115,7 +120,7 @@ export class CppConfigurationProvider implements cpp.CustomConfigurationProvider
public dispose(): void { }
private fileIndex = new Map<string, cpp.SourceFileConfigurationItem>();
private fileIndex = new Map<string, SourceFileConfigurationItem>();
public logConfigurationProviderBrowse(): void {
logger.message("Sending Workspace Browse Configuration: -----------------------------------", "Verbose");
@ -129,7 +134,7 @@ export class CppConfigurationProvider implements cpp.CustomConfigurationProvider
logger.message("----------------------------------------------------------------------------", "Verbose");
}
public logConfigurationProviderItem(filePath: cpp.SourceFileConfigurationItem, fromCache: boolean = false): void {
public logConfigurationProviderItem(filePath: SourceFileConfigurationItem, fromCache: boolean = false): void {
let uriObj: vscode.Uri = <vscode.Uri>filePath.uri;
logger.message("Sending configuration " + (fromCache ? "(from cache) " : "") + "for file " + uriObj.fsPath + " -----------------------------------", "Normal");
logger.message(" Defines: " + filePath.configuration.defines.join(";"), "Verbose");

Просмотреть файл

@ -137,9 +137,14 @@ export class MakefileToolsExtension {
// of all the compiler invocations of the current configuration
customConfigProviderItem.files.forEach(filePath => {
let uri: vscode.Uri = vscode.Uri.file(filePath);
let sourceFileConfigurationItem: cpp.SourceFileConfigurationItem = {
let sourceFileConfigurationItem: cpptools.SourceFileConfigurationItem = {
uri,
configuration,
compileCommand: {
command: customConfigProviderItem.line,
directory: customConfigProviderItem.currentPath,
file: filePath
}
};
// These are the configurations processed during the current configure.

Просмотреть файл

@ -79,7 +79,7 @@ export enum TriggeredBy {
launch = "Launch (debug|run)",
}
let fileIndex: Map<string, cpp.SourceFileConfigurationItem> = new Map<string, cpp.SourceFileConfigurationItem>();
let fileIndex: Map<string, cpptools.SourceFileConfigurationItem> = new Map<string, cpptools.SourceFileConfigurationItem>();
let workspaceBrowseConfiguration: cpp.WorkspaceBrowseConfiguration = { browsePath: [] };
export function getDeltaCustomConfigurationProvider(): cpptools.CustomConfigurationProvider {
let provider: cpptools.CustomConfigurationProvider = {
@ -711,6 +711,7 @@ interface ConfigurationCache {
fileIndex: [string, {
uri: string | vscode.Uri;
configuration: cpp.SourceFileConfiguration;
compileCommand: parser.CompileCommand;
}][];
};
}
@ -857,6 +858,8 @@ export async function configure(triggeredBy: TriggeredBy, updateTargets: boolean
readCache = true;
}
let compileCommandsPath: string | undefined = configuration.getCompileCommandsPath();
// Identify for telemetry whether:
// - this configure will need to double the workload, if it needs to analyze the build targets separately.
// - this configure will need to reset the build target to the default, which will need a reconfigure.
@ -955,6 +958,12 @@ export async function configure(triggeredBy: TriggeredBy, updateTargets: boolean
util.writeFile(configurationCachePath, JSON.stringify(ConfigurationCache));
}
// Export the compile_commands.json file if the option is enabled.
if (compileCommandsPath && retc !== ConfigureBuildReturnCodeTypes.cancelled) {
let compileCommands: parser.CompileCommand[] = ConfigurationCache.customConfigurationProvider.fileIndex.map(([, {compileCommand}]) => compileCommand);
util.writeFile(compileCommandsPath, JSON.stringify(compileCommands, undefined, 4));
}
let newBuildTarget: string | undefined = configuration.getCurrentTarget();
let configureElapsedTime: number = util.elapsedTimeSince(configureStartTime);
const telemetryMeasures: telemetry.Measures = {
@ -1246,7 +1255,7 @@ export async function loadConfigurationFromCache(progress: vscode.Progress<{}>,
extension.getCppConfigurationProvider().setCustomConfigurationProvider({
workspaceBrowse: configurationCache.customConfigurationProvider.workspaceBrowse,
// Trick to read a map from json
fileIndex: new Map<string, cpp.SourceFileConfigurationItem>(configurationCache.customConfigurationProvider.fileIndex)
fileIndex: new Map<string, cpptools.SourceFileConfigurationItem>(configurationCache.customConfigurationProvider.fileIndex)
});
});

Просмотреть файл

@ -834,6 +834,16 @@ async function currentPathAfterCommand(line: string, currentPathHistory: string[
return currentPathHistory;
}
// Structure used to describe a compilation command. Reference documentation is
// hosted here https://clang.llvm.org/docs/JSONCompilationDatabase.html
export interface CompileCommand {
directory: string;
file: string;
command: string;
arguments?: string[];
output?: string;
}
export interface CustomConfigProviderItem {
defines: string[];
includes: string[];
@ -844,11 +854,15 @@ export interface CustomConfigProviderItem {
compilerArgs: string[];
files: string[];
windowsSDKVersion?: string;
currentPath: string;
line: string;
}
// Parse the output of the make dry-run command in order to provide CppTools
// with information about includes, defines, compiler path....etc...
// as needed by CustomConfigurationProvider
// as needed by CustomConfigurationProvider. In addition generate a
// CompileCommand entry for every file with a compiler invocation to build
// a compile_commands.json file.
export async function parseCustomConfigProvider(cancel: vscode.CancellationToken, dryRunOutputStr: string,
statusCallback: (message: string) => void,
onFoundCustomConfigProviderItem: (customConfigProviderItem: CustomConfigProviderItem) => void): Promise<number> {
@ -955,8 +969,9 @@ export async function parseCustomConfigProvider(cancel: vscode.CancellationToken
if (language) {
// More standard validation and defaults, in the context of the whole command.
let standard: util.StandardVersion = parseStandard(ext.extension.getCppToolsVersion(), standardStr, language);
if (ext.extension) {
onFoundCustomConfigProviderItem({ defines, includes, forcedIncludes, standard, intelliSenseMode, compilerFullPath, compilerArgs, files, windowsSDKVersion });
onFoundCustomConfigProviderItem({ defines, includes, forcedIncludes, standard, intelliSenseMode, compilerFullPath, compilerArgs, files, windowsSDKVersion, currentPath, line });
}
} else {
// If the compiler command is mixing c and c++ source files, send a custom configuration for each of the source files separately,
@ -970,8 +985,9 @@ export async function parseCustomConfigProvider(cancel: vscode.CancellationToken
// More standard validation and defaults, in the context of each source file.
let standard: util.StandardVersion = parseStandard(ext.extension.getCppToolsVersion(), standardStr, language);
if (ext.extension) {
onFoundCustomConfigProviderItem({ defines, includes, forcedIncludes, standard, intelliSenseMode, compilerFullPath, compilerArgs, files: [file], windowsSDKVersion });
onFoundCustomConfigProviderItem({ defines, includes, forcedIncludes, standard, intelliSenseMode, compilerFullPath, compilerArgs, files: [file], windowsSDKVersion, currentPath, line });
}
});
}