* Replace workspace path read from vscode to constructor variable

- The workspace is read from vscode, this makes it hard to refactor
  cms-driver to test compiler integration on a lower level.
  The workspace needed to be set by a higher level, to make low level driver
  independent of vscode ui and data structures.

- Make non public function protected.

* Replaced pickGenerator by variable for generator

The cms client normally started a pick call in the UI.
This modification moves the questions infront of the start.
This removes the required callback from cms client.

* Clean up CMake Build Start Problem UI interaction

The CMake Driver has many dependencies to UI. This
modification moves the dependencies from CMakeDriver into
cmake-tools. This allows to test the driver without UI interaction.

- Move quickstart and missing source dir warning
- Remove not required configuration setting `autoRestartBuild`
  I was not able to bring the driver into a state where two
  builds run in parallel. The `CMakePreconditionProblemSolver`
  never signals `busy` CMake.
- Change visability of some do methods in cms-driver.

* Fix working path getter

* Fix parallel clean and configure on driver level

The parallel execution of cmake configure is allowed by the driver. This modification will
forbid a parallel configuration.

* Move selection of prefered generator

The prefered generators are extracted by the cmake drive from configuration,
kit files or settings. This modification moves the aggregation of different
prefered generator out of the driver into the cmake tools.
This allows to defines exact preferred generators for testing (no hidden
dependency). The driver only checks the prefered generator list,
if there is more then one then the first existing generator is selected.

- Fix wrong preferred generator definition in test
- Extend debug information

* Remove toolset dependency in cms-client

The CMS Server client had a dependency to configuration reader to
read the toolset. For my this is a inconsistent information. Only
the toolset was from config file but not the platform.

* Add cms driver test for linux

* Fix problem with invalid preferred generator

* Add tests for reconfiguration and generator switch

* Harmonize cleanConfigure with configure in driver.

* Remove Workspace state dependency from driver

This modification reduces the dependency of the cmake driver layer to the  workspace context. This modification allows to setup low level CMake driver tests, so that the driver code could be tested with different cmake versions.
The UI parts can then tested with a driver mock, or by system tests.

- Replace DirectoryContext by ConfigurationReader
- Moved access to DirectoryContext state by setter funcions (setVarianOptions). This allows to set variant in a consistent way.
- Reduces fakes from tests

* Clean up and fix variant naming

* Extract cmake flags generation out of configure method for readability

* Stabilize tests and reformat code

* Set timeout for teardown of driver

* Fix some errors

* Restructure drivers

* Move cmake server client driver part

* Add first parts of cmake file api implementation

* Clean up cmake file api code

* Add workaround for preconfigured projects

Added a workaround to get the preconfigured generator and
its settings from cache file.

* Implement target listing and restructure code

- Fix test for switching kit files
- Implement target map for configurations
- Add workaround for recover already configured cmake projects
- Add  file api helpers and interface

* Fix wrong generator configuration in tests and assign default value of generator in driver.

* Fix problems on generator errors or missing query response

- CI: Add cmake to search path
- Fix FileAPI driver compatibility on missing generator
- Fix FileAPI driver problem with missing reply file
- Add test for configuration behavior on configuration error of cmake

* Generate api query files on start of cmake configuration

The generation of the api query files should although be done when
a "unspecified kit" is used. This requires to create the file on each
configuration, not only on init of a new build directory or an clean up
before build.

* CI loads wrong cmake version

* Fix cmake version problem for unix global

* Try to fix travis mac os problem with bad executable

* Restructure codemodel api

This modification extracts the code model from
the UI elements (tree view) and compile information into a code model API.
It is necessary to implement two driver variants
- CMake Server CodeModel
- CMake FileAPI Code Model

The code model support is detected by a special driver family
(`CMakeCodeModelDriver`), which provides a property for
`onCodeModelChanged`.

The driver api contains one required information from
cmake code model for generation of cpptool compiler information and
for the cmake tree view.

* Implement code model parts for the tree view.

- Inherit from `CMakeCodeModelDriver`  instead of `CMakeDriver`
- Implement FileAPI translation functions for Extension Driver_API

* Add first implementation for cpptool required compile information

- Replace cmake server depencencies by driver_api
- Extend API py missing language information
- Extend CMake FileAPI interface for CompileGroups
- Implement required convert functions

* Fix access on map from undefined object

* Add missing "all" target and "install" target and fix problems on switch between MsBuild and ninja

* Fix some path resolving problems in code model

* Fix cmake tree view - open file and missing headers

* Fix/harmonize cms-driver and cmfileapi-driver tests

- Fix `Test generator switch`
  Now it is a more realistic test case. This test used the same
  driver methods like the pre-configured cmake build folder test.
  This makes it difficult to detect pre-configured build folders. The
  cmake-tool class(extension front-end) use a different way to
  switch the kits on a running cmake driver instance used.
- Add cmake configuration fail test to cms-driver.

* Fix problem with invalid active kit

* Add driver code model interface

- Create driver code model interfac
  The cms client class has a code model. This code model is defined
  by the cmake cmake-server(7). The cmake-tools-extension needs
  a code model, which does not have to be identical to the cmake server.
  For more flexibility in future, a separate model is created.
  The first version contains all information required for the extension
  (e.g. for cpptools, code model tree) and is based on
  the cmake-server(7) model.

- Separate code mode driver support from CMS driver version

* Add first code model test and fix typos and a small bug

* Fix unix build by separate test for single configuration generators

* Fix node.js version problem with streamfilter

* Add tests for project and target information

* Fix linux build and reformat code model tests

* Extend tests for shared library and utilities

- Add tests for interface parts of cpptools
- Add tests for utilities parts of tree view
- Extend CMake test Project by required calls

* Fix missing isGenerated in cms-client

* Fix wrong library filename

* Fix MacOS build and disable windows test for mingw (#775)

* Fix regex

* Update codemodel driver interface .documentation

* Clean up tests and add sysroot test

Clean up
- Add helper for code model generation
- Restructure test
- Fix spelling

Add missing sysroot test

* Fix instable tests

- Different CMake versions generate different code model outputs.
- macOS test fails. I am unable to test the problem,
  but it seems the sysroot variable is required to build on macOS.

* Remove test filter

* Refactor test suite generation for driver test

This modification allows reusing driver tests for all driver types.
The tests check information and behavior to be according to the
driver interface. We know then that the CMake tools frontend
see the same behavior from the driver.

* Added execution of driver tests to FileAPI Driver

* Clean up description and reformat code

* Update driver creation with new configuration setting

* Implement sysroot mapping for FileApi

* Fix lint and style

* Review comments

- Move setting description
- Replace cmake version for CI tests
- Move convertion of  `useCMakeServer` to `cmakeCommunicationMode`
- Create missing interface types
- Search for newest index file

* Insert review comments

- Update setting behavior for automatic mode
- Fix spelling
- Remove not required query content
- Add version check for code model and cache (index file has no version)

* Fix merge conflicts

* Change code model load behavior on init of driver

* Insert localization to warnings.
This commit is contained in:
KoeMai 2020-01-27 21:20:47 +01:00 коммит произвёл Bob Brown
Родитель 3ded66a373
Коммит 0b55e5bea9
31 изменённых файлов: 1431 добавлений и 685 удалений

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

@ -1097,10 +1097,22 @@
},
"cmake.useCMakeServer": {
"type": "boolean",
"default": true,
"default": false,
"description": "%cmake-tools.configuration.cmake.useCMakeServer.description%",
"scope": "resource"
},
"cmake.cmakeCommunicationMode": {
"type": "string",
"default": "automatic",
"enum": [
"legacy",
"serverAPI",
"fileAPI",
"automatic"
],
"description": "%cmake-tools.configuration.cmake.cmakeCommunicationMode%",
"scope": "resource"
},
"cmake.ignoreKitEnv": {
"type": "boolean",
"default": false,

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

@ -106,5 +106,6 @@
"cmake-tools.configuration.cmake.enableTraceLogging.description": "Enable trace logging to file and console (very noisy).",
"cmake-tools.configuration.cmake.autoSelectActiveFolder.description": "Select active folder automatically",
"cmake-tools.configuration.views.cmake.folders.description": "Folders",
"cmake-tools.configuration.views.cmake.outline.description": "Project Outline"
"cmake-tools.configuration.views.cmake.outline.description": "Project Outline",
"cmake-tools.configuration.cmake.cmakeCommunicationMode": "The protocol used to communicate between the extension and CMake"
}

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

@ -111,7 +111,7 @@ Invoke-ChronicCommand "Compiling Tests" $yarn run pretest
Invoke-ChronicCommand "Running TSLint" $yarn run lint:nofix
# Get the CMake binary that we will use to run our tests
$cmake_binary = Install-TestCMake -Version "3.10.0"
$cmake_binary = Install-TestCMake -Version "3.16.2"
$Env:CMAKE_EXECUTABLE = $cmake_binary
# Add cmake to search path environment variable

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

@ -36,6 +36,7 @@ import rollbar from './rollbar';
import * as telemetry from './telemetry';
import {setContextValue} from './util';
import {VariantManager} from './variant';
import { CMakeFileApiDriver } from '@cmt/drivers/cmfileapi-driver';
import * as nls from 'vscode-nls';
import { CMakeToolsFolder } from './folders';
@ -270,26 +271,42 @@ export class CMakeTools implements vscode.Disposable, api.CMakeToolsAPI {
let drv: CMakeDriver;
const preferredGenerators = this.getPreferredGenerators();
const preConditionHandler = async (e: CMakePreconditionProblems) => this.cmakePreConditionProblemHandler(e);
if (this.workspaceContext.config.useCMakeServer) {
if (cmake.isServerModeSupported) {
drv = await CMakeServerClientDriver
.create(cmake, this.workspaceContext.config, kit, workspace, preConditionHandler, preferredGenerators);
let communicationMode = this.workspaceContext.config.cmakeCommunicationMode;
if (communicationMode == 'automatic') {
if (cmake.isFileApiModeSupported) {
communicationMode = 'fileAPI';
} else if (cmake.isServerModeSupported) {
communicationMode = 'serverAPI';
} else {
communicationMode = 'legacy';
}
if (communicationMode != 'fileAPI') {
log.warning(
localize('please.upgrade.cmake',
'CMake Server is not available with the current CMake executable. Please upgrade to CMake {0} or newer.',
'For the best experience, CMake server or file-api support is required. Please upgrade CMake to {0} or newer.',
versionToString(cmake.minimalServerModeVersion)));
drv = await LegacyCMakeDriver
.create(cmake, this.workspaceContext.config, kit, workspace, preConditionHandler, preferredGenerators);
}
} else {
// We didn't start the server backend, so we'll use the legacy one
try {
this._statusMessage.set(localize('starting.cmake.driver.status', 'Starting CMake Server...'));
drv = await LegacyCMakeDriver
.create(cmake, this.workspaceContext.config, kit, workspace, preConditionHandler, preferredGenerators);
} finally { this._statusMessage.set(localize('ready.status', 'Ready')); }
}
try {
this._statusMessage.set(localize('starting.cmake.driver.status', 'Starting CMake Server...'));
switch (communicationMode) {
case 'fileAPI':
drv = await CMakeFileApiDriver
.create(cmake, this.workspaceContext.config, kit, workspace, preConditionHandler, preferredGenerators);
break;
case 'serverAPI':
drv = await CMakeServerClientDriver
.create(cmake, this.workspaceContext.config, kit, workspace, preConditionHandler, preferredGenerators);
break;
default:
drv = await LegacyCMakeDriver
.create(cmake, this.workspaceContext.config, kit, workspace, preConditionHandler, preferredGenerators);
}
} finally { this._statusMessage.set(localize('ready.status', 'Ready')); }
await drv.setVariant(this._variantManager.activeVariantOptions, this._variantManager.activeKeywordSetting);
this._targetName.set(this.defaultBuildTarget || drv.allTargetName);
await this._ctestController.reloadTests(drv);

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

@ -5,8 +5,10 @@ export interface CMakeExecutable {
path: string;
isPresent: boolean;
isServerModeSupported?: boolean;
isFileApiModeSupported?: boolean;
version?: util.Version;
minimalServerModeVersion: util.Version;
minimalFileApiModeVersion: util.Version;
}
export async function getCMakeExecutableInformation(path: string): Promise<CMakeExecutable> {
@ -14,6 +16,7 @@ export async function getCMakeExecutableInformation(path: string): Promise<CMake
path,
isPresent: false,
minimalServerModeVersion: util.parseVersion('3.7.1'),
minimalFileApiModeVersion: util.parseVersion('3.15.0'),
};
if (path.length != 0) {
@ -27,6 +30,10 @@ export async function getCMakeExecutableInformation(path: string): Promise<CMake
// We purposefully exclude versions <3.7.1, which have some major CMake
// server bugs
cmake.isServerModeSupported = util.versionGreater(cmake.version, cmake.minimalServerModeVersion);
// Support for new file based API, it replace the server mode
cmake.isFileApiModeSupported = util.versionGreater(cmake.version, cmake.minimalFileApiModeVersion) ||
util.versionEquals(cmake.version, cmake.minimalFileApiModeVersion);
cmake.isPresent = true;
}
} catch {

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

@ -4,12 +4,21 @@
* `ConfigurationReader` class.
*/ /** */
import * as logging from '@cmt/logging';
import * as util from '@cmt/util';
import * as os from 'os';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
nls.config({messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone})();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
const log = logging.createLogger('config');
export type LogLevelKey = 'trace'|'debug'|'info'|'note'|'warning'|'error'|'fatal';
export type CMakeCommunicationMode = 'legacy'|'serverAPI'|'fileAPI'|'automatic';
interface HardEnv {
[key: string]: string;
}
@ -49,6 +58,7 @@ export interface ExtensionConfigurationSettings {
copyCompileCommands: string|null;
configureOnOpen: boolean|null;
useCMakeServer: boolean;
cmakeCommunicationMode: CMakeCommunicationMode;
ignoreKitEnv: boolean;
buildTask: boolean;
outputLogEncoding: string;
@ -191,6 +201,17 @@ export class ConfigurationReader implements vscode.Disposable {
get useCMakeServer(): boolean { return this.configData.useCMakeServer; }
get cmakeCommunicationMode(): CMakeCommunicationMode {
let communicationMode = this.configData.cmakeCommunicationMode;
if (communicationMode == "automatic" && this.useCMakeServer) {
log.warning(localize(
'please.upgrade.configuration',
'The setting \'useCMakeServer\' is replaced by \'cmakeCommunicationMode\'. Please upgrade your configuration.'));
communicationMode = 'serverAPI';
}
return communicationMode;
}
get numJobs(): number {
const jobs = this.parallelJobs;
if (!!jobs) {
@ -259,6 +280,7 @@ export class ConfigurationReader implements vscode.Disposable {
copyCompileCommands: new vscode.EventEmitter<string|null>(),
configureOnOpen: new vscode.EventEmitter<boolean|null>(),
useCMakeServer: new vscode.EventEmitter<boolean>(),
cmakeCommunicationMode: new vscode.EventEmitter<CMakeCommunicationMode>(),
ignoreKitEnv: new vscode.EventEmitter<boolean>(),
buildTask: new vscode.EventEmitter<boolean>(),
outputLogEncoding: new vscode.EventEmitter<string>(),

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

@ -0,0 +1,135 @@
/**
* This module defines the cmake file API for typescript. This makes the use of
* the FileAPI simpler to use.
*
* For details see (cmake-file-api(7))[https://cmake.org/cmake/help/v3.15/manual/cmake-file-api.7.html].
* This file implements only the now required structures.
*/ /** */
export interface ApiVersion {
major: number;
minor: number;
}
export namespace Index {
export interface GeneratorInformation {
name: string;
platform?: string;
}
export interface CMake {
generator: GeneratorInformation;
}
export interface ObjectKind {
kind: string;
version: ApiVersion;
jsonFile: string;
}
export interface IndexFile {
cmake: CMake;
objects: ObjectKind[];
}
}
export namespace Cache {
export interface CacheContent {
version: ApiVersion;
entries: CMakeCacheEntry[];
}
export interface CacheEntryProperties {
name: string;
value: string;
}
export interface CMakeCacheEntry {
name: string;
properties: CacheEntryProperties[];
type: string;
value: string;
}
}
export namespace CodeModelKind {
export interface PathInfo {
build: string;
source: string;
}
export interface DirectoryMetadata {
source: string;
build: string;
hasInstallRule: boolean;
}
export interface ProjectMetadata {
name: string;
targetIndexes?: number[];
directoryIndexes: number[];
}
export interface Configuration {
name: string;
targets: Target[];
directories: DirectoryMetadata[];
projects: ProjectMetadata[];
}
export interface Content {
version: ApiVersion;
paths: PathInfo;
configurations: Configuration[];
}
export interface Target {
name: string;
type: string;
jsonFile: string;
}
export interface PreprocessorDefinitionMetadata {
define: string;
}
export interface IncludeMetadata {
path: string;
}
export interface SysRoot {
path: string;
}
export interface CompileCommandFragments {
fragment: string;
}
export interface CompileGroup {
language: string;
includes: IncludeMetadata[];
defines: PreprocessorDefinitionMetadata[];
compileCommandFragments: CompileCommandFragments[];
sourceIndexes: number[];
sysroot: SysRoot;
}
export interface ArtifactPath {
path: string;
}
export interface TargetSourcefile {
path: string;
compileGroupIndex?: number;
isGenerated?: boolean;
}
export interface TargetObject {
name: string;
type: string;
artifacts: ArtifactPath[];
nameOnDisk: string;
paths: PathInfo;
sources: TargetSourcefile[];
compileGroups?: CompileGroup[];
}
}

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

@ -0,0 +1,271 @@
import * as api from '@cmt/api';
import * as cache from '@cmt/cache';
import * as index_api from '@cmt/drivers/cmakefileapi/api';
import {
CodeModelConfiguration,
CodeModelContent,
CodeModelFileGroup,
CodeModelProject,
CodeModelTarget
} from '@cmt/drivers/codemodel-driver-interface';
import * as logging from '@cmt/logging';
import {fs} from '@cmt/pr';
import * as path from 'path';
import * as nls from 'vscode-nls';
nls.config({messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone})();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
const log = logging.createLogger('cmakefileapi-parser');
export async function createQueryFileForApi(api_path: string): Promise<string> {
const query_path = path.join(api_path, 'query', 'client-vscode');
const query_file_path = path.join(query_path, 'query.json');
await fs.mkdir_p(query_path);
const requests = {requests: [{kind: 'cache', version: 2}, {kind: 'codemodel', version: 2}]};
await fs.writeFile(query_file_path, JSON.stringify(requests));
return query_file_path;
}
export async function loadIndexFile(reply_path: string): Promise<index_api.Index.IndexFile|null> {
log.debug(`Read reply folder: ${reply_path}`);
if (!await fs.exists(reply_path)) {
return null;
}
const files = await fs.readdir(reply_path);
log.debug(`Found index files: ${JSON.stringify(files)}`);
const index_files = files.filter(filename => filename.startsWith('index-')).sort();
if (index_files.length == 0) {
throw Error('No index file found.');
}
const index_file_path = path.join(reply_path, index_files[index_files.length - 1]);
const file_content = await fs.readFile(index_file_path);
return JSON.parse(file_content.toString()) as index_api.Index.IndexFile;
}
export async function loadCacheContent(filename: string): Promise<Map<string, api.CacheEntry>> {
const file_content = await fs.readFile(filename);
const cache_from_cmake = JSON.parse(file_content.toString()) as index_api.Cache.CacheContent;
const expected_version = {major: 2, minor: 0};
const detected_version = cache_from_cmake.version;
if (detected_version.major != expected_version.major || detected_version.minor != expected_version.minor) {
log.warning(localize(
'cache.object.version',
'Cache object version ({0}.{1}) of cmake-file-api is unexpected. Expecting ({2}.{3}). IntelliSense configuration may be incorrect.',
detected_version.major,
detected_version.minor,
expected_version.major,
expected_version.minor));
}
return convertFileApiCacheToExtensionCache(cache_from_cmake);
}
function findPropertyValue(cacheElement: index_api.Cache.CMakeCacheEntry, name: string): string {
const property_element = cacheElement.properties.find(prop => prop.name == name);
return property_element ? property_element.value : '';
}
function convertFileApiCacheToExtensionCache(cache_from_cmake: index_api.Cache.CacheContent):
Map<string, api.CacheEntry> {
return cache_from_cmake.entries.reduce((acc, el) => {
const entry_type_translation_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 type = entry_type_translation_map[el.type];
if (type === undefined) {
log.warning(localize('cache.entry.unknowntype', 'Unknown cache entry type: {0}.', el.type));
return acc;
}
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'));
return acc;
}, new Map<string, api.CacheEntry>());
}
export async function loadCodeModelContent(filename: string): Promise<index_api.CodeModelKind.Content> {
const file_content = await fs.readFile(filename);
const codemodel = JSON.parse(file_content.toString()) as index_api.CodeModelKind.Content;
const expected_version = {major: 2, minor: 0};
const detected_version = codemodel.version;
if (detected_version.major != expected_version.major || detected_version.minor != expected_version.minor) {
log.warning(localize(
'code.model.version',
'Code model version ({0}.{1}) of cmake-file-api is unexpected. Expecting ({2}.{3}). IntelliSense configuration may be incorrect.',
detected_version.major,
detected_version.minor,
expected_version.major,
expected_version.minor));
}
return codemodel;
}
export async function loadTargetObject(filename: string): Promise<index_api.CodeModelKind.TargetObject> {
const file_content = await fs.readFile(filename);
return JSON.parse(file_content.toString()) as index_api.CodeModelKind.TargetObject;
}
async function convertTargetObjectFileToExtensionTarget(build_dir: string, file_path: string): Promise<api.Target> {
const targetObject = await loadTargetObject(file_path);
let executable_path = undefined;
if (targetObject.artifacts) {
executable_path = targetObject.artifacts.find(artifact => artifact.path.endsWith(targetObject.nameOnDisk));
if (executable_path) {
executable_path = path.normalize(path.join(build_dir, executable_path.path));
}
}
return {
name: targetObject.name,
filepath: executable_path ? executable_path : 'Utility target',
targetType: targetObject.type,
type: 'rich' as 'rich'
} as api.RichTarget;
}
export async function loadAllTargetsForBuildTypeConfiguration(reply_path: string,
builddir: string,
configuration: index_api.CodeModelKind.Configuration):
Promise<{name: string, targets: api.Target[]}> {
const metaTargets = [];
if (configuration.directories[0].hasInstallRule) {
metaTargets.push({
type: 'rich' as 'rich',
name: 'install',
filepath: 'A special target to install all available targets',
targetType: 'META'
});
}
const targetsList = Promise.all(configuration.targets.map(
t => convertTargetObjectFileToExtensionTarget(builddir, path.join(reply_path, t.jsonFile))));
return {name: configuration.name, targets: [...metaTargets, ...await targetsList]};
}
export async function loadConfigurationTargetMap(reply_path: string, codeModel_filename: string) {
const codeModelContent = await loadCodeModelContent(path.join(reply_path, codeModel_filename));
const build_dir = codeModelContent.paths.build;
const targets = await Promise.all(codeModelContent.configurations.map(
config_element => loadAllTargetsForBuildTypeConfiguration(reply_path, build_dir, config_element)));
return targets.reduce((acc, el) => {
acc.set(el.name, el.targets);
return acc;
}, new Map<string, api.Target[]>());
}
function convertToAbsolutePath(input_path: string, base_path: string) {
return path.normalize(path.join(base_path, input_path));
}
function convertToExtCodeModelFileGroup(targetObject: index_api.CodeModelKind.TargetObject): CodeModelFileGroup[] {
const fileGroup: CodeModelFileGroup[] = !targetObject.compileGroups ? [] : targetObject.compileGroups.map(group => {
const compileFlags
= group.compileCommandFragments ? group.compileCommandFragments.map(frag => frag.fragment).join(' ') : '';
return {
isGenerated: false,
sources: [],
language: group.language,
includePath: group.includes ? group.includes : [],
compileFlags,
defines: group.defines ? group.defines.map(define => define.define) : []
};
});
// Collection all without compilegroup like headers
const defaultIndex = fileGroup.push({sources: [], isGenerated: false} as CodeModelFileGroup) - 1;
targetObject.sources.forEach(sourcefile => {
const file_path = path.relative(targetObject.paths.source, sourcefile.path).replace('\\', '/');
if (sourcefile.compileGroupIndex !== undefined) {
fileGroup[sourcefile.compileGroupIndex].sources.push(file_path);
} else {
fileGroup[defaultIndex].sources.push(file_path);
if (!!sourcefile.isGenerated) {
fileGroup[defaultIndex].isGenerated = sourcefile.isGenerated;
}
}
});
return fileGroup;
}
async function loadCodeModelTarget(root_paths: index_api.CodeModelKind.PathInfo, jsonfile: string) {
const targetObject = await loadTargetObject(jsonfile);
const fileGroups = convertToExtCodeModelFileGroup(targetObject);
// This implementation expects that there is only one sysroot in a target.
// The ServerAPI only has provided one sysroot. In the FileAPI,
// each compileGroup has its separate sysroot.
let sysroot;
if (targetObject.compileGroups) {
const all_sysroots
= targetObject.compileGroups.map(x => !!x.sysroot ? x.sysroot.path : undefined).filter(x => x !== undefined);
sysroot = all_sysroots.length != 0 ? all_sysroots[0] : undefined;
}
return {
name: targetObject.name,
type: targetObject.type,
sourceDirectory: convertToAbsolutePath(targetObject.paths.source, root_paths.source),
fullName: targetObject.nameOnDisk,
artifacts: targetObject.artifacts ? targetObject.artifacts.map(
a => convertToAbsolutePath(path.join(targetObject.paths.build, a.path), root_paths.build))
: [],
fileGroups,
sysroot
} as CodeModelTarget;
}
export async function loadProject(root_paths: index_api.CodeModelKind.PathInfo,
reply_path: string,
projectIndex: number,
configuration: index_api.CodeModelKind.Configuration) {
const project = configuration.projects[projectIndex];
const project_paths = {
build: project.directoryIndexes
? path.join(root_paths.build, configuration.directories[project.directoryIndexes[0]].build)
: root_paths.build,
source: project.directoryIndexes
? path.join(root_paths.source, configuration.directories[project.directoryIndexes[0]].source)
: root_paths.source,
};
const targets = await Promise.all((project.targetIndexes || []).map(targetIndex => {
return loadCodeModelTarget(root_paths, path.join(reply_path, configuration.targets[targetIndex].jsonFile));
}));
return {name: project.name, targets, sourceDirectory: project_paths.source} as CodeModelProject;
}
export async function loadConfig(paths: index_api.CodeModelKind.PathInfo,
reply_path: string,
configuration: index_api.CodeModelKind.Configuration) {
const projects = await Promise.all(
(configuration.projects).map((_, index) => loadProject(paths, reply_path, index, configuration)));
return {projects} as CodeModelConfiguration;
}
export async function loadExtCodeModelContent(reply_path: string, codeModel_filename: string) {
const codeModelContent = await loadCodeModelContent(path.join(reply_path, codeModel_filename));
const configurations
= await Promise.all((codeModelContent.configurations)
.map(config_element => loadConfig(codeModelContent.paths, reply_path, config_element)));
return {configurations} as CodeModelContent;
}

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

@ -0,0 +1,257 @@
import * as api from '@cmt/api';
import {ExecutableTarget} from '@cmt/api';
import {CMakeCache} from '@cmt/cache';
import {CMakeExecutable} from '@cmt/cmake/cmake-executable';
import {ConfigurationReader} from '@cmt/config';
import * as index_api from '@cmt/drivers/cmakefileapi/api';
import {
createQueryFileForApi,
loadCacheContent,
loadConfigurationTargetMap,
loadExtCodeModelContent,
loadIndexFile
} from '@cmt/drivers/cmakefileapi/api_helpers';
import * as codemodel from '@cmt/drivers/codemodel-driver-interface';
import {CMakePreconditionProblemSolver} from '@cmt/drivers/driver';
import {CMakeGenerator, Kit} from '@cmt/kit';
import * as logging from '@cmt/logging';
import {fs} from '@cmt/pr';
import * as proc from '@cmt/proc';
import rollbar from '@cmt/rollbar';
import * as util from '@cmt/util';
import * as path from 'path';
import * as vscode from 'vscode';
import {NoGeneratorError} from './cms-driver';
const log = logging.createLogger('cmakefileapi-driver');
/**
* The CMake driver with FileApi of CMake >= 3.15.0
*/
export class CMakeFileApiDriver extends codemodel.CodeModelDriver {
private constructor(cmake: CMakeExecutable,
readonly config: ConfigurationReader,
workspaceRootPath: string|null,
preconditionHandler: CMakePreconditionProblemSolver) {
super(cmake, config, workspaceRootPath, preconditionHandler);
}
static async create(cmake: CMakeExecutable,
config: ConfigurationReader,
kit: Kit|null,
workspaceRootPath: string|null,
preconditionHandler: CMakePreconditionProblemSolver,
preferredGenerators: CMakeGenerator[]): Promise<CMakeFileApiDriver> {
log.debug('Creating instance of CMakeFileApiDriver');
return this.createDerived(new CMakeFileApiDriver(cmake, config, workspaceRootPath, preconditionHandler),
kit,
preferredGenerators);
}
private _needsReconfigure = true;
/**
* Watcher for the CMake cache file on disk.
*/
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 _generatorInformation: index_api.Index.GeneratorInformation|null = null;
private _target_map: Map<string, api.Target[]> = new Map();
async loadGeneratorInformationFromCache(cache_file_path: string) {
const cache = await CMakeCache.fromPath(cache_file_path);
this._generator = {
name: cache.get('CMAKE_GENERATOR')!.value,
toolset: cache.get('CMAKE_GENERATOR_PLATFORM') ? cache.get('CMAKE_GENERATOR_PLATFORM')!.value : undefined,
platform: cache.get('CMAKE_GENERATOR_TOOLSET') ? cache.get('CMAKE_GENERATOR_TOOLSET')!.value : undefined
} as CMakeGenerator;
this._generatorInformation = {
name: cache.get('CMAKE_GENERATOR')!.value,
platform: cache.get('CMAKE_GENERATOR_TOOLSET') ? cache.get('CMAKE_GENERATOR_TOOLSET')!.value : undefined
};
}
async doInit() {
// The seems to be a different between server mode and fileapi on load of a existing project
// If the existing project is not generated by the IDE then the fileapi queries are missing.
// but the generator information are needed to get the code mode, cache and cmake files.
// This workaround load the information from cache.
if (await fs.exists(this.cachePath)) {
await this.loadGeneratorInformationFromCache(this.cachePath);
const code_model_exist = await this.updateCodeModel();
if (!code_model_exist) {
await this.doConfigure([], undefined);
}
} else {
this._generatorInformation = this.generator;
}
if (!this.generator) {
throw new NoGeneratorError();
}
this._cacheWatcher.onDidChange(() => {
log.debug(`Reload CMake cache: ${this.cachePath} changed`);
rollbar.invokeAsync('Reloading CMake Cache', () => this.updateCodeModel());
});
}
doConfigureSettingsChange() { this._needsReconfigure = true; }
async checkNeedsReconfigure(): Promise<boolean> { return this._needsReconfigure; }
async doSetKit(need_clean: boolean, cb: () => Promise<void>): Promise<void> {
this._needsReconfigure = true;
if (need_clean) {
await this._cleanPriorConfiguration();
}
await cb();
if (!this.generator) {
throw new NoGeneratorError();
}
}
async asyncDispose() {
this._codeModelChanged.dispose();
this._cacheWatcher.dispose();
}
protected async doPreCleanConfigure(): Promise<void> { await this._cleanPriorConfiguration(); }
async doConfigure(args_: string[], outputConsumer?: proc.OutputConsumer): Promise<number> {
const api_path = this.getCMakeFileApiPath();
await createQueryFileForApi(api_path);
// Dup args so we can modify them
const args = Array.from(args_);
args.push(`-H${util.lightNormalizePath(this.sourceDir)}`);
const bindir = util.lightNormalizePath(this.binaryDir);
args.push(`-B${bindir}`);
const gen = this.generator;
if (gen) {
args.push('-G');
args.push(gen.name);
if (gen.toolset) {
args.push('-T');
args.push(gen.toolset);
}
if (gen.platform) {
args.push('-A');
args.push(gen.platform);
}
}
const cmake = this.cmake.path;
log.debug('Invoking CMake', cmake, 'with arguments', JSON.stringify(args));
const env = await this.getConfigureEnvironment();
const res = await this.executeCommand(cmake, args, outputConsumer, {environment: env}).result;
log.trace(res.stderr);
log.trace(res.stdout);
if (res.retc == 0) {
this._needsReconfigure = false;
await this.updateCodeModel();
}
return res.retc === null ? -1 : res.retc;
}
async doPostBuild(): Promise<boolean> {
await this.updateCodeModel();
return true;
}
private getCMakeFileApiPath() { return path.join(this.binaryDir, '.cmake', 'api', 'v1'); }
private getCMakeReplyPath() {
const api_path = this.getCMakeFileApiPath();
return path.join(api_path, 'reply');
}
private async updateCodeModel(): Promise<boolean> {
const reply_path = this.getCMakeReplyPath();
const indexFile = await loadIndexFile(reply_path);
if (indexFile) {
this._generatorInformation = indexFile.cmake.generator;
// load cache
const cache_obj = indexFile.objects.find((value: index_api.Index.ObjectKind) => value.kind === 'cache');
if (!cache_obj) {
throw Error('No cache object found');
}
this._cache = await loadCacheContent(path.join(reply_path, cache_obj.jsonFile));
// load targets
const codemodel_obj = indexFile.objects.find((value: index_api.Index.ObjectKind) => value.kind === 'codemodel');
if (!codemodel_obj) {
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._codeModelChanged.fire(this._codeModel);
}
return indexFile !== null;
}
private _codeModel: codemodel.CodeModelContent|null = null;
get cmakeCacheEntries(): Map<string, api.CacheEntryProperties> { return this._cache; }
get generatorName(): string|null { return this._generatorInformation ? this._generatorInformation.name : null; }
get targets(): api.Target[] {
const targets = this._target_map.get(this.currentBuildType);
if (targets) {
const metaTargets = [{
type: 'rich' as 'rich',
name: this.allTargetName,
filepath: 'A special target to build all available targets',
targetType: 'META'
}];
return [...metaTargets, ...targets].filter((value, idx, self) => self.findIndex(e => value.name === e.name)
=== idx);
} else {
return [];
}
}
/**
* List of unique targets known to CMake
*/
get uniqueTargets(): api.Target[] { return this.targets.reduce(targetReducer, []); }
get executableTargets(): ExecutableTarget[] {
return this.uniqueTargets.filter(t => t.type === 'rich' && (t as api.RichTarget).targetType === 'EXECUTABLE')
.map(t => ({
name: t.name,
path: (t as api.RichTarget).filepath,
}));
}
private readonly _codeModelChanged = new vscode.EventEmitter<null|codemodel.CodeModelContent>();
get onCodeModelChanged() { return this._codeModelChanged.event; }
}
/**
* Helper function for Array.reduce
*
* @param set the accumulator
* @t the RichTarget currently being examined.
*/
function targetReducer(set: api.Target[], t: api.Target): api.Target[] {
if (!set.find(t2 => compareTargets(t, t2))) {
set.push(t);
}
return set;
}
function compareTargets(a: api.Target, b: api.Target): boolean {
let same = false;
if (a.type === b.type) {
same = a.name == b.name;
if (a.type === 'rich' && b.type === 'rich') {
same = same && (a.filepath == b.filepath);
same = same && (a.targetType == b.targetType);
}
}
return same;
}

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

@ -859,6 +859,14 @@ export abstract class CMakeDriver implements vscode.Disposable {
*/
private _currentBuildProcess: proc.Subprocess|null = null;
private correctAllTargetName(targetname: string) {
if (targetname === 'all' || targetname == 'ALL_BUILD') {
return this.allTargetName;
} else {
return targetname;
}
}
async getCMakeBuildCommand(target: string): Promise<proc.BuildCommand|null> {
const ok = await this._beforeConfigureOrBuild();
if (!ok) {
@ -866,6 +874,8 @@ export abstract class CMakeDriver implements vscode.Disposable {
}
const gen = this.generatorName;
target = this.correctAllTargetName(target);
const generator_args = (() => {
if (!gen)
return [];

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

@ -1,12 +0,0 @@
import {expect} from 'chai';
import * as chai from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
chai.use(chaiAsPromised);
// tslint:disable:no-unused-expression
suite('cmake-executeable', () => {
test('Teste', () => {
expect(true).to.be.true;
});
});

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

@ -75,10 +75,8 @@ const DEFAULT_VS_KITS: KitEnvironment[] = [
},
];
const DEFAULT_CYGWIN_KITS: KitEnvironment[] = [
{defaultKit: /^GCC/, expectedDefaultGenerator: /^Unix Makefiles/, path: ['c:\\cygwin64\\bin']},
{defaultKit: /^Clang/, expectedDefaultGenerator: /^Unix Makefiles/, path: ['c:\\cygwin64\\bin']}
];
const DEFAULT_CYGWIN_KITS: KitEnvironment[] =
[{defaultKit: /^GCC/, expectedDefaultGenerator: /^Unix Makefiles/, path: ['c:\\cygwin64\\bin']}];
const DEFAULT_MINGW_KITS: KitEnvironment[] = [
{
@ -108,7 +106,8 @@ const DEFAULT_MINGW_KITS: KitEnvironment[] = [
{defaultKit: /^GCC 5.3.0/, expectedDefaultGenerator: /^MinGW Makefiles/, path: ['C:\\MinGW\\bin']}
];
const DEFAULT_WINDOWS_KITS: KitEnvironment[] = DEFAULT_VS_KITS.concat(DEFAULT_CYGWIN_KITS, DEFAULT_MINGW_KITS);
const DEFAULT_WINDOWS_KITS: KitEnvironment[]
= DEFAULT_VS_KITS.concat(DEFAULT_CYGWIN_KITS, DEFAULT_MINGW_KITS);
const KITS_BY_PLATFORM: {[osName: string]: KitEnvironment[]} = {
['win32']: DEFAULT_WINDOWS_KITS.concat([{

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

@ -7,6 +7,7 @@ smokeSuite('bad-project', suite => {
return test.withCMakeTools({
kit: await smokeTestDefaultKit(),
async run(cmt) {
expect( (await cmt.getCMakeExecutable()).isFileApiModeSupported).to.be.equal(true);
const retc = await cmt.configure();
// Test will fail because of a bad command:
expect(retc, 'Configure should have failed').to.eq(1);

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

@ -1,241 +0,0 @@
import {getCMakeExecutableInformation} from '@cmt/cmake/cmake-executable';
import {ConfigurationReader} from '@cmt/config';
import * as cms_driver from '@cmt/drivers/cms-driver';
import * as codemodel_api from '@cmt/drivers/codemodel-driver-interface';
import * as chai from 'chai';
import {expect} from 'chai';
import * as chaiString from 'chai-string';
import * as fs from 'fs';
import * as path from 'path';
import * as rimraf from 'rimraf';
chai.use(chaiString);
import {Kit} from '@cmt/kit';
import {CMakeDriver} from '@cmt/drivers/driver';
const here = __dirname;
function getTestRootFilePath(filename: string): string {
return path.normalize(path.join(here, '../../../..', filename));
}
function cleanupBuildDir(build_dir: string): boolean {
if (fs.existsSync(build_dir)) {
rimraf.sync(build_dir);
}
return !fs.existsSync(build_dir);
}
let driver: CMakeDriver|null = null;
// tslint:disable:no-unused-expression
suite('CMake-Server-Driver tests', () => {
const cmakePath: string = process.env.CMAKE_EXECUTABLE ? process.env.CMAKE_EXECUTABLE : 'cmake';
const workspacePath: string = 'test/unit-tests/cms-driver/workspace';
const root = getTestRootFilePath(workspacePath);
const defaultWorkspaceFolder = getTestRootFilePath('test/unit-tests/cms-driver/workspace/test_project');
const emptyWorkspaceFolder = getTestRootFilePath('test/unit-tests/cms-driver/workspace/empty_project');
let kitDefault: Kit;
if (process.platform === 'win32') {
kitDefault = {
name: 'Visual Studio Community 2017 - amd64',
visualStudio: 'VisualStudio.15.0',
visualStudioArchitecture: 'amd64',
preferredGenerator: {name: 'Visual Studio 15 2017', platform: 'x64'}
} as Kit;
} else {
kitDefault = {name: 'GCC', compilers: {C: 'gcc', CXX: 'g++'}, preferredGenerator: {name: 'Unix Makefiles'}} as Kit;
}
setup(async function(this: Mocha.IBeforeAndAfterContext, done) {
driver = null;
if (!cleanupBuildDir(path.join(defaultWorkspaceFolder, 'build'))) {
done('Default build folder still exists');
}
if (!cleanupBuildDir(path.join(emptyWorkspaceFolder, 'build'))) {
done('Empty project build folder still exists');
}
done();
});
teardown(async function(this: Mocha.IBeforeAndAfterContext) {
this.timeout(20000);
if (driver) {
return driver.asyncDispose();
}
});
async function generateCodeModelForConfiguredDriver(args: string[] =
[]): Promise<null|codemodel_api.CodeModelContent> {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
let code_model: null|codemodel_api.CodeModelContent = null;
if (driver instanceof codemodel_api.CodeModelDriver) {
driver.onCodeModelChanged(cm => { code_model = cm; });
}
await driver.configure(args);
return code_model;
}
test('Test generation of code model with multi configuration like VS', async () => {
if (process.platform !== 'win32')
return;
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
expect(codemodel_data!.configurations.length).to.be.eql(4);
}).timeout(90000);
test('Test generation of code model with one configuration like make on linux', async () => {
if (process.platform === 'win32')
return;
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
expect(codemodel_data!.configurations.length).to.be.eql(1);
}).timeout(90000);
test('Test project information', async () => {
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
const project = codemodel_data!.configurations[0].projects[0];
// Test project name
expect(project.name).to.be.eq('TestBuildProcess');
// Test location of project source directory
// Used by tree view to make paths relative
expect(path.normalize(project.sourceDirectory).toLowerCase())
.to.eq(path.normalize(path.join(root, 'test_project')).toLowerCase());
}).timeout(90000);
test('Test first executable target directory', async () => {
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
const target = codemodel_data!.configurations[0].projects[0].targets.find(t => t.type == 'EXECUTABLE');
expect(target).to.be.not.undefined;
// Test target name used for node label
expect(target!.name).to.be.eq('TestBuildProcess');
const executableName = process.platform === 'win32' ? 'TestBuildProcess.exe' : 'TestBuildProcess';
expect(target!.fullName).to.be.eq(executableName);
expect(target!.type).to.be.eq('EXECUTABLE');
// Test location of project source directory
// used by tree view to make paths relative
expect(path.normalize(target!.sourceDirectory!).toLowerCase())
.to.eq(path.normalize(path.join(root, 'test_project')).toLowerCase());
// Test main source file used in by tree view
expect(target!.fileGroups).to.be.not.undefined;
const compile_information = target!.fileGroups!.find(t => !!t.language);
expect(compile_information).to.be.not.undefined;
expect(compile_information!.sources).to.include('main.cpp');
}).timeout(90000);
test('Test first static library target directory', async () => {
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
const target = codemodel_data!.configurations[0].projects[0].targets.find(t => t.type == 'STATIC_LIBRARY');
expect(target).to.be.not.undefined;
// Test target name used for node label
expect(target!.name).to.be.eq('StaticLibDummy');
const executableName = process.platform === 'win32' ? 'StaticLibDummy.lib' : 'libStaticLibDummy.a';
expect(target!.fullName).to.be.eq(executableName);
expect(target!.type).to.be.eq('STATIC_LIBRARY');
// Test location of project source directory
// Used by tree view to make paths relative
expect(path.normalize(target!.sourceDirectory!).toLowerCase())
.to.eq(path.normalize(path.join(root, 'test_project', 'static_lib_dummy')).toLowerCase());
// Language
const compile_information = target!.fileGroups!.find(t => !!t.language);
expect(compile_information).to.be.not.undefined;
expect(compile_information!.language).to.eq('CXX');
// Test main source file
expect(compile_information!.sources).to.include('info.cpp');
expect(compile_information!.sources).to.include('test2.cpp');
// compile flags for file groups
if (process.platform === 'win32') {
expect(compile_information!.compileFlags).to.eq('/DWIN32 /D_WINDOWS /W3 /GR /EHsc /MDd /Zi /Ob0 /Od /RTC1 ');
}
}).timeout(90000);
test('Test first shared library target directory', async () => {
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
const target = codemodel_data!.configurations[0].projects[0].targets.find(t => t.type == 'SHARED_LIBRARY');
expect(target).to.be.not.undefined;
// Test target name used for node label
expect(target!.name).to.be.eq('SharedLibDummy');
const executableNameRegex = process.platform === 'win32' ? /^SharedLibDummy.dll/ : /^libSharedLibDummy.(so|dylib)/;
expect(target!.fullName).to.match(executableNameRegex);
expect(target!.type).to.be.eq('SHARED_LIBRARY');
// Test location of project source directory
// Used by tree view to make paths relative
expect(path.normalize(target!.sourceDirectory!).toLowerCase())
.to.eq(path.normalize(path.join(root, 'test_project', 'shared_lib_dummy')).toLowerCase());
// Test main source file
expect(target!.fileGroups).to.be.not.undefined;
expect(target!.fileGroups![0].sources[0]).to.eq('src/info.c');
expect(target!.fileGroups![0].defines).contains('TEST_CMAKE_DEFINE');
expect(target!.fileGroups![0].isGenerated).to.be.false;
expect(path.normalize(target!.fileGroups![0].includePath![0].path).toLowerCase())
.to.eq(path.normalize(path.join(root, 'test_project', 'shared_lib_dummy', 'inc')).toLowerCase());
// Language
expect(target!.fileGroups![0].language).to.eq('C');
// compile flags for file groups
if (process.platform === 'win32') {
expect(target!.fileGroups![0].compileFlags).to.eq('/DWIN32 /D_WINDOWS /W3 /MDd /Zi /Ob0 /Od /RTC1 ');
}
}).timeout(90000);
test('Test cache access', async () => {
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
const target = codemodel_data!.configurations[0].projects[0].targets.find(t => t.type == 'UTILITY'
&& t.name == 'runTestTarget');
expect(target).to.be.not.undefined;
// maybe could be used to exclude file list from utility targets
expect(target!.fileGroups![0].isGenerated).to.be.true;
}).timeout(90000);
test('Test sysroot access', async () => {
// This test does not work with VisualStudio.
// VisualStudio generator does not provide the sysroot in the code model.
// macOS has separate sysroot variable (see CMAKE_OSX_SYSROOT); this build fails.
if (process.platform === 'win32' || process.platform === 'darwin')
return;
const codemodel_data = await generateCodeModelForConfiguredDriver(['-DCMAKE_SYSROOT=/tmp']);
expect(codemodel_data).to.be.not.null;
const target = codemodel_data!.configurations[0].projects[0].targets.find(t => t.type == 'EXECUTABLE');
expect(target).to.be.not.undefined;
expect(target!.sysroot).to.be.eq('/tmp');
}).timeout(90000);
});

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

@ -1,408 +0,0 @@
import {getCMakeExecutableInformation} from '@cmt/cmake/cmake-executable';
import * as cms_driver from '@cmt/drivers/cms-driver';
import {ConfigurationReader} from '@cmt/config';
import * as chai from 'chai';
import {expect} from 'chai';
import * as chaiString from 'chai-string';
import * as fs from 'fs';
import * as path from 'path';
import * as rimraf from 'rimraf';
chai.use(chaiString);
import {Kit} from '@cmt/kit';
import {CMakePreconditionProblems, CMakeDriver} from '@cmt/drivers/driver';
const here = __dirname;
function getTestRootFilePath(filename: string): string {
return path.normalize(path.join(here, '../../../..', filename));
}
function cleanupBuildDir(build_dir: string): boolean {
if (fs.existsSync(build_dir)) {
rimraf.sync(build_dir);
}
return !fs.existsSync(build_dir);
}
let driver: CMakeDriver|null = null;
// tslint:disable:no-unused-expression
suite('CMake-Server-Driver tests', () => {
const cmakePath: string = process.env.CMAKE_EXECUTABLE? process.env.CMAKE_EXECUTABLE: 'cmake';
const defaultWorkspaceFolder = getTestRootFilePath('test/unit-tests/cms-driver/workspace/test_project');
const emptyWorkspaceFolder = getTestRootFilePath('test/unit-tests/cms-driver/workspace/empty_project');
const badCommandWorkspaceFolder = getTestRootFilePath('test/unit-tests/cms-driver/workspace/bad_command');
let kitDefault: Kit;
if (process.platform === 'win32') {
kitDefault = {
name: 'Visual Studio Community 2017 - amd64',
visualStudio: 'VisualStudio.15.0',
visualStudioArchitecture: 'amd64',
preferredGenerator: {name: 'Visual Studio 15 2017', platform: 'x64'}
} as Kit;
} else {
kitDefault = {name: 'GCC', compilers: {C: 'gcc', CXX: 'g++'}, preferredGenerator: {name: 'Unix Makefiles'}} as Kit;
}
let kitNinja: Kit;
if (process.platform === 'win32') {
kitNinja = {
name: 'Visual Studio Community 2017 - amd64',
visualStudio: 'VisualStudio.15.0',
visualStudioArchitecture: 'amd64',
preferredGenerator: {name: 'Ninja'}
} as Kit;
} else {
kitNinja = {name: 'GCC', compilers: {C: 'gcc', CXX: 'g++'}, preferredGenerator: {name: 'Ninja'}} as Kit;
}
setup(async function(this: Mocha.IBeforeAndAfterContext, done) {
driver = null;
let isDone = false;
if (!cleanupBuildDir(path.join(defaultWorkspaceFolder, 'build'))) {
done('Default build folder still exists');
isDone = true;
}
if (!cleanupBuildDir(path.join(emptyWorkspaceFolder, 'build'))) {
if (!isDone) {
done('Empty project build folder still exists');
isDone = true;
}
}
if (!cleanupBuildDir(path.join(badCommandWorkspaceFolder, 'build'))) {
if (!isDone) {
done('Bad command build folder still exists');
isDone = true;
}
}
if(!isDone)
done();
});
teardown(async function(this: Mocha.IBeforeAndAfterContext) {
this.timeout(20000);
if (driver) {
return driver.asyncDispose();
}
});
test(`All target for ${kitDefault.name}`, async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
const allTargetName = driver.allTargetName;
if (process.platform === 'win32') {
expect(allTargetName).to.eq('ALL_BUILD');
} else {
expect(allTargetName).to.eq('all');
}
}).timeout(60000);
test('Check binary dir', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
expect(driver.binaryDir).to.endsWith('test/unit-tests/cms-driver/workspace/test_project/build');
}).timeout(60000);
test('Configure fails', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, badCommandWorkspaceFolder, async () => {}, []);
expect(await driver.cleanConfigure([])).to.be.eq(1);
}).timeout(90000);
test('Build', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
expect(await driver.cleanConfigure([])).to.be.eq(0);
expect(await driver.build(driver.allTargetName)).to.be.eq(0);
expect(driver.executableTargets.length).to.be.eq(1);
expect(driver.executableTargets[0].name).to.be.equal('TestBuildProcess');
expect(fs.existsSync(driver.executableTargets[0].path)).to.be.true;
}).timeout(90000);
test('Configure fails on invalid preferred generator', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
const kit = {name: 'GCC', preferredGenerator: {name: 'BlaBla'}} as Kit;
// tslint:disable-next-line: no-floating-promises
expect(cms_driver.CMakeServerClientDriver.create(executable, config, kit, defaultWorkspaceFolder, async () => {}, []))
.to.be.rejectedWith('No usable generator found.');
}).timeout(60000);
test('Throw exception on set kit without preferred generator found', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
await expect(driver.setKit({name: 'GCC'}, [])).to.be.rejectedWith('No usable generator found.');
}).timeout(90000);
test('Try build on empty dir', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
expect(e).to.be.eq(CMakePreconditionProblems.MissingCMakeListsFile);
called = true;
};
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, emptyWorkspaceFolder, checkPreconditionHelper, []);
expect(await driver.cleanConfigure([])).to.be.eq(-1);
expect(called).to.be.true;
}).timeout(60000);
test('No parallel configuration', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
expect(e).to.be.eq(CMakePreconditionProblems.ConfigureIsAlreadyRunning);
called = true;
};
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
const configure1 = driver.configure([]);
const configure2 = driver.configure([]);
expect(await configure1).to.be.equal(0);
expect(await configure2).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No parallel clean configuration', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
expect(e).to.be.eq(CMakePreconditionProblems.ConfigureIsAlreadyRunning);
called = true;
};
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
const configure1 = driver.cleanConfigure([]);
const configure2 = driver.cleanConfigure([]);
expect(await configure1).to.be.equal(0);
expect(await configure2).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No parallel builds', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
if(e == CMakePreconditionProblems.BuildIsAlreadyRunning) {
called = true;
}
};
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
expect(await driver.configure([])).to.be.equal(0);
const build1 = driver.build(driver.allTargetName);
const build2 = driver.build(driver.allTargetName);
expect(await build1).to.be.equal(0);
expect(await build2).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No build parallel to configure', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
if(e == CMakePreconditionProblems.ConfigureIsAlreadyRunning) {
called = true;
}
};
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
expect(await driver.configure([])).to.be.equal(0);
const configure = driver.configure([]);
const build = driver.build(driver.allTargetName);
expect(await configure).to.be.equal(0);
expect(await build).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No configure parallel to build', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
if(e == CMakePreconditionProblems.BuildIsAlreadyRunning) {
called = true;
}
};
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
expect(await driver.configure([])).to.be.equal(0);
const build = driver.build(driver.allTargetName);
const configure = driver.configure([]);
expect(await build).to.be.equal(0);
expect(await configure).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No build parallel to clean configuration', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
if(e == CMakePreconditionProblems.ConfigureIsAlreadyRunning) {
called = true;
}
};
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
const configure = driver.cleanConfigure([]);
const build = driver.build(driver.allTargetName);
expect(await configure).to.be.equal(0);
expect(await build).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No clean configuration parallel to build', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
if(e == CMakePreconditionProblems.BuildIsAlreadyRunning) {
called = true;
}
};
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
expect(await driver.configure([])).to.be.equal(0);
const build = driver.build(driver.allTargetName);
const configure = driver.cleanConfigure([]);
expect(await build).to.be.equal(0);
expect(await configure).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('Test pre-configured workspace', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitNinja, defaultWorkspaceFolder, async () => {}, []);
await driver.cleanConfigure([]);
await driver.asyncDispose();
driver = null;
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
expect(await driver.configure([])).to.be.eq(0);
expect(driver.cmakeCacheEntries.get('CMAKE_GENERATOR')!.value).to.be.eq('Ninja');
}).timeout(60000);
test('Test generator switch', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
await driver.cleanConfigure([]);
expect(driver.cmakeCacheEntries.get('CMAKE_GENERATOR')!.value).to.be.not.eq('Ninja');
await driver.setKit(kitNinja, [{name:'Ninja'}]);
expect(await driver.configure([])).to.be.eq(0);
expect(driver.cmakeCacheEntries.get('CMAKE_GENERATOR')!.value).to.be.eq('Ninja');
}).timeout(90000);
test('Test extra arguments on configure', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
await driver.configure(['-DEXTRA_ARGS_TEST=Hallo']);
expect(driver.cmakeCacheEntries.get('extraArgsEnvironment')!.value).to.be.eq('Hallo');
}).timeout(90000);
test('Test extra arguments on clean and configure', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
await driver.cleanConfigure(['-DEXTRA_ARGS_TEST=Hallo']);
expect(driver.cmakeCacheEntries.get('extraArgsEnvironment')!.value).to.be.eq('Hallo');
}).timeout(90000);
test('Cancel build', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
expect(await driver.cleanConfigure(['-DDELAY_BUILD=1'])).to.be.equal(0);
// Start build
const build_prom = driver.build(driver.allTargetName);
// Prepare delayed build cancel
let cancel_prom: any;
const d = driver;
setTimeout(() => { cancel_prom = d.stopCurrentProcess(); }, 3000);
// Wait for build
const ret = await build_prom;
// Check results
await cancel_prom;
expect(ret).to.be.not.eq(0);
// Test driver is still working
expect(await driver.configure([])).to.be.equal(0);
}).timeout(90000);
test('Stop and start cmake-server client', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await cms_driver.CMakeServerClientDriver
.create(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
expect(await driver.configure([])).to.be.equal(0);
await driver.stopCurrentProcess();
expect(await driver.configure([])).to.be.equal(0);
}).timeout(90000);
});

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

@ -1,2 +0,0 @@
# This file is only here as a test toolchain!
message("Hello from test-toolchain.cmake")

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

@ -40,6 +40,7 @@ function createConfig(conf: Partial<ExtensionConfigurationSettings>): Configurat
copyCompileCommands: null,
configureOnOpen: null,
useCMakeServer: true,
cmakeCommunicationMode: 'automatic',
ignoreKitEnv: false,
buildTask: false,
outputLogEncoding: 'auto',

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

@ -0,0 +1,22 @@
import {CMakeExecutable} from '@cmt/cmake/cmake-executable';
import {ConfigurationReader} from '@cmt/config';
import {CMakeFileApiDriver} from '@cmt/drivers/cmfileapi-driver';
import {CMakeDriver, CMakePreconditionProblemSolver} from '@cmt/drivers/driver';
import {CMakeGenerator, Kit} from '@cmt/kit';
import {makeCodeModelDriverTestsuite} from './driver-codemodel-tests';
import {makeDriverTestsuite} from './driver-test';
async function cmakeFileApiDriverFactory(cmake: CMakeExecutable,
config: ConfigurationReader,
kit: Kit|null,
workspaceFolder: string|null,
preconditionHandler: CMakePreconditionProblemSolver,
preferredGenerators: CMakeGenerator[]) {
const d: CMakeDriver
= await CMakeFileApiDriver.create(cmake, config, kit, workspaceFolder, preconditionHandler, preferredGenerators);
return d;
}
makeDriverTestsuite(cmakeFileApiDriverFactory);
makeCodeModelDriverTestsuite(cmakeFileApiDriverFactory);

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

@ -0,0 +1,22 @@
import {CMakeExecutable} from '@cmt/cmake/cmake-executable';
import {ConfigurationReader} from '@cmt/config';
import * as cms_driver from '@cmt/drivers/cms-driver';
import {CMakeDriver, CMakePreconditionProblemSolver} from '@cmt/drivers/driver';
import {CMakeGenerator, Kit} from '@cmt/kit';
import {makeCodeModelDriverTestsuite} from './driver-codemodel-tests';
import {makeDriverTestsuite} from './driver-test';
async function cmakeServerDriverFactory(cmake: CMakeExecutable,
config: ConfigurationReader,
kit: Kit|null,
workspaceFolder: string|null,
preconditionHandler: CMakePreconditionProblemSolver,
preferredGenerators: CMakeGenerator[]) {
const d: CMakeDriver = await cms_driver.CMakeServerClientDriver
.create(cmake, config, kit, workspaceFolder, preconditionHandler, preferredGenerators);
return d;
}
makeDriverTestsuite(cmakeServerDriverFactory);
makeCodeModelDriverTestsuite(cmakeServerDriverFactory);

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

@ -0,0 +1,249 @@
import {CMakeExecutable, getCMakeExecutableInformation} from '@cmt/cmake/cmake-executable';
import {ConfigurationReader} from '@cmt/config';
import * as codemodel_api from '@cmt/drivers/codemodel-driver-interface';
import * as chai from 'chai';
import {expect} from 'chai';
import * as chaiString from 'chai-string';
import * as fs from 'fs';
import * as path from 'path';
import * as rimraf from 'rimraf';
chai.use(chaiString);
import {Kit, CMakeGenerator} from '@cmt/kit';
import {CMakeDriver, CMakePreconditionProblemSolver} from '@cmt/drivers/driver';
const here = __dirname;
function getTestRootFilePath(filename: string): string {
return path.normalize(path.join(here, '../../../..', filename));
}
function cleanupBuildDir(build_dir: string): boolean {
if (fs.existsSync(build_dir)) {
rimraf.sync(build_dir);
}
return !fs.existsSync(build_dir);
}
let driver: CMakeDriver|null = null;
// tslint:disable:no-unused-expression
export function makeCodeModelDriverTestsuite(
driver_generator: (cmake: CMakeExecutable,
config: ConfigurationReader,
kit: Kit|null,
workspaceFolder: string|null,
preconditionHandler: CMakePreconditionProblemSolver,
preferredGenerators: CMakeGenerator[]) => Promise<CMakeDriver>) {
suite('CMake-CodeModel-Driver tests', () => {
const cmakePath: string = process.env.CMAKE_EXECUTABLE ? process.env.CMAKE_EXECUTABLE : 'cmake';
const workspacePath: string = 'test/unit-tests/driver/workspace';
const root = getTestRootFilePath(workspacePath);
const defaultWorkspaceFolder = getTestRootFilePath('test/unit-tests/driver/workspace/test_project');
const emptyWorkspaceFolder = getTestRootFilePath('test/unit-tests/driver/workspace/empty_project');
let kitDefault: Kit;
if (process.platform === 'win32') {
kitDefault = {
name: 'Visual Studio Community 2017 - amd64',
visualStudio: 'VisualStudio.15.0',
visualStudioArchitecture: 'amd64',
preferredGenerator: {name: 'Visual Studio 15 2017', platform: 'x64'}
} as Kit;
} else {
kitDefault
= {name: 'GCC', compilers: {C: 'gcc', CXX: 'g++'}, preferredGenerator: {name: 'Unix Makefiles'}} as Kit;
}
setup(async function(this: Mocha.IBeforeAndAfterContext, done) {
driver = null;
if (!cleanupBuildDir(path.join(defaultWorkspaceFolder, 'build'))) {
done('Default build folder still exists');
}
if (!cleanupBuildDir(path.join(emptyWorkspaceFolder, 'build'))) {
done('Empty project build folder still exists');
}
done();
});
teardown(async function(this: Mocha.IBeforeAndAfterContext) {
this.timeout(20000);
if (driver) {
return driver.asyncDispose();
}
});
async function generateCodeModelForConfiguredDriver(args: string[] =
[]): Promise<null|codemodel_api.CodeModelContent> {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
let code_model: null|codemodel_api.CodeModelContent = null;
if (driver instanceof codemodel_api.CodeModelDriver) {
driver.onCodeModelChanged(cm => { code_model = cm; });
}
expect(await driver.configure(args)).to.be.eq(0);
return code_model;
}
test('Test generation of code model with multi configuration like VS', async () => {
if (process.platform !== 'win32')
return;
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
expect(codemodel_data!.configurations.length).to.be.eql(4);
}).timeout(90000);
test('Test generation of code model with one configuration like make on linux', async () => {
if (process.platform === 'win32')
return;
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
expect(codemodel_data!.configurations.length).to.be.eql(1);
}).timeout(90000);
test('Test project information', async () => {
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
const project = codemodel_data!.configurations[0].projects[0];
// Test project name
expect(project.name).to.be.eq('TestBuildProcess');
// Test location of project source directory
// Used by tree view to make paths relative
expect(path.normalize(project.sourceDirectory).toLowerCase())
.to.eq(path.normalize(path.join(root, 'test_project')).toLowerCase());
}).timeout(90000);
test('Test first executable target directory', async () => {
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
const target = codemodel_data!.configurations[0].projects[0].targets.find(t => t.type == 'EXECUTABLE');
expect(target).to.be.not.undefined;
// Test target name used for node label
expect(target!.name).to.be.eq('TestBuildProcess');
const executableName = process.platform === 'win32' ? 'TestBuildProcess.exe' : 'TestBuildProcess';
expect(target!.fullName).to.be.eq(executableName);
expect(target!.type).to.be.eq('EXECUTABLE');
// Test location of project source directory
// used by tree view to make paths relative
expect(path.normalize(target!.sourceDirectory!).toLowerCase())
.to.eq(path.normalize(path.join(root, 'test_project')).toLowerCase());
// Test main source file used in by tree view
expect(target!.fileGroups).to.be.not.undefined;
const compile_information = target!.fileGroups!.find(t => !!t.language);
expect(compile_information).to.be.not.undefined;
expect(compile_information!.sources).to.include('main.cpp');
}).timeout(90000);
test('Test first static library target directory', async () => {
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
const target = codemodel_data!.configurations[0].projects[0].targets.find(t => t.type == 'STATIC_LIBRARY');
expect(target).to.be.not.undefined;
// Test target name used for node label
expect(target!.name).to.be.eq('StaticLibDummy');
const executableName = process.platform === 'win32' ? 'StaticLibDummy.lib' : 'libStaticLibDummy.a';
expect(target!.fullName).to.be.eq(executableName);
expect(target!.type).to.be.eq('STATIC_LIBRARY');
// Test location of project source directory
// Used by tree view to make paths relative
expect(path.normalize(target!.sourceDirectory!).toLowerCase())
.to.eq(path.normalize(path.join(root, 'test_project', 'static_lib_dummy')).toLowerCase());
// Language
const compile_information = target!.fileGroups!.find(t => !!t.language);
expect(compile_information).to.be.not.undefined;
expect(compile_information!.language).to.eq('CXX');
// Test main source file
expect(compile_information!.sources).to.include('info.cpp');
expect(compile_information!.sources).to.include('test2.cpp');
// compile flags for file groups
if (process.platform === 'win32') {
expect(compile_information!.compileFlags).to.eq('/DWIN32 /D_WINDOWS /W3 /GR /EHsc /MDd /Zi /Ob0 /Od /RTC1 ');
}
}).timeout(90000);
test('Test first shared library target directory', async () => {
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
const target = codemodel_data!.configurations[0].projects[0].targets.find(t => t.type == 'SHARED_LIBRARY');
expect(target).to.be.not.undefined;
// Test target name used for node label
expect(target!.name).to.be.eq('SharedLibDummy');
const executableNameRegex
= process.platform === 'win32' ? /^SharedLibDummy.dll/ : /^libSharedLibDummy.(so|dylib)/;
expect(target!.fullName).to.match(executableNameRegex);
expect(target!.type).to.be.eq('SHARED_LIBRARY');
// Test location of project source directory
// Used by tree view to make paths relative
expect(path.normalize(target!.sourceDirectory!).toLowerCase())
.to.eq(path.normalize(path.join(root, 'test_project', 'shared_lib_dummy')).toLowerCase());
// Test main source file
expect(target!.fileGroups).to.be.not.undefined;
expect(target!.fileGroups![0].sources[0]).to.eq('src/info.c');
expect(target!.fileGroups![0].defines).contains('TEST_CMAKE_DEFINE');
expect(target!.fileGroups![0].isGenerated).to.be.false;
expect(path.normalize(target!.fileGroups![0].includePath![0].path).toLowerCase())
.to.eq(path.normalize(path.join(root, 'test_project', 'shared_lib_dummy', 'inc')).toLowerCase());
// Language
expect(target!.fileGroups![0].language).to.eq('C');
// compile flags for file groups
if (process.platform === 'win32') {
expect(target!.fileGroups![0].compileFlags).to.eq('/DWIN32 /D_WINDOWS /W3 /MDd /Zi /Ob0 /Od /RTC1 ');
}
}).timeout(90000);
test('Test cache access', async () => {
const codemodel_data = await generateCodeModelForConfiguredDriver();
expect(codemodel_data).to.be.not.null;
const target = codemodel_data!.configurations[0].projects[0].targets.find(t => t.type == 'UTILITY'
&& t.name == 'runTestTarget');
expect(target).to.be.not.undefined;
// maybe could be used to exclude file list from utility targets
expect(target!.fileGroups![0].isGenerated).to.be.true;
}).timeout(90000);
test('Test sysroot access', async () => {
// This test does not work with VisualStudio.
// VisualStudio generator does not provide the sysroot in the code model.
// macOS has separate sysroot variable (see CMAKE_OSX_SYSROOT); this build fails.
if (process.platform === 'win32' || process.platform === 'darwin')
return;
const codemodel_data = await generateCodeModelForConfiguredDriver(['-DCMAKE_SYSROOT=/tmp']);
expect(codemodel_data).to.be.not.null;
const target = codemodel_data!.configurations[0].projects[0].targets.find(t => t.type == 'EXECUTABLE');
expect(target).to.be.not.undefined;
expect(target!.sysroot).to.be.eq('/tmp');
}).timeout(90000);
});
}

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

@ -0,0 +1,383 @@
import {CMakeExecutable, getCMakeExecutableInformation} from '@cmt/cmake/cmake-executable';
import {ConfigurationReader} from '@cmt/config';
import * as chai from 'chai';
import {expect} from 'chai';
import * as chaiString from 'chai-string';
import * as fs from 'fs';
import * as path from 'path';
import * as rimraf from 'rimraf';
chai.use(chaiString);
import {Kit, CMakeGenerator} from '@cmt/kit';
import {CMakePreconditionProblems, CMakeDriver, CMakePreconditionProblemSolver} from '@cmt/drivers/driver';
const here = __dirname;
function getTestRootFilePath(filename: string): string {
return path.normalize(path.join(here, '../../../..', filename));
}
function cleanupBuildDir(build_dir: string): boolean {
if (fs.existsSync(build_dir)) {
rimraf.sync(build_dir);
}
return !fs.existsSync(build_dir);
}
// tslint:disable-next-line: no-unused-expression
export function makeDriverTestsuite(driver_generator: (cmake: CMakeExecutable,
config: ConfigurationReader,
kit: Kit|null,
workspaceFolder: string|null,
preconditionHandler: CMakePreconditionProblemSolver,
preferredGenerators: CMakeGenerator[]) => Promise<CMakeDriver>) {
let driver: CMakeDriver|null = null;
// tslint:disable:no-unused-expression
suite('CMake-Driver tests', () => {
const cmakePath: string = process.env.CMAKE_EXECUTABLE ? process.env.CMAKE_EXECUTABLE : 'cmake';
const defaultWorkspaceFolder = getTestRootFilePath('test/unit-tests/driver/workspace/test_project');
const emptyWorkspaceFolder = getTestRootFilePath('test/unit-tests/driver/workspace/empty_project');
const badCommandWorkspaceFolder = getTestRootFilePath('test/unit-tests/driver/workspace/bad_command');
let kitDefault: Kit;
if (process.platform === 'win32') {
kitDefault = {
name: 'Visual Studio Community 2017 - amd64',
visualStudio: 'VisualStudio.15.0',
visualStudioArchitecture: 'amd64',
preferredGenerator: {name: 'Visual Studio 15 2017', platform: 'x64'}
} as Kit;
} else {
kitDefault
= {name: 'GCC', compilers: {C: 'gcc', CXX: 'g++'}, preferredGenerator: {name: 'Unix Makefiles'}} as Kit;
}
let kitNinja: Kit;
if (process.platform === 'win32') {
kitNinja = {
name: 'Visual Studio Community 2017 - amd64',
visualStudio: 'VisualStudio.15.0',
visualStudioArchitecture: 'amd64',
preferredGenerator: {name: 'Ninja'}
} as Kit;
} else {
kitNinja = {name: 'GCC', compilers: {C: 'gcc', CXX: 'g++'}, preferredGenerator: {name: 'Ninja'}} as Kit;
}
setup(async function(this: Mocha.IBeforeAndAfterContext, done) {
driver = null;
if (!cleanupBuildDir(path.join(defaultWorkspaceFolder, 'build'))) {
done('Default build folder still exists');
}
if (!cleanupBuildDir(path.join(emptyWorkspaceFolder, 'build'))) {
done('Empty project build folder still exists');
}
if (!cleanupBuildDir(path.join(badCommandWorkspaceFolder, 'build'))) {
done('Bad command build folder still exists');
}
done();
});
teardown(async function(this: Mocha.IBeforeAndAfterContext) {
this.timeout(20000);
if (driver) {
return driver.asyncDispose();
}
});
test(`All target for ${kitDefault.name}`, async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
const allTargetName = driver.allTargetName;
if (process.platform === 'win32') {
expect(allTargetName).to.eq('ALL_BUILD');
} else {
expect(allTargetName).to.eq('all');
}
}).timeout(60000);
test('Check binary dir', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
expect(driver.binaryDir).to.endsWith('test/unit-tests/driver/workspace/test_project/build');
}).timeout(60000);
test('Configure fails', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitDefault, badCommandWorkspaceFolder, async () => {}, []);
expect(await driver.cleanConfigure([])).to.be.eq(1);
}).timeout(90000);
test('Build', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
expect(await driver.cleanConfigure([])).to.be.eq(0);
expect(await driver.build(driver.allTargetName)).to.be.eq(0);
expect(driver.executableTargets.length).to.be.eq(1);
expect(driver.executableTargets[0].name).to.be.equal('TestBuildProcess');
expect(fs.existsSync(driver.executableTargets[0].path)).to.be.true;
}).timeout(90000);
test('Configure fails on invalid preferred generator', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
const kit = {name: 'GCC', preferredGenerator: {name: 'BlaBla'}} as Kit;
// tslint:disable-next-line: no-floating-promises
expect(driver_generator(executable, config, kit, defaultWorkspaceFolder, async () => {}, []))
.to.be.rejectedWith('No usable generator found.');
}).timeout(60000);
test('Throw exception on set kit without preferred generator found', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
await expect(driver.setKit({name: 'GCC'}, [])).to.be.rejectedWith('No usable generator found.');
}).timeout(90000);
test('Try build on empty dir', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
expect(e).to.be.eq(CMakePreconditionProblems.MissingCMakeListsFile);
called = true;
};
driver
= await driver_generator(executable, config, kitDefault, emptyWorkspaceFolder, checkPreconditionHelper, []);
expect(await driver.cleanConfigure([])).to.be.eq(-1);
expect(called).to.be.true;
}).timeout(60000);
test('No parallel configuration', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
expect(e).to.be.eq(CMakePreconditionProblems.ConfigureIsAlreadyRunning);
called = true;
};
driver
= await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
const configure1 = driver.configure([]);
const configure2 = driver.configure([]);
expect(await configure1).to.be.equal(0);
expect(await configure2).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No parallel clean configuration', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
expect(e).to.be.eq(CMakePreconditionProblems.ConfigureIsAlreadyRunning);
called = true;
};
driver
= await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
const configure1 = driver.cleanConfigure([]);
const configure2 = driver.cleanConfigure([]);
expect(await configure1).to.be.equal(0);
expect(await configure2).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No parallel builds', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
if (e == CMakePreconditionProblems.BuildIsAlreadyRunning) {
called = true;
}
};
driver
= await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
expect(await driver.configure([])).to.be.equal(0);
const build1 = driver.build(driver.allTargetName);
const build2 = driver.build(driver.allTargetName);
expect(await build1).to.be.equal(0);
expect(await build2).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No build parallel to configure', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
if (e == CMakePreconditionProblems.ConfigureIsAlreadyRunning) {
called = true;
}
};
driver
= await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
expect(await driver.configure([])).to.be.equal(0);
const configure = driver.configure([]);
const build = driver.build(driver.allTargetName);
expect(await configure).to.be.equal(0);
expect(await build).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No configure parallel to build', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
if (e == CMakePreconditionProblems.BuildIsAlreadyRunning) {
called = true;
}
};
driver
= await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
expect(await driver.configure([])).to.be.equal(0);
const build = driver.build(driver.allTargetName);
const configure = driver.configure([]);
expect(await build).to.be.equal(0);
expect(await configure).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No build parallel to clean configuration', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
if (e == CMakePreconditionProblems.ConfigureIsAlreadyRunning) {
called = true;
}
};
driver
= await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
const configure = driver.cleanConfigure([]);
const build = driver.build(driver.allTargetName);
expect(await configure).to.be.equal(0);
expect(await build).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('No clean configuration parallel to build', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
let called = false;
const checkPreconditionHelper = async (e: CMakePreconditionProblems) => {
if (e == CMakePreconditionProblems.BuildIsAlreadyRunning) {
called = true;
}
};
driver
= await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, checkPreconditionHelper, []);
expect(await driver.configure([])).to.be.equal(0);
const build = driver.build(driver.allTargetName);
const configure = driver.cleanConfigure([]);
expect(await build).to.be.equal(0);
expect(await configure).to.be.equal(-1);
expect(called).to.be.true;
}).timeout(90000);
test('Test pre-configured workspace', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitNinja, defaultWorkspaceFolder, async () => {}, []);
await driver.cleanConfigure([]);
await driver.asyncDispose();
driver = null;
driver = await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
expect(await driver.configure([])).to.be.eq(0);
expect(driver.generatorName).to.be.eq(kitNinja.preferredGenerator!.name);
expect(driver.cmakeCacheEntries.get('CMAKE_GENERATOR')!.value).to.be.eq('Ninja');
}).timeout(60000);
test('Test generator switch', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
await driver.cleanConfigure([]);
expect(driver.cmakeCacheEntries.get('CMAKE_GENERATOR')!.value).to.be.not.eq('Ninja');
await driver.setKit(kitNinja, [{name: 'Ninja'}]);
expect(await driver.configure([])).to.be.eq(0);
expect(driver.cmakeCacheEntries.get('CMAKE_GENERATOR')!.value).to.be.eq('Ninja');
}).timeout(90000);
test('Test Visual Studio kit with wrong all target name', async () => {
if (process.platform !== 'win32')
return;
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
await driver.cleanConfigure([]);
expect(await driver.build('all')).to.be.eq(0, 'Automatic correction of all target failed');
}).timeout(90000);
test('Test Ninja kit with wrong all target name', async () => {
if (process.platform !== 'win32')
return;
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitNinja, defaultWorkspaceFolder, async () => {}, []);
await driver.cleanConfigure([]);
expect(await driver.build('ALL_BUILD')).to.be.eq(0, 'Automatic correction of ALL_BUILD target failed');
}).timeout(90000);
test('Test extra arguments on configure', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
await driver.configure(['-DEXTRA_ARGS_TEST=Hallo']);
expect(driver.cmakeCacheEntries.get('extraArgsEnvironment')!.value).to.be.eq('Hallo');
}).timeout(90000);
test('Test extra arguments on clean and configure', async () => {
const config = ConfigurationReader.create();
const executable = await getCMakeExecutableInformation(cmakePath);
driver = await driver_generator(executable, config, kitDefault, defaultWorkspaceFolder, async () => {}, []);
await driver.cleanConfigure(['-DEXTRA_ARGS_TEST=Hallo']);
expect(driver.cmakeCacheEntries.get('extraArgsEnvironment')!.value).to.be.eq('Hallo');
}).timeout(90000);
});
}