mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 00:15:32 +00:00
feat(developer): run project validation from IDE
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled
When option 'SkipMetadataFiles' is not set in the project (inverse of 'Build metadata files for Keyman Cloud' in Project Settings dialog), then will run the validation step and generate .keyboard_info / .model_info from the IDE. Also add corresponding log messages (and add formatting for some others that were missing). Fixes: #16068 Test-bot: skip
This commit is contained in:
parent
f21960ef31
commit
1da2600818
11 changed files with 119 additions and 16 deletions
|
|
@ -198,6 +198,13 @@ The following parameters are available:
|
|||
project (which can also be controlled at a project level with the
|
||||
`skipMetadataFiles` option). This option is only valid for compiling projects.
|
||||
|
||||
`--publish-only`
|
||||
|
||||
: Skip building component files within a project, and only run the validation
|
||||
step. Must be combined with `--for-publishing` in order to have an effect.
|
||||
This option is only valid for compiling projects, and is mostly intended for
|
||||
use by the IDE. Will fail if build artifacts for components are not present.
|
||||
|
||||
### Examples
|
||||
|
||||
```shell
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export function declareBuild(program: Command) {
|
|||
buildCommand.command('file [infile...]', {isDefault: true})
|
||||
.description(`Compile one or more source files or projects ('file' subcommand is default).`)
|
||||
.option('--for-publishing', 'Verify that project meets @keymanapp repository requirements')
|
||||
.option('--publish-only', 'Only run the for-publishing validation, skip all other build steps')
|
||||
.addHelpText('after', `
|
||||
Supported file types:
|
||||
* folder: Keyman project in folder
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ export class BuildKeyboardInfo extends BuildActivity {
|
|||
sourcePath: calculateSourcePath(infile),
|
||||
lastCommitDate,
|
||||
forPublishing: !!options.forPublishing,
|
||||
publishOnly: !!options.publishOnly,
|
||||
};
|
||||
// Note: should we always ignore the passed-in output filename for .keyboard_info?
|
||||
const outputFilename = project.getOutputFilePath(KeymanFileTypes.Binary.KeyboardInfo);
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ export class BuildModelInfo extends BuildActivity {
|
|||
kpsFilename: project.resolveInputFilePath(kps),
|
||||
lastCommitDate,
|
||||
forPublishing: !!options.forPublishing,
|
||||
publishOnly: !!options.publishOnly,
|
||||
};
|
||||
|
||||
// Note: should we always ignore the passed-in output filename for .model_info?
|
||||
|
|
|
|||
|
|
@ -51,15 +51,17 @@ class ProjectBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
// Go through the various file types and build them
|
||||
for(const builder of buildActivities) {
|
||||
if(builder.sourceExtension == KeymanFileTypes.Source.Project) {
|
||||
// We don't support nested projects
|
||||
continue;
|
||||
}
|
||||
if(!this.options.publishOnly) {
|
||||
// Go through the various file types and build them
|
||||
for(const builder of buildActivities) {
|
||||
if(builder.sourceExtension == KeymanFileTypes.Source.Project) {
|
||||
// We don't support nested projects
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!await this.buildProjectTargets(builder)) {
|
||||
return false;
|
||||
if(!await this.buildProjectTargets(builder)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +115,11 @@ class ProjectBuilder {
|
|||
|
||||
const buildFilename = path.relative(process.cwd(), infile).replace(/\\/g, '/');
|
||||
const callbacks = new CompilerFileCallbacks(buildFilename, options, this.callbacks);
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename: infile, relativeFilename:buildFilename}));
|
||||
if(activity.sourceExtension == KeymanFileTypes.Source.Project) {
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_ValidatingProject({filename: infile, relativeFilename:buildFilename}));
|
||||
} else {
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename: infile, relativeFilename:buildFilename}));
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(outfile), {recursive:true});
|
||||
|
||||
|
|
@ -123,12 +129,19 @@ class ProjectBuilder {
|
|||
// note: command line option here, if set, overrides project setting
|
||||
result = result && !callbacks.hasFailureMessage(this.options.compilerWarningsAsErrors ?? this.project.options.compilerWarningsAsErrors);
|
||||
|
||||
if(result) {
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_FileBuiltSuccessfully({filename: infile, relativeFilename:buildFilename}));
|
||||
if(activity.sourceExtension == KeymanFileTypes.Source.Project) {
|
||||
if(result) {
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_ProjectValidatedSuccessfully({filename: infile, relativeFilename:buildFilename}));
|
||||
} else {
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_ProjectNotValidatedSuccessfully({filename: infile, relativeFilename:buildFilename}));
|
||||
}
|
||||
} else {
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename: infile, relativeFilename: buildFilename}));
|
||||
if(result) {
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_FileBuiltSuccessfully({filename: infile, relativeFilename:buildFilename}));
|
||||
} else {
|
||||
callbacks.reportMessage(InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename: infile, relativeFilename: buildFilename}));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -197,5 +197,26 @@ export class InfrastructureMessages {
|
|||
`Failed to generate new project '${def(o.id)}'.`,
|
||||
)});
|
||||
|
||||
// For this message, we override the filename with the passed-in file. A bit of a hack but does the job
|
||||
static INFO_ValidatingProject = SevInfo | 0x0029;
|
||||
static Info_ValidatingProject = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(
|
||||
this.INFO_ValidatingProject,
|
||||
`Validating ${def(o.relativeFilename)}`,
|
||||
)});
|
||||
|
||||
// For this message, we override the filename with the passed-in file. A bit of a hack but does the job
|
||||
static INFO_ProjectValidatedSuccessfully = SevInfo | 0x002A;
|
||||
static Info_ProjectValidatedSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(
|
||||
this.INFO_ProjectValidatedSuccessfully,
|
||||
`${def(o.relativeFilename)} validated successfully.`,
|
||||
)});
|
||||
|
||||
// For this message, we override the filename with the passed-in file. A bit of a hack but does the job
|
||||
static INFO_ProjectNotValidatedSuccessfully = SevInfo | 0x002B;
|
||||
static Info_ProjectNotValidatedSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(
|
||||
this.INFO_ProjectNotValidatedSuccessfully,
|
||||
`${def(o.relativeFilename)} failed to validate.`
|
||||
)});
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -242,22 +242,26 @@ export class NodeCompilerCallbacks implements CompilerCallbacks {
|
|||
* We treat a few certain infrastructure messages with special colours
|
||||
* @param event
|
||||
* @returns
|
||||
* Keep in sync with: UfrmMessages.pas, TfrmMessages.Add
|
||||
*/
|
||||
messageSpecialColor(event: CompilerEvent) {
|
||||
switch(event.code) {
|
||||
case InfrastructureMessages.INFO_BuildingFile:
|
||||
case InfrastructureMessages.INFO_CopyingProject:
|
||||
case InfrastructureMessages.INFO_GeneratingProject:
|
||||
case InfrastructureMessages.INFO_ValidatingProject:
|
||||
return color.whiteBright;
|
||||
case InfrastructureMessages.INFO_FileNotBuiltSuccessfully:
|
||||
case InfrastructureMessages.INFO_ProjectNotBuiltSuccessfully:
|
||||
case InfrastructureMessages.INFO_ProjectNotCopiedSuccessfully:
|
||||
case InfrastructureMessages.INFO_ProjectNotGeneratedSuccessfully:
|
||||
case InfrastructureMessages.INFO_ProjectNotValidatedSuccessfully:
|
||||
return color.red;
|
||||
case InfrastructureMessages.INFO_FileBuiltSuccessfully:
|
||||
case InfrastructureMessages.INFO_ProjectBuiltSuccessfully:
|
||||
case InfrastructureMessages.INFO_ProjectCopiedSuccessfully:
|
||||
case InfrastructureMessages.INFO_ProjectGeneratedSuccessfully:
|
||||
case InfrastructureMessages.INFO_ProjectValidatedSuccessfully:
|
||||
return color.green;
|
||||
}
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@ export interface ExtendedCompilerOptions extends CompilerOptions {
|
|||
* MIT
|
||||
*/
|
||||
forPublishing?: boolean;
|
||||
/**
|
||||
* Do not build components, just do the publish phase for the project. This is
|
||||
* used mostly by Keyman Developer IDE, which will run the .keyboard_info /
|
||||
* .package_info validation and compilation step if skipMetadataFiles is true.
|
||||
*/
|
||||
publishOnly?: boolean;
|
||||
/**
|
||||
* Overrides for message reporting
|
||||
*/
|
||||
|
|
@ -198,6 +204,7 @@ export function commanderOptionsToCompilerOptions(options: any, callbacks: Compi
|
|||
warnDeprecatedCode: options.warnDeprecatedCode,
|
||||
// ExtendedOptions
|
||||
forPublishing: options.forPublishing,
|
||||
publishOnly: options.publishOnly,
|
||||
messageOverrides: overrides,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ type
|
|||
function Run(const parameters: TArray<string>; const path, logFilename: string; Callback: TUtilExecuteCallbackEvent): Boolean;
|
||||
public
|
||||
function Compile(ProjectFile: TProjectFile; const infile, outfile: string; debug: Boolean; Callback: TUtilExecuteCallbackEvent = nil): Boolean;
|
||||
function CompileForPublishing(ProjectFile: TProjectFile; const projectFilename: string; debug: Boolean; Callback: TUtilExecuteCallbackEvent = nil): Boolean;
|
||||
function Copy(const source, dest, cwd: string; relocateExternal: Boolean; Callback: TUtilExecuteCallbackEvent = nil): Boolean;
|
||||
end;
|
||||
|
||||
|
|
@ -39,6 +40,39 @@ uses
|
|||
|
||||
{ TKmcWrapper }
|
||||
|
||||
function TKmcWrapper.CompileForPublishing(
|
||||
ProjectFile: TProjectFile;
|
||||
const projectFilename: string;
|
||||
debug: Boolean;
|
||||
Callback: TUtilExecuteCallbackEvent
|
||||
): Boolean;
|
||||
var
|
||||
cmdline: TArray<string>;
|
||||
begin
|
||||
cmdline := [
|
||||
'build',
|
||||
'--log-format', 'tsv',
|
||||
'--log-level', 'info',
|
||||
'--for-publishing',
|
||||
'--publish-only',
|
||||
projectFilename
|
||||
];
|
||||
|
||||
if Assigned(FGlobalProject) and (FGlobalProject.Options.CompilerWarningsAsErrors) then
|
||||
cmdline := cmdline + ['--compiler-warnings-as-errors']
|
||||
else
|
||||
cmdline := cmdline + ['--no-compiler-warnings-as-errors'];
|
||||
|
||||
if not FGlobalProject.Options.WarnDeprecatedCode then
|
||||
cmdline := cmdline + ['--no-warn-deprecated-code'];
|
||||
|
||||
if debug then
|
||||
cmdline := cmdline + ['--debug'];
|
||||
|
||||
Result := Run(cmdline, ExtractFileDir(projectFilename), projectFilename, Callback);
|
||||
end;
|
||||
|
||||
|
||||
function TKmcWrapper.Compile(
|
||||
ProjectFile: TProjectFile;
|
||||
const infile: string;
|
||||
|
|
|
|||
|
|
@ -170,6 +170,12 @@ const // ABGR - text on white bg, Colors similar to Light+ VSCode color theme
|
|||
INFO_FileNotBuiltSuccessfully = NAMESPACE_Infrastructure or $0007;
|
||||
INFO_ProjectBuiltSuccessfully = NAMESPACE_Infrastructure or $000B;
|
||||
INFO_ProjectNotBuiltSuccessfully = NAMESPACE_Infrastructure or $000C;
|
||||
INFO_ProjectCopiedSuccessfully = NAMESPACE_Infrastructure or $0024;
|
||||
INFO_ProjectNotCopiedSuccessfully = NAMESPACE_Infrastructure or $0025;
|
||||
INFO_ProjectGeneratedSuccessfully = NAMESPACE_Infrastructure or $0027;
|
||||
INFO_ProjectNotGeneratedSuccessfully = NAMESPACE_Infrastructure or $0028;
|
||||
INFO_ProjectValidatedSuccessfully = NAMESPACE_Infrastructure or $002A;
|
||||
INFO_ProjectNotValidatedSuccessfully = NAMESPACE_Infrastructure or $002B;
|
||||
|
||||
Segment_Filename = 0;
|
||||
Segment_Filename_Separator = 1;
|
||||
|
|
@ -254,12 +260,18 @@ begin
|
|||
FColor := clBlack;
|
||||
FTextColor := Color_Text;
|
||||
|
||||
// Override formatting for 4 known messages
|
||||
// Override formatting for known messages -- keep in sync with messageSpecialColor() in NodeCompilerCallbacks.ts
|
||||
if (MsgCode = INFO_FileBuiltSuccessfully) or
|
||||
(MsgCode = INFO_ProjectBuiltSuccessfully) then
|
||||
(MsgCode = INFO_ProjectBuiltSuccessfully) or
|
||||
(MsgCode = INFO_ProjectCopiedSuccessfully) or
|
||||
(MsgCode = INFO_ProjectGeneratedSuccessfully) or
|
||||
(MsgCode = INFO_ProjectValidatedSuccessfully) then
|
||||
state := plsSuccess
|
||||
else if (MsgCode = INFO_FileNotBuiltSuccessfully) or
|
||||
(MsgCode = INFO_ProjectNotBuiltSuccessfully) then
|
||||
(MsgCode = INFO_ProjectNotBuiltSuccessfully) or
|
||||
(MsgCode = INFO_ProjectNotCopiedSuccessfully) or
|
||||
(MsgCode = INFO_ProjectNotGeneratedSuccessfully) or
|
||||
(MsgCode = INFO_ProjectNotValidatedSuccessfully) then
|
||||
state := plsFailure;
|
||||
|
||||
case state of
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ begin
|
|||
w := TKmcWrapper.Create;
|
||||
try
|
||||
Result := w.Compile(Self, FileName, TargetFilename, False);
|
||||
if not OwnerProject.Options.SkipMetadataFiles then
|
||||
Result := Result and w.CompileForPublishing(Self, OwnerProject.FileName, False);
|
||||
// TODO(lowpri): FDebug flag
|
||||
finally
|
||||
w.Free;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue