Merge branch 'master' into feat/windows/13566/check-metered-connection

This commit is contained in:
rc-swag 2026-06-16 15:58:17 +10:00 committed by GitHub
commit cc8b4d2426
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 154 additions and 18 deletions

View file

@ -119,7 +119,7 @@ jobs:
- name: Install meson
shell: bash
run: |
pip3 install --user meson==1.10.1
pip3 install --user meson==1.11.0
- name: Run test
shell: bash

View file

@ -1,5 +1,11 @@
# Keyman Version History
## 19.0.246 alpha 2026-06-15
* feat(developer): run project validation from IDE (#16076)
* fix(core): use meson subsystem and needs_exe_wrapper for cross builds on Windows (#16088)
* docs(windows): add context documentation for additional Advanced options (#16089)
## 19.0.245 alpha 2026-06-10
* chore(web): hide KMX keyboard test page (#16083)

View file

@ -1 +1 @@
19.0.246
19.0.247

View file

@ -1,5 +1,6 @@
[host_machine]
system = 'windows'
subsystem = 'windows'
cpu_family = 'aarch64'
cpu = 'arm64'
endian = 'little'

View file

@ -1,5 +1,6 @@
[host_machine]
system = 'windows'
subsystem = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'
@ -11,3 +12,6 @@ ar = 'lib'
ld = 'link'
strip = 'llvm-strip'
pkgconfig = 'pkg-config'
[properties]
needs_exe_wrapper = false

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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?

View file

@ -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;
}

View file

@ -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.`
)});
}

View file

@ -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;

View file

@ -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,
}
}

View file

@ -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 Assigned(FGlobalProject) and (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;

View file

@ -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

View file

@ -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;

View file

@ -180,6 +180,28 @@ To open the Options tab of Keyman Configuration:
log on for short and isolated tests as specifically requested by
Keyman Technical Support.
- Proxy Settings
In some environments, you may need to configure proxy settings in
order to download keyboards or updates for Keyman. Check with your
network administrator. More information is available in the
[Proxy Configuration](../../advanced/proxy_config) documentation.
- Keyman System Settings
The Keyman System Settings dialog provides access to various low-level
Keyman configuration settings. Generally, these settings do not need
to be changed, unless you instructed to do so by Keyman support staff.
- Base Keyboard
The Base Keyboard dialog allows you to select the physical Latin-script
layout of your keyboard, which will allow certain Keyman keyboards to remap
themselves dynamically to match your key caps.
[Base Keyboard dialog](../../context/base-keyboard)
## Related Topics
- [Keyman Configuration](../config/)