Add debugger information to kit file

The needed debugger dependes in some cases from the used compiler.
This modification adds a section for the debugger into the kit file.

This allows to switch gdb dependent on the used gcc or clang compiler.
This commit is contained in:
KoeMai 2018-05-13 17:05:42 +02:00
Родитель 9686ee0be4
Коммит 0e583a10bf
12 изменённых файлов: 177 добавлений и 29 удалений

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

@ -68,6 +68,24 @@
"required": [
"name"
]
},
"debugger": {
"type": "object",
"description": "Contains all options to configure the cmake debugger calls",
"properties": {
"type": {
"type": "string",
"enum": ["Visual Studio", "LLDB", "GDB"],
"description": "Type of debugger, it is used to define the MiMode and Cppdbg"
},
"debuggerPath": {
"type": "string",
"description": "Path to gdb or lldb executable"
}
},
"required": [
"type"
]
}
}
}

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

@ -3,6 +3,8 @@
*/
import {CMakeCache} from '@cmt/cache';
import {CMakeExecutable, getCMakeExecutableInformation} from '@cmt/cmake/cmake-executable';
import * as debugger_config from '@cmt/debugger';
import {getDebugConfigurationFromKit} from '@cmt/debugger';
import {versionToString} from '@cmt/util';
import {DirectoryContext} from '@cmt/workspace';
import * as http from 'http';
@ -11,7 +13,6 @@ import * as vscode from 'vscode';
import * as ws from 'ws';
import * as api from './api';
import * as debugger_config from '@cmt/debugger';
import {ExecutionOptions, ExecutionResult} from './api';
import {CacheEditorContentProvider} from './cache-editor';
import {CMakeServerClientDriver} from './cms-driver';
@ -808,8 +809,31 @@ export class CMakeTools implements vscode.Disposable, api.CMakeToolsAPI {
return null;
}
const cache = await CMakeCache.fromPath(drv.cachePath);
const debug_config = debugger_config.getDebugConfigurationFromCache(cache, target, process.platform);
let debug_config;
try {
const kit = this._kitManager.activeKit;
if (kit !== null && kit.debugger !== undefined) {
debug_config = await getDebugConfigurationFromKit(kit.debugger, target);
}
if (debug_config === undefined) {
const cache = await CMakeCache.fromPath(drv.cachePath);
debug_config = await debugger_config.getDebugConfigurationFromCache(cache, target, process.platform);
}
} catch (error) {
vscode.window
.showErrorMessage(error.message, {
title: 'Learn more',
isLearnMore: true,
})
.then(item => {
if (item && item.isLearnMore) {
open('https://vector-of-bool.github.io/docs/vscode-cmake-tools/debugging.html');
}
});
return null;
}
// add debug configuration from settings
const user_config = this.workspaceContext.config.debugConfig;

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

@ -1,5 +1,35 @@
import {ExecutableTarget} from '@cmt/api';
import {CMakeCache} from '@cmt/cache';
import * as proc from '@cmt/proc';
export enum DebuggerType {
VISUALSTUDIO = 'Visual Studio',
LLDB = 'LLDB',
GDB = 'GDB',
// LAUNCH // Future
}
/**
* Describes configuration options for debugger in kit
*/
export interface DebuggerConfiguration {
/**
* Identifier of the type to launch
*/
type: DebuggerType;
/**
* Path to gdb or lldb executable.
*/
debuggerPath?: string;
/**
* Name of a existing launch configuration
*/
// launchConfiguration?: string; // Future
}
export interface Configuration {
type: string;
@ -8,7 +38,14 @@ export interface Configuration {
[key: string]: any;
}
function createGDBDebugConfiguration(debuggerPath: string, target: ExecutableTarget): Configuration {
async function createGDBDebugConfiguration(debuggerPath: string, target: ExecutableTarget): Promise<Configuration> {
if (!await checkDebugger(debuggerPath)) {
debuggerPath = 'gdb';
if (!await checkDebugger(debuggerPath)) {
throw new Error(`Unable to find GDB in default search path and ${debuggerPath}.`);
}
}
return {
type: 'cppdbg',
name: `Debug ${target.name}`,
@ -28,7 +65,7 @@ function createGDBDebugConfiguration(debuggerPath: string, target: ExecutableTar
};
}
function createLLDBDebugConfiguration(debuggerPath: string, target: ExecutableTarget): Configuration {
async function createLLDBDebugConfiguration(debuggerPath: string, target: ExecutableTarget): Promise<Configuration> {
return {
type: 'cppdbg',
name: `Debug ${target.name}`,
@ -54,14 +91,14 @@ function createMSVCDebugConfiguration(target: ExecutableTarget): Configuration {
const possible_debuggers: {
[debugger_name: string]:
{mi_mode: string, config_factory: (debugger_path: string, target: ExecutableTarget) => Configuration}
{mi_mode: string, config_factory: (debugger_path: string, target: ExecutableTarget) => Promise<Configuration>}
}
= {
gdb: {mi_mode: 'gdb', config_factory: createGDBDebugConfiguration},
lldb: {mi_mode: 'lldb', config_factory: createLLDBDebugConfiguration}
};
function searchForCompilerPath(cache: CMakeCache): string|null {
function searchForCompilerPathInCache(cache: CMakeCache): string|null {
const languages = ['CXX', 'C', 'CUDA'];
for (const lang of languages) {
const entry = cache.get(`CMAKE_${lang}_COMPILER`);
@ -73,8 +110,9 @@ function searchForCompilerPath(cache: CMakeCache): string|null {
return null;
}
export function getDebugConfigurationFromCache(cache: CMakeCache, target: ExecutableTarget, platform: string):
Configuration {
export async function getDebugConfigurationFromCache(cache: CMakeCache, target: ExecutableTarget, platform: string):
Promise<Configuration> {
const entry = cache.get('CMAKE_LINKER');
if (entry !== null) {
const linker = entry.value as string;
@ -84,16 +122,21 @@ export function getDebugConfigurationFromCache(cache: CMakeCache, target: Execut
}
}
const compiler_path = searchForCompilerPath(cache);
const compiler_path = searchForCompilerPathInCache(cache);
if (compiler_path === null) {
throw Error('No compiler found in cache file.'); // MSVC should be already found by CMAKE_LINKER
}
const clang_compiler_regex = /(clang[\+]{0,2})+(?!-cl)/gi;
const clang_debugger_path = compiler_path.replace(clang_compiler_regex, 'lldb');
if (clang_debugger_path.search(/lldb/) != -1) {
let clang_debugger_path = compiler_path.replace(clang_compiler_regex, 'lldb');
if ((clang_debugger_path.search(new RegExp('lldb')) != -1) && await checkDebugger(clang_debugger_path)) {
return createLLDBDebugConfiguration(clang_debugger_path, target);
} else {
clang_debugger_path = compiler_path.replace(clang_compiler_regex, 'gdb');
if ((clang_debugger_path.search(new RegExp('gdb')) != -1) && await checkDebugger(clang_debugger_path)) {
return createGDBDebugConfiguration(clang_debugger_path, target);
}
}
const debugger_name = platform == 'darwin' ? 'lldb' : 'gdb';
@ -110,4 +153,36 @@ export function getDebugConfigurationFromCache(cache: CMakeCache, target: Execut
}
return {type: '', name: '', request: ''} as Configuration;
}
export async function checkDebugger(debugger_path: string): Promise<boolean> {
const res = await proc.execute(debugger_path, ['--version'], null, {shell: true}).result;
return res.retc == 0;
}
export async function getDebugConfigurationFromKit(debuggerConfig: DebuggerConfiguration,
target: ExecutableTarget): Promise<Configuration> {
const debugger_path = debuggerConfig.debuggerPath;
switch (debuggerConfig.type) {
case DebuggerType.VISUALSTUDIO:
return createMSVCDebugConfiguration(target);
case DebuggerType.GDB:
if (debugger_path !== undefined && await checkDebugger(debugger_path)) {
return createGDBDebugConfiguration(debugger_path, target);
} else {
throw new Error(`Unable to find GDB debugger (${debugger_path})`);
}
case DebuggerType.LLDB:
if (debugger_path !== undefined && await checkDebugger(debugger_path)) {
return createLLDBDebugConfiguration(debugger_path, target);
} else {
throw new Error(`Unable to find LLDB debugger (${debugger_path})`);
}
default:
throw new Error(`Invalid debugger type (${debuggerConfig.type}).`);
}
}

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

@ -3,6 +3,7 @@
*/ /** */
import {ConfigurationReader} from '@cmt/config';
import {DebuggerConfiguration} from '@cmt/debugger';
import {StateManager} from '@cmt/state';
import * as json5 from 'json5';
import * as path from 'path';
@ -75,6 +76,11 @@ export interface Kit {
* Path to a CMake toolchain file.
*/
toolchainFile?: string;
/**
* Configuration options for the debugger in cmaketools
*/
debugger?: DebuggerConfiguration;
}
/**

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

@ -0,0 +1,2 @@
GNU gdb (GDB) 7.6.1
I am a fake GDB

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

@ -0,0 +1,2 @@
GNU gdb (GDB) 7.6.1
I am a fake GDB

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

@ -0,0 +1 @@
lldb version 6.0.0

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

@ -1,6 +1,9 @@
#include <iostream>
#include <fstream>
#include <string>
#ifdef _WIN32
#include <Windows.h>
#endif
std::string generateConfigFilename(std::string inputFileName) {
#ifdef _WIN32
@ -12,8 +15,14 @@ std::string generateConfigFilename(std::string inputFileName) {
}
int main(int argc, char** argv) {
#ifdef _WIN32
char buffer[MAX_PATH];
DWORD length = GetModuleFileName( NULL, buffer, MAX_PATH);
std::string filePath( buffer);
#else
std::string filePath = argv[0];
#endif
std::string configFilePath = generateConfigFilename(filePath);
std::ifstream inputData(configFilePath.c_str());
@ -24,6 +33,7 @@ int main(int argc, char** argv) {
std::cerr << line << std::endl;
}
} else {
std::cerr << "Argv[0]" << argv[0] << std::endl;
std::cerr << "ERROR: config file is missing '" << configFilePath << "'" << std::endl;
return -99;
}

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

@ -27,7 +27,7 @@ CMAKE_AR:FILEPATH=/usr/bin/ar
CMAKE_BUILD_TYPE:STRING=Debug
//CXX compiler
CMAKE_CXX_COMPILER:FILEPATH=/usr/local/bin/g++
CMAKE_CXX_COMPILER:FILEPATH=g++
//Flags used by the compiler during all build types.
CMAKE_CXX_FLAGS:STRING=

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

@ -27,7 +27,7 @@ CMAKE_AR:FILEPATH=/usr/bin/ar
CMAKE_BUILD_TYPE:STRING=Debug
//CXX compiler
CMAKE_CXX_COMPILER:FILEPATH=/usr/local/bin/g++
CMAKE_CXX_COMPILER:FILEPATH=g++
//Flags used by the compiler during all build types.
CMAKE_CXX_FLAGS:STRING=
@ -46,7 +46,7 @@ CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
//C compiler
CMAKE_C_COMPILER:FILEPATH=/usr/local/bin/gcc
CMAKE_C_COMPILER:FILEPATH=gcc
//Flags used by the compiler during all build types.
CMAKE_C_FLAGS:STRING=

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

@ -27,7 +27,7 @@ CMAKE_AR:FILEPATH=/usr/bin/ar
CMAKE_BUILD_TYPE:STRING=Debug
//CXX compiler
CMAKE_CXX_COMPILER:FILEPATH=/usr/local/bin/clang++
CMAKE_CXX_COMPILER:FILEPATH=clang++
//Flags used by the compiler during all build types.
CMAKE_CXX_FLAGS:STRING=
@ -46,7 +46,7 @@ CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
//C compiler
CMAKE_C_COMPILER:FILEPATH=/usr/local/bin/clang
CMAKE_C_COMPILER:FILEPATH=clang
//Flags used by the compiler during all build types.
CMAKE_C_FLAGS:STRING=

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

@ -15,60 +15,70 @@ function getTestResourceFilePath(filename: string): string {
return path.normalize(path.join(here, '../../../test/unit-tests', filename));
}
suite.only('Select debugger', async () => {
function getFakeBin(): string { return path.normalize(path.join(here, '../../../test/fakebin')); }
suite('Select debugger', async () => {
let path_backup: string|undefined = '';
setup(() => {
path_backup = process.env.PATH;
process.env['PATH'] = getFakeBin();
});
teardown(() => { process.env['PATH'] = path_backup; });
test('Create debug config from cache - clang', async () => {
const target = {name: 'Test', path: 'Target'};
const cache = await CMakeCache.fromPath(getTestResourceFilePath('TestCMakeCache.txt'));
const config = Debugger.getDebugConfigurationFromCache(cache, target, 'linux');
const config = await Debugger.getDebugConfigurationFromCache(cache, target, 'linux');
expect(config.name).to.be.eq('Debug Test');
expect(config['MIMode']).to.be.eq('lldb');
expect(config.type).to.be.eq('cppdbg');
expect(config['miDebuggerPath']).to.be.eq('/usr/local/bin/lldb');
expect(config['miDebuggerPath']).to.be.eq('lldb');
});
test('Create debug config from cache - GCC', async () => {
const target = {name: 'Test', path: 'Target'};
const cache = await CMakeCache.fromPath(getTestResourceFilePath('TestCMakeCache-gcc.txt'));
const config = Debugger.getDebugConfigurationFromCache(cache, target, 'linux');
const config = await Debugger.getDebugConfigurationFromCache(cache, target, 'linux');
expect(config.name).to.be.eq('Debug Test');
expect(config['MIMode']).to.be.eq('gdb');
expect(config.type).to.be.eq('cppdbg');
expect(config['miDebuggerPath']).to.be.eq('/usr/local/bin/gdb');
expect(config['miDebuggerPath']).to.be.eq('gdb');
});
test('Create debug config from cache - GCC Apple', async () => {
const target = {name: 'Test', path: 'Target'};
const cache = await CMakeCache.fromPath(getTestResourceFilePath('TestCMakeCache-gcc.txt'));
const config = Debugger.getDebugConfigurationFromCache(cache, target, 'darwin');
const config = await Debugger.getDebugConfigurationFromCache(cache, target, 'darwin');
expect(config.name).to.be.eq('Debug Test');
expect(config['MIMode']).to.be.eq('lldb');
expect(config.type).to.be.eq('cppdbg');
expect(config['miDebuggerPath']).to.be.eq('/usr/local/bin/lldb');
expect(config['miDebuggerPath']).to.be.eq('lldb');
});
test('Create debug config from cache - g++', async () => {
const target = {name: 'Test', path: 'Target'};
const cache = await CMakeCache.fromPath(getTestResourceFilePath('TestCMakeCache-g++.txt'));
const config = Debugger.getDebugConfigurationFromCache(cache, target, 'linux');
const config = await Debugger.getDebugConfigurationFromCache(cache, target, 'linux');
expect(config.name).to.be.eq('Debug Test');
expect(config['MIMode']).to.be.eq('gdb');
expect(config.type).to.be.eq('cppdbg');
expect(config['miDebuggerPath']).to.be.eq('/usr/local/bin/gdb');
expect(config['miDebuggerPath']).to.be.eq('gdb');
});
test('Create debug config from cache - Visual Studio Community 2017', async () => {
const target = {name: 'Test', path: 'Target'};
const cache = await CMakeCache.fromPath(getTestResourceFilePath('TestCMakeCache-msvc-com-2017.txt'));
const config = Debugger.getDebugConfigurationFromCache(cache, target, 'win32');
const config = await Debugger.getDebugConfigurationFromCache(cache, target, 'win32');
expect(config.name).to.be.eq('Debug Test');
expect(config.type).to.be.eq('cppvsdbg');