Merge pull request #8907 from keymanapp/refactor/developer/8883-move-filename-consistency-check-to-kmc

refactor(developer): move filename consistency check to kmc
This commit is contained in:
Marc Durdin 2023-06-02 13:20:57 +10:00 committed by GitHub
commit 355b5f9085
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 174 additions and 186 deletions

View file

@ -27,8 +27,8 @@ export class KPJFileReader {
return data as KPJFile;
}
public validate(source: KPJFile, schemaBuffer: Buffer): void {
const schema = JSON.parse(schemaBuffer.toString('utf8'));
public validate(source: KPJFile, schemaBuffer: Uint8Array): void {
const schema = JSON.parse(new TextDecoder().decode(schemaBuffer));
const ajv = new Ajv();
if(!ajv.validate(schema, source)) {
throw new Error(ajv.errorsText());

View file

@ -68,8 +68,8 @@ export default class KVKSFileReader {
}
}
public validate(source: KVKSourceFile, schemaBuffer: Buffer): void {
const schema = JSON.parse(schemaBuffer.toString('utf8'));
public validate(source: KVKSourceFile, schemaBuffer: Uint8Array): void {
const schema = JSON.parse(new TextDecoder().decode(schemaBuffer));
const ajv = new Ajv();
if(!ajv.validate(schema, source)) {
throw new Error(ajv.errorsText());

View file

@ -7,6 +7,7 @@ import { CompilerCallbacks } from '../util/compiler-interfaces.js';
import { constants } from '@keymanapp/ldml-keyboard-constants';
import { CommonTypesMessages } from '../util/common-events.js';
import { LDMLKeyboardTestDataXMLSourceFile, LKTTest, LKTTests } from './ldml-keyboard-testdata-xml.js';
import { fileURLToPath } from 'url';
interface NameAndProps {
'$'?: any; // content
@ -21,9 +22,9 @@ export default class LDMLKeyboardXMLSourceFileReader {
this.callbacks = callbacks;
}
readImportFile(version: string, subpath: string): Buffer {
// TODO-LDML: sanitize input string
let importPath = new URL(`../import/${version}/${subpath}`, import.meta.url);
readImportFile(version: string, subpath: string): Uint8Array {
// TODO-LDML: use this.callbacks.resolveFilename to get the actual path
let importPath = fileURLToPath(new URL(`../import/${version}/${subpath}`, import.meta.url));
return this.callbacks.loadFile(importPath);
}
@ -201,8 +202,8 @@ export default class LDMLKeyboardXMLSourceFileReader {
/**
* @returns true if valid, false if invalid
*/
public validate(source: LDMLKeyboardXMLSourceFile | LDMLKeyboardTestDataXMLSourceFile, schemaSource: Buffer): boolean {
const schema = JSON.parse(schemaSource.toString('utf8'));
public validate(source: LDMLKeyboardXMLSourceFile | LDMLKeyboardTestDataXMLSourceFile, schemaSource: Uint8Array): boolean {
const schema = JSON.parse(new TextDecoder().decode(schemaSource));
const ajv = new Ajv();
if(!ajv.validate(schema, source)) {
for (let err of ajv.errors) {

View file

@ -120,13 +120,11 @@ export interface CompilerFileSystemCallbacks {
export interface CompilerCallbacks {
/**
* Attempt to load a file. Return falsy if not found.
* TODO: accept only string
* TODO: never return falsy, just throw if not found?
* TODO: Buffer is Node-only.
* @param baseFilename
* @param filename
*/
loadFile(filename: string | URL): Buffer;
loadFile(filename: string): Uint8Array;
get path(): CompilerPathCallbacks;
get fs(): CompilerFileSystemCallbacks;
@ -138,7 +136,7 @@ export interface CompilerCallbacks {
*/
resolveFilename(baseFilename: string, filename: string): string;
loadSchema(schema: CompilerSchema): Buffer;
loadSchema(schema: CompilerSchema): Uint8Array;
reportMessage(event: CompilerEvent): void;
debug(msg: string): void;
};

View file

@ -42,7 +42,7 @@ export class TestCompilerCallbacks implements CompilerCallbacks {
return resolveFilename(baseFilename, filename);
}
loadFile(filename: string | URL): Buffer {
loadFile(filename: string): Uint8Array {
// TODO: error management, does it belong here?
try {
return loadFile(filename);

View file

@ -224,8 +224,8 @@
#define CWARN_KeyShouldIncludeNCaps 0x000020AD
#define CHINT_UnreachableRule 0x000010AE
#define CHINT_FilenameHasDifferingCase 0x000010AF
#define CWARN_MissingFile 0x000020B0
#define CHINT_FilenameHasDifferingCase 0x000010AF // only used in kmcmpdll
#define CWARN_MissingFile 0x000020B0 // only used in kmcmpdll
#define CERR_BufferOverflow 0x000080C0
#define CERR_Break 0x000080C1

View file

@ -36,7 +36,7 @@ export class TestCompilerCallbacks implements CompilerCallbacks {
/* CompilerCallbacks */
loadFile(filename: string | URL): Buffer {
loadFile(filename: string): Uint8Array {
try {
return fs.readFileSync(filename);
} catch(e) {
@ -77,7 +77,7 @@ export class TestCompilerCallbacks implements CompilerCallbacks {
this.messages.push(event);
}
loadSchema(schema: CompilerSchema): Buffer {
loadSchema(schema: CompilerSchema): Uint8Array {
return fs.readFileSync(new URL(SCHEMA_BASE + schema + '.schema.json', import.meta.url));
}

View file

@ -1,4 +1,5 @@
import 'mocha';
import path from 'path';
import { assert } from 'chai';
import { CompilerMessages } from '../src/compiler/messages.js';
import { TestCompilerCallbacks, verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers';
@ -26,7 +27,7 @@ describe('CompilerMessages', function () {
assert(compiler.verifyInitialized());
const kmnPath = makePathToFixture(...fixture);
const outfile = callbacks.path.basename(kmnPath, '.kmn') + '.kmx';
const outfile = path.basename(kmnPath, '.kmn') + '.kmx';
// Note: throwing away compile results (just to memory)
compiler.runCompiler(kmnPath, outfile, {saveDebug: true, shouldAddCompilerVersion: false});

View file

@ -16,7 +16,12 @@ export class KmpCompiler {
public transformKpsToKmpObject(kpsFilename: string): KmpJsonFile.KmpJsonFile {
// Load the KPS data from XML as JS structured data.
const data = this.callbacks.fs.readFileSync(kpsFilename, 'utf-8');
const buffer = this.callbacks.loadFile(kpsFilename);
if(!buffer) {
this.callbacks.reportMessage(CompilerMessages.Error_FileDoesNotExist({filename: kpsFilename}));
return null;
}
const data = new TextDecoder().decode(buffer);
const kpsPackage = (() => {
let a: KpsFile.KpsPackage;
@ -24,7 +29,8 @@ export class KmpCompiler {
tagNameProcessors: [xml2js.processors.firstCharLowerCase],
explicitArray: false
});
parser.parseString(data, (e: unknown, r: unknown) => { a = r as KpsFile.KpsPackage });
// TODO: add unit test for xml errors parsing .kps file
parser.parseString(data, (e: unknown, r: unknown) => { if(e) throw e; a = r as KpsFile.KpsPackage });
return a;
})();
@ -284,9 +290,20 @@ export class KmpCompiler {
* we want that to remain the responsibility of the keyboard compiler, so we'll warn the
* few users who are still doing this
*/
private warnIfKvkFileIsNotBinary(filename: string, data: Buffer) {
// TODO: Buffer is not available on web
if(filename.match(/\.kvk$/) && data.compare(Buffer.from(KvkFile.KVK_HEADER_IDENTIFIER_BYTES), 0, 3, 0, 3) != 0) {
private warnIfKvkFileIsNotBinary(filename: string, data: Uint8Array) {
if(!filename.match(/\.kvk$/)) {
return;
}
if(data.byteLength < 4) {
// TODO: Not a valid .kvk file; should we be reporting this?
return;
}
if(data[0] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[0] ||
data[1] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[1] ||
data[2] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[2] ||
data[3] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[3]) {
this.callbacks.reportMessage(CompilerMessages.Warn_FileIsNotABinaryKvkFile({filename: filename}));
}
}

View file

@ -1,14 +1,52 @@
import * as fs from 'fs';
import * as path from 'path';
import { CompilerCallbacks, CompilerSchema, CompilerEvent, compilerErrorSeverityName, CompilerPathCallbacks, CompilerFileSystemCallbacks } from '@keymanapp/common-types';
import { InfrastructureMessages } from './messages.js';
/**
* Concrete implementation for CLI use
*/
// TODO: Make a common class for all the CompilerCallbacks implementations
export class NodeCompilerCallbacks implements CompilerCallbacks {
// TODO: REMOVE!
loadFile(filename: string | URL): Buffer {
/* NodeCompilerCallbacks */
messages: CompilerEvent[] = [];
silent: boolean;
constructor(silent?: boolean) {
this.silent = !!silent;
}
clear() {
this.messages = [];
}
hasMessage(code: number): boolean {
return this.messages.find((item) => item.code == code) === undefined ? false : true;
}
private verifyFilenameConsistency(originalFilename: string): void {
if(fs.existsSync(originalFilename)) {
// Note, we only check this if the file exists, because
// if it is not found, that will be returned as an error
// from loadFile anyway.
const filename = fs.realpathSync(originalFilename);
const nativeFilename = fs.realpathSync.native(filename);
if(filename != nativeFilename) {
this.reportMessage(InfrastructureMessages.Hint_FilenameHasDifferingCase({
reference: originalFilename,
filename: nativeFilename
}));
}
}
}
/* CompilerCallbacks */
loadFile(filename: string): Uint8Array {
this.verifyFilenameConsistency(filename);
try {
return fs.readFileSync(filename);
} catch (e) {
@ -29,6 +67,10 @@ export class NodeCompilerCallbacks implements CompilerCallbacks {
}
reportMessage(event: CompilerEvent): void {
this.messages.push(event);
if(this.silent) {
return;
}
const code = event.code.toString(16);
if(event.line) {
console.log(`${compilerErrorSeverityName(event.code)} ${code} [${event.line}]: ${event.message}`);
@ -41,7 +83,7 @@ export class NodeCompilerCallbacks implements CompilerCallbacks {
console.debug(msg);
}
loadSchema(schema: CompilerSchema) {
loadSchema(schema: CompilerSchema): Uint8Array {
let schemaPath = new URL('../util/' + schema + '.schema.json', import.meta.url);
return fs.readFileSync(schemaPath);
}

View file

@ -2,7 +2,7 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m
const Namespace = CompilerErrorNamespace.Infrastructure;
const SevInfo = CompilerErrorSeverity.Info | Namespace;
// const SevHint = CompilerErrorSeverity.Hint | Namespace;
const SevHint = CompilerErrorSeverity.Hint | Namespace;
// const SevWarn = CompilerErrorSeverity.Warn | Namespace;
const SevError = CompilerErrorSeverity.Error | Namespace;
const SevFatal = CompilerErrorSeverity.Fatal | Namespace;
@ -39,5 +39,9 @@ export class InfrastructureMessages {
static Error_InvalidProjectFile = (o:{message:string}) => m(this.ERROR_InvalidProjectFile,
`Project file is not valid: ${o.message}`);
static ERROR_InvalidProjectFile = SevError | 0x0008;
static Hint_FilenameHasDifferingCase = (o:{reference:string, filename:string}) => m(this.HINT_FilenameHasDifferingCase,
`File ${o.filename} differs in case from reference ${o.reference}; this will fail on platforms with case-sensitive filesystems.`);
static HINT_FilenameHasDifferingCase = SevHint | 0x0009;
}

View file

@ -0,0 +1,6 @@
store(&name) 'hint_filename_has_differing_case'
store(&version) '7.0'
begin unicode > use(main)
group(main) using keys

View file

@ -1,9 +1,80 @@
import 'mocha';
import { assert } from 'chai';
import { InfrastructureMessages } from '../src/messages/messages.js';
import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers';
import { makePathToFixture } from './helpers/index.js';
import { NodeCompilerCallbacks } from '../src/messages/NodeCompilerCallbacks.js';
describe('InfrastructureMessages', function () {
it('should have a valid InfrastructureMessages object', function() {
return verifyCompilerMessagesObject(InfrastructureMessages);
});
//
// Message tests
//
/*
TODO:
let callbacks = new TestCompilerCallbacks();
async function testForMessage(context: Mocha.Context, fixture: string[], messageId?: number) {
context.timeout(10000);
callbacks.clear();
const builder = new BuildKmnKeyboard();
const path = makePathToFixture(...fixture);
let result = await builder.build(path, callbacks, {
compilerVersion: false,
compilerWarningsAsErrors: true,
debug: false,
warnDeprecatedCode: true,
});
if(messageId) {
assert.isTrue(callbacks.hasMessage(messageId), `messageId ${messageId.toString(16)} not generated, instead got: `+JSON.stringify(callbacks.messages,null,2));
assert.lengthOf(callbacks.messages, 1);
} else {
assert.lengthOf(callbacks.messages, 0, `messages should be empty, but instead got: `+JSON.stringify(callbacks.messages,null,2));
assert.isTrue(result);
}
}
// ERROR_FileDoesNotExist
it('should generate ERROR_FileDoesNotExist if a file does not exist', async function() {
await testForMessage(this, ['invalid-keyboards', 'error_file_does_not_exist.kmn'], CompilerMessages.ERROR_FileDoesNotExist);
});
// ERROR_FileTypeNotRecognized
it('should generate ERROR_FileTypeNotRecognized if a file is not a recognized type', async function() {
await testForMessage(this, ['invalid-keyboards', 'error_file_type_not_recognized.xxx'], CompilerMessages.ERROR_FileTypeNotRecognized);
});
// ERROR_OutFileNotValidForProjects
it('should generate ERROR_OutFileNotValidForProjects if an output file is specified for a project build', async function() {
await testForMessage(this, ['invalid-keyboards', 'error_out_file_not_valid_for_projects.kpj'], CompilerMessages.ERROR_OutFileNotValidForProjects);
});
// ERROR_InvalidProjectFile
it('should generate ERROR_InvalidProjectFile if a project file is invalid', async function() {
await testForMessage(this, ['invalid-keyboards', 'error_invalid_project_file.kpj'], CompilerMessages.ERROR_InvalidProjectFile);
});
*/
// HINT_FilenameHasDifferingCase
it('should generate HINT_FilenameHasDifferingCase if a referenced file has differing case', async function() {
// This message is generated by NodeCompilerCallbacks, because that's where the filesystem is visible,
// so we can't use our usual testForMessage pattern.
const ncb = new NodeCompilerCallbacks(true);
ncb.loadFile(makePathToFixture('invalid-keyboards', 'Hint_Filename_Has_Differing_Case.kmn'));
assert.isTrue(ncb.hasMessage(InfrastructureMessages.HINT_FilenameHasDifferingCase),
`HINT_FilenameHasDifferingCase not generated, instead got: `+JSON.stringify(ncb.messages,null,2));
});
});

View file

@ -1,112 +0,0 @@
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING 1
#include "pch.h"
#include "compfile.h"
#include <kmn_compiler_errors.h>
#include "kmcmplib.h"
#include <string>
#include "CheckFilenameConsistency.h"
#include "kmx_u16.h"
#ifdef _MSC_VER
#include <io.h>
#endif
KMX_DWORD CheckFilenameConsistency( KMX_CHAR const * Filename, bool ReportMissingFile) {
PKMX_WCHAR WFilename = strtowstr(( KMX_CHAR *)Filename);
KMX_DWORD const result = CheckFilenameConsistency(WFilename, ReportMissingFile);
delete WFilename;
return result;
}
KMX_DWORD CheckFilenameConsistency(KMX_WCHAR const * Filename, bool ReportMissingFile) {
// TODO: we no longer have filesystem access here. We could move this check to
// kmc itself, and make it consistent across all compilers that use the same
// loader callback -- see #8883
return CERR_None;
#if 0
// not ready yet: needs more attention-> common includes for non-Windows platforms
KMX_WCHAR Name[260]; // TODO: fixed buffer sizes bad
if (IsRelativePath(Filename)) {
PKMX_WCHAR WCompileDir = strtowstr(kmcmp::CompileDir);
u16ncpy(Name, WCompileDir, _countof(Name)); // I3481
u16ncat(Name, Filename, _countof(Name)); // I3481
delete[] WCompileDir;
} else {
u16ncpy(Name, Filename, _countof(Name)); // I3481
}
#ifndef _MSC_VER
// Filename consistency only needs to be checked on Windows, because other
// platforms are going to fail if the filename is inconsistent anyway!
if(!kmcmp_FileExists(Name)) {
if (ReportMissingFile) {
u16cpy(ErrExtraW, u"referenced file '");
u16ncat(ErrExtraW, Filename, 256);
u16ncat(ErrExtraW, u"'", 256);
strcpy(ErrExtraLIB, string_from_u16string(ErrExtraW).c_str());
AddWarning(CWARN_MissingFile);
}
return CERR_None;
}
return CERR_None;
#else
_wfinddata_t fi;
intptr_t n;
if ((n = _wfindfirst((const wchar_t*) Name, &fi)) == -1) {
if (ReportMissingFile) {
sprintf(ErrExtraLIB, "referenced file '%ls'", (wchar_t*) Filename);
AddWarning(CWARN_MissingFile);
}
return CERR_None;
}
_findclose(n);
KMX_WCHAR FName[_MAX_FNAME], Ext[_MAX_EXT];
wchar_t WChName[_MAX_PATH];
_wsplitpath_s((const wchar_t*)Filename, nullptr, 0, nullptr, 0, (wchar_t*) FName, _MAX_FNAME, (wchar_t*) Ext, _MAX_EXT);
_wmakepath_s(WChName, _MAX_PATH, nullptr, nullptr, (const wchar_t*) FName, (const wchar_t*) Ext);
if (wcscmp(WChName, fi.name) != 0) {
sprintf(ErrExtraLIB, "reference '%ls' does not match actual filename '%ls'", WChName, fi.name);
AddWarning(CHINT_FilenameHasDifferingCase);
}
#endif
return CERR_None;
#endif
}
KMX_DWORD CheckFilenameConsistencyForCalls(PFILE_KEYBOARD fk) {
// call() statements depend on a fairly ugly hack for js,
// where store(DllFunction) "my.dll:func" will look for a
// file called function.call_js. ( or should this be func.call_js ? )
// This is ripe for rewrite!
// But let's check what we have anyway
PFILE_STORE sp;
KMX_DWORD i, msg;
for (i = 0, sp = fk->dpStoreArray; i < fk->cxStoreArray; i++, sp++) {
if (!sp->fIsCall) continue;
const std::u16string callsite(sp->dpString);
const auto colon = callsite.find(':');
if (colon == std::u16string::npos) continue;
auto func1 = callsite.substr(colon + 1);
std::u16string str_js(u".call_js");
std::u16string func = func1+ str_js;
if ((msg = CheckFilenameConsistency(func.c_str(), FALSE)) != CERR_None) {
return msg;
}
}
return CERR_None;
}

View file

@ -1,9 +0,0 @@
#pragma once
#include "compfile.h"
#include "kmcmplib.h"
KMX_DWORD CheckFilenameConsistencyForCalls(PFILE_KEYBOARD fk);
KMX_DWORD CheckFilenameConsistency(KMX_CHAR const * Filename, bool ReportMissingFile);
KMX_DWORD CheckFilenameConsistency(KMX_WCHAR const * Filename, bool ReportMissingFile);

View file

@ -111,7 +111,6 @@ const struct CompilerError CompilerErrors[] = {
{ CERR_DuplicateStore , "A store with this name has already been defined."},
{ CERR_RepeatedBegin , "Begin has already been set"},
{ CHINT_FilenameHasDifferingCase , "Casing differences may fail on some platforms: "},
{ CHINT_UnreachableRule , "This rule will never be matched as another rule takes precedence"},
{ CWARN_TooManyWarnings , "Too many warnings or errors"},
@ -142,7 +141,6 @@ const struct CompilerError CompilerErrors[] = {
{ CWARN_NulNotFirstStatementInContext , "nul must be the first statement in the context"},
{ CWARN_IfShouldBeAtStartOfContext , "if, platform and baselayout should be at start of context (after nul, if present)"},
{ CWARN_KeyShouldIncludeNCaps , "Other rules which reference this key include CAPS or NCAPS modifiers, so this rule must include NCAPS modifier to avoid inconsistent matches"},
{ CWARN_MissingFile , "The referenced file could not be found: "},
{ 0, nullptr }
};

View file

@ -1,7 +1,6 @@
#include "pch.h"
#include <kmn_compiler_errors.h>
#include "kmcmplib.h"
#include "CheckFilenameConsistency.h"
#include "CheckNCapsConsistency.h"
#include "DeprecationChecks.h"
#include "versioning.h"
@ -143,11 +142,6 @@ bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk)
return FALSE;
}
if ((msg = CheckFilenameConsistencyForCalls(fk)) != CERR_None) {
AddCompileError(msg);
return FALSE;
}
delete str;
if (!kmcmp::CheckKeyboardFinalVersion(fk)) {

View file

@ -97,7 +97,6 @@
#include <codecvt>
#include <locale>
#include "CheckFilenameConsistency.h"
#include "UnreachableRules.h"
#include "CheckForDuplicates.h"
#include "kmx_u16.h"
@ -1045,10 +1044,6 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE
delete[] sp->dpString;
sp->dpString = q;
if ((msg = CheckFilenameConsistency( (sp->dpString), FALSE)) != CERR_None) {
return msg;
}
}
break;
case TSS_KMW_RTL:
@ -1059,16 +1054,10 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE
case TSS_KMW_HELPFILE:
case TSS_KMW_EMBEDJS:
VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly);
if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) {
return msg;
}
break;
case TSS_KMW_EMBEDCSS:
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyEmbedCSS);
if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) {
return msg;
}
break;
case TSS_TARGETS: // I4504
@ -1118,9 +1107,6 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE
case TSS_LAYOUTFILE: // I3483
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyLayoutFile); // I4140
if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) {
return msg;
}
// Used by KMW compiler
break;

View file

@ -24,7 +24,6 @@
#include "pch.h"
#include <limits.h>
#include "NamedCodeConstants.h"
#include "CheckFilenameConsistency.h"
#include <kmcmplib.h>
#include "kmcompx.h"
@ -117,10 +116,6 @@ char *kmc_strupr(char *s) {
KMX_BOOL NamedCodeConstants::LoadFile(PFILE_KEYBOARD fk, const KMX_WCHAR *filename) {
const int str_size = 256;
if (CheckFilenameConsistency(filename, FALSE) != 0) {
return FALSE;
}
auto szNameUtf8 = string_from_u16string(filename);
int FileSize;

View file

@ -37,29 +37,25 @@ icuuc_dep = icu.get_variable('icuuc_dep')
lib = library('kmcmplib',
'CasedKeys.cpp',
'CharToKeyConversion.cpp',
'CheckForDuplicates.cpp',
'CheckNCapsConsistency.cpp',
'CompileKeyboardBuffer.cpp',
'Compiler.cpp',
'CompilerInterfaces.cpp',
'CompilerInterfacesWasm.cpp',
'CompMsg.cpp',
'DeprecationChecks.cpp',
'Edition.cpp',
'kmx_u16.cpp',
'NamedCodeConstants.cpp',
'UnreachableRules.cpp',
'uset-api.cpp',
'versioning.cpp',
'virtualcharkeys.cpp',
'xstring.cpp',
'CharToKeyConversion.cpp',
'CheckFilenameConsistency.cpp',
'CheckForDuplicates.cpp',
'CheckNCapsConsistency.cpp',
'UnreachableRules.cpp',
'kmx_u16.cpp',
'CompMsg.cpp',
'uset-api.cpp',
'../../../../common/windows/cpp/src/ConvertUTF.c',
'../../../../common/windows/cpp/src/crc32.cpp',
'../../../../common/windows/cpp/src/vkeys.cpp',
'xstring.cpp',
version_res,
cpp_args: defns + warns + flags,