Skip to content

Commit

Permalink
Read cmake file API on open (#1909)
Browse files Browse the repository at this point in the history
  • Loading branch information
elahehrashedi authored Jun 21, 2021
1 parent a48d1ac commit 0113841
Show file tree
Hide file tree
Showing 12 changed files with 343 additions and 239 deletions.
28 changes: 20 additions & 8 deletions src/cmake-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const CMAKE_LOGGER = logging.createLogger('cmake');
export enum ConfigureType {
Normal,
Clean,
Cache,
Cache
}

export enum ConfigureTrigger {
Expand Down Expand Up @@ -358,9 +358,9 @@ export class CMakeTools implements vscode.Disposable, api.CMakeToolsAPI {
/**
* Event fired when the code model from CMake is updated
*/
get codeModel() { return this._codeModel.value; }
get onCodeModelChanged() { return this._codeModel.changeEvent; }
private readonly _codeModel = new Property<codemodel_api.CodeModelContent|null>(null);
get codeModelContent() { return this._codeModelContent.value; }
get onCodeModelChanged() { return this._codeModelContent.changeEvent; }
private readonly _codeModelContent = new Property<codemodel_api.CodeModelContent|null>(null);
private _codeModelDriverSub: vscode.Disposable|null = null;

private readonly _communicationModeSub = this.workspaceContext.config.onChange('cmakeCommunicationMode', () => {
Expand Down Expand Up @@ -904,8 +904,8 @@ export class CMakeTools implements vscode.Disposable, api.CMakeToolsAPI {
}
const drv = await this._cmakeDriver;
console.assert(drv !== null, 'Null driver immediately after creation?');
if (drv instanceof codemodel_api.CodeModelDriver) {
this._codeModelDriverSub = drv.onCodeModelChanged(cm => { this._codeModel.set(cm); });
if (drv && !(drv instanceof LegacyCMakeDriver)) {
this._codeModelDriverSub = drv.onCodeModelChanged(cm => { this._codeModelContent.set(cm); });
}
}

Expand Down Expand Up @@ -990,7 +990,20 @@ export class CMakeTools implements vscode.Disposable, api.CMakeToolsAPI {
return this.configureInternal(ConfigureTrigger.api, extra_args, ConfigureType.Normal);
}

configureInternal(trigger: ConfigureTrigger = ConfigureTrigger.api, extra_args: string[] = [], type: ConfigureType = ConfigureType.Normal): Thenable<number> {
async configureInternal(trigger: ConfigureTrigger = ConfigureTrigger.api, extra_args: string[] = [], type: ConfigureType = ConfigureType.Normal): Promise<number> {
const drv: CMakeDriver | null = await this.getCMakeDriverInstance();
// Don't show a progress bar when the extension is using Cache for configuration.
// Using cache for configuration happens only one time.
if (drv && drv.shouldUseCachedConfiguration(trigger)) {
const retc: number = await drv.configure(trigger, []);
if (retc === 0) {
await this._refreshCompileDatabase(drv.expansionOptions);
}
await this._ctestController.reloadTests(drv);
this._onReconfiguredEmitter.fire();
return retc;
}

return vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
Expand All @@ -1000,7 +1013,6 @@ export class CMakeTools implements vscode.Disposable, api.CMakeToolsAPI {
progress.report({message: localize('preparing.to.configure', 'Preparing to configure')});
log.info(localize('run.configure', 'Configuring folder: {0}', this.folderName), extra_args);
return this._doConfigure(progress, async consumer => {
const drv = await this.getCMakeDriverInstance();
const IS_CONFIGURING_KEY = 'cmake:isConfiguring';
if (drv) {
let old_prog = 0;
Expand Down
10 changes: 5 additions & 5 deletions src/cpptools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ export interface CodeModelParams {
/**
* The CMake codemodel content. This is the important one.
*/
codeModel: codemodel_api.CodeModelContent;
codeModelContent: codemodel_api.CodeModelContent;
/**
* The contents of the CMakeCache.txt, which also provides supplementary
* configuration information.
Expand Down Expand Up @@ -413,9 +413,9 @@ export class CppConfigurationProvider implements cpt.CustomConfigurationProvider
const lang = fileGroup.language === "RC" ? undefined : fileGroup.language;
// First try to get toolchain values directly reported by CMake. Check the
// group's language compiler, then the C++ compiler, then the C compiler.
const comp_toolchains = opts.codeModel.toolchains?.get(lang ?? "")
|| opts.codeModel.toolchains?.get('CXX')
|| opts.codeModel.toolchains?.get('C');
const comp_toolchains: codemodel_api.CodeModelToolchain | undefined = opts.codeModelContent.toolchains?.get(lang ?? "")
|| opts.codeModelContent.toolchains?.get('CXX')
|| opts.codeModelContent.toolchains?.get('C');
// If none of those work, fall back to the same order, but in the cache.
const comp_cache = opts.cache.get(`CMAKE_${lang}_COMPILER`)
|| opts.cache.get('CMAKE_CXX_COMPILER')
Expand Down Expand Up @@ -525,7 +525,7 @@ export class CppConfigurationProvider implements cpt.CustomConfigurationProvider
let hadMissingCompilers = false;
this._workspaceBrowseConfiguration = {browsePath: []};
this._activeTarget = opts.activeTarget;
for (const config of opts.codeModel.configurations) {
for (const config of opts.codeModelContent.configurations) {
for (const project of config.projects) {
for (const target of project.targets) {
// Now some shenanigans since header files don't have config data:
Expand Down
6 changes: 5 additions & 1 deletion src/drivers/cmakefileapi/api_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,11 @@ export async function loadToolchains(filename: string): Promise<Map<string, Code

return toolchains.toolchains.reduce((acc, el) => {
if (el.compiler.path) {
acc.set(el.language, { path: el.compiler.path, target: el.compiler.target });
if (el.compiler.target) {
acc.set(el.language, { path: el.compiler.path, target: el.compiler.target });
} else {
acc.set(el.language, { path: el.compiler.path });
}
}
return acc;
}, new Map<string, CodeModelToolchain>());
Expand Down
25 changes: 18 additions & 7 deletions src/drivers/cmfileapi-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
loadToolchains
} from '@cmt/drivers/cmakefileapi/api_helpers';
import * as codemodel from '@cmt/drivers/codemodel-driver-interface';
import {CMakePreconditionProblemSolver} from '@cmt/drivers/driver';
import {CMakeDriver, CMakePreconditionProblemSolver} from '@cmt/drivers/driver';
import {CMakeGenerator, Kit} from '@cmt/kit';
import * as logging from '@cmt/logging';
import {fs} from '@cmt/pr';
Expand All @@ -37,7 +37,12 @@ const log = logging.createLogger('cmakefileapi-driver');
/**
* The CMake driver with FileApi of CMake >= 3.15.0
*/
export class CMakeFileApiDriver extends codemodel.CodeModelDriver {
export class CMakeFileApiDriver extends CMakeDriver {

get isCacheConfigSupported(): boolean {
return true;
}

private constructor(cmake: CMakeExecutable,
readonly config: ConfigurationReader,
workspaceRootPath: string|null,
Expand Down Expand Up @@ -198,6 +203,12 @@ export class CMakeFileApiDriver extends codemodel.CodeModelDriver {
await this._cleanPriorConfiguration();
}

async doCacheConfigure(): Promise<number> {
this._needsReconfigure = true;
await this.updateCodeModel();
return 0;
}

async doConfigure(args_: string[], outputConsumer?: proc.OutputConsumer): Promise<number> {
const api_path = this.getCMakeFileApiPath();
await createQueryFileForApi(api_path);
Expand Down Expand Up @@ -237,7 +248,6 @@ export class CMakeFileApiDriver extends codemodel.CodeModelDriver {
this._needsReconfigure = false;
await this.updateCodeModel();
}

return res.retc === null ? -1 : res.retc;
}

Expand Down Expand Up @@ -273,7 +283,7 @@ export class CMakeFileApiDriver extends codemodel.CodeModelDriver {
throw Error('No code model object found');
}
this._target_map = await loadConfigurationTargetMap(reply_path, codemodel_obj.jsonFile);
this._codeModel = await loadExtCodeModelContent(reply_path, codemodel_obj.jsonFile);
this._codeModelContent = await loadExtCodeModelContent(reply_path, codemodel_obj.jsonFile);

// load toolchains
const toolchains_obj = indexFile.objects.find((value: index_api.Index.ObjectKind) => value.kind === 'toolchains');
Expand All @@ -288,15 +298,16 @@ export class CMakeFileApiDriver extends codemodel.CodeModelDriver {
'This version of CMake does not support the "toolchains" object kind. Compiler paths will be determined by reading CMakeCache.txt.'));
}
} else {
this._codeModel.toolchains = await loadToolchains(path.join(reply_path, toolchains_obj.jsonFile));
this._codeModelContent.toolchains = await loadToolchains(path.join(reply_path, toolchains_obj.jsonFile));
}

this._codeModelChanged.fire(this._codeModel);
this._codeModelChanged.fire(this._codeModelContent);
}
return indexFile !== null;
}

private _codeModel: codemodel.CodeModelContent|null = null;
private _codeModelContent: codemodel.CodeModelContent|null = null;
get codeModelContent() { return this._codeModelContent; }

get cmakeCacheEntries(): Map<string, api.CacheEntryProperties> { return this._cache; }
get generatorName(): string|null { return this._generatorInformation ? this._generatorInformation.name : null; }
Expand Down
18 changes: 16 additions & 2 deletions src/drivers/cms-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {CacheEntryProperties, ExecutableTarget, RichTarget} from '@cmt/api';
import * as cache from '@cmt/cache';
import * as cms from '@cmt/drivers/cms-client';
import * as codemodel from '@cmt/drivers/codemodel-driver-interface';
import {CMakePreconditionProblemSolver} from '@cmt/drivers/driver';
import {CMakeDriver, CMakePreconditionProblemSolver} from '@cmt/drivers/driver';
import {Kit, CMakeGenerator} from '@cmt/kit';
import {createLogger} from '@cmt/logging';
import * as proc from '@cmt/proc';
Expand All @@ -29,7 +29,16 @@ export class NoGeneratorError extends Error {
message: string = localize('no.usable.generator.found', 'No usable generator found.');
}

export class CMakeServerClientDriver extends codemodel.CodeModelDriver {
export class CMakeServerClientDriver extends CMakeDriver {

get isCacheConfigSupported(): boolean {
return false;
}

async doCacheConfigure(): Promise<number> {
throw new Error('Method not implemented.');
}

private constructor(cmake: CMakeExecutable, readonly config: ConfigurationReader, workspaceFolder: string | null, preconditionHandler: CMakePreconditionProblemSolver) {
super(cmake, config, workspaceFolder, preconditionHandler);
this.config.onChange('environment', () => this._restartClient());
Expand Down Expand Up @@ -384,6 +393,10 @@ export class CMakeServerClientDriver extends codemodel.CodeModelDriver {
});
}

get codeModelContent(): codemodel.CodeModelContent | null {
return null;
}

static async create(cmake: CMakeExecutable,
config: ConfigurationReader,
useCMakePresets: boolean,
Expand All @@ -402,6 +415,7 @@ export class CMakeServerClientDriver extends codemodel.CodeModelDriver {
testPreset,
preferredGenerators);
}

}

/**
Expand Down
21 changes: 0 additions & 21 deletions src/drivers/codemodel-driver-interface.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,3 @@
import {CMakeDriver} from '@cmt/drivers/driver';
import * as vscode from 'vscode';

/**
* This file contains the API description between IDE parts and the CMake model driver.
* This API CodeModel contains only the current required CMake code model parts.
* There is more information provided by CMake than is mapped.
*/

/**
* Describes all required methods for access to the build code model of the driver
*/
export abstract class CodeModelDriver extends CMakeDriver {
/**
* Event registration for code model updates
*
* This event is fired after update of the code model, like after cmake configuration.
*/
abstract onCodeModelChanged: vscode.Event<CodeModelContent|null>;
}

export type TargetTypeString
= ('STATIC_LIBRARY'|'MODULE_LIBRARY'|'SHARED_LIBRARY'|'OBJECT_LIBRARY'|'EXECUTABLE'|'UTILITY'|'INTERFACE_LIBRARY');

Expand Down
52 changes: 49 additions & 3 deletions src/drivers/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {ConfigureArguments, VariantOption} from '@cmt/variant';
import * as nls from 'vscode-nls';
import { majorVersionSemver, minorVersionSemver, parseTargetTriple, TargetTriple } from '@cmt/triple';
import * as preset from '@cmt/preset';
import * as codemodel from '@cmt/drivers/codemodel-driver-interface';

nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
Expand Down Expand Up @@ -69,6 +70,12 @@ export abstract class CMakeDriver implements vscode.Disposable {
* @returns The exit code from CMake
*/
protected abstract doConfigure(extra_args: string[], consumer?: proc.OutputConsumer): Promise<number>;
protected abstract doCacheConfigure(): Promise<number>;

private _isConfiguredAtLeastOnce = false;
protected get isConfiguredAtLeastOnce(): boolean {
return this._isConfiguredAtLeastOnce;
}

protected async doPreCleanConfigure(): Promise<void> {
return Promise.resolve();
Expand All @@ -82,16 +89,29 @@ export abstract class CMakeDriver implements vscode.Disposable {
return Promise.resolve(true);
}

/**
* Check if using cached configuration is supported.
*/
protected abstract get isCacheConfigSupported(): boolean;

/**
* Check if we need to reconfigure, such as if an important file has changed
*/
abstract checkNeedsReconfigure(): Promise<boolean>;
/**
* Event registration for code model updates
*
* This event is fired after update of the code model, like after cmake configuration.
*/
abstract onCodeModelChanged: vscode.Event<codemodel.CodeModelContent|null>;

/**
* List of targets known to CMake
*/
abstract get targets(): api.Target[];

abstract get codeModelContent(): codemodel.CodeModelContent|null;

/**
* List of executable targets known to CMake
*/
Expand Down Expand Up @@ -1094,7 +1114,16 @@ export abstract class CMakeDriver implements vscode.Disposable {
return count;
}

public shouldUseCachedConfiguration(trigger: ConfigureTrigger): boolean {
return (this.isCacheConfigSupported && !this.isConfiguredAtLeastOnce &&
trigger === ConfigureTrigger.configureOnOpen && !this.config.configureOnOpen) ?
true : false;
}

async configure(trigger: ConfigureTrigger, extra_args: string[], consumer?: proc.OutputConsumer, withoutCmakeSettings: boolean = false): Promise<number> {
// Check if the configuration is using cache in the first configuration and adjust the logging messages based on that.
const usingCachedConfiguration: boolean = this.shouldUseCachedConfiguration(trigger);

if (this.configRunning) {
await this.preconditionHandler(CMakePreconditionProblems.ConfigureIsAlreadyRunning);
return -1;
Expand All @@ -1108,13 +1137,18 @@ export abstract class CMakeDriver implements vscode.Disposable {
// _beforeConfigureOrBuild needs to refresh expansions early because it reads various settings
// (example: cmake.sourceDirectory).
await this._refreshExpansions();
log.debug(localize('start.configure', 'Start configure'), extra_args);
if (!usingCachedConfiguration) {
log.debug(localize('start.configure', 'Start configure'), extra_args);
} else {
log.debug(localize('use.cached.configuration', 'Use cached configuration'), extra_args);
}

const pre_check_ok = await this._beforeConfigureOrBuild();
if (!pre_check_ok) {
return -2;
}

// Cache flags will construct the command line for cmake.
const init_cache_flags = this.generateInitCacheFlags();

let expanded_flags: string[];
Expand All @@ -1139,13 +1173,23 @@ export abstract class CMakeDriver implements vscode.Disposable {
async (value: string) => expand.expandString(value, {...opts, envOverride: expanded_configure_env}));
expanded_flags = await Promise.all(expanded_flags_promises);
}
log.trace(localize('cmake.flags.are', 'CMake flags are {0}', JSON.stringify(expanded_flags)));
if (!usingCachedConfiguration) {
log.trace(localize('cmake.flags.are', 'CMake flags are {0}', JSON.stringify(expanded_flags)));
}

// A more complete round of expansions
await this._refreshExpansions();

const timeStart: number = new Date().getTime();
const retc = await this.doConfigure(expanded_flags, consumer);
let retc: number;
if (usingCachedConfiguration) {
retc = await this.doCacheConfigure();
this._isConfiguredAtLeastOnce = true;
return retc;
} else {
retc = await this.doConfigure(expanded_flags, consumer);
this._isConfiguredAtLeastOnce = true;
}
const timeEnd: number = new Date().getTime();

const cmakeVersion = this.cmake.version;
Expand Down Expand Up @@ -1235,6 +1279,7 @@ export abstract class CMakeDriver implements vscode.Disposable {
telemetryMeasures['ErrorCount'] = retc ? 1 : 0;
}
}

telemetry.logEvent('configure', telemetryProperties, telemetryMeasures);

return retc;
Expand Down Expand Up @@ -1603,4 +1648,5 @@ export abstract class CMakeDriver implements vscode.Disposable {
await inst._baseInit(useCMakePresets, kit, configurePreset, buildPreset, testPreset, preferredGenerators);
return inst;
}

}
Loading

0 comments on commit 0113841

Please sign in to comment.