This commit is contained in:
Ben McMorran 2022-10-24 17:28:43 -07:00 коммит произвёл GitHub
Родитель cd555861b4
Коммит e7ed1b7dcd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
25 изменённых файлов: 391 добавлений и 585 удалений

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

@ -2114,6 +2114,7 @@
"tslint": "^6.1.3",
"typescript": "^4.1.5",
"vsce": "^2.7.0",
"vscode-cmake-tools": "^1.0.0",
"vscode-nls-dev": "^3.3.2",
"webpack": "^5.38.1",
"webpack-cli": "^4.5.0"

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

@ -1,317 +1,101 @@
/**
* This module defines the external API for the extension. Other
* extensions can access this API via the exports instance for the extension.
*
* Look at the `CMakeToolsAPI` interface for the actual exported API.
*
* Copy the `api.ts` source file into your project to use it.
*/ /** */
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DebugSession, Disposable, Event, Terminal } from 'vscode';
import * as vscode from 'vscode';
import * as api from 'vscode-cmake-tools';
import CMakeProject from '@cmt/cmakeProject';
import { ExtensionManager } from '@cmt/extension';
import { assertNever } from '@cmt/util';
/**
* The result of executing a program.
*/
export interface ExecutionResult {
/**
* The return code of the program.
*/
retc: number | null;
/**
* The full standard output of the program. May be `` if standard out
* was not captured.
*/
stdout: string;
/**
* Standard error output of the program. May be `` if standard error was
* not captured
*/
stderr: string;
export class CMakeToolsApiImpl implements api.CMakeToolsApi {
constructor(private readonly manager: ExtensionManager) {}
version: api.Version = api.Version.v1;
showUIElement(element: api.UIElement): Promise<void> {
return this.setUIElementVisibility(element, true);
}
/**
* Options for executing a command.
*/
export interface ExecutionOptions {
/**
* Whether output from the command should be suppressed from CMake Tools'
* output channel.
*/
silent: boolean;
/**
* Additional environment variables to define when executing the command.
*/
environment: { [key: string]: string };
/**
* Whether we should collect output from the command.
*
* @note All output from the command is collected into a single string, so
* commands which emit a lot of output may consume a lot of memory if
* `collectOutput` is set to `true`.
*/
collectOutput?: boolean;
/**
* The working directory for the command. The default directory is
* unspecified.
*/
workingDirectory?: string;
hideUIElement(element: api.UIElement): Promise<void> {
return this.setUIElementVisibility(element, false);
}
/**
* The type of a CMake cache entry
*/
export enum CacheEntryType {
Bool = 0,
String = 1,
Path = 2,
FilePath = 3,
Internal = 4,
Uninitialized = 5,
Static = 6,
get onBuildTargetChanged() {
return this.manager.onBuildTargetChanged;
}
/**
* Information about a CTest test
*/
export interface Test {
id: number;
name: string;
get onLaunchTargetChanged() {
return this.manager.onLaunchTargetChanged;
}
/**
* The properties of a CMake cache entry.
*/
export interface CacheEntryProperties {
type: CacheEntryType;
helpString: string;
/** The name of the cache entry */
key: string;
/** The entry's value. Type depends on `type`. */
value: any;
/** Whether this entry is ADVANCED, meaning it hidden from the user. */
advanced: boolean;
/** List of allowed values, as specified by STRINGS property */
choices: string[];
get onActiveProjectChanged() {
return this.manager.onActiveProjectChanged;
}
/**
* A cache entry from a CMake cache.
*/
export interface CacheEntry extends CacheEntryProperties {
/**
* Return the value as a `T` instance. Does no actual conversion. It's up to
* you to check the value of `CacheEntryProperties.type`.
*/
as<T>(): T;
async getProject(uri: vscode.Uri) {
const project = this.manager.cmakeWorkspaceFolders.get([uri.fsPath])?.cmakeProject;
return project && new CMakeProjectWrapper(project);
}
/**
* Description of an executable CMake target, defined via `add_executable()`.
*/
export interface ExecutableTarget {
/**
* The name of the target.
*/
name: string;
/**
* The absolute path to the build output.
*/
path: string;
private async setUIElementVisibility(element: api.UIElement, visible: boolean): Promise<void> {
switch (element) {
case api.UIElement.StatusBarDebugButton:
await this.manager.hideDebugCommand(!visible);
break;
case api.UIElement.StatusBarLaunchButton:
await this.manager.hideLaunchCommand(!visible);
break;
default:
assertNever(element);
}
}
}
export interface VariantKeywordSettings {
[key: string]: string;
async function withErrorCheck(name: string, action: () => Thenable<number>): Promise<void> {
const code = await action();
if (code !== 0) {
throw new Error(`${name} failed with code ${code}`);
}
}
/**
* A target with a name, but no output. This may be created via `add_custom_command()`.
*/
export interface NamedTarget {
type: 'named';
name: string;
class CMakeProjectWrapper implements api.Project {
constructor(private readonly project: CMakeProject) {}
get codeModel() {
return this.project.codeModelContent ?? undefined;
}
/**
* A target with a name, path, and type.
*/
export interface RichTarget {
type: 'rich';
name: string;
filepath: string;
targetType: string;
get onCodeModelChanged() {
return this.project.onCodeModelChangedApiEvent;
}
export type Target = NamedTarget | RichTarget;
/**
* The CMake Tools extension API obtained via `getExtension().exports`
*/
export interface CMakeToolsAPI extends Disposable {
/**
* The source directory, containing the root of the project
*/
readonly sourceDir: string;
/**
* The `CMakeLists.txt` at to the root of the project
*/
readonly mainListFile: Thenable<string>;
/**
* The root build directory for the project. May change based on build
* configuration.
*/
readonly binaryDir: Thenable<string>;
/**
* The path to the `CMakeCache.txt for the project.
*/
readonly cachePath: Thenable<string>;
/**
* List of CMake targets created via `add_executable()`.
*/
readonly executableTargets: Thenable<ExecutableTarget[]>;
/**
* All targets available to be built
*/
readonly targets: Thenable<Target[]>;
/**
* Event fired when the configure/generate stage completes
*/
readonly onReconfigured: Event<void>;
/**
* Event fired when the active target changes.
*/
readonly onTargetChanged: Event<void>;
/**
* Execute a command using the CMake executable.
*
* @param args Arguments to CMake
* @param options Additional execution options
* @returns The result of execution.
*/
executeCMakeCommand(args: string[], options?: ExecutionOptions): Thenable<ExecutionResult>;
// Execute an arbitrary program in the active environments
/**
* Execute an arbitrary program.
*
* @param program Path to an executable binary
* @param args List of command-line arguments to the program
* @param options Additional execution options
* @returns The result of execution
*
* ## Why you should use this API:
*
* You can execute a program on your own, but if it requires access to
* environment variables that CMake Tools knows about, such as Visual C++
* environment variables, this is the most reliable way to ensure that you
* execute in the context that the user is expecting.
*/
execute(program: string, args: string[], options?: ExecutionOptions): Thenable<ExecutionResult>;
/**
* Configure the project.
*
* @param extraArgs Extra arguments to pass on the CMake command line
* @returns The exit code of CMake
*/
configure(extraArgs?: string[]): Thenable<number>;
/**
* Build the project
*
* @param target The target to build. If not provided, will build the user's
* active build target.
* @returns the exit code of the build command
*/
build(targets?: string[]): Thenable<number>;
/**
* Installs the project
* @returns The exit code from CMake
*/
install(): Thenable<number>;
/**
* Clean the build output. Runs the `clean` target.
*
* @returns The exit code from the build command
*/
clean(): Thenable<number>;
/**
* Clean up old configuration and reconfigure.
*
* @returns The exit code from CMake
*
* @note This is *not* the same as running `clean`, then `configure`.
* Cleaning up configure includes removing the CMake cache file and any
* intermediate configuration files.
*/
cleanConfigure(): Thenable<number>;
/**
* Clean the build output and rebuild
*
* @returns The exit code from the build command.
*/
cleanRebuild(): Thenable<number>;
/**
* Execute CTest
*
* @returns The exit code from CTest
*/
ctest(): Thenable<number>;
/**
* Stop the currently running command.
*
* @returns `true` on success. `false` otherwise.
*/
stop(): Thenable<boolean>;
/**
* Start the active target without a debugger.
*/
launchTarget(): Thenable<Terminal | null>;
/**
* Start the active target with a debugger.
*/
debugTarget(): Thenable<DebugSession | null>;
/**
* Get the path to the active launch target
*/
launchTargetPath(): Thenable<string | null>;
/**
* Get the directory to the active launch target
*/
launchTargetDirectory(): Thenable<string | null>;
/**
* Get the filename of the active launch target
*/
launchTargetFilename(): Thenable<string | null>;
/**
* Get the selected build type
*/
currentBuildType(): Thenable<string | null>;
/**
* Get the build directory.
*/
buildDirectory(): Thenable<string | null>;
/**
* Get the build command string for the active target
*/
tasksBuildCommand(): Thenable<string | null>;
/**
* Get the build kit
*/
buildKit(): Thenable<string | null>;
configure(): Promise<void> {
return withErrorCheck('configure', () => this.project.configure());
}
build(targets?: string[]): Promise<void> {
return withErrorCheck('build', () => this.project.build(targets));
}
install(): Promise<void> {
return withErrorCheck('install', () => this.project.install());
}
clean(): Promise<void> {
return withErrorCheck('clean', () => this.project.clean());
}
reconfigure(): Promise<void> {
return withErrorCheck('reconfigure', () => this.project.cleanConfigure());
}
async getBuildDirectory(): Promise<string | undefined> {
return (await this.project.buildDirectory()) ?? undefined;
}
async getActiveBuildType(): Promise<string | undefined> {
return (await this.project.currentBuildType()) ?? undefined;
}
}

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

@ -2,7 +2,6 @@
* Module for reading from the CMake cache
*/ /** */
import * as api from './api';
import * as logging from './logging';
import { fs } from './pr';
import rollbar from './rollbar';
@ -15,19 +14,39 @@ const localize: nls.LocalizeFunc = nls.loadMessageBundle();
const log = logging.createLogger('cache');
/**
* Implements access to CMake cache entries. See `api.CacheEntry` for more
* information.
* The type of a CMake cache entry
*/
export class Entry implements api.CacheEntry {
public readonly type: api.CacheEntryType = api.CacheEntryType.Uninitialized;
export enum CacheEntryType {
Bool = 0,
String = 1,
Path = 2,
FilePath = 3,
Internal = 4,
Uninitialized = 5,
Static = 6,
}
/**
* Implements access to CMake cache entries.
*/
export class CacheEntry {
public readonly type: CacheEntryType = CacheEntryType.Uninitialized;
public readonly helpString: string = '';
/** The name of the cache entry */
public readonly key: string = '';
/** The entry's value. Type depends on `type`. */
public readonly value: any = null;
/** Whether this entry is ADVANCED, meaning it hidden from the user. */
advanced: boolean = false;
/** List of allowed values, as specified by STRINGS property */
choices: string[] = [];
serializedKey: string = '';
/**
* Return the value as a `T` instance. Does no actual conversion. It's up to
* you to check the value of `CacheEntryProperties.type`.
*/
as<T>(): T {
return this.value as T;
}
@ -41,11 +60,11 @@ export class Entry implements api.CacheEntry {
* @param docString The `DOC` string in the cache
* @param advanced Whether the entry is `ADVANCED`
*/
constructor(key: string, value: string, type: api.CacheEntryType, docString: string, advanced: boolean) {
constructor(key: string, value: string, type: CacheEntryType, docString: string, advanced: boolean) {
this.key = key;
this.serializedKey = key; // may be overwritten later with quoted version of `key`
this.type = type;
if (type === api.CacheEntryType.Bool) {
if (type === CacheEntryType.Bool) {
this.value = util.isTruthy(value);
} else {
this.value = value;
@ -85,7 +104,7 @@ export class CMakeCache {
}
/** Get a list of all cache entries */
get allEntries(): Entry[] {
get allEntries(): CacheEntry[] {
return Array.from(this.cacheEntries.values());
}
@ -95,7 +114,7 @@ export class CMakeCache {
* @param path Path to the cache file
* @param cacheEntries Entries in the cache
*/
private constructor(public readonly path: string, private readonly cacheEntries: Map<string, Entry>) {}
private constructor(public readonly path: string, private readonly cacheEntries: Map<string, CacheEntry>) {}
/**
* Reload the cache file and return a new instance. This will not modify this
@ -112,11 +131,11 @@ export class CMakeCache {
* @param content The contents of a CMake cache file.
* @returns A map from the cache keys to the entries in the cache.
*/
static parseCache(content: string): Map<string, Entry> {
static parseCache(content: string): Map<string, CacheEntry> {
log.debug(localize('parsing.cmake.cache.string', 'Parsing CMake cache string'));
const lines = content.split(/\r\n|\n|\r/).filter(line => !!line.length).filter(line => !/^\s*#/.test(line));
const entries = new Map<string, Entry>();
const entries = new Map<string, CacheEntry>();
let docStringAccumulator = '';
const advancedNames: string[] = [];
const choices: Map<string, string[]> = new Map();
@ -147,14 +166,14 @@ export class CMakeCache {
} else {
const key = name;
const typemap = {
BOOL: api.CacheEntryType.Bool,
STRING: api.CacheEntryType.String,
PATH: api.CacheEntryType.Path,
FILEPATH: api.CacheEntryType.FilePath,
INTERNAL: api.CacheEntryType.Internal,
UNINITIALIZED: api.CacheEntryType.Uninitialized,
STATIC: api.CacheEntryType.Static
} as { [type: string]: api.CacheEntryType | undefined };
BOOL: CacheEntryType.Bool,
STRING: CacheEntryType.String,
PATH: CacheEntryType.Path,
FILEPATH: CacheEntryType.FilePath,
INTERNAL: CacheEntryType.Internal,
UNINITIALIZED: CacheEntryType.Uninitialized,
STATIC: CacheEntryType.Static
} as { [type: string]: CacheEntryType | undefined };
const type = typemap[typeName];
const docString = docStringAccumulator.trim();
docStringAccumulator = '';
@ -162,7 +181,7 @@ export class CMakeCache {
rollbar.error(localize('cache.entry.unknown', 'Cache entry {0} has unknown type: {1}', `"${name}"`, `"${typeName}"`));
} else {
log.trace(localize('constructing.new.cache.entry', 'Constructing a new cache entry from the given line'));
const entry = new Entry(key, value, type, docString, false);
const entry = new CacheEntry(key, value, type, docString, false);
entry.serializedKey = serializedName;
entries.set(name, entry);
}
@ -278,7 +297,7 @@ export class CMakeCache {
* @param key The name of a cache entry
* @returns The cache entry, or `null` if the cache entry is not present.
*/
get(key: string): Entry | null {
get(key: string): CacheEntry | null {
const ret = this.cacheEntries.get(key) || null;
if (ret) {
log.trace(localize('get.cache.key', 'Get cache key {0}={1}', key, ret.value));

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

@ -1,10 +1,9 @@
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import * as telemetry from '@cmt/telemetry';
import * as api from './api';
import * as util from './util';
import { CMakeCache } from './cache';
import { CacheEntryType, CMakeCache } from './cache';
import * as logging from './logging';
const log = logging.createLogger('cache');
@ -222,8 +221,8 @@ export class ConfigurationWebview {
for (const entry of cmakeCache.allEntries) {
// Static cache entries are set automatically by CMake, overriding any value set by the user in this view.
// Not useful to show these entries in the list.
if (entry.type !== api.CacheEntryType.Static) {
options.push({ key: entry.key, helpString: entry.helpString, choices: entry.choices, type: (entry.type === api.CacheEntryType.Bool) ? "Bool" : "String", value: entry.value, dirty: false });
if (entry.type !== CacheEntryType.Static) {
options.push({ key: entry.key, helpString: entry.helpString, choices: entry.choices, type: (entry.type === CacheEntryType.Bool) ? "Bool" : "String", value: entry.value, dirty: false });
}
}
return options;

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

@ -14,8 +14,6 @@ import { DirectoryContext } from '@cmt/workspace';
import * as path from 'path';
import * as vscode from 'vscode';
import * as proc from '@cmt/proc';
import * as api from './api';
import { ExecutionOptions, ExecutionResult } from './api';
import { CodeModelContent } from '@cmt/drivers/codeModel';
import { BadHomeDirectoryError } from '@cmt/drivers/cmakeServerClient';
import { CMakeServerDriver, NoGeneratorError } from '@cmt/drivers/cmakeServerDriver';
@ -23,14 +21,14 @@ import { CTestDriver, BasicTestResults } from './ctest';
import { CMakeBuildConsumer } from './diagnostics/build';
import { CMakeOutputConsumer } from './diagnostics/cmake';
import { populateCollection } from './diagnostics/util';
import { CMakeDriver, CMakePreconditionProblems } from '@cmt/drivers/cmakeDriver';
import { CMakeDriver, CMakePreconditionProblems, ExecutableTarget } from '@cmt/drivers/cmakeDriver';
import { expandStrings, expandString, ExpansionOptions } from './expand';
import { CMakeGenerator, Kit } from './kit';
import { CMakeLegacyDriver } from '@cmt/drivers/cmakeLegacyDriver';
import * as logging from './logging';
import { fs } from './pr';
import { buildCmdStr, DebuggerEnvironmentVariable } from './proc';
import { Property } from './prop';
import { buildCmdStr, DebuggerEnvironmentVariable, ExecutionResult, ExecutionOptions } from './proc';
import { FireLate, Property } from './prop';
import rollbar from './rollbar';
import * as telemetry from './telemetry';
import { setContextValue } from './util';
@ -98,7 +96,7 @@ export enum ConfigureTrigger {
* The second phases of fields will be called by the second phase of the parent
* class. See the `init` private method for this initialization.
*/
export class CMakeProject implements api.CMakeToolsAPI {
export class CMakeProject {
/**
* Construct a new instance. The instance isn't ready, and must be initalized.
* @param extensionContext The extension context
@ -108,6 +106,7 @@ export class CMakeProject implements api.CMakeToolsAPI {
private constructor(readonly extensionContext: vscode.ExtensionContext, readonly workspaceContext: DirectoryContext) {
// Handle the active kit changing. We want to do some updates and teardown
log.debug(localize('constructing.cmakeproject', 'Constructing new CMakeProject instance'));
this.onCodeModelChanged(FireLate, (_) => this._codeModelChangedApiEventEmitter.fire());
}
/**
@ -478,6 +477,11 @@ export class CMakeProject implements api.CMakeToolsAPI {
private readonly _codeModelContent = new Property<CodeModelContent | null>(null);
private codeModelDriverSub: vscode.Disposable | null = null;
get onCodeModelChangedApiEvent() {
return this._codeModelChangedApiEventEmitter.event;
}
private readonly _codeModelChangedApiEventEmitter = new vscode.EventEmitter<void>();
private readonly communicationModeSub = this.workspaceContext.config.onChange('cmakeCommunicationMode', () => {
log.info(localize('communication.changed.restart.driver', "Restarting the CMake driver after a communication mode change."));
return this.shutDownCMakeDriver();
@ -1931,7 +1935,7 @@ export class CMakeProject implements api.CMakeToolsAPI {
return chosen.detail;
}
async getCurrentLaunchTarget(): Promise<api.ExecutableTarget | null> {
async getCurrentLaunchTarget(): Promise<ExecutableTarget | null> {
const targetName = this.workspaceContext.state.launchTargetName;
const target = (await this.executableTargets).find(e => e.name === targetName);
@ -2056,8 +2060,8 @@ export class CMakeProject implements api.CMakeToolsAPI {
}
}
async prepareLaunchTargetExecutable(name?: string): Promise<api.ExecutableTarget | null> {
let chosen: api.ExecutableTarget;
async prepareLaunchTargetExecutable(name?: string): Promise<ExecutableTarget | null> {
let chosen: ExecutableTarget;
// Ensure that we've configured the project already. If we haven't, `getOrSelectLaunchTarget` won't see any
// executable targets and may show an uneccessary prompt to the user
@ -2096,7 +2100,7 @@ export class CMakeProject implements api.CMakeToolsAPI {
return chosen;
}
async getOrSelectLaunchTarget(): Promise<api.ExecutableTarget | null> {
async getOrSelectLaunchTarget(): Promise<ExecutableTarget | null> {
const current = await this.getCurrentLaunchTarget();
if (current) {
return current;
@ -2217,7 +2221,7 @@ export class CMakeProject implements api.CMakeToolsAPI {
}
});
private async createTerminal(executable: api.ExecutableTarget): Promise<vscode.Terminal> {
private async createTerminal(executable: ExecutableTarget): Promise<vscode.Terminal> {
const launchBehavior = this.workspaceContext.config.launchBehavior.toLowerCase();
if (launchBehavior !== "newterminal") {
for (const [, terminal] of this.launchTerminals) {

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

@ -4,7 +4,6 @@ import * as vscode from 'vscode';
import * as xml2js from 'xml2js';
import * as zlib from 'zlib';
import * as api from './api';
import { CMakeDriver } from '@cmt/drivers/cmakeDriver';
import * as logging from './logging';
import { fs } from './pr';
@ -20,6 +19,14 @@ const localize: nls.LocalizeFunc = nls.loadMessageBundle();
const log = logging.createLogger('ctest');
/**
* Information about a CTest test
*/
export interface CTest {
id: number;
name: string;
}
export interface BasicTestResults {
passing: number;
total: number;
@ -331,16 +338,16 @@ export class CTestDriver implements vscode.Disposable {
/**
* Holds the most recent test informations
*/
private _tests: api.Test[] = [];
get tests(): api.Test[] {
private _tests: CTest[] = [];
get tests(): CTest[] {
return this._tests;
}
set tests(v: api.Test[]) {
set tests(v: CTest[]) {
this._tests = v;
this.testsChangedEmitter.fire(v);
}
private readonly testsChangedEmitter = new vscode.EventEmitter<api.Test[]>();
private readonly testsChangedEmitter = new vscode.EventEmitter<CTest[]>();
readonly onTestsChanged = this.testsChangedEmitter.event;
private _testResults?: CTestResults;
@ -418,7 +425,7 @@ export class CTestDriver implements vscode.Disposable {
/**
* @brief Reload the list of CTest tests
*/
async reloadTests(driver: CMakeDriver): Promise<api.Test[]> {
async reloadTests(driver: CMakeDriver): Promise<CTest[]> {
const ctestFile = path.join(driver.binaryDir, 'CTestTestfile.cmake');
if (!(await fs.exists(ctestFile))) {
this.testingEnabled = false;

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

@ -1,4 +1,3 @@
import { ExecutableTarget } from '@cmt/api';
import { CMakeCache } from '@cmt/cache';
import * as proc from '@cmt/proc';
import { createLogger } from './logging';
@ -6,6 +5,7 @@ import * as nls from 'vscode-nls';
import * as path from 'path';
import * as vscode from 'vscode';
import { fs } from './pr';
import { ExecutableTarget } from '@cmt/drivers/cmakeDriver';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();

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

@ -5,7 +5,6 @@
import * as path from 'path';
import * as vscode from 'vscode';
import * as api from '@cmt/api';
import { CMakeExecutable } from '@cmt/cmake/cmakeExecutable';
import * as codepages from '@cmt/codePageTable';
import { ConfigureTrigger } from "@cmt/cmakeProject";
@ -33,6 +32,7 @@ import { DiagnosticsConfiguration } from '@cmt/cmakeWorkspaceFolder';
import { Environment, EnvironmentUtils } from '@cmt/environmentVariables';
import { CustomBuildTaskTerminal } from '@cmt/cmakeTaskProvider';
import { getValue } from '@cmt/preset';
import { CacheEntry } from '@cmt/cache';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
@ -57,6 +57,40 @@ function nullableValueToString(arg: any | null | undefined): string {
return arg === null ? 'empty' : arg;
}
/**
* Description of an executable CMake target, defined via `add_executable()`.
*/
export interface ExecutableTarget {
/**
* The name of the target.
*/
name: string;
/**
* The absolute path to the build output.
*/
path: string;
}
/**
* A target with a name, but no output. This may be created via `add_custom_command()`.
*/
export interface NamedTarget {
type: 'named';
name: string;
}
/**
* A target with a name, path, and type.
*/
export interface RichTarget {
type: 'rich';
name: string;
filepath: string;
targetType: string;
}
export type Target = NamedTarget | RichTarget;
/**
* Base class for CMake drivers.
*
@ -112,19 +146,19 @@ export abstract class CMakeDriver implements vscode.Disposable {
/**
* List of targets known to CMake
*/
abstract get targets(): api.Target[];
abstract get targets(): Target[];
abstract get codeModelContent(): codeModel.CodeModelContent | null;
/**
* List of executable targets known to CMake
*/
abstract get executableTargets(): api.ExecutableTarget[];
abstract get executableTargets(): ExecutableTarget[];
/**
* List of unique targets known to CMake
*/
abstract get uniqueTargets(): api.Target[];
abstract get uniqueTargets(): Target[];
/**
* List of all files (CMakeLists.txt and included .cmake files) used by CMake
@ -1747,7 +1781,7 @@ export abstract class CMakeDriver implements vscode.Disposable {
*
* Will be automatically reloaded when the file on disk changes.
*/
abstract get cmakeCacheEntries(): Map<string, api.CacheEntryProperties>;
abstract get cmakeCacheEntries(): Map<string, CacheEntry>;
private async _baseInit(useCMakePresets: boolean,
kit: Kit | null,

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

@ -6,7 +6,6 @@
* This file implements only the new required structures.
*/
import * as api from '@cmt/api';
import * as cache from '@cmt/cache';
import {
CodeModelConfiguration,
@ -22,6 +21,7 @@ import * as path from 'path';
import * as nls from 'vscode-nls';
import rollbar from '@cmt/rollbar';
import { removeEmpty } from '@cmt/util';
import { RichTarget, Target } from '@cmt/drivers/cmakeDriver';
export interface ApiVersion {
major: number;
@ -260,7 +260,7 @@ export async function loadIndexFile(replyPath: string): Promise<Index.IndexFile
return JSON.parse(fileContent.toString()) as Index.IndexFile;
}
export async function loadCacheContent(filename: string): Promise<Map<string, api.CacheEntry>> {
export async function loadCacheContent(filename: string): Promise<Map<string, cache.CacheEntry>> {
const fileContent = await tryReadFile(filename);
if (!fileContent) {
return new Map();
@ -310,16 +310,16 @@ function findPropertyValue(cacheElement: Cache.CMakeCacheEntry, name: string): s
return propertyElement ? propertyElement.value : '';
}
function convertFileApiCacheToExtensionCache(cmakeCacheContent: Cache.CacheContent): Map<string, api.CacheEntry> {
function convertFileApiCacheToExtensionCache(cmakeCacheContent: Cache.CacheContent): Map<string, cache.CacheEntry> {
return cmakeCacheContent.entries.reduce((acc, el) => {
const fileApiToExtensionCacheMap: { [key: string]: api.CacheEntryType | undefined } = {
BOOL: api.CacheEntryType.Bool,
STRING: api.CacheEntryType.String,
PATH: api.CacheEntryType.Path,
FILEPATH: api.CacheEntryType.FilePath,
INTERNAL: api.CacheEntryType.Internal,
UNINITIALIZED: api.CacheEntryType.Uninitialized,
STATIC: api.CacheEntryType.Static
const fileApiToExtensionCacheMap: { [key: string]: cache.CacheEntryType | undefined } = {
BOOL: cache.CacheEntryType.Bool,
STRING: cache.CacheEntryType.String,
PATH: cache.CacheEntryType.Path,
FILEPATH: cache.CacheEntryType.FilePath,
INTERNAL: cache.CacheEntryType.Internal,
UNINITIALIZED: cache.CacheEntryType.Uninitialized,
STATIC: cache.CacheEntryType.Static
};
const type = fileApiToExtensionCacheMap[el.type];
if (type === undefined) {
@ -328,9 +328,9 @@ function convertFileApiCacheToExtensionCache(cmakeCacheContent: Cache.CacheConte
}
const helpString = findPropertyValue(el, 'HELPSTRING');
const advanced = findPropertyValue(el, 'ADVANCED');
acc.set(el.name, new cache.Entry(el.name, el.value, type, helpString, advanced === '1'));
acc.set(el.name, new cache.CacheEntry(el.name, el.value, type, helpString, advanced === '1'));
return acc;
}, new Map<string, api.CacheEntry>());
}, new Map<string, cache.CacheEntry>());
}
export async function loadCodeModelContent(filename: string): Promise<CodeModelKind.Content | null> {
@ -363,7 +363,7 @@ export async function loadTargetObject(filename: string): Promise<CodeModelKind.
return JSON.parse(fileContent.toString()) as CodeModelKind.TargetObject;
}
async function convertTargetObjectFileToExtensionTarget(buildDirectory: string, filePath: string): Promise<api.Target | null> {
async function convertTargetObjectFileToExtensionTarget(buildDirectory: string, filePath: string): Promise<Target | null> {
const targetObject = await loadTargetObject(filePath);
if (!targetObject) {
return null;
@ -382,10 +382,10 @@ async function convertTargetObjectFileToExtensionTarget(buildDirectory: string,
filepath: executablePath,
targetType: targetObject.type,
type: 'rich' as 'rich'
} as api.RichTarget;
} as RichTarget;
}
export async function loadAllTargetsForBuildTypeConfiguration(replyPath: string, buildDirectory: string, configuration: CodeModelKind.Configuration): Promise<{ name: string; targets: api.Target[] }> {
export async function loadAllTargetsForBuildTypeConfiguration(replyPath: string, buildDirectory: string, configuration: CodeModelKind.Configuration): Promise<{ name: string; targets: Target[] }> {
const metaTargets = [];
if (configuration.directories[0].hasInstallRule) {
metaTargets.push({
@ -403,7 +403,7 @@ export async function loadAllTargetsForBuildTypeConfiguration(replyPath: string,
};
}
export async function loadConfigurationTargetMap(replyPath: string, codeModelFileName: string): Promise<Map<string, api.Target[]>> {
export async function loadConfigurationTargetMap(replyPath: string, codeModelFileName: string): Promise<Map<string, Target[]>> {
const codeModelContent = await loadCodeModelContent(path.join(replyPath, codeModelFileName));
if (!codeModelContent) {
return new Map();
@ -414,7 +414,7 @@ export async function loadConfigurationTargetMap(replyPath: string, codeModelFil
return targets.reduce((acc, el) => {
acc.set(el.name, el.targets);
return acc;
}, new Map<string, api.Target[]>());
}, new Map<string, Target[]>());
}
function convertToAbsolutePath(inputPath: string, basePath: string) {

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

@ -1,7 +1,5 @@
import * as api from '@cmt/api';
import { ConfigureTrigger } from '@cmt/cmakeProject';
import { ExecutableTarget } from '@cmt/api';
import { CMakeCache } from '@cmt/cache';
import { CMakeCache, CacheEntry } from '@cmt/cache';
import { CMakeExecutable } from '@cmt/cmake/cmakeExecutable';
import { ConfigurationReader } from '@cmt/config';
import {
@ -15,7 +13,13 @@ import {
Index
} from '@cmt/drivers/cmakeFileApi';
import * as codeModel from '@cmt/drivers/codeModel';
import { CMakeDriver, CMakePreconditionProblemSolver } from '@cmt/drivers/cmakeDriver';
import {
CMakeDriver,
CMakePreconditionProblemSolver,
ExecutableTarget,
RichTarget,
Target
} from '@cmt/drivers/cmakeDriver';
import { CMakeGenerator, Kit } from '@cmt/kit';
import * as logging from '@cmt/logging';
import { fs } from '@cmt/pr';
@ -79,10 +83,10 @@ export class CMakeFileApiDriver extends CMakeDriver {
private readonly _cacheWatcher = vscode.workspace.createFileSystemWatcher(this.cachePath);
// Information from cmake file api
private _cache: Map<string, api.CacheEntry> = new Map<string, api.CacheEntry>();
private _cache: Map<string, CacheEntry> = new Map<string, CacheEntry>();
private _cmakeFiles: string[] | null = null;
private _generatorInformation: Index.GeneratorInformation | null = null;
private _target_map: Map<string, api.Target[]> = new Map();
private _target_map: Map<string, Target[]> = new Map();
async getGeneratorFromCache(cache_file_path: string): Promise<string | undefined> {
const cache = await CMakeCache.fromPath(cache_file_path);
@ -355,13 +359,13 @@ export class CMakeFileApiDriver extends CMakeDriver {
return this._codeModelContent;
}
get cmakeCacheEntries(): Map<string, api.CacheEntryProperties> {
get cmakeCacheEntries(): Map<string, CacheEntry> {
return this._cache;
}
get generatorName(): string | null {
return this._generatorInformation ? this._generatorInformation.name : null;
}
get targets(): api.Target[] {
get targets(): Target[] {
const targets = this._target_map.get(this.currentBuildType);
if (targets) {
const metaTargets = [{
@ -379,15 +383,15 @@ export class CMakeFileApiDriver extends CMakeDriver {
/**
* List of unique targets known to CMake
*/
get uniqueTargets(): api.Target[] {
get uniqueTargets(): Target[] {
return this.targets.reduce(targetReducer, []);
}
get executableTargets(): ExecutableTarget[] {
return this.uniqueTargets.filter(t => t.type === 'rich' && (t as api.RichTarget).targetType === 'EXECUTABLE')
return this.uniqueTargets.filter(t => t.type === 'rich' && (t as RichTarget).targetType === 'EXECUTABLE')
.map(t => ({
name: t.name,
path: (t as api.RichTarget).filepath
path: (t as RichTarget).filepath
}));
}
@ -407,14 +411,14 @@ export class CMakeFileApiDriver extends CMakeDriver {
* @param set the accumulator
* @t the RichTarget currently being examined.
*/
function targetReducer(set: api.Target[], t: api.Target): api.Target[] {
function targetReducer(set: Target[], t: Target): Target[] {
if (!set.find(t2 => compareTargets(t, t2))) {
set.push(t);
}
return set;
}
function compareTargets(a: api.Target, b: api.Target): boolean {
function compareTargets(a: Target, b: Target): boolean {
let same = false;
if (a.type === b.type) {
same = a.name === b.name;

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

@ -6,8 +6,7 @@
import { CMakeExecutable } from '@cmt/cmake/cmakeExecutable';
import * as vscode from 'vscode';
import * as api from '@cmt/api';
import { CMakeCache } from '@cmt/cache';
import { CMakeCache, CacheEntry } from '@cmt/cache';
import { CMakeDriver, CMakePreconditionProblemSolver } from '@cmt/drivers/cmakeDriver';
import { Kit, CMakeGenerator } from '@cmt/kit';
import * as logging from '@cmt/logging';
@ -198,7 +197,7 @@ export class CMakeLegacyDriver extends CMakeDriver {
}
get cmakeCacheEntries() {
let ret = new Map<string, api.CacheEntryProperties>();
let ret = new Map<string, CacheEntry>();
if (this.cmakeCache) {
ret = util.reduce(this.cmakeCache.allEntries, ret, (acc, entry) => acc.set(entry.key, entry));
}

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

@ -3,11 +3,15 @@ import { InputFileSet } from '@cmt/dirty';
import { ConfigureTrigger } from '@cmt/cmakeProject';
import * as path from 'path';
import * as vscode from 'vscode';
import * as api from '@cmt/api';
import { CacheEntryProperties, ExecutableTarget, RichTarget } from '@cmt/api';
import * as cache from '@cmt/cache';
import * as cms from '@cmt/drivers/cmakeServerClient';
import { CMakeDriver, CMakePreconditionProblemSolver } from '@cmt/drivers/cmakeDriver';
import {
CMakeDriver,
CMakePreconditionProblemSolver,
ExecutableTarget,
RichTarget,
Target
} from '@cmt/drivers/cmakeDriver';
import { Kit, CMakeGenerator } from '@cmt/kit';
import { createLogger } from '@cmt/logging';
import * as proc from '@cmt/proc';
@ -47,7 +51,7 @@ export class CMakeServerDriver extends CMakeDriver {
private _cmsClient: Promise<cms.CMakeServerClient | null> = Promise.resolve(null);
private _clientChangeInProgress: Promise<void> = Promise.resolve();
private _globalSettings!: cms.GlobalSettingsContent;
private _cacheEntries = new Map<string, cache.Entry>();
private _cacheEntries = new Map<string, cache.CacheEntry>();
private _cmakeInputFileSet = InputFileSet.createEmpty();
private readonly _progressEmitter = new vscode.EventEmitter<cms.ProgressMessage>();
@ -207,14 +211,14 @@ export class CMakeServerDriver extends CMakeDriver {
this._cmakeInputFileSet = await InputFileSet.create(cmake_inputs);
const clcache = await client.getCMakeCacheContent();
this._cacheEntries = clcache.cache.reduce((acc, el) => {
const entry_map: { [key: string]: api.CacheEntryType | undefined } = {
BOOL: api.CacheEntryType.Bool,
STRING: api.CacheEntryType.String,
PATH: api.CacheEntryType.Path,
FILEPATH: api.CacheEntryType.FilePath,
INTERNAL: api.CacheEntryType.Internal,
UNINITIALIZED: api.CacheEntryType.Uninitialized,
STATIC: api.CacheEntryType.Static
const entry_map: { [key: string]: cache.CacheEntryType | undefined } = {
BOOL: cache.CacheEntryType.Bool,
STRING: cache.CacheEntryType.String,
PATH: cache.CacheEntryType.Path,
FILEPATH: cache.CacheEntryType.FilePath,
INTERNAL: cache.CacheEntryType.Internal,
UNINITIALIZED: cache.CacheEntryType.Uninitialized,
STATIC: cache.CacheEntryType.Static
};
const type = entry_map[el.type];
if (type === undefined) {
@ -222,9 +226,9 @@ export class CMakeServerDriver extends CMakeDriver {
return acc;
}
acc.set(el.key,
new cache.Entry(el.key, el.value, type, el.properties.HELPSTRING, el.properties.ADVANCED === '1'));
new cache.CacheEntry(el.key, el.value, type, el.properties.HELPSTRING, el.properties.ADVANCED === '1'));
return acc;
}, new Map<string, cache.Entry>());
}, new Map<string, cache.CacheEntry>());
// Convert ServerCodeModel to general CodeModel.
this.codeModel = this.convertServerCodeModel(await client.codemodel());
this._codeModelChanged.fire(this.codeModel);
@ -287,7 +291,7 @@ export class CMakeServerDriver extends CMakeDriver {
.map(t => ({ name: t.name, path: t.filepath }));
}
get uniqueTargets(): api.Target[] {
get uniqueTargets(): Target[] {
return this.targets.reduce(targetReducer, []);
}
@ -318,7 +322,7 @@ export class CMakeServerDriver extends CMakeDriver {
return this._cmakeInputFileSet.checkOutOfDate();
}
get cmakeCacheEntries(): Map<string, CacheEntryProperties> {
get cmakeCacheEntries(): Map<string, cache.CacheEntry> {
return this._cacheEntries;
}

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

@ -1,114 +1,17 @@
import { CMakeCache } from "@cmt/cache";
export type TargetTypeString = 'STATIC_LIBRARY' | 'MODULE_LIBRARY' | 'SHARED_LIBRARY' | 'OBJECT_LIBRARY' | 'EXECUTABLE' | 'UTILITY' | 'INTERFACE_LIBRARY';
import * as api from "vscode-cmake-tools";
/** Describes a cmake target */
export interface CodeModelTarget {
/**
* A string specifying the logical name of the target.
*
* (Source CMake Documentation cmake-file-api(7))
*/
readonly name: string;
/**
* A string specifying the type of the target.
* The value is one of EXECUTABLE, STATIC_LIBRARY, SHARED_LIBRARY, MODULE_LIBRARY, OBJECT_LIBRARY, or UTILITY.
*
* (Source CMake Documentation cmake-file-api(7))
*
* \todo clarify need of INTERFACE_LIBRARY type
*/
type: TargetTypeString;
/** A string specifying the absolute path to the targets source directory. */
sourceDirectory?: string;
/** Name of the target artifact on disk (library or executable file name). */
fullName?: string;
/** List of absolute paths to a target´s build artifacts. */
artifacts?: string[];
/**
* The file groups describe a list of compilation information for artifacts of this target.
* The file groups contains source code files that use the same compilation information
* and are known by CMake.
*/
fileGroups?: CodeModelFileGroup[];
/**
* Represents the CMAKE_SYSROOT variable
*/
sysroot?: string;
}
/**
* Describes a file group to describe the build settings.
*/
export interface CodeModelFileGroup {
/** List of source files with the same compilation information */
sources: string[];
/** Specifies the language (C, C++, ...) for the toolchain */
language?: string;
/** Include paths for compilation of a source file */
includePath?: {
/** include path */
path: string;
}[];
/** Compiler flags */
compileCommandFragments?: string[];
/** Defines */
defines?: string[];
/** CMake generated file group */
isGenerated: boolean;
}
/**
* Describes cmake project and all its related targets
*/
export interface CodeModelProject {
/** Name of the project */
name: string;
/** List of targets */
targets: CodeModelTarget[];
/** Location of the Project */
sourceDirectory: string;
hasInstallRule?: boolean; // Exists in ServerCodeModelProject.
}
/**
* Describes cmake configuration
*/
export interface CodeModelConfiguration {
/** List of project() from CMakeLists.txt */
projects: CodeModelProject[];
/** Name of the active configuration in a multi-configuration generator.*/
name: string;
}
export interface CodeModelToolchain {
path: string;
target?: string;
}
/** Describes the cmake model */
export interface CodeModelContent {
/** List of configurations provided by the selected generator */
configurations: CodeModelConfiguration[];
toolchains?: Map<string, CodeModelToolchain>;
}
// Re-export API types. This gives us flexibility add fields to the internal
// representation of these data structures in the future without modifying the
// public API.
export type CodeModelConfiguration = api.CodeModel.Configuration;
export type CodeModelContent = api.CodeModel.Content;
export type CodeModelFileGroup = api.CodeModel.FileGroup;
export type CodeModelProject = api.CodeModel.Project;
export type CodeModelTarget = api.CodeModel.Target;
export type CodeModelToolchain = api.CodeModel.Toolchain;
export type TargetTypeString = api.CodeModel.TargetType;
/**
* Type given when updating the configuration data stored in the file index.

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

@ -10,6 +10,7 @@ import * as path from 'path';
import * as vscode from 'vscode';
import * as cpt from 'vscode-cpptools';
import * as nls from 'vscode-nls';
import * as api from 'vscode-cmake-tools';
import { CMakeCache } from '@cmt/cache';
import { CMakeProject, ConfigureType, ConfigureTrigger } from '@cmt/cmakeProject';
@ -41,6 +42,7 @@ import paths from '@cmt/paths';
import { CMakeDriver, CMakePreconditionProblems } from './drivers/cmakeDriver';
import { platform } from 'os';
import { defaultBuildPreset } from './preset';
import { CMakeToolsApiImpl } from './api';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
@ -78,7 +80,7 @@ interface Diagnostics {
* necessitate user input, this class acts as intermediary and will send
* important information down to the lower layers.
*/
class ExtensionManager implements vscode.Disposable {
export class ExtensionManager implements vscode.Disposable {
constructor(public readonly extensionContext: vscode.ExtensionContext) {
telemetry.activate(extensionContext);
this.showCMakeLists = new Promise<boolean>(resolve => {
@ -120,7 +122,9 @@ class ExtensionManager implements vscode.Disposable {
// We already have this folder, do nothing
} else {
const subs: vscode.Disposable[] = [];
subs.push(newCmt.onCodeModelChanged(FireLate, () => this.updateCodeModel(cmakeWorkspaceFolder)));
subs.push(newCmt.onCodeModelChanged(FireLate, () => {
this.updateCodeModel(cmakeWorkspaceFolder);
}));
subs.push(newCmt.onTargetNameChanged(FireLate, () => this.updateCodeModel(cmakeWorkspaceFolder)));
subs.push(newCmt.onLaunchTargetNameChanged(FireLate, () => this.updateCodeModel(cmakeWorkspaceFolder)));
subs.push(newCmt.onActiveBuildPresetChanged(FireLate, () => this.updateCodeModel(cmakeWorkspaceFolder)));
@ -167,6 +171,7 @@ class ExtensionManager implements vscode.Disposable {
}
this.statusBar.setAutoSelectActiveFolder(v);
});
this.api = new CMakeToolsApiImpl(this);
}
private onDidChangeActiveTextEditorSub: vscode.Disposable = new DummyDisposable();
@ -204,7 +209,9 @@ class ExtensionManager implements vscode.Disposable {
for (const cmakeWorkspaceFolder of this.cmakeWorkspaceFolders) {
this.onUseCMakePresetsChangedSub = cmakeWorkspaceFolder.onUseCMakePresetsChanged(useCMakePresets => this.statusBar.useCMakePresets(useCMakePresets));
this.codeModelUpdateSubs.set(cmakeWorkspaceFolder.folder.uri.fsPath, [
cmakeWorkspaceFolder.cmakeProject.onCodeModelChanged(FireLate, () => this.updateCodeModel(cmakeWorkspaceFolder)),
cmakeWorkspaceFolder.cmakeProject.onCodeModelChanged(FireLate, () => {
this.updateCodeModel(cmakeWorkspaceFolder);
}),
cmakeWorkspaceFolder.cmakeProject.onTargetNameChanged(FireLate, () => this.updateCodeModel(cmakeWorkspaceFolder)),
cmakeWorkspaceFolder.cmakeProject.onLaunchTargetNameChanged(FireLate, () => this.updateCodeModel(cmakeWorkspaceFolder)),
cmakeWorkspaceFolder.cmakeProject.onActiveBuildPresetChanged(FireLate, () => this.updateCodeModel(cmakeWorkspaceFolder))
@ -266,7 +273,7 @@ class ExtensionManager implements vscode.Disposable {
/**
* The folder controller manages multiple instances. One per folder.
*/
private readonly cmakeWorkspaceFolders = new CMakeWorkspaceFolderController(this.extensionContext);
public readonly cmakeWorkspaceFolders = new CMakeWorkspaceFolderController(this.extensionContext);
/**
* The map caching for each folder whether it is a CMake project or not.
@ -679,6 +686,7 @@ class ExtensionManager implements vscode.Disposable {
}
this.projectOutlineProvider.setActiveFolder(ws);
this.setupSubscriptions();
this.onActiveProjectChangedEmitter.fire(ws?.uri);
}
private disposeSubs() {
@ -810,10 +818,12 @@ class ExtensionManager implements vscode.Disposable {
this.statusMessageSub = cmakeProject.onStatusMessageChanged(FireNow, s => this.statusBar.setStatusMessage(s));
this.targetNameSub = cmakeProject.onTargetNameChanged(FireNow, t => {
this.statusBar.setBuildTargetName(t);
this.onBuildTargetChangedEmitter.fire(t);
});
this.buildTypeSub = cmakeProject.onActiveVariantNameChanged(FireNow, bt => this.statusBar.setVariantLabel(bt));
this.launchTargetSub = cmakeProject.onLaunchTargetNameChanged(FireNow, t => {
this.statusBar.setLaunchTargetName(t || '');
this.onLaunchTargetChangedEmitter.fire(t || '');
});
this.ctestEnabledSub = cmakeProject.onCTestEnabledChanged(FireNow, e => this.statusBar.setCTestEnabled(e));
this.testResultsSub = cmakeProject.onTestResultsChanged(FireNow, r => this.statusBar.setTestResults(r));
@ -1614,9 +1624,26 @@ class ExtensionManager implements vscode.Disposable {
return presetSelected;
}
public api: CMakeToolsApiImpl;
get onBuildTargetChanged() {
return this.onBuildTargetChangedEmitter.event;
}
private readonly onBuildTargetChangedEmitter = new vscode.EventEmitter<string>();
get onLaunchTargetChanged() {
return this.onLaunchTargetChangedEmitter.event;
}
private readonly onLaunchTargetChangedEmitter = new vscode.EventEmitter<string>();
get onActiveProjectChanged() {
return this.onActiveProjectChangedEmitter.event;
}
private readonly onActiveProjectChangedEmitter = new vscode.EventEmitter<vscode.Uri | undefined>();
}
async function setup(context: vscode.ExtensionContext, progress?: ProgressHandle) {
async function setup(context: vscode.ExtensionContext, progress?: ProgressHandle): Promise<api.CMakeToolsExtensionExports> {
reportProgress(localize('initial.setup', 'Initial setup'), progress);
// Load a new extension manager
@ -1776,6 +1803,8 @@ async function setup(context: vscode.ExtensionContext, progress?: ProgressHandle
vscode.commands.registerCommand('cmake.outline.selectWorkspace',
(what: WorkspaceFolderNode) => runCommand('selectWorkspace', what.wsFolder))
]);
return { getApi: (_version) => ext.api };
}
class SchemaProvider implements vscode.TextDocumentContentProvider {
@ -1797,7 +1826,7 @@ class SchemaProvider implements vscode.TextDocumentContentProvider {
* @param context The extension context
* @returns A promise that will resolve when the extension is ready for use
*/
export async function activate(context: vscode.ExtensionContext) {
export async function activate(context: vscode.ExtensionContext): Promise<api.CMakeToolsExtensionExports> {
// CMakeTools versions newer or equal to #1.2 should not coexist with older versions
// because the publisher changed (from vector-of-bool into ms-vscode),
// causing many undesired behaviors (duplicate operations, registrations for UI elements, etc...)
@ -1816,10 +1845,6 @@ export async function activate(context: vscode.ExtensionContext) {
taskProvider = vscode.tasks.registerTaskProvider(CMakeTaskProvider.CMakeScriptType, cmakeTaskProvider);
return setup(context);
// TODO: Return the extension API
// context.subscriptions.push(vscode.commands.registerCommand('cmake._extensionInstance', () => cmakeProject));
}
// Enable all or part of the CMake Tools palette commands

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

@ -12,9 +12,7 @@ import rollbar from './rollbar';
import * as util from './util';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { ExecutionResult } from './api';
import { Environment, EnvironmentUtils } from './environmentVariables';
export { ExecutionResult } from './api';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
@ -76,6 +74,26 @@ export interface BuildCommand {
export interface DebuggerEnvironmentVariable { name: string; value: string }
/**
* The result of executing a program.
*/
export interface ExecutionResult {
/**
* The return code of the program.
*/
retc: number | null;
/**
* The full standard output of the program. May be `` if standard out
* was not captured.
*/
stdout: string;
/**
* Standard error output of the program. May be `` if standard error was
* not captured
*/
stderr: string;
}
export interface ExecutionOptions {
environment?: Environment;
shell?: boolean;

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

@ -849,3 +849,11 @@ export async function scheduleAsyncTask<T>(task: () => Promise<T>): Promise<T> {
});
});
}
/**
* Asserts that the given value has no valid type. Useful for exhaustiveness checks.
* @param value The value to be checked.
*/
export function assertNever(value: never): never {
throw new Error(`Unexpected value: ${value}`);
}

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

@ -1,5 +1,4 @@
import * as api from '@cmt/api';
import { CMakeCache } from '@cmt/cache';
import { CMakeCache, CacheEntryType } from '@cmt/cache';
import { clearExistingKitConfigurationFile, DefaultEnvironment, expect, getFirstSystemKit } from '@test/util';
import { fs } from '@cmt/pr';
import * as path from 'path';
@ -51,7 +50,7 @@ suite('Environment Variables in Variants', () => {
const cacheEntry_ = cache.get('variantEnv');
expect(cacheEntry_).to.not.be.eq(null, '[variantEnv] Cache entry was not present');
const cacheEntry = cacheEntry_!;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[variantEnv] unexpected cache entry type');
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[variantEnv] unexpected cache entry type');
expect(cacheEntry.key).to.eq('variantEnv', '[variantEnv] unexpected cache entry key name');
expect(typeof cacheEntry.value).to.eq('string', '[variantEnv] unexpected cache entry value type');
expect(cacheEntry.as<string>())

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

@ -1,5 +1,4 @@
import * as api from '@cmt/api';
import { CMakeCache } from '@cmt/cache';
import { CMakeCache, CacheEntryType } from '@cmt/cache';
import { clearExistingKitConfigurationFile, DefaultEnvironment, expect, getFirstSystemKit } from '@test/util';
import { fs } from '@cmt/pr';
import * as path from 'path';
@ -57,7 +56,7 @@ suite('Environment Variables in Variants', () => {
const cacheEntry_ = cache.get('variantEnv');
expect(cacheEntry_).to.not.be.eq(null, '[variantEnv] Cache entry was not present');
const cacheEntry = cacheEntry_!;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[variantEnv] unexpected cache entry type');
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[variantEnv] unexpected cache entry type');
expect(cacheEntry.key).to.eq('variantEnv', '[variantEnv] unexpected cache entry key name');
expect(typeof cacheEntry.value).to.eq('string', '[variantEnv] unexpected cache entry value type');
expect(cacheEntry.as<string>())

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

@ -1,5 +1,4 @@
import * as api from '@cmt/api';
import { CMakeCache } from '@cmt/cache';
import { CMakeCache, CacheEntryType } from '@cmt/cache';
import { DefaultEnvironment, expect } from '@test/util';
import * as vscode from 'vscode';
@ -40,7 +39,7 @@ suite('Environment Variables in Presets', () => {
const cacheEntry_ = cache.get('variantEnv');
expect(cacheEntry_).to.not.be.eq(null, '[variantEnv] Cache entry was not present');
const cacheEntry = cacheEntry_!;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[variantEnv] unexpected cache entry type');
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[variantEnv] unexpected cache entry type');
expect(cacheEntry.key).to.eq('variantEnv', '[variantEnv] unexpected cache entry key name');
expect(typeof cacheEntry.value).to.eq('string', '[variantEnv] unexpected cache entry value type');
expect(cacheEntry.as<string>()).to.eq('0cbfb6ae-f2ec-4017-8ded-89df8759c502', '[variantEnv] incorrect environment variable');

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

@ -1,5 +1,4 @@
import * as api from '@cmt/api';
import { CMakeCache } from '@cmt/cache';
import { CMakeCache, CacheEntryType, CacheEntry } from '@cmt/cache';
import { CMakeProject } from '@cmt/cmakeProject';
import { clearExistingKitConfigurationFile, DefaultEnvironment, expect, getFirstSystemKit } from '@test/util';
import * as path from 'path';
@ -42,8 +41,8 @@ suite('Environment', () => {
expect(testEnv.projectFolder.buildDirectory.isCMakeCachePresent).to.eql(true, 'expected cache not present');
const cache = await CMakeCache.fromPath(await cmakeProject.cachePath);
const cacheEntry = cache.get('configureEnvironment') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[configureEnvironment] unexpected cache entry type');
const cacheEntry = cache.get('configureEnvironment') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[configureEnvironment] unexpected cache entry type');
expect(cacheEntry.key).to.eq('configureEnvironment', '[configureEnvironment] unexpected cache entry key name');
expect(cacheEntry.as<string>())
.to.eq(path.basename(testEnv.projectFolder.location), '[configureEnvironment] substitution incorrect');
@ -64,8 +63,8 @@ suite('Environment', () => {
expect(testEnv.projectFolder.buildDirectory.isCMakeCachePresent).to.eql(true, 'expected cache not present');
const cache = await CMakeCache.fromPath(await cmakeProject.cachePath);
const cacheEntry = cache.get('buildEnvironment') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[buildEnvironment] unexpected cache entry type');
const cacheEntry = cache.get('buildEnvironment') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[buildEnvironment] unexpected cache entry type');
expect(cacheEntry.key).to.eq('buildEnvironment', '[buildEnvironment] unexpected cache entry key name');
expect(cacheEntry.as<string>()).to.be.eq('', '[buildEnvironment] env-var got passed to CMake');
expect(typeof cacheEntry.value).to.eq('string', '[buildEnvironment] unexpected cache entry value type');
@ -86,8 +85,8 @@ suite('Environment', () => {
expect(testEnv.projectFolder.buildDirectory.isCMakeCachePresent).to.eql(true, 'expected cache not present');
const cache = await CMakeCache.fromPath(await cmakeProject.cachePath);
const cacheEntry = cache.get('environment') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[environment] unexpected cache entry type');
const cacheEntry = cache.get('environment') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[environment] unexpected cache entry type');
expect(cacheEntry.key).to.eq('environment', '[environment] unexpected cache entry key name');
expect(cacheEntry.as<string>())
.to.eq(path.basename(testEnv.projectFolder.location), '[environment] substitution incorrect');

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

@ -1,6 +1,5 @@
/* eslint-disable no-unused-expressions */
import * as api from '@cmt/api';
import { CMakeCache } from '@cmt/cache';
import { CMakeCache, CacheEntry } from '@cmt/cache';
import { CMakeProject, ConfigureTrigger } from '@cmt/cmakeProject';
import { readKitsFile, kitsForWorkspaceDirectory, getAdditionalKits, USER_KITS_FILEPATH } from '@cmt/kit';
import { platformNormalizePath } from '@cmt/util';
@ -52,7 +51,7 @@ suite('Toolchain Substitution', () => {
expect(testEnv.projectFolder.buildDirectory.isCMakeCachePresent).to.eql(true, 'expected cache not present');
const cache = await CMakeCache.fromPath(await cmakeProject.cachePath);
const cacheEntry = cache.get('CMAKE_TOOLCHAIN_FILE') as api.CacheEntry;
const cacheEntry = cache.get('CMAKE_TOOLCHAIN_FILE') as CacheEntry;
expect(cacheEntry).to.not.be.null;
expect(cacheEntry.key).to.eq('CMAKE_TOOLCHAIN_FILE', '[toolchain] unexpected cache entry key name');
expect(platformNormalizePath(cacheEntry.as<string>()))

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

@ -1,5 +1,4 @@
import * as api from '@cmt/api';
import { CMakeCache } from '@cmt/cache';
import { CMakeCache, CacheEntryType, CacheEntry } from '@cmt/cache';
import { CMakeProject, ConfigureTrigger } from '@cmt/cmakeProject';
import paths from '@cmt/paths';
import { objectPairs, platformNormalizePath, makeHashString } from '@cmt/util';
@ -55,46 +54,46 @@ suite('Variable Substitution', () => {
const cache = await CMakeCache.fromPath(await cmakeProject.cachePath);
// Check substitution for "workspaceRoot"
let cacheEntry = cache.get('workspaceRoot') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[workspaceRoot] unexpected cache entry type');
let cacheEntry = cache.get('workspaceRoot') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[workspaceRoot] unexpected cache entry type');
expect(cacheEntry.key).to.eq('workspaceRoot', '[workspaceRoot] unexpected cache entry key name');
expect(platformNormalizePath(cacheEntry.as<string>()))
.to.eq(platformNormalizePath(testEnv.projectFolder.location), '[workspaceRoot] substitution incorrect');
expect(typeof cacheEntry.value).to.eq('string', '[workspaceRoot] unexpected cache entry value type');
// Check substitution for "workspaceFolder".
cacheEntry = cache.get('workspaceFolder') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[workspaceFolder] unexpected cache entry type');
cacheEntry = cache.get('workspaceFolder') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[workspaceFolder] unexpected cache entry type');
expect(cacheEntry.key).to.eq('workspaceFolder', '[workspaceFolder] unexpected cache entry key name');
expect(platformNormalizePath(cacheEntry.as<string>()))
.to.eq(platformNormalizePath(testEnv.projectFolder.location), '[workspaceFolder] substitution incorrect');
expect(typeof cacheEntry.value).to.eq('string', '[workspaceFolder] unexpected cache entry value type');
// Check substitution for "workspaceHash".
cacheEntry = cache.get('workspaceHash') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[workspaceHash] unexpected cache entry type');
cacheEntry = cache.get('workspaceHash') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[workspaceHash] unexpected cache entry type');
expect(cacheEntry.key).to.eq('workspaceHash', '[workspaceHash] unexpected cache entry key name');
expect(cacheEntry.as<string>()).to.eq(makeHashString(testEnv.projectFolder.location), '[workspaceHash] substitution incorrect');
expect(typeof cacheEntry.value).to.eq('string', '[workspaceHash] unexpected cache entry value type');
// Check substitution for "buildType".
cacheEntry = cache.get('buildType') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[buildType] unexpected cache entry type');
cacheEntry = cache.get('buildType') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[buildType] unexpected cache entry type');
expect(cacheEntry.key).to.eq('buildType', '[buildType] unexpected cache entry key name');
expect(cacheEntry.as<string>()).to.eq('Debug', '[buildType] substitution incorrect');
expect(typeof cacheEntry.value).to.eq('string', '[buildType] unexpected cache entry value type');
// Check substitution for "buildKit".
cacheEntry = cache.get('buildKit') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[buildKit] unexpected cache entry type');
cacheEntry = cache.get('buildKit') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[buildKit] unexpected cache entry type');
expect(cacheEntry.key).to.eq('buildKit', '[buildKit] unexpected cache entry key name');
const kit = cmakeProject.activeKit;
expect(cacheEntry.as<string>()).to.eq(kit!.name, '[buildKit] substitution incorrect');
expect(typeof cacheEntry.value).to.eq('string', '[buildKit] unexpected cache entry value type');
// Check substitution for "workspaceRootFolderName".
cacheEntry = cache.get('workspaceRootFolderName') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[workspaceRootFolderName] unexpected cache entry type');
cacheEntry = cache.get('workspaceRootFolderName') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[workspaceRootFolderName] unexpected cache entry type');
expect(cacheEntry.key)
.to.eq('workspaceRootFolderName', '[workspaceRootFolderName] unexpected cache entry key name');
expect(cacheEntry.as<string>())
@ -102,8 +101,8 @@ suite('Variable Substitution', () => {
expect(typeof cacheEntry.value).to.eq('string', '[workspaceRootFolderName] unexpected cache entry value type');
// Check substitution for "workspaceFolderBasename".
cacheEntry = cache.get('workspaceFolderBasename') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[workspaceFolderBasename] unexpected cache entry type');
cacheEntry = cache.get('workspaceFolderBasename') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[workspaceFolderBasename] unexpected cache entry type');
expect(cacheEntry.key)
.to.eq('workspaceFolderBasename', '[workspaceFolderBasename] unexpected cache entry key name');
expect(cacheEntry.as<string>())
@ -111,23 +110,23 @@ suite('Variable Substitution', () => {
expect(typeof cacheEntry.value).to.eq('string', '[workspaceFolderBasename] unexpected cache entry value type');
// Check substitution for "generator".
cacheEntry = cache.get('generator') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[generator] unexpected cache entry type');
cacheEntry = cache.get('generator') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[generator] unexpected cache entry type');
expect(cacheEntry.key).to.eq('generator', '[generator] unexpected cache entry key name');
const generator = cache.get('CMAKE_GENERATOR') as api.CacheEntry;
const generator = cache.get('CMAKE_GENERATOR') as CacheEntry;
expect(cacheEntry.as<string>()).to.eq(generator.as<string>(), '[generator] substitution incorrect');
expect(typeof cacheEntry.value).to.eq('string', '[generator] unexpected cache entry value type');
// Check substitution for "userHome".
cacheEntry = cache.get('userHome') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[userHome] unexpected cache entry type');
cacheEntry = cache.get('userHome') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[userHome] unexpected cache entry type');
expect(cacheEntry.key).to.eq('userHome', '[userHome] unexpected cache entry key name');
expect(cacheEntry.as<string>()).to.eq(paths.userHome, '[userHome] substitution incorrect');
expect(typeof cacheEntry.value).to.eq('string', '[userHome] unexpected cache entry value type');
// Check substitution within "cmake.installPrefix".
cacheEntry = cache.get('CMAKE_INSTALL_PREFIX') as api.CacheEntry;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[cmakeInstallPrefix] unexpected cache entry type');
cacheEntry = cache.get('CMAKE_INSTALL_PREFIX') as CacheEntry;
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[cmakeInstallPrefix] unexpected cache entry type');
expect(cacheEntry.key).to.eq('CMAKE_INSTALL_PREFIX', '[cmakeInstallPrefix] unexpected cache entry key name');
expect(platformNormalizePath(cacheEntry.as<string>()))
.to.eq(platformNormalizePath(testEnv.projectFolder.buildDirectory.location.concat('/dist')),
@ -155,10 +154,10 @@ suite('Variable Substitution', () => {
const [key, expected] = testKey;
// Get cache entry for given test key
const cacheEntry = testCache.get(key) as api.CacheEntry;
const cacheEntry = testCache.get(key) as CacheEntry;
// Check type and value of the retrieved cache entry
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, `[variant:${key}] unexpected cache entry type`);
expect(cacheEntry.type).to.eq(CacheEntryType.String, `[variant:${key}] unexpected cache entry type`);
expect(cacheEntry.key).to.eql(key, `[variant:${key}] unexpected cache entry key name`);
expect(cacheEntry.as<string>()).to.eql(expected, `[variant:${key}] incorrect substitution`);
expect(typeof cacheEntry.value).to.eq('string', `[variant:${key}] unexpected cache entry value type`);

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

@ -1,7 +1,6 @@
/* eslint-disable no-unused-expressions */
import * as api from '@cmt/api';
import { CMakeCache } from '@cmt/cache';
import { CMakeCache, CacheEntryType } from '@cmt/cache';
import { CMakeProject } from '@cmt/cmakeProject';
import { clearExistingKitConfigurationFile, DefaultEnvironment, expect, getFirstSystemKit } from '@test/util';
import { fs } from '@cmt/pr';
@ -80,7 +79,7 @@ suite('Environment Variables in Variants', () => {
const cacheEntry_ = cache.get('variantEnv');
expect(cacheEntry_).to.not.be.eq(null, '[variantEnv] Cache entry was not present');
const cacheEntry = cacheEntry_!;
expect(cacheEntry.type).to.eq(api.CacheEntryType.String, '[variantEnv] unexpected cache entry type');
expect(cacheEntry.type).to.eq(CacheEntryType.String, '[variantEnv] unexpected cache entry type');
expect(cacheEntry.key).to.eq('variantEnv', '[variantEnv] unexpected cache entry key name');
expect(typeof cacheEntry.value).to.eq('string', '[variantEnv] unexpected cache entry value type');
expect(cacheEntry.as<string>()).to.eq('0xCAFE', '[variantEnv] incorrect environment variable');

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

@ -6,8 +6,7 @@ import * as path from 'path';
chai.use(chaiAsPromised);
import { expect } from 'chai';
import * as api from '../../src/api';
import { CMakeCache } from '../../src/cache';
import { CMakeCache, CacheEntryType, CacheEntry } from '../../src/cache';
import * as util from '../../src/util';
const here = __dirname;
@ -18,14 +17,14 @@ function getTestResourceFilePath(filename: string): string {
suite('Cache test', () => {
test('Read CMake Cache', async () => {
const cache = await CMakeCache.fromPath(getTestResourceFilePath('TestCMakeCache.txt'));
const generator = cache.get('CMAKE_GENERATOR') as api.CacheEntry;
expect(generator.type).to.eq(api.CacheEntryType.Internal);
const generator = cache.get('CMAKE_GENERATOR') as CacheEntry;
expect(generator.type).to.eq(CacheEntryType.Internal);
expect(generator.key).to.eq('CMAKE_GENERATOR');
expect(generator.as<string>()).to.eq('Ninja');
expect(typeof generator.value).to.eq('string');
const build_testing = cache.get('BUILD_TESTING') as api.CacheEntry;
expect(build_testing.type).to.eq(api.CacheEntryType.Bool);
const build_testing = cache.get('BUILD_TESTING') as CacheEntry;
expect(build_testing.type).to.eq(CacheEntryType.Bool);
expect(build_testing.as<boolean>()).to.be.true;
});
test('Read cache with various newlines', async () => {
@ -36,7 +35,7 @@ suite('Cache test', () => {
expect(entries.has('SOMETHING')).to.be.true;
const entry = entries.get('SOMETHING')!;
expect(entry.value).to.eq('foo');
expect(entry.type).to.eq(api.CacheEntryType.String);
expect(entry.type).to.eq(CacheEntryType.String);
expect(entry.helpString).to.eq('This line is docs');
}
});
@ -47,7 +46,7 @@ suite('Cache test', () => {
expect(entries.has('FIRSTPART:SECONDPART')).to.be.true;
const entry = entries.get('FIRSTPART:SECONDPART')!;
expect(entry.value).to.eq('value');
expect(entry.type).to.eq(api.CacheEntryType.String);
expect(entry.type).to.eq(CacheEntryType.String);
});
test('Read cache entry with double quotes, but no colon', async () => {
const str = "\"QUOTED\":STRING=value";
@ -56,7 +55,7 @@ suite('Cache test', () => {
expect(entries.has('QUOTED')).to.be.true;
const entry = entries.get('QUOTED')!;
expect(entry.value).to.eq('value');
expect(entry.type).to.eq(api.CacheEntryType.String);
expect(entry.type).to.eq(CacheEntryType.String);
});
test('Falsey values', () => {
const false_things = [

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

@ -6250,6 +6250,11 @@ vsce@^2.7.0:
yauzl "^2.3.1"
yazl "^2.2.2"
vscode-cmake-tools@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/vscode-cmake-tools/-/vscode-cmake-tools-1.0.0.tgz#0321a74cd073c858c440b01602e164ecb824bd98"
integrity sha512-oZO+Ulo+OGaFuGQaPlNh+qDUWth0kmyz0d45Ws6KmqLGf1Q9gCOBct6jz7mAtzR/IqrCe/VV3O/CUmHfXpIZEA==
vscode-cpptools@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/vscode-cpptools/-/vscode-cpptools-6.1.0.tgz#d89bb225f91da45dbee6acbf45f6940aa3926df1"