diff --git a/.github/workflows/auto-merge-keyman-server-pr.yml b/.github/workflows/auto-merge-keyman-server-pr.yml index 6b93e716fa..39cafc3f91 100644 --- a/.github/workflows/auto-merge-keyman-server-pr.yml +++ b/.github/workflows/auto-merge-keyman-server-pr.yml @@ -17,12 +17,15 @@ jobs: if: ${{ github.repository == 'keymanapp/keyman' && github.actor == 'keyman-server' && startsWith(github.event.pull_request.title, 'auto:') }} steps: - name: auto approve PR from keyman-server - uses: hmarr/auto-approve-action@v3 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - - name: auto merge PR from keyman-server - uses: "pascalgn/automerge-action@7854d3bd607dccdaf0b2c134b699a812c8960213" + shell: bash env: - GITHUB_TOKEN: "${{ secrets.AUTOINC_GITHUB_TOKEN }}" - MERGE_LABELS: "" - MERGE_FORKS: false + GH_TOKEN: "${{ github.token }}" + run: | + gh pr review -R keymanapp/keyman --approve ${{ github.event.pull_request.number }} + - name: mark PR for auto-merge from keyman-server + shell: bash + if: contains(github.event.pull_request.labels.*.name, 'automerge') + env: + GH_TOKEN: "${{ secrets.AUTOINC_GITHUB_TOKEN }}" + run: | + gh pr merge -R keymanapp/keyman --merge --auto ${{ github.event.pull_request.number }} diff --git a/.github/workflows/deb-packaging.yml b/.github/workflows/deb-packaging.yml index aaeb1c6306..a40fcb7259 100644 --- a/.github/workflows/deb-packaging.yml +++ b/.github/workflows/deb-packaging.yml @@ -86,7 +86,14 @@ jobs: shell: bash run: | THIS_SCRIPT="$GITHUB_WORKSPACE/.github/workflows/deb-packaging.yml" - . "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" + if [[ -f "${THIS_SCRIPT%/*}/../../resources/build/builder-basic.inc.sh" ]]; then + . "${THIS_SCRIPT%/*}/../../resources/build/builder-basic.inc.sh" + else + # TODO: remove following block post v19 release (file was renamed in v19 alpha) + if [[ -f "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" ]]; then + . "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" + fi + fi echo "KEYMAN_VERSION=${KEYMAN_VERSION:-${VERSION}}" >> $GITHUB_OUTPUT - name: Set prerelease tag as output parameter @@ -127,7 +134,7 @@ jobs: strategy: fail-fast: true matrix: - dist: [jammy, noble, oracular] + dist: [jammy, noble, plucky] steps: - name: Checkout @@ -153,7 +160,7 @@ jobs: strategy: fail-fast: true matrix: - dist: [plucky] + dist: [questing] steps: - name: Checkout diff --git a/.github/workflows/pr-build-status.yml b/.github/workflows/pr-build-status.yml new file mode 100644 index 0000000000..236970f141 --- /dev/null +++ b/.github/workflows/pr-build-status.yml @@ -0,0 +1,208 @@ +# GENERATED FILE - DO NOT EDIT! +# +# Keyman is copyright (C) SIL Global. MIT License. +# +# Do not modify the script in .github/workflows/pr-build-status.yml directly; +# instead work on the sources in resources/build/pr-build-status and use the +# build.sh script to rebuild the .github/workflows/pr-build-status.yml file from +# them. +# +# build.sh will append the relevant portions of +# resources/build/pr-build-status/pr-build-status.mjs to the content in +# resources/build/pr-build-status/pr-build-status.prefix.yml to form the .github +# workflow file. +# +name: Keyman Build Summary +on: + status: + push: + branches-ignore: + - master + - beta + - stable-* + workflow_dispatch: + inputs: + commit: + description: 'Commit sha' + required: true + type: string +jobs: + run_pr_build_status: + name: Summarize build status checks + runs-on: ubuntu-latest + steps: + - name: Check PR build status + id: run_pr_build_status_script + uses: actions/github-script@v7 + with: + script: | + // This code is copied out of resources/build/pr-build-status/pr-build-status.mjs + // where it is tested. It is copied inline here in order to avoid requiring the + // repository to be checked out, which dramatically reduces the run time of the + // check. + // + // Note: we don't currently look at check runs, only statuses + // + // Verify the following statuses: + // 'user_testing' + // 'API Verification' (github-actions[bot]) + // + // At least 1 of the following statuses must be found: + // 'Test*' (keyman-server), e.g. 'Test Build (Keyman)' + // 'Ubuntu Packaging' (github-actions[bot]) + // + // Ignore the following statuses: + // check/web/file-size + // + + function reduceStatuses(statuses) { + const filtered_statuses = statuses.reduce((o, status) => { + if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) { + if(!o[status.context]) o[status.context] = {type: 'build', state: status.state}; + } else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') { + if(!o[status.context]) o[status.context] = {type: 'user-test', state: status.state};; + } else if(status.context == 'API Verification') { + if(!o[status.context]) o[status.context] = {type: 'check', state: status.state}; + } else if(status.context == 'Ubuntu Packaging') { + if(!o[status.context]) o[status.context] = {type: 'build', state: status.state}; + } else if(status.context == 'check/web/file-size') { + // Ignore check/web/file-size -- we won't block automerge for this at this point + } else { + // We fail with an 'unknown status' response if we get a new status check + // so we can be sure we are not skipping known status checks + o[status.context] = {type: 'unknown', state: status.state}; + } + return o; + + }, {}); + return filtered_statuses; + } + + // + // Given the collection of status checks we care about, return + // an aggregate status -- error, failed, pending, or success, + // and a summary description + // + function calculateFinalStatus(filtered_statuses) { + const counts = {}; + let hasBuilds = false; + for(const context of Object.keys(filtered_statuses)) { + const { state, type } = filtered_statuses[context]; + if(type == 'unknown') { + // We special-case for unknown status checks, and never permit them + return [ + 'error', `An unknown context ${context} was found, cannot calculate build status.` + ]; + } + if(type == 'build') { + hasBuilds = true; + } + counts[state] = counts[state] ? counts[state] + 1 : 1; + } + + // If we do not have any statuses yet, we wait + if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) { + return ['pending', 'Builds have not yet been triggered ⌛']; + } + + const state = + counts.error ? 'error' : + counts.failed ? 'failed' : + counts.pending ? 'pending' : + 'success'; + + let description = ''; + function appendDescription(count, state) { + if(!count) return; + if(description != '') description += '; '; + description += `${count} check${count == 1 ? '' : 's'} ${state}`; + } + appendDescription(counts.error, 'in an error state ❌'); + appendDescription(counts.failed, 'failed ❌'); + appendDescription(counts.pending, 'pending ⌛'); + appendDescription(counts.success, 'completed successfully ✅'); + + return [ state, description ]; + } + + async function getCommitStatuses(github, owner, repo, sha) { + const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', { + owner, + repo, + sha, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }); + return statuses; + } + + async function getCommitCheckRuns(github, owner, repo, sha) { + const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', { + owner, + repo, + sha, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }); + return statuses; + } + + function calculateCheckResult(statuses) { + if(!Array.isArray(statuses)) { + return ['error', 'Failed to retrieve status checks from GitHub ❌']; + } + + const filtered_statuses = reduceStatuses(statuses); + + const result = calculateFinalStatus(filtered_statuses); + return result; + } + + async function test(github, owner, repo, sha) { + // Get statuses from sha + const statuses = await getCommitStatuses(github, owner, repo, sha); + return calculateCheckResult(statuses); + } + + async function createCheck(github, owner, repo, sha) { + const check = await github.rest.checks.create({ + owner, + repo, + head_sha: sha, + name: 'Build Outcome', + status: 'in_progress', + }); + return check.data.id; + } + + async function updateCheck(github, owner, repo, checkRunId, status, description) { + const checkStatus = status == 'pending' ? 'in_progress' : 'completed'; + const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure'); + + await github.rest.checks.update({ + owner, + repo, + check_run_id: checkRunId, + status: checkStatus, + conclusion, + output: { + title: description, + summary: '' + } + }); + } + + const { owner, repo } = context.repo; + const sha = + context.payload?.check_suite?.sha || /* check run completed */ + context.payload?.inputs?.commit || /* manual run */ + context.payload?.after || /* push */ + context.payload?.commit?.sha || /* status */ + context.sha; /* probably 'master'! */ + + const checkRunId = await createCheck(github, owner, repo, sha); + const res = await test(github, owner, repo, sha); + await updateCheck(github, owner, repo, checkRunId, res[0], res[1]); + diff --git a/.gitignore b/.gitignore index 6c8fbfbec8..6316adb5d2 100644 --- a/.gitignore +++ b/.gitignore @@ -164,7 +164,7 @@ tsconfig.tsbuildinfo # Windows system files Thumbs.db -# Temporary file for logging scripts in xcode runs, see build-utils.sh for +# Temporary file for logging scripts in xcode runs, see builder-basic.inc.sh for # details /xcodebuild-scripts.log diff --git a/HISTORY.md b/HISTORY.md index daf9a20257..6d8aecb9ed 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,229 @@ # Keyman Version History +## 19.0.99 alpha 2025-08-14 + +* maint(web): skip unnecessary steps for `build` buildLevel (#14516) + +## 19.0.98 alpha 2025-08-14 + +* maint(resources): specify `keymanapp/keyman` for gh in automerge (#14524) + +## 19.0.97 alpha 2025-08-14 + +* maint(resources): use GH_TOKEN variable for gh calls (#14522) + +## 19.0.96 alpha 2025-08-14 + +* maint(resources): add build-bot commands to increment-version auto PRs (#14504) +* maint(resources): use GH builtin automerge for auto PRs (#14519) + +## 19.0.95 alpha 2025-08-13 + +* fix(web): use actual legacy model for legacy-model support unit tests (#14460) +* chore(common): Update Crowdin strings for Spanish (#14464) +* fix(android): Get default sizes before initializing keyboard (#14472) +* docs(resources): clarify Build-bot usage (#14282) +* maint(resources): build.sh-style commands for build-bot (#14461) +* chore(android): Update keycode test app dependencies (#14474) +* maint(common): update npm modules brace-expansion to 1.1.12, form-data to 4.0.4 (#14479) +* chore(android): Specify Java language version 21 (#14491) +* maint(ios): skip unnecessary network steps when buildLevel is build (#14466) +* maint(common): rename shellHelperFunctions.sh to utils.inc.sh (#14446) +* maint(common): consolidate builder scripts (#14449) +* maint(common): rename and move node-related script functions into node.inc.sh (#14451) +* maint(common): move typescript script functions into typescript.inc.sh (#14452) +* maint(common): reorganize mac/ios build script includes (#14458) +* docs(resources): add note on builder-basic vs builder-full (#14507) + +## 19.0.94 alpha 2025-08-01 + +* maint(common): use `cpp_std=c++17` in meson project default_options (#14433) + +## 19.0.93 alpha 2025-07-29 + +* test(developer): add unit tests for `isValidUtf8` (#14381) +* maint(linux): fix stopping of test server (#14403) + +## 19.0.92 alpha 2025-07-28 + +* build(deps): bump multer from 2.0.1 to 2.0.2 (#14373) +* refactor(linux): remove shellcheck warnings (#14409) +* refactor(linux): move dbus defines to common include (#14407) +* docs(linux): update readme for test service (#14408) +* fix(windows): add check a fix for registry datatypes (#14358) + +## 19.0.91 alpha 2025-07-26 + +* maint(windows): fix calls of renamed functions (#14413) + +## 19.0.90 alpha 2025-07-25 + +* maint(common): fix standalone use of `resources/builder.inc.sh` (#14399) +* maint(resources): use `tc_` prefix for all `tc-*.inc.sh` functions (#14267) + +## 19.0.89 alpha 2025-07-24 + +* docs(common): Document minimum version of Chrome 95.0 for Android (#14385) +* refactor(android): Update minimum target Chrome version to 95.0 and refactor to use constants (#14391) + +## 19.0.88 alpha 2025-07-23 + +* maint(windows): consider `GO_FAST` env variable in `add_zip_files` (#14338) +* maint(common): add unit tests for `locate_emscripten.inc.sh` (#14346) +* refactor(android): create functions for actions (#14351) +* maint(linux): fix `locate_emscripten` if `$EMSCRIPTEN_BASE` dir missing (#14347) +* maint(android): consolidate creating zip file (#14353) + +## 19.0.87 alpha 2025-07-22 + +* refactor(common): use test framework for zip tests (#14380) +* maint(common): fix use of `jq.inc.sh` in git hooks (#14382) +* test(common): improve zip tests (#14386) +* maint(common): fix `add_zip_files` if flags come before zip filename (#14387) + +## 19.0.86 alpha 2025-07-21 + +* maint(common): fail build if adding relative file to zip (#14352) +* maint(common): set C++17 in standard.meson.build (#14355) +* fix(linux): add `python3-pkg-resources` as explicit dependency (#14356) +* docs(developer): add ansi and iso context and image, change img tags, and polish for readability (#14243) +* maint(mac): consolidate `write-download_info` (#14325) +* maint(common): add simple unit testing framework for bash (#14345) +* maint(common): consolidate functions to determine OS (#14324) +* fix(android): use correct path to zip.inc.sh for CI (#14377) +* test(common): add unit test for reporting test failures (#14379) + +## 19.0.85 alpha 2025-07-18 + +* refactor(common): remove warnings (#14368) +* refactor(developer): replace deprecated std::codecvt_utf8_utf16 (#14369) +* refactor(linux): move defines to common header files (#14372) + +## 19.0.84 alpha 2025-07-15 + +* maint(common): add support for inline file exclusions to `add_zip_files` and unit tests (#14333) +* maint(developer): use `add_zip_files` function (#14334) + +## 19.0.83 alpha 2025-07-14 + +* maint(resources): add `build.sh` to run all `resource/build/test` tests (#14335) +* maint(developer): consolidate creating KMC_ZIP file (#14309) + +## 19.0.82 alpha 2025-07-10 + +* maint(android): fix Android release build (#14329) +* maint(mac): fix end of blocks (#14330) +* maint(mac): don't run `build.sh publish` twice (#14331) + +## 19.0.81 alpha 2025-07-09 + +* docs(developer): explanation of the Description field added in Details section. (#14304) +* fix(ios): restore keyboard scale map for iPad Air 2 (#14305) +* maint(linux): remove Oracular, add Questing (#14319) +* maint(android): remove `--ci` parameter from Android release builds (#14320) +* maint(android): use absolute paths (#14308) +* maint(windows): fix platform name for Windows (#14321) +* maint(developer): include missing shellHelperFunctions.sh file (#14322) +* maint(windows): replace hardcoded `symbols` paths with parameters (#14323) + +## 19.0.80 alpha 2025-07-08 + +* maint(common): remove include of deleted `tc-download-info.inc.sh` (#14311) +* maint(resources): fix triggering of builds (#14314) + +## 19.0.79 alpha 2025-07-07 + +* maint(android): use `--release` for release builds (#14307) +* refactor(common): some reformattings in `shellHelperFunctions.sh` (#14269) +* maint(common): consolidate `write_download_info` functions (#14270) + +## 19.0.78 alpha 2025-07-04 + +* docs(developer): Fix links in gestures guide (#14297) + +## 19.0.77 alpha 2025-07-04 + +* maint(mac): rename `_do_publish` function to `do_publish` to fix release build (#14273) +* maint(ios): remove extra `cd` command from ios release builds (#14272) +* maint(android): remove `--ci` parameter from android builds (#14264) +* refactor(resources): replace shebang for TC include files (#14265) +* fix(core): fix failing `common-test-core.sh` script (#14271) +* maint(common): match OSTYPE correctly on macOS (#14277) +* fix(common): fix triggering builds if no builds to start (#14274) +* maint(common): trigger common builds for resources/teamcity/common changes (#14279) + +## 19.0.76 alpha 2025-07-03 + +* maint(common): move TC configuration for common tests (#14205) +* refactor(common): improve downloads include script (#14253) +* maint(web): move rsync code to shared functions (#14254) +* maint(developer): replace Powershell scripts (#14255) +* maint(windows): move TC configuration for Keyman Windows to scripts (#14186) +* feat(windows): human text message for 413 error (#14055) +* refactor(web): move helper functions to beginning of function (#14252) +* maint(android): add TC build scripts for Keyman for Android (#14222) +* maint(linux): use `tc_rsync_upload` and `write_download_info` functions (#14258) +* maint(ios): add TC build scripts for iOS (#14229) +* maint(mac): add TC build scripts for macOS (#14239) + +## 19.0.75 alpha 2025-07-02 + +* feat(resources): support ignoring unknown options in builder (#14216) +* maint(common): consolidate 7Z_HOME, SEVENZ_HOME, SEVEN_Z_HOME env vars (#14162) + +## 19.0.74 alpha 2025-07-01 + +* maint(web): adjust rsync path (#14249) + +## 19.0.73 alpha 2025-07-01 + +* fix(web): deploy `web/build/publish` folder (#14237) +* fix(web): change env variable name (#14244) + +## 19.0.72 alpha 2025-07-01 + +* maint(web): cleanup rsync call from bash for Windows agents and refactor (#14236) + +## 19.0.71 alpha 2025-06-30 + +* refactor(linux): rename linux agent specific functions (#14231) + +## 19.0.70 alpha 2025-06-27 + +* docs(developer): Add details on Template choices (#14228) +* chore(common,developer): expand 'x' to 'compileContext' (#14218) + +## 19.0.69 alpha 2025-06-25 + +* maint(resources): support additional workflow events for pr-build-status (#14219) +* chore: add tips and caution of gestures for Keyman Developer + update license + removal of span tag (#14204) + +## 19.0.68 alpha 2025-06-24 + +* maint(resources): Build-bot now available to control test builds (#14125) +* maint(developer): support buildLevel (#14196) +* fix(resources): allow branch names in run-required-test-builds shortcut call (#14217) +* maint(resources): add pr-build-status GitHub Action to summarize build status (#14199) + +## 19.0.67 alpha 2025-06-22 + +* chore(developer): ldml: additional line numbers (#14104) + +## 19.0.66 alpha 2025-06-16 + +* fix(windows): add values wucUpdateAvailable and wucNotChecked to TRemoteUpdateCheckResult enum (#14123) + +## 19.0.65 alpha 2025-06-13 + +* maint(developer): fix developer release build (#14185) +* maint(developer,web): use double quotes inside `--rsync-path` again (#14194) + +## 19.0.64 alpha 2025-06-13 + +* fix(windows): fix download failing when cache.json missing (#14095) +* maint(resources): only automatically merge keyman-server PRs with automerge label (#14171) +* maint(developer,web): use braces for variables in powershell scripts (#14184) + ## 19.0.63 alpha 2025-06-12 * maint(web): add some more arguments for web release build script (#14167) diff --git a/VERSION.md b/VERSION.md index efcfb4b252..a053bc0ccb 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -19.0.64 \ No newline at end of file +19.0.100 \ No newline at end of file diff --git a/android/KMAPro/build.sh b/android/KMAPro/build.sh index fd54215d1c..525b951138 100755 --- a/android/KMAPro/build.sh +++ b/android/KMAPro/build.sh @@ -3,10 +3,10 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" . "$KEYMAN_ROOT/resources/build/build-help.inc.sh" . "$KEYMAN_ROOT/resources/build/build-download-resources.sh" diff --git a/android/KMAPro/kMAPro/build.gradle b/android/KMAPro/kMAPro/build.gradle index 864fb36ee9..705fb17a1c 100644 --- a/android/KMAPro/kMAPro/build.gradle +++ b/android/KMAPro/kMAPro/build.gradle @@ -11,6 +11,12 @@ apply from: "$rootPath/version.gradle" String tier = new File('../../TIER.md').getText('UTF-8').trim(); +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + android { compileSdk 34 namespace="com.tavultesoft.kmapro" @@ -24,11 +30,6 @@ android { buildConfig = true } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 - } - defaultConfig { applicationId "com.tavultesoft.kmapro" minSdkVersion 21 diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 3eec444fbd..40b41b6176 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -864,6 +864,9 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene private void displayUpdateChrome() { // TextView's default string is to update Chrome + TextView textView = (TextView)findViewById(R.id.kmWebViewChromeTextView); + textView.setText(String.format(getString(R.string.text_require_chrome_version), + WebViewUtils.KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME)); Button button = (Button)findViewById(R.id.webViewChromeButton); button.setText(getString(R.string.button_update_chrome)); diff --git a/android/KMAPro/kMAPro/src/main/res/layout/check_chrome_webview_layout.xml b/android/KMAPro/kMAPro/src/main/res/layout/check_chrome_webview_layout.xml index 09716bdd8a..ea5a9220d5 100644 --- a/android/KMAPro/kMAPro/src/main/res/layout/check_chrome_webview_layout.xml +++ b/android/KMAPro/kMAPro/src/main/res/layout/check_chrome_webview_layout.xml @@ -15,7 +15,7 @@ android:background="@android:color/white" android:gravity="start" android:layout_gravity="center_vertical" - android:text="@string/text_require_chrome_57" + android:text="@string/text_require_chrome_version" android:layout_marginStart="@dimen/button_playstore_margin" android:textSize="@dimen/update_chrome_label_textsize" /> diff --git a/android/KMAPro/kMAPro/src/main/res/values/strings.xml b/android/KMAPro/kMAPro/src/main/res/values/strings.xml index a0447d0f89..32cdf07f1a 100644 --- a/android/KMAPro/kMAPro/src/main/res/values/strings.xml +++ b/android/KMAPro/kMAPro/src/main/res/values/strings.xml @@ -30,10 +30,13 @@ Version: %1$s - + Keyman requires Chrome version 57 or newer. + + Keyman requires Chrome version %1$s or newer. + Update Chrome diff --git a/android/KMEA/app/build.gradle b/android/KMEA/app/build.gradle index dd4251025b..da7191ad5c 100644 --- a/android/KMEA/app/build.gradle +++ b/android/KMEA/app/build.gradle @@ -12,6 +12,12 @@ base { archivesName = "keyman-engine" } +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + android { compileSdk 34 namespace "com.keyman.engine" @@ -21,11 +27,6 @@ android { buildConfig = true } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 - } - defaultConfig { minSdkVersion 21 targetSdkVersion 34 diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java index 75b0443537..5553d256b0 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java @@ -495,6 +495,11 @@ public final class KMManager { didCopyAssets = true; } + calculateDefaultKeyboardHeights(context); + SharedPreferences prefs = context.getSharedPreferences(KMManager.KMEngine_PrefsKey, Context.MODE_PRIVATE); + KeyboardHeight_Context_Portrait_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightPortrait, KMManager.KeyboardHeight_Context_Portrait_Default); + KeyboardHeight_Context_Landscape_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightLandscape, KMManager.KeyboardHeight_Context_Landscape_Default); + if (keyboardType == KeyboardType.KEYBOARD_TYPE_UNDEFINED) { String msg = "Cannot initialize: Invalid keyboard type"; KMLog.LogError(TAG, msg); @@ -508,12 +513,6 @@ public final class KMManager { migrateCloudKeyboards(appContext); CloudDownloadMgr.getInstance().initialize(appContext); - - calculateDefaultKeyboardHeights(context); - SharedPreferences prefs = context.getSharedPreferences(KMManager.KMEngine_PrefsKey, Context.MODE_PRIVATE); - KeyboardHeight_Context_Portrait_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightPortrait, KMManager.KeyboardHeight_Context_Portrait_Default); - KeyboardHeight_Context_Landscape_Current = prefs.getInt(KMManager.KMKey_KeyboardHeightLandscape, KMManager.KeyboardHeight_Context_Landscape_Default); - } public static void executeResourceUpdate(Context aContext) diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/util/WebViewUtils.java b/android/KMEA/app/src/main/java/com/keyman/engine/util/WebViewUtils.java index de8b37f73e..0586ecbcc8 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/util/WebViewUtils.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/util/WebViewUtils.java @@ -32,6 +32,13 @@ public final class WebViewUtils { FULL; // WebView installed and enabled } + // Min version of Chrome for Keyman for Android for EngineWebViewVersionStatus.DEGRADED + public static final String KEYMAN_MIN_TARGET_VERSION_DEGRADED_ANDROID_CHROME = "37.0"; + + // Min version of Chrome for Keyman for Android (EngineWebViewVersionStatus.FULL) + // TODO: Keep this version in sync with resources/build/minimum-versions.inc.sh + public static final String KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME = "95.0"; + private static final String CHROME_INSTALL_PATTERN_FORMATSTR = "^.*Chrome/([\\d.]+).*$"; private static final Pattern installPattern = Pattern.compile(CHROME_INSTALL_PATTERN_FORMATSTR); @@ -52,9 +59,11 @@ public final class WebViewUtils { chromeVersion = getChromeVersion(context, webView); } - if (FileUtils.compareVersions("37.0", chromeVersion) == FileUtils.VERSION_GREATER) { + if (FileUtils.compareVersions(KEYMAN_MIN_TARGET_VERSION_DEGRADED_ANDROID_CHROME, chromeVersion) + == FileUtils.VERSION_GREATER) { return EngineWebViewVersionStatus.DISABLED; - } else if (FileUtils.compareVersions("57.0", chromeVersion) == FileUtils.VERSION_GREATER) { + } else if (FileUtils.compareVersions(KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME, chromeVersion) + == FileUtils.VERSION_GREATER) { return EngineWebViewVersionStatus.DEGRADED; } diff --git a/android/KMEA/app/src/main/res/values-es-rES/strings.xml b/android/KMEA/app/src/main/res/values-es-rES/strings.xml index 0ab0ea317b..4846da0dac 100644 --- a/android/KMEA/app/src/main/res/values-es-rES/strings.xml +++ b/android/KMEA/app/src/main/res/values-es-rES/strings.xml @@ -83,13 +83,13 @@ Error en el teclado %1$s:%2$s para %3$s idioma. - Keyman requires WebView to be installed.\" + Keyman requiere que WebView esté instalado.\" - Keyman requires WebView to be enabled.\" + Keyman requiere que WebView esté habilitado.\" - Install WebView + Instalar WebView - Enable WebView + Habilitar WebView Comprobando si hay un diccionario asociado para descargar No se puede conectar al servidor Keyman para comprobar si hay un diccionario asociado para descargar @@ -124,7 +124,7 @@ ¡Falló al acceder al servidor! - DownloadManager disabled - cannot check for updates + Descargador deshabilitado: no se pueden comprobar actualizaciones "¡Todos los recursos están actualizados!" diff --git a/android/KMEA/app/src/test/java/com/keyman/engine/util/WebViewUtilsTest.java b/android/KMEA/app/src/test/java/com/keyman/engine/util/WebViewUtilsTest.java index 344b01879a..342934200a 100644 --- a/android/KMEA/app/src/test/java/com/keyman/engine/util/WebViewUtilsTest.java +++ b/android/KMEA/app/src/test/java/com/keyman/engine/util/WebViewUtilsTest.java @@ -32,28 +32,30 @@ public class WebViewUtilsTest { @Test public void test_Chrome36_EngineWebViewVersionStatusDisabled() { - String chromeVersion = "36.0"; + double chromeVersionFloat = Float.parseFloat(WebViewUtils.KEYMAN_MIN_TARGET_VERSION_DEGRADED_ANDROID_CHROME) - 1.0; + String chromeVersion = String.valueOf(chromeVersionFloat); Assert.assertEquals(WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion), WebViewUtils.EngineWebViewVersionStatus.DISABLED); } @Test public void test_Chrome37_EngineWebViewVersionStatusDegraded() { - String chromeVersion = "37.0"; + String chromeVersion = WebViewUtils.KEYMAN_MIN_TARGET_VERSION_DEGRADED_ANDROID_CHROME; Assert.assertEquals(WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion), WebViewUtils.EngineWebViewVersionStatus.DEGRADED); } @Test - public void test_Chrome56_EngineWebViewVersionStatusDegraded() { - String chromeVersion = "56.0"; + public void test_Chrome94_EngineWebViewVersionStatusDegraded() { + double chromeVersionFloat = Float.parseFloat(WebViewUtils.KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME) - 1.0; + String chromeVersion = String.valueOf(chromeVersionFloat); Assert.assertEquals(WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion), WebViewUtils.EngineWebViewVersionStatus.DEGRADED); } @Test - public void test_Chrome57_EngineWebViewVersionStatusFull() { - String chromeVersion = "57.0"; + public void test_Chrome95_EngineWebViewVersionStatusFull() { + String chromeVersion = WebViewUtils.KEYMAN_MIN_TARGET_VERSION_ANDROID_CHROME; Assert.assertEquals(WebViewUtils.getEngineWebViewVersionStatus(context, null, chromeVersion), WebViewUtils.EngineWebViewVersionStatus.FULL); } diff --git a/android/KMEA/build.sh b/android/KMEA/build.sh index 4d4ea7f8e3..9b6d0d4892 100755 --- a/android/KMEA/build.sh +++ b/android/KMEA/build.sh @@ -4,10 +4,10 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" # ################################ Main script ################################ diff --git a/android/Samples/KMSample1/app/build.gradle b/android/Samples/KMSample1/app/build.gradle index cc92dee298..025dd366f0 100644 --- a/android/Samples/KMSample1/app/build.gradle +++ b/android/Samples/KMSample1/app/build.gradle @@ -2,6 +2,12 @@ plugins { id 'com.android.application' } +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + android { compileSdk 34 namespace="com.keyman.kmsample1" diff --git a/android/Samples/KMSample1/build.sh b/android/Samples/KMSample1/build.sh index 172ef6c5e7..65e5619815 100755 --- a/android/Samples/KMSample1/build.sh +++ b/android/Samples/KMSample1/build.sh @@ -4,10 +4,10 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" ################################ Main script ################################ @@ -23,7 +23,7 @@ builder_describe "Build KMSample1 app for Android." \ "configure" \ "build" \ "test" \ - ":app KMSample1" + ":app KMSample1" # parse before describe_outputs to check debug flags builder_parse "$@" diff --git a/android/Samples/KMSample2/app/build.gradle b/android/Samples/KMSample2/app/build.gradle index cd2f4d6225..472a70a1b4 100644 --- a/android/Samples/KMSample2/app/build.gradle +++ b/android/Samples/KMSample2/app/build.gradle @@ -2,6 +2,12 @@ plugins { id 'com.android.application' } +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + android { compileSdk 34 namespace="com.keyman.kmsample2" diff --git a/android/Samples/KMSample2/build.sh b/android/Samples/KMSample2/build.sh index 2f7a99c764..08c5261778 100755 --- a/android/Samples/KMSample2/build.sh +++ b/android/Samples/KMSample2/build.sh @@ -4,10 +4,10 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" ################################ Main script ################################ @@ -23,7 +23,7 @@ builder_describe "Build KMSample2 app for Android." \ "configure" \ "build" \ "test" \ - ":app KMSample2" + ":app KMSample2" # parse before describe_outputs to check debug flags builder_parse "$@" diff --git a/android/Tests/KeyboardHarness/app/build.gradle b/android/Tests/KeyboardHarness/app/build.gradle index ab8066393b..6b28b62d9c 100644 --- a/android/Tests/KeyboardHarness/app/build.gradle +++ b/android/Tests/KeyboardHarness/app/build.gradle @@ -5,8 +5,14 @@ plugins { ext.rootPath = '../../../' apply from: "$rootPath/version.gradle" +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + android { - compileSdk 34 + compileSdk 35 namespace="com.keyman.android.tests.keyboardHarness" // Don't compress kmp files so they can be copied via AssetManager @@ -21,7 +27,7 @@ android { defaultConfig { applicationId "com.keyman.android.tests.keyboardHarness" minSdkVersion 21 - targetSdkVersion 34 + targetSdkVersion 35 // KEYMAN_VERSION_CODE and KEYMAN_VERSION_NAME from version.gradle versionCode KEYMAN_VERSION_CODE as Integer diff --git a/android/Tests/KeyboardHarness/build.gradle b/android/Tests/KeyboardHarness/build.gradle index 8fdf2aa422..966127346a 100644 --- a/android/Tests/KeyboardHarness/build.gradle +++ b/android/Tests/KeyboardHarness/build.gradle @@ -15,8 +15,8 @@ buildscript { allprojects { repositories { + maven { url uri("${projectDir}/libs") } google() - jcenter() mavenCentral() } } diff --git a/android/Tests/KeyboardHarness/build.sh b/android/Tests/KeyboardHarness/build.sh index c23f8ca0f5..66f2b0496e 100755 --- a/android/Tests/KeyboardHarness/build.sh +++ b/android/Tests/KeyboardHarness/build.sh @@ -4,10 +4,10 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" ################################ Main script ################################ @@ -23,7 +23,7 @@ builder_describe "Build KeyboardHarness test app for Android." \ "configure" \ "build" \ "test" \ - ":app KeyboardHarness" + ":app KeyboardHarness" # parse before describe outputs to check debug flags builder_parse "$@" diff --git a/android/Tests/keycode/app/build.gradle b/android/Tests/keycode/app/build.gradle index a0b24abb44..2f828db571 100644 --- a/android/Tests/keycode/app/build.gradle +++ b/android/Tests/keycode/app/build.gradle @@ -1,11 +1,18 @@ -apply plugin: 'com.android.application' +plugins { + id 'com.android.application' +} ext.rootPath = '../../../' apply from: "$rootPath/version.gradle" android { compileSdk 34 - namespace="com.keyman.android.tests.keycode" + namespace "com.keyman.android.tests.keycode" + + buildFeatures { + // needed for custom BuildConfigField values in defaultConfig + buildConfig = true + } defaultConfig { applicationId "com.keyman.android.tests.keycode" @@ -27,11 +34,10 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.appcompat:appcompat:1.7.0' + implementation 'androidx.constraintlayout:constraintlayout:2.2.1' implementation 'com.google.android.material:material:1.12.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - - testImplementation 'androidx.test:core:1.5.0' - testImplementation 'androidx.test.ext:junit:1.1.5' - testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'androidx.test.ext:junit:1.2.1' + testImplementation 'org.robolectric:robolectric:4.14.1' + testImplementation 'androidx.test:core:1.6.1' } diff --git a/android/Tests/keycode/app/src/androidTest/java/com/keyman/android/tests/keycode/ExampleInstrumentedTest.java b/android/Tests/keycode/app/src/test/java/com/keyman/android/tests/keycode/ExampleInstrumentedTest.java similarity index 72% rename from android/Tests/keycode/app/src/androidTest/java/com/keyman/android/tests/keycode/ExampleInstrumentedTest.java rename to android/Tests/keycode/app/src/test/java/com/keyman/android/tests/keycode/ExampleInstrumentedTest.java index d4bbda1b05..cf2b42ce88 100644 --- a/android/Tests/keycode/app/src/androidTest/java/com/keyman/android/tests/keycode/ExampleInstrumentedTest.java +++ b/android/Tests/keycode/app/src/test/java/com/keyman/android/tests/keycode/ExampleInstrumentedTest.java @@ -1,25 +1,25 @@ package com.keyman.android.tests.keycode; -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.core.app.ApplicationProvider; -import org.junit.Test; -import org.junit.runner.RunWith; +import android.content.Context; import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ -@RunWith(AndroidJUnit4.class) +@RunWith(RobolectricTestRunner.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); + Context appContext = ApplicationProvider.getApplicationContext(); assertEquals("com.keyman.android.tests.keycode", appContext.getPackageName()); } diff --git a/android/Tests/keycode/app/src/test/java/com/keyman/android/tests/keycode/ExampleUnitTest.java b/android/Tests/keycode/app/src/test/java/com/keyman/android/tests/keycode/ExampleUnitTest.java index 7e0e3c7481..544b21224c 100644 --- a/android/Tests/keycode/app/src/test/java/com/keyman/android/tests/keycode/ExampleUnitTest.java +++ b/android/Tests/keycode/app/src/test/java/com/keyman/android/tests/keycode/ExampleUnitTest.java @@ -1,4 +1,4 @@ -package com.example.darcy.keycode; +package com.keyman.android.tests.keycode; import org.junit.Test; diff --git a/android/Tests/keycode/build.gradle b/android/Tests/keycode/build.gradle index 47a85343bf..3a7f319289 100644 --- a/android/Tests/keycode/build.gradle +++ b/android/Tests/keycode/build.gradle @@ -4,10 +4,10 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath 'com.android.tools.build:gradle:8.9.2' // NOTE: Do not place your application dependencies here; they belong @@ -18,10 +18,6 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() } } - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/android/Tests/keycode/gradle.properties b/android/Tests/keycode/gradle.properties index 7d43aa3719..44ff6135fe 100644 --- a/android/Tests/keycode/gradle.properties +++ b/android/Tests/keycode/gradle.properties @@ -14,4 +14,6 @@ org.gradle.jvmargs=-Xmx1024m # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +# AGP 8.0 generates R classes for resources defined in the current module only. +android.nonTransitiveRClass=false diff --git a/android/build.sh b/android/build.sh index 12017e2c6b..e00c99e634 100755 --- a/android/build.sh +++ b/android/build.sh @@ -10,12 +10,13 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" -. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" -. "$KEYMAN_ROOT/resources/build/zip.inc.sh" +# shellcheck disable=SC2154 +. "${KEYMAN_ROOT}/resources/build/utils.inc.sh" +. "${KEYMAN_ROOT}/resources/build/build-utils-ci.inc.sh" +. "${KEYMAN_ROOT}/resources/build/zip.inc.sh" ################################ Main script ################################ @@ -24,8 +25,8 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" # oemtargets=() -if [ ! -z "${RELEASE_OEM+x}" ]; then - if [ "${RELEASE_OEM_FIRSTVOICES-false}" = true ]; then +if [[ ! -z "${RELEASE_OEM+x}" ]]; then + if [[ "${RELEASE_OEM_FIRSTVOICES-false}" = true ]]; then oemtargets+=(":fv=../oem/firstvoices/android OEM FirstVoices for Android app") fi fi @@ -38,8 +39,7 @@ builder_describe \ build \ test \ "publish Publishes symbols to Sentry and the APKs to the Play Store." \ - "archive Copy release artifacts to upload/ and rsync to downloads.keyman" \ - "--ci+ Deprecated build option. Remove in 20.0" \ + "archive Copy release artifacts to upload/ and create zip" \ --upload-sentry+ \ ":engine=KMEA Keyman Engine for Android" \ ":app=KMAPro Keyman for Android" \ @@ -51,72 +51,85 @@ builder_describe \ builder_parse "$@" -# Override JAVA_HOME to OpenJDK 11 -set_java_home - -# This script also responsible for cleaning up /android/upload -builder_run_child_actions clean - -if builder_start_action clean; then +function do_clean() { builder_heading "Cleanup /android/upload" - rm -rf "$KEYMAN_ROOT/android/upload" - builder_finish_action success clean -fi - -builder_run_child_actions configure build test - -function do_test_help() { - check-markdown "$KEYMAN_ROOT/android/docs/help" - check-markdown "$KEYMAN_ROOT/android/docs/engine" + rm -rf "${KEYMAN_ROOT}/android/upload" } -builder_run_action test:help do_test_help +function do_test_help() { + check-markdown "${KEYMAN_ROOT}/android/docs/help" + check-markdown "${KEYMAN_ROOT}/android/docs/engine" +} -builder_run_child_actions publish +function archive_artifacts() { + local UPLOAD_PATH KEYMAN_ENGINE_ANDROID_ZIP KEYMAN_APK FIRSTVOICES_APK -# Copy release artifacts to upload/ and rsync to downloads.keyman.com -if builder_start_action archive; then - UPLOAD_PATH="$KEYMAN_ROOT/android/upload/${KEYMAN_VERSION}" + UPLOAD_PATH="${KEYMAN_ROOT}/android/upload/${KEYMAN_VERSION}" KEYMAN_ENGINE_ANDROID_ZIP="keyman-engine-android-${KEYMAN_VERSION}.zip" KEYMAN_APK="keyman-${KEYMAN_VERSION}.apk" FIRSTVOICES_APK="firstvoices-${KEYMAN_VERSION}.apk" - ZIP_FILE="${UPLOAD_PATH}/${KEYMAN_ENGINE_ANDROID_ZIP}" - ZIP_FLAGS=("-q" "-r") # quiet, recursive + rm -rf "${UPLOAD_PATH}" mkdir -p "${UPLOAD_PATH}" # Create Keyman Engine for Android archive - builder_echo "Copying Keyman Engine for Android into ${UPLOAD_PATH}..." - cd "${UPLOAD_PATH}" - cp "${KEYMAN_ROOT}/android/KMAPro/kMAPro/libs/keyman-engine.aar" ./ - add_zip_files "${ZIP_FILE}" "${ZIP_FLAGS[@]}" "keyman-engine.aar" - rm -f "keyman-engine.aar" + builder_echo "Adding Keyman Engine for Android to zip..." + ( + # shellcheck disable=SC2164 + cd "${KEYMAN_ROOT}/android/KMAPro/kMAPro/libs" + add_zip_files -q -r "${UPLOAD_PATH}/${KEYMAN_ENGINE_ANDROID_ZIP}" keyman-engine.aar + ) - builder_echo "Copying Keyman Engine for Android Sample projects into ${UPLOAD_PATH}..." - cp -rf "${KEYMAN_ROOT}/android/Samples" ./ - add_zip_files "${ZIP_FILE}" "-x@../../zip-excludes" "${ZIP_FLAGS[@]}" "Samples" - rm -rf "Samples" + builder_echo "Adding Keyman Engine for Android Sample projects to zip..." + ( + # shellcheck disable=SC2164 + cd "${KEYMAN_ROOT}/android" + add_zip_files -q -r -xr!build.sh "${UPLOAD_PATH}/${KEYMAN_ENGINE_ANDROID_ZIP}" Samples + ) # Copy release APK - cp "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/release/${KEYMAN_APK}" ./ + cp "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/release/${KEYMAN_APK}" "${UPLOAD_PATH}" # FirstVoices app - if [ "${RELEASE_OEM_FIRSTVOICES-false}" = true ]; then - cp "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/release/${FIRSTVOICES_APK}" ./ + if [[ "${RELEASE_OEM_FIRSTVOICES-false}" = true ]]; then + cp "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/release/${FIRSTVOICES_APK}" "${UPLOAD_PATH}" fi # # Write download info files # - cd "${UPLOAD_PATH}" - write_download_info "Keyman Engine for Android" "${KEYMAN_ENGINE_ANDROID_ZIP}" "${KEYMAN_VERSION}" "${KEYMAN_TIER}" "android" - write_download_info "Keyman for Android" "${KEYMAN_APK}" "${KEYMAN_VERSION}" "${KEYMAN_TIER}" "android" + write_download_info "${UPLOAD_PATH}" "${KEYMAN_ENGINE_ANDROID_ZIP}" "Keyman Engine for Android" zip android + write_download_info "${UPLOAD_PATH}" "${KEYMAN_APK}" "Keyman for Android" apk android - if [ "${RELEASE_OEM_FIRSTVOICES-false}" = true ]; then - write_download_info "FirstVoices Keyboards" "${FIRSTVOICES_APK}" "${KEYMAN_VERSION}" "${KEYMAN_TIER}" "android" + if [[ "${RELEASE_OEM_FIRSTVOICES-false}" = true ]]; then + write_download_info "${UPLOAD_PATH}" "${FIRSTVOICES_APK}" "FirstVoices Keyboards" apk android fi +} - builder_finish_action success archive -fi + +# For CI compatibility of building Keyman for Android 18.0 with OpenJDK 11, +# this overrides JAVA_HOME for the builder script to use OpenJDK 21. +android_set_java_home() { + if [[ ! -z ${JAVA_HOME_21+x} ]]; then + builder_echo "Setting JAVA_HOME to JAVA_HOME_21 (${JAVA_HOME_21})" + export JAVA_HOME="${JAVA_HOME_21}" + fi +} + +# Override JAVA_HOME +android_set_java_home + +# This script also responsible for cleaning up /android/upload +builder_run_child_actions clean + +builder_run_action clean do_clean + +builder_run_child_actions configure build test + +builder_run_action test:help do_test_help + +builder_run_child_actions publish + +builder_run_action archive archive_artifacts diff --git a/android/docs/help/about/system-requirements.md b/android/docs/help/about/system-requirements.md index 16efbf28af..ae43794793 100644 --- a/android/docs/help/about/system-requirements.md +++ b/android/docs/help/about/system-requirements.md @@ -9,7 +9,7 @@ Keyman for Android will run on Android phones and tablets that have a minimum ve [Android 5.0 (Lollipop)](https://developer.android.com/about/versions/lollipop). ### Minimum Chrome Version -Keyman for Android requires a minimum version 53.0 of [Google +Keyman for Android requires a minimum version 95.0 of [Google Chrome](https://play.google.com/store/apps/details?id=com.android.chrome). If you have an older device, you may need to upgrade Chrome before you can use Keyman, e.g. through the Play Store. diff --git a/android/utility/i18n-check-unused-strings.sh b/android/utility/i18n-check-unused-strings.sh index 062ff8cff4..9e3a0a21c3 100755 --- a/android/utility/i18n-check-unused-strings.sh +++ b/android/utility/i18n-check-unused-strings.sh @@ -25,7 +25,7 @@ set -u # die on undefined variables ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" +. "${THIS_SCRIPT%/*}/../../resources/build/builder-basic.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE # Preparation diff --git a/build.sh b/build.sh index a455d3307f..2589a162b1 100755 --- a/build.sh +++ b/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Keyman -- all projects" \ diff --git a/common/build.sh b/common/build.sh index 5119bca8ac..a066cf15d3 100755 --- a/common/build.sh +++ b/common/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Keyman common and resources modules" \ diff --git a/common/include/build.sh b/common/include/build.sh index 9115bd661a..962d13d5c7 100755 --- a/common/include/build.sh +++ b/common/include/build.sh @@ -5,7 +5,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Build keymanversion_build.h" configure build clean diff --git a/common/linux/build.sh b/common/linux/build.sh index 756826cc2d..2df05e03ba 100755 --- a/common/linux/build.sh +++ b/common/linux/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE # @@ -18,7 +18,7 @@ builder_describe "Keyman common Linux modules" \ builder_parse "$@" -if [[ $BUILDER_OS != linux ]]; then +if ! builder_is_linux; then builder_echo grey "Platform is not linux; skipping common/linux" exit 0 fi diff --git a/common/mac/build.sh b/common/mac/build.sh index 392966d84c..1acf2f3ccd 100755 --- a/common/mac/build.sh +++ b/common/mac/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE # @@ -18,7 +18,7 @@ builder_describe "Keyman common mac modules" \ builder_parse "$@" -if [[ $BUILDER_OS != mac ]]; then +if ! builder_is_macos; then builder_echo grey "Platform is not macOS; skipping common/mac" exit 0 fi diff --git a/common/test/keyboards/baseline/build.sh b/common/test/keyboards/baseline/build.sh index 065e2593c5..1428b5e0c4 100755 --- a/common/test/keyboards/baseline/build.sh +++ b/common/test/keyboards/baseline/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Baseline keyboards tests -- built with 16.0 compiler" \ diff --git a/common/test/keyboards/build.sh b/common/test/keyboards/build.sh index 2d6a986b87..489bf17543 100755 --- a/common/test/keyboards/build.sh +++ b/common/test/keyboards/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE . "$KEYMAN_ROOT/resources/build/zip.inc.sh" diff --git a/common/test/keyboards/issue/6254-invalid-version-99/build.sh b/common/test/keyboards/issue/6254-invalid-version-99/build.sh index 7bf37f54e4..80c5e575e6 100755 --- a/common/test/keyboards/issue/6254-invalid-version-99/build.sh +++ b/common/test/keyboards/issue/6254-invalid-version-99/build.sh @@ -3,8 +3,8 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/build-utils.sh" -# . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-basic.inc.sh" +# . "$KEYMAN_ROOT/resources/build/utils.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE THIS_DIR="$(dirname "$THIS_SCRIPT")" diff --git a/common/test/resources/model-helpers.mjs b/common/test/resources/model-helpers.mjs index a3f141cd54..6ea3620c9d 100644 --- a/common/test/resources/model-helpers.mjs +++ b/common/test/resources/model-helpers.mjs @@ -19,7 +19,9 @@ import { fileURLToPath } from 'url'; /** * Creates a MessageEvent (for inter-worker communication), with the given data payload. * - * @param {*} data + * @template {T} + * @param {T} data + * @returns {T} */ export function createMessageEventWithData(data) { return { data }; diff --git a/common/test/resources/test_kill_browserstack.sh b/common/test/resources/test_kill_browserstack.sh index 82e40f85cd..cbca52da45 100755 --- a/common/test/resources/test_kill_browserstack.sh +++ b/common/test/resources/test_kill_browserstack.sh @@ -8,11 +8,11 @@ set -eu ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-basic.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -if [[ $BUILDER_OS == win ]]; then +if builder_is_windows; then # BrowserStackLocal may not exist, so always pass taskkill //f //im BrowserStackLocal.exe || true fi diff --git a/common/tools/hextobin/build.sh b/common/tools/hextobin/build.sh index 073b008bf2..45706c35b4 100755 --- a/common/tools/hextobin/build.sh +++ b/common/tools/hextobin/build.sh @@ -2,10 +2,11 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" ################################ Main script ################################ @@ -17,5 +18,5 @@ builder_describe_outputs \ builder_parse "$@" builder_run_action clean rm -rf build/ node_modules/ -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build tsc --build diff --git a/common/tools/sourcemap-path-remapper/build.sh b/common/tools/sourcemap-path-remapper/build.sh index 3d50eccf83..871de4c546 100755 --- a/common/tools/sourcemap-path-remapper/build.sh +++ b/common/tools/sourcemap-path-remapper/build.sh @@ -6,10 +6,11 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" ################################ Main script ################################ @@ -34,7 +35,7 @@ fi ### CONFIGURE ACTIONS if builder_start_action configure; then - verify_npm_setup + node_select_version_and_npm_ci builder_finish_action success configure fi diff --git a/common/web/build.sh b/common/web/build.sh index 2370e3ecf4..182de85d62 100755 --- a/common/web/build.sh +++ b/common/web/build.sh @@ -5,7 +5,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Keyman common web modules" \ diff --git a/common/web/keyman-version/build.sh b/common/web/keyman-version/build.sh index 9de3da3f8f..0c9657f74b 100755 --- a/common/web/keyman-version/build.sh +++ b/common/web/keyman-version/build.sh @@ -2,10 +2,11 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" ################################ Main script ################################ @@ -53,6 +54,6 @@ export default KEYMAN_VERSION; } builder_run_action clean rm -rf version.inc.ts keyman-version.mts build/ -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build do_build builder_run_action publish builder_publish_npm diff --git a/common/web/langtags/build.sh b/common/web/langtags/build.sh index f125448d4a..d1b951b624 100755 --- a/common/web/langtags/build.sh +++ b/common/web/langtags/build.sh @@ -2,10 +2,11 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_describe "Build Keyman langtags.js common module" \ @@ -32,7 +33,7 @@ function compile_langtags() { } function do_configure() { - verify_npm_setup + node_select_version_and_npm_ci compile_langtags } diff --git a/common/web/types/build.sh b/common/web/types/build.sh index 5a9243e6cd..1ece69f2af 100755 --- a/common/web/types/build.sh +++ b/common/web/types/build.sh @@ -2,10 +2,11 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_describe "Build Keyman common file types module" \ @@ -76,7 +77,7 @@ function compile_schemas() { function do_configure() { compile_schemas - verify_npm_setup + node_select_version_and_npm_ci } function do_test() { diff --git a/common/web/types/src/kmx/kmx-plus/element-string.ts b/common/web/types/src/kmx/kmx-plus/element-string.ts index e65d996f34..af17145427 100644 --- a/common/web/types/src/kmx/kmx-plus/element-string.ts +++ b/common/web/types/src/kmx/kmx-plus/element-string.ts @@ -81,11 +81,11 @@ export class ElementString extends Array { // error. So we can just exit here. return null; // UnicodeSet error } - const uset = sections.usetparser.parseUnicodeSet(item.segment, needRanges, options?.x); + const uset = sections.usetparser.parseUnicodeSet(item.segment, needRanges, options?.compileContext); if (!uset) { return null; // UnicodeSet error already added to callback } - elem.uset = sections.uset.allocUset(uset, sections, options?.x); + elem.uset = sections.uset.allocUset(uset, sections, options?.compileContext); elem.value = sections.strs.allocString('', {...options, singleOk: true}); // no string } else if (item.type === ElementType.codepoint || item.type === ElementType.escaped || item.type === ElementType.string) { // some kind of a string diff --git a/common/web/types/src/kmx/kmx-plus/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus/kmx-plus.ts index 373cc57b27..4fd5dcd632 100644 --- a/common/web/types/src/kmx/kmx-plus/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus/kmx-plus.ts @@ -141,7 +141,7 @@ export class StrsItem { /** add any context from the options to this strsitem */ setContext(opts?: StrsOptions) { // At present, there's only a single piece of context available - this._context = this._context || opts?.x; + this._context = this._context || opts?.compileContext; } get context() : any { @@ -174,7 +174,7 @@ export interface StrsOptions { /** string can be stored as a single CharStrsItem, not in strs table. */ singleOk?: boolean; /** optional context */ - x?: any; + compileContext?: any; }; export class Strs extends Section { @@ -374,13 +374,13 @@ export class Vars extends Section { export class VarsItem extends Section { id: StrsItem; value: StrsItem; - x?: any; + compileContext?: any; - constructor(id: string, value: string, sections: DependencySections, x?: any) { + constructor(id: string, value: string, sections: DependencySections, compileContext?: any) { super(); this.id = sections.strs.allocString(id); - this.value = sections.strs.allocString(value, {unescape: true}); - this.x = x; + this.value = sections.strs.allocString(value, { unescape: true }); + this.compileContext = compileContext; } valid() : boolean { @@ -389,11 +389,11 @@ export class VarsItem extends Section { }; export class UnicodeSetItem extends VarsItem { - constructor(id: string, value: string, sections: DependencySections, usetparser: UnicodeSetParser, x?: any) { - super(id, value, sections, x); + constructor(id: string, value: string, sections: DependencySections, usetparser: UnicodeSetParser, compileContext?: any) { + super(id, value, sections, compileContext); const needRanges = sections.usetparser.sizeUnicodeSet(value); if (needRanges >= 0) { - this.unicodeSet = sections.usetparser.parseUnicodeSet(value, needRanges); + this.unicodeSet = sections.usetparser.parseUnicodeSet(value, needRanges, compileContext); } // otherwise: error (was recorded via callback) } unicodeSet?: UnicodeSet; @@ -403,9 +403,9 @@ export class UnicodeSetItem extends VarsItem { }; export class SetVarItem extends VarsItem { - constructor(id: string, value: string[], sections: DependencySections, x?: any) { - super(id, value.join(' '), sections, x); - this.items = sections.elem.allocElementString(sections, {x}, value); + constructor(id: string, value: string[], sections: DependencySections, compileContext?: any) { + super(id, value.join(' '), sections, compileContext); + this.items = sections.elem.allocElementString(sections, { compileContext }, value); } items: ElementString; // element string array valid() : boolean { @@ -414,8 +414,8 @@ export class SetVarItem extends VarsItem { }; export class StringVarItem extends VarsItem { - constructor(id: string, value: string, sections: DependencySections, x?: any) { - super(id, value, sections, x); + constructor(id: string, value: string, sections: DependencySections, compileContext?: any) { + super(id, value, sections, compileContext); } // no added fields }; @@ -453,7 +453,7 @@ export class Tran extends Section { }; export class UsetItem { - constructor(public uset: UnicodeSet, public str: StrsItem, public x?: any) { + constructor(public uset: UnicodeSet, public str: StrsItem, public compileContext?: any) { } compareTo(other: UsetItem) : number { return this.str.compareTo(other.str); @@ -462,11 +462,11 @@ export class UsetItem { export class Uset extends Section { usets: UsetItem[] = []; - allocUset(set: UnicodeSet, sections: DependencySections, x?: any) : UsetItem { + allocUset(set: UnicodeSet, sections: DependencySections, compileContext?: any) : UsetItem { // match the same pattern let result = this.usets.find(s => set.pattern == s.uset.pattern); if (result === undefined) { - result = new UsetItem(set, sections.strs.allocString(set.pattern), x); + result = new UsetItem(set, sections.strs.allocString(set.pattern), compileContext); this.usets.push(result); } return result; diff --git a/common/web/types/src/ldml-keyboard/unicodeset-parser-api.ts b/common/web/types/src/ldml-keyboard/unicodeset-parser-api.ts index 797a5415b9..6ad16cd4ee 100644 --- a/common/web/types/src/ldml-keyboard/unicodeset-parser-api.ts +++ b/common/web/types/src/ldml-keyboard/unicodeset-parser-api.ts @@ -8,13 +8,13 @@ export interface UnicodeSetParser { * @param pattern string to parse such as `[a-z]` * @param rangeCount number of ranges to allow for */ - parseUnicodeSet(pattern: string, rangeCount: number, x?: any) : UnicodeSet | null; + parseUnicodeSet(pattern: string, rangeCount: number, compileContext?: any) : UnicodeSet | null; /** * Calculate the number of ranges in a UnicodeSet * @param pattern string to parse such as `[a-z]` * @returns number of ranges, or -1 (with callback-reported err) on err */ - sizeUnicodeSet(pattern: string, x?: any) : number; + sizeUnicodeSet(pattern: string, compileContext?: any) : number; } /** diff --git a/common/web/types/src/lexical-model-types.ts b/common/web/types/src/lexical-model-types.ts index 1b02b413d1..9938abec5a 100644 --- a/common/web/types/src/lexical-model-types.ts +++ b/common/web/types/src/lexical-model-types.ts @@ -424,13 +424,24 @@ export interface ProbabilityMass { export type Distribution = ProbabilityMass[]; /** - * A type augmented with an optional probability. + * A type augmented with optional probability data. */ export type Outcome = T & { /** - * [optional] probability of this outcome. + * [optional] the modeled likelihood associated with this outcome. */ p?: number; + + /** + * The likelihood of the suggestion itself based solely on the lexical model + */ + ['lexical-p']?: number + + /** + * The likelihood associated with the keystroke sequence and/or associated + * text corrections best matching the suggestion. + */ + ['correction-p']?: number }; /** @@ -441,6 +452,21 @@ export type WithOutcome = T & { * Probability of this outcome. */ p: number; + + /** + * The likelihood of the suggestion itself based solely on the lexical model + * + * Only emitted for verbose mode. + */ + ['lexical-p']?: number + + /** + * The likelihood associated with the keystroke sequence and/or associated + * text corrections best matching the suggestion. + * + * Only omitted for verbose mode. + */ + ['correction-p']?: number }; diff --git a/common/web/types/src/main.ts b/common/web/types/src/main.ts index 071c931e9e..779af42ba5 100644 --- a/common/web/types/src/main.ts +++ b/common/web/types/src/main.ts @@ -19,6 +19,8 @@ export * as KmpJsonFile from './package/kmp-json-file.js'; export { Uni_IsSurrogate1, Uni_IsSurrogate2 } from './util/util.js'; export * as util from './util/util.js'; +export { ObjectWithCompileContext } from './util/types.js'; + export * as KeymanFileTypes from './util/file-types.js'; export * as Schemas from './schemas.js'; diff --git a/common/web/types/src/util/types.ts b/common/web/types/src/util/types.ts new file mode 100644 index 0000000000..3ed1dda4f9 --- /dev/null +++ b/common/web/types/src/util/types.ts @@ -0,0 +1,6 @@ +/** + * Any object with metadata, for example for line number errs. + * Defined as 'any' here to reduce noise on the client side. + * @see {@link KeymanXMLReader.getMetaData()} + */ +export type ObjectWithCompileContext = any; diff --git a/common/web/types/src/util/util.ts b/common/web/types/src/util/util.ts index 70e1077cc5..06cb91dd5d 100644 --- a/common/web/types/src/util/util.ts +++ b/common/web/types/src/util/util.ts @@ -3,7 +3,7 @@ export { MATCH_HEX_ESCAPE, CONTAINS_QUAD_ESCAPE, MATCH_QUAD_ESCAPE }; /** * xml2js will not place single-entry objects into arrays. Easiest way to fix - * this is to box them ourselves as needed. Ensures that o.x is an array. + * this is to box them ourselves as needed. Ensures that o[x] is always an array. * * @param o Object with property to box * @param x Name of element to box diff --git a/common/windows/build.sh b/common/windows/build.sh index 06f5dd0201..45941b668d 100755 --- a/common/windows/build.sh +++ b/common/windows/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE # @@ -18,7 +18,7 @@ builder_describe "Keyman common Windows modules" \ builder_parse "$@" -if [[ $BUILDER_OS != win ]]; then +if ! builder_is_windows; then builder_echo grey "Platform is not Windows; skipping common/windows" exit 0 fi diff --git a/common/windows/cef-checkout.sh b/common/windows/cef-checkout.sh index b8326561b8..c669961328 100755 --- a/common/windows/cef-checkout.sh +++ b/common/windows/cef-checkout.sh @@ -13,8 +13,8 @@ set -u ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" -# . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "${THIS_SCRIPT%/*}/../../resources/build/builder-basic.inc.sh" +# . "$KEYMAN_ROOT/resources/build/utils.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE pushd $KEYMAN_CEF4DELPHI_ROOT > /dev/null diff --git a/common/windows/data/build.sh b/common/windows/data/build.sh index ac559612bb..2b70c426ed 100755 --- a/common/windows/data/build.sh +++ b/common/windows/data/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/common/windows/delphi/build.sh b/common/windows/delphi/build.sh index b3e0ddfc1f..81042422fd 100755 --- a/common/windows/delphi/build.sh +++ b/common/windows/delphi/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/common/windows/delphi/components/build.sh b/common/windows/delphi/components/build.sh index 86bad135e0..6fe3dfa9ec 100755 --- a/common/windows/delphi/components/build.sh +++ b/common/windows/delphi/components/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Common Delphi components" clean configure build test edit diff --git a/common/windows/delphi/ext/build.sh b/common/windows/delphi/ext/build.sh index 3fb17d98b3..05dffca69d 100755 --- a/common/windows/delphi/ext/build.sh +++ b/common/windows/delphi/ext/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/common/windows/delphi/ext/cef4delphi/build.sh b/common/windows/delphi/ext/cef4delphi/build.sh index 340cfbf9b1..856694dd27 100755 --- a/common/windows/delphi/ext/cef4delphi/build.sh +++ b/common/windows/delphi/ext/cef4delphi/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "cef4delphi component" clean configure build test diff --git a/common/windows/delphi/ext/dcpcrypt/build.sh b/common/windows/delphi/ext/dcpcrypt/build.sh index cbda46a94d..eceaf361b0 100755 --- a/common/windows/delphi/ext/dcpcrypt/build.sh +++ b/common/windows/delphi/ext/dcpcrypt/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "dcpcrypt component" clean configure build test diff --git a/common/windows/delphi/ext/jwa/build.sh b/common/windows/delphi/ext/jwa/build.sh index 12b27dfe81..30cbbeb919 100755 --- a/common/windows/delphi/ext/jwa/build.sh +++ b/common/windows/delphi/ext/jwa/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "jwa component" clean configure build test diff --git a/common/windows/delphi/ext/sentry/build.sh b/common/windows/delphi/ext/sentry/build.sh index b25d9c3253..c0b0cc9147 100755 --- a/common/windows/delphi/ext/sentry/build.sh +++ b/common/windows/delphi/ext/sentry/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Sentry component for Delphi" clean configure build test diff --git a/common/windows/delphi/ext/tds2dbg/build.sh b/common/windows/delphi/ext/tds2dbg/build.sh index a8fc83989d..805f957ea1 100755 --- a/common/windows/delphi/ext/tds2dbg/build.sh +++ b/common/windows/delphi/ext/tds2dbg/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE # TODO: move this out of ext and into tools diff --git a/common/windows/delphi/general/KeymanVersionInfo.inc b/common/windows/delphi/general/KeymanVersionInfo.inc index 4991b42385..41e316e051 100644 --- a/common/windows/delphi/general/KeymanVersionInfo.inc +++ b/common/windows/delphi/general/KeymanVersionInfo.inc @@ -1,6 +1,6 @@ type TKeymanVersionInfo = record - // These fields match fields in /resources/build/build-utils.sh + // These fields match fields in /resources/build/builder-basic.inc.sh Version: string; // 14.0.2 VersionWin: string; // 14.0.2.0 VersionRelease: string; // 14.0 diff --git a/common/windows/delphi/general/keymanversion_build.in b/common/windows/delphi/general/keymanversion_build.in index edd55572a6..a5ada65e15 100644 --- a/common/windows/delphi/general/keymanversion_build.in +++ b/common/windows/delphi/general/keymanversion_build.in @@ -6,7 +6,7 @@ // Keyman Version Information CKeymanVersionInfo: TKeymanVersionInfo = ( - // These fields match fields in /resources/build/build-utils.sh + // These fields match fields in /resources/build/builder-basic.inc.sh Version: '$Version'; VersionWin: '$VersionWin'; VersionRelease: '$VersionRelease'; diff --git a/common/windows/delphi/tools/build.sh b/common/windows/delphi/tools/build.sh index 6fad40a4d6..220463620b 100755 --- a/common/windows/delphi/tools/build.sh +++ b/common/windows/delphi/tools/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/common/windows/delphi/tools/build_standards_data/build.sh b/common/windows/delphi/tools/build_standards_data/build.sh index 3d39aefe9d..239bde79f0 100755 --- a/common/windows/delphi/tools/build_standards_data/build.sh +++ b/common/windows/delphi/tools/build_standards_data/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Build build_standards_data tool" clean configure build test edit diff --git a/common/windows/delphi/tools/buildunidata/build.sh b/common/windows/delphi/tools/buildunidata/build.sh index b4e13c264d..36b069351b 100755 --- a/common/windows/delphi/tools/buildunidata/build.sh +++ b/common/windows/delphi/tools/buildunidata/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Unicode character database build tool" \ diff --git a/common/windows/delphi/tools/certificates/build.sh b/common/windows/delphi/tools/certificates/build.sh index 10ab4c75ce..dfe4911f18 100755 --- a/common/windows/delphi/tools/certificates/build.sh +++ b/common/windows/delphi/tools/certificates/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Build and install test certificates" clean configure build test \ diff --git a/common/windows/delphi/tools/devtools/build.sh b/common/windows/delphi/tools/devtools/build.sh index f514ea7578..f7b9dd52d4 100755 --- a/common/windows/delphi/tools/devtools/build.sh +++ b/common/windows/delphi/tools/devtools/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/common/windows/delphi/tools/sentrytool/build.sh b/common/windows/delphi/tools/sentrytool/build.sh index ca4e427c39..c1672b0aeb 100755 --- a/common/windows/delphi/tools/sentrytool/build.sh +++ b/common/windows/delphi/tools/sentrytool/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Sentrytool for converting Delphi symbols into sentry-readable format" \ diff --git a/common/windows/delphi/tools/test-klog/build.sh b/common/windows/delphi/tools/test-klog/build.sh index b271565cae..acdd90cd53 100755 --- a/common/windows/delphi/tools/test-klog/build.sh +++ b/common/windows/delphi/tools/test-klog/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Tool for validating klog is disabled for release builds" \ diff --git a/common/windows/delphi/tools/verify_signatures/build.sh b/common/windows/delphi/tools/verify_signatures/build.sh index f53883c70e..d1b29e89cb 100755 --- a/common/windows/delphi/tools/verify_signatures/build.sh +++ b/common/windows/delphi/tools/verify_signatures/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Tool to verify all Windows executable signatures and manifests" \ diff --git a/common/windows/mkver.sh b/common/windows/mkver.sh index 8ddb9e34ed..84886666bd 100755 --- a/common/windows/mkver.sh +++ b/common/windows/mkver.sh @@ -9,7 +9,7 @@ set -u ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" +. "${THIS_SCRIPT%/*}/../../resources/build/builder-basic.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE display_usage() { diff --git a/core/build.sh b/core/build.sh index 94e8ae4e38..1c1d337b1c 100755 --- a/core/build.sh +++ b/core/build.sh @@ -3,11 +3,11 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" . "$THIS_SCRIPT_PATH/commands.inc.sh" ################################ Main script ################################ diff --git a/core/commands.inc.sh b/core/commands.inc.sh index 761be3db4d..73ad033a8e 100644 --- a/core/commands.inc.sh +++ b/core/commands.inc.sh @@ -91,7 +91,7 @@ do_test() { if [[ $target =~ ^(x86|x64)$ ]]; then cmd //C build.bat $target $BUILDER_CONFIGURATION test $testparams else - if [[ $target == wasm ]] && [[ $BUILDER_OS == mac ]]; then + if [[ $target == wasm ]] && builder_is_macos; then # 11794 -- parallel tests failing on some mac build agents; temporary # mitigation until we diagnose root cause meson test -j 1 -C "$MESON_PATH" $testparams @@ -136,7 +136,7 @@ do_uninstall() { # ---------------------------------------------------------------------------- build_meson_cross_file_for_wasm() { - if [ $BUILDER_OS == win ]; then + if builder_is_windows; then local R=$(cygpath -w $(echo $EMSCRIPTEN_BASE) | sed 's_\\_\\\\_g') else local R=$(echo $EMSCRIPTEN_BASE | sed 's_/_\\/_g') diff --git a/core/include/ldml/build.sh b/core/include/ldml/build.sh index 17902411d3..f2633ed348 100755 --- a/core/include/ldml/build.sh +++ b/core/include/ldml/build.sh @@ -5,10 +5,11 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_describe "Keyman ldml-keyboard-constants package" \ @@ -30,7 +31,7 @@ builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- builder_run_action clean rm -rf ./build/ -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build tsc --build # builder_run_action test # no tests at this time builder_run_action publish builder_publish_npm diff --git a/core/tests/unit/ldml/keyboards/meson.build b/core/tests/unit/ldml/keyboards/meson.build index 8e9539a3dd..0fe3980681 100644 --- a/core/tests/unit/ldml/keyboards/meson.build +++ b/core/tests/unit/ldml/keyboards/meson.build @@ -65,6 +65,7 @@ kmc_cmd = [node, '--enable-source-maps', kmc_root] # Build all keyboards in output folder foreach kbd : tests_needing_copy + # This will output a (wrong) deprecation warning with meson < 1.5 - see #13250 configure_file(input: kbd + '.xml', output: kbd + '.xml', copy: true) endforeach @@ -77,6 +78,7 @@ foreach kbd : tests_with_testdata endforeach foreach kbd : tests_from_cldr + # This will output a (wrong) deprecation warning with meson < 1.5 - see #13250 configure_file(input: ldml_data / kbd + '.xml', output: kbd + '.xml', copy: true) configure_file( command: kmc_cmd + ['build', 'ldml-test-data', '@INPUT@', '--out-file', '@OUTPUT@'], diff --git a/core/tools/api-header-extractor/build.sh b/core/tools/api-header-extractor/build.sh index fe048ce48b..bf26598ce2 100755 --- a/core/tools/api-header-extractor/build.sh +++ b/core/tools/api-header-extractor/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE CORE_ROOT="$KEYMAN_ROOT/core" diff --git a/core/tools/ldml-const-builder/build.sh b/core/tools/ldml-const-builder/build.sh index 26c7c0a60e..8c8bc364b5 100755 --- a/core/tools/ldml-const-builder/build.sh +++ b/core/tools/ldml-const-builder/build.sh @@ -5,10 +5,10 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" CORE_LDML_H_FILE="../../include/ldml/keyman_core_ldml.h" diff --git a/developer/build.sh b/developer/build.sh index b75967a4ce..74269abcc3 100755 --- a/developer/build.sh +++ b/developer/build.sh @@ -2,7 +2,7 @@ ## START STANDARD UTILITY SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../resources/build/build-utils.sh" +. "${THIS_SCRIPT%/*}/../resources/build/builder-basic.inc.sh" ## END STANDARD UTILITY SCRIPT INCLUDE # This is not a builder script but calls a builder script diff --git a/developer/docs/help/context/keyboard-editor.md b/developer/docs/help/context/keyboard-editor.md index fb0be6d34c..577640fc65 100644 --- a/developer/docs/help/context/keyboard-editor.md +++ b/developer/docs/help/context/keyboard-editor.md @@ -13,13 +13,10 @@ multiple source files, which are all managed within this one editor. The following image shows the first tab of the editor, the Details tab, for a brand new keyboard, with those three initial tabs. -![Keyboard Editor - New file, Details tab](../images/ui/frmKeymanWizard_New.png) +Keyboard Editor - New file, Details tab -> ### Note -Users of earlier versions of Keyman Developer may initially have trouble -finding tabs such as the Icon tab or the On-Screen tab, as they are not -initially visible. The Features grid on the Details tab allows you to -add these extra features into the keyboard. +> [!Note] +> Users of earlier versions of Keyman Developer may initially have trouble finding tabs such as the Icon tab or the On-Screen tab, as they are not initially visible. The Features grid on the Details tab allows you to add these extra features into the keyboard. ## Keyboard component files @@ -47,7 +44,7 @@ because that reflects both the Details and the Layout tab. ## Details tab -![Keyboard Editor - Details tab](../images/ui/frmKeymanWizard_Details.png) +Keyboard Editor - Details tab The Details tab grows as you add more options to a keyboard. The fields here are: @@ -102,7 +99,7 @@ Features ## Layout tab -![Keyboard Editor - Layout tab, Design view](../images/ui/frmKeymanWizard_Layout_Design.png) +Keyboard Editor - Layout tab, Design view The Layout tab gives you a simple interface to quickly create a keyboard using a visual representation of a desktop/laptop computer keyboard. You @@ -124,13 +121,11 @@ There are two ways to assign characters to the keys: onto the appropriate key. This will set the key to output that character. -> ### Hint - To add the character to a key with existing characters, hold -Ctrl while dropping it onto the key. +> [!TIP] +> To add the character to a key with existing characters, hold Ctrl while dropping it onto the key. - -> ### Note - Any key that does not have a character assigned to it will output what the selected Windows layout specifies. +> [!Note] +> Any key that does not have a character assigned to it will output what the selected Windows layout specifies. Distinguish between left and right Ctrl/Alt : A Keyman keyboard can treat left and right Ctrl and Alt identically, @@ -151,9 +146,7 @@ Display 102nd Key (as on European keyboards) You can press and release Ctrl to select another key on the keyboard using your keyboard. -
- -![Keyboard Editor - Layout tab, Code view](../images/ui/frmKeymanWizard_Layout_Code.png) +Keyboard Editor - Layout tab, Code view The Code view shows the source code of the keyboard file. This is where all the information in the Details and Layout tab is stored, and @@ -165,7 +158,7 @@ editor shown within this view. ## On-Screen tab -![Keyboard Editor - On-Screen tab](../images/ui/OnScreenKeyboard.png) +Keyboard Editor - On-Screen tab This tab allows you to edit the visual representation of your keyboard layout. The content on this tab is stored in the .kvks file associated @@ -213,17 +206,28 @@ Distinguish between left and right Ctrl/Alt : If checked, treats the left and right Ctrl/Alt as separate layers Display 102nd Key (as on European keyboards) -: European keyboards have one extra key that is not on US keyboards. +: European keyboards (ISO layout) have one extra key that is not on US keyboards (ANSI layout). This key is positioned to the right of the left shift key. Some other keyboards have additional keys; these are not shown on the - layout designer. If you have a European layout selected as your + layout designer. + + If you have a European layout selected as your Windows layout, the 102nd key will always be visible in the designer; ensure you select this checkbox if you want the 102nd key to always be visible to end users of the layout, irrespective of - their selected base layout. When a European layout is selected as + their selected base layout. + + When a European layout is selected as your Windows layout, the shape of the Enter key will also change to take two rows, and the backslash key will move down one row, but not - otherwise change in behaviour. + otherwise change in behaviour. + + Keyboard Editor - On-Screen tab with ISO layout + *See the difference between the two layouts in Keyman Developer.* + + > [!NOTE] + > The "Display 102nd Key (as on European keyboards)" checkbox can be left unchecked. + > To ensure compatibility, the extra key shouldn't be assigned since it won't be accessible on an ANSI physical keyboard. Auto-fill underlying layout : When the Fill from layout button is clicked, if this option is @@ -232,7 +236,7 @@ Auto-fill underlying layout ## Touch Layout tab -![Keyboard Editor - Touch Layout tab, Design view](../images/ui/TouchLayout_Design.png) +Keyboard Editor - Touch Layout tab, Design view The Touch Layout tab is used to create the visible representation of the keyboard layout for touch devices. It works similarly to the On Screen @@ -251,7 +255,7 @@ including iPhone and iPad, in different orientations, to allow you to visualize the keyboard layout before you load it onto a device. The following image shows all aspects of the touch design view. -![Keyboard Editor - Touch Layout tab, Design view closeup](../images/ui/TouchLayout_Design_2.png) +Keyboard Editor - Touch Layout tab, Design view closeup ### Left sidebar controls @@ -266,6 +270,20 @@ Template... be lost. Therefore, selecting an appropriate template early in the development process is suggested. + - template-basic: has only a phone layout (4 rows of keys), with three layers: default (K_A through K_Z + plus some punctuation), shift (Shift K_A through Shift K_Z, plus some punctuation), numeric (K_0 through + K_9 unshifted and shifted, plus other non-alphanumerice keys). + + - template-latin: has both phone and tablet layouts (which are identical except for some key spacing, + each with 4 rows of keys), with four layers: default, shift, numeric, symbol. The first three layers + are similar to those of template-basic, with the addition of longpress keys for accented characters. + The symbol layer has many symbols not usually seen on a physical keyboard. + + - template-traditional: has both phone (4 rows of keys) and tablet (5 rows of keys) layouts. The phone + layout is similar to the template-basic layout. The tablet layout has eight layers: default, shift, ctrl, + shift-ctrl, alt, shift-alt, ctrl-alt, shift-ctrl-alt. The default layer has the keys from the default layer + of the desktop layout, the shift layer has the keys from the shift layer of the desktop layout, and so on. + Import from On Screen : If you have an existing On-Screen Keyboard for your keyboard layout, importing the design from the On-Screen Keyboard can reduce the @@ -454,7 +472,7 @@ dragged between the main keyboard area and the long press area. The bar below the long press area contains the corresponding controls for the long press keys. -![Keyboard Editor - Touch Layout tab, Code view](../images/ui/frmKeymanWizard_TouchLayout_Code.png) +Keyboard Editor - Touch Layout tab, Code view The Code view allows you to edit the JSON source of the touch layout file. This makes certain operations simpler, such as batched rearranging @@ -467,7 +485,7 @@ The format of the layout file is described in [Creating a touch keyboard layout Underneath the keyboard area is the Long-press, Flicks, and Multitaps controls. -![Long-press, Flicks, and Multitaps layout](../images/ui/LongPress_Flicks_Multitaps.png) +Long-press, Flicks, and Multitaps layout Red circle with an X : This button to the top right of the key will delete the key from the @@ -494,7 +512,7 @@ the selected key for different Gesture Type. The only difference between this co ## Icon tab -![Keyboard Editor - Icon tab](../images/ui/frmKeymanWizard_Icon.png) +Keyboard Editor - Icon tab The icon tab allows you to edit the icon associated with the keyboard. An external icon editor will allow you to create more sophisticated icons with alpha transparency and multiple resolutions, but this will cover the standard 16x16 256 colour icon, which is all that many keyboards require. The controls at the top left are: @@ -560,7 +578,7 @@ This tab allows you to edit the [named constants](/developer/language/guide/cons ## Build tab -![Keyboard Editor - Build tab](../images/ui/BuildTab.png) +Keyboard Editor - Build tab A set of common controls is across the top of this tab: diff --git a/developer/docs/help/context/package-editor.md b/developer/docs/help/context/package-editor.md index 38026732bc..85335a2c1b 100644 --- a/developer/docs/help/context/package-editor.md +++ b/developer/docs/help/context/package-editor.md @@ -129,6 +129,12 @@ are optional. A web site is encouraged and should be filled in the web site field, including the initial "http://" or "https://". +The Description is the keyboard summary that users will see on the keyboard’s +overview page at the keyman.com web site. +[GitHub style Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) +can be used in the description text. However, HTML tags are not supported. + + A package can optionally include a 140x250 JPEG or PNG image file to be shown during installation. This image file must be added to the Files list in step 1, then selected from the list here. @@ -191,4 +197,4 @@ keyboard to the [Keyman Cloud Keyboards Repository](/developer/keyboards/). ## Package Installers As of Keyman Developer 17, bundled executable package installers for Keyman for -Windows can be created using [kmc](kmc), but cannot be created within the IDE. \ No newline at end of file +Windows can be created using [kmc](kmc), but cannot be created within the IDE. diff --git a/developer/docs/help/guides/develop/touch-keyboard-tutorial/gestures.md b/developer/docs/help/guides/develop/touch-keyboard-tutorial/gestures.md new file mode 100644 index 0000000000..b09d5919cf --- /dev/null +++ b/developer/docs/help/guides/develop/touch-keyboard-tutorial/gestures.md @@ -0,0 +1,26 @@ +--- +title: Using Gestures in Keyman Developer +--- + +Keyman Developer offers **Longpress** keys, **Flicks**, and **Multitaps** for touch keyboard layout. To allow gestures onto your keyboard, go to the Touch layout tab and define the keys as follows: + +Keyman Developer touch layout gestures + +## Tips + +***Flicks** and **long presses** can both be used in a single keyboard. In this scenario, it is recommended to use flicks in only one direction—south (down). Long presses work well alongside this pattern. See, for example, [sil_euro_latin](https://help.keyman.com/keyboard/sil_euro_latin/3.0.3/sil_euro_latin#toc-mobile-keyboard-layout). + * Another helpful tip seen in the sil_euro_latin keyboard is using south-flicks on the base and shift layers corresponding to the numeric/symbol layer, this aids in both memorization and discovery. +* **Flicks** in multiple directions are possible, but currently Keyman does not support hints for multiple directions, so they are not very discoverable. +* **Long presses** are more discoverable, but **flicks** are faster. We suggest using south flicks along with long presses, and including the flick output in the long press for maximum discoverability and optimum ergonomics. + +## Caution + +* **Flicks** near screen edges tend to be problematic--once you drag off the screen, then Keyman can no longer tell that you are flicking (or you may end up activating system controls on the bottom edge): + * On the bottom row, south flicks are not recommended. + * On the top row, we have the banner space reserved in order to be able to support flicks and long presses up into that region of the screen. + * Left/right screen edges, horizontal flicks are not the best practice. + +## See also + +* The [Winchus keyboard](https://help.keyman.com/keyboard/winchus/1.4/winchus#guia-rapida-celular) +* The [GFF Amharic keyboard](https://help.keyman.com/keyboard/gff_amharic/3.2.1/gff_amharic#toc-using-this-keyboard) \ No newline at end of file diff --git a/developer/docs/help/guides/develop/touch-keyboard-tutorial/index.md b/developer/docs/help/guides/develop/touch-keyboard-tutorial/index.md index bc7c8e792f..acfe3efcec 100644 --- a/developer/docs/help/guides/develop/touch-keyboard-tutorial/index.md +++ b/developer/docs/help/guides/develop/touch-keyboard-tutorial/index.md @@ -38,17 +38,23 @@ ago) used the / key as the deadkey. /d gave ɗ, and so on. It would be possible to use that desktop keyboard logic on touch -screens.  But your mobile device users will find these rules annoying. +screens. But your mobile device users will find these rules annoying. + Instead of two keys they would probably have to press four, since the standard alphabetical keyboard on most touch devices does not have the slash key. So they would have to press the key to switch to the numeric -keyboard, then press slash, then press the key to go back to the -alphabetical keyboard, then the letter. What touch screen users would +keyboard, then press slash, then press the key to switch back to the +alphabetical keyboard, and then the letter. What touch screen users would appreciate is something that uses the great feature of touch screens, the long press or press and hold on a letter, to see analogous letters. -So we’ll set up a touch screen keyboard that lets you press and hold ‘d’ -to see the two hooked d characters, press and hold ‘b’ to see the hooked -b characters, and so on. + +So we’ll set up a touch screen keyboard that lets you +* press and hold ‘d’ to see the two hooked d characters +* press and hold ‘b’ to see the hooked b characters, and so on. [Next: Making, testing and distributing the touch keyboard](making-touch-keyboard) + +## See also + +* Tips for using [gestures](gestures) in Keyman Developer diff --git a/developer/docs/help/images/touch-keyboard-tutorial/touch-layout-gestures.jpg b/developer/docs/help/images/touch-keyboard-tutorial/touch-layout-gestures.jpg new file mode 100644 index 0000000000..1d407cc22a Binary files /dev/null and b/developer/docs/help/images/touch-keyboard-tutorial/touch-layout-gestures.jpg differ diff --git a/developer/docs/help/images/ui/OnScreenKeyboard_ISO.png b/developer/docs/help/images/ui/OnScreenKeyboard_ISO.png new file mode 100644 index 0000000000..64e3f6ef7e Binary files /dev/null and b/developer/docs/help/images/ui/OnScreenKeyboard_ISO.png differ diff --git a/developer/docs/help/main/credits.md b/developer/docs/help/main/credits.md index f2c8a027c5..a4c2124870 100644 --- a/developer/docs/help/main/credits.md +++ b/developer/docs/help/main/credits.md @@ -2,6 +2,6 @@ title: Additional Credits --- -## Code2000, Code2001 and Code2002 fonts +## Code2000, Code2001 and Code2002 fonts © 2006 James Kass. diff --git a/developer/docs/help/main/license.md b/developer/docs/help/main/license.md index 73b788f900..83f45978e9 100644 --- a/developer/docs/help/main/license.md +++ b/developer/docs/help/main/license.md @@ -4,7 +4,7 @@ title: Keyman® Developer License MIT License -Copyright (c) 1994-2018 SIL International +Copyright (c) 1994-2025 SIL Global Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/developer/docs/help/reference/bcp-47.md b/developer/docs/help/reference/bcp-47.md index f5c8a49539..0d087911e2 100644 --- a/developer/docs/help/reference/bcp-47.md +++ b/developer/docs/help/reference/bcp-47.md @@ -30,26 +30,30 @@ The following are all examples of valid BCP 47 tags: * `en-US`: English, in United States * `km-Khmr-KH`: Khmer, written in the Khmer script, in Cambodia * `km-fonipa`: Khmer, transcribed in IPA +* `und-cpmn`: Undetermined (Cypro-Minoan) +* `tok-Zzzz`: Toki Pona (Code for uncoded script) + +> [!NOTE] +> `Zzzz` is an uncoded script of ISO 15924 and one of the Special codes of the Script subtag. ### The language subtag -The only required option is the Language subtag, which is an [ISO 639-1][2] or -[ISO 639-3][3] code. +The only required option is the Language subtag, which is an [ISO 639][4] code. If a two-letter code[ISO 639-1][2] is available, it is used instead of the three-letter code [ISO 639-3][3]. -ISO 639-1 tags are a two-letter code. ISO 639-3 tags are a three-letter code. First, try to find your language on the list of two-letter ISO 639-1 codes. -[This Wikipedia page][4] lists all of the two-letter codes. +[This Wikipedia page][5] lists all of the two-letter codes. If you can't find a two-letter code, you'll need to find the closest -three-letter code. You can use [Glottolog][5] to search for your language, and +three-letter code. You can use [Glottolog][6] to search for your language, and it will give you an appropriate code. In this example, I searched Glottolog for -“[Saanich][6]” (name of the First Nations that speak SENĆOŦEN) and found `str` +“[Saanich][7]” (name of the First Nations that speak SENĆOŦEN) and found `str` as the code for all Straits Salish languages. -The Language subtag is conventionally written in lower case. +> [!IMPORTANT] +> The Language subtag is conventionally written in lower case. -The next two subtags are **optional**, however, they allow you to be more -specific about your language. +> [!NOTE] +> The next two subtags are **optional**, however, they allow you to be more specific about your language. ### The script subtag @@ -58,7 +62,7 @@ model or keyboard. If your language only uses one writing system, omit the Script subtag. Otherwise, in cases where a language can be written in many different writing -systems, you can choose the four letter [ISO 15924][7] script tag that your +systems, you can choose the four letter [ISO 15924][8] script tag that your keyboard or lexical model produces. For example, Plains Cree can either be written in _standard Roman orthography_, @@ -67,8 +71,8 @@ of the **Canadian Aboriginal syllabics** family of writing systems. If I wrote a keyboard or lexical model that produced syllabics, I would choose `Cans`, as that is the **ISO 15924** tag for Canadian Aboriginal syllabics. -The Script subtag is conventionally written in title case - first letter -capitalized. +> [!IMPORTANT] +> The Script subtag is conventionally written in title case - first letter capitalized. ### The region subtag @@ -88,11 +92,11 @@ Latin America. Additionally, regions may have vocabulary that doesn't exist in the other regions where the language is spoken. If I were working with a language specific to one country, I would use the [ISO -3166-1 alpha-2][8] country code for the region subtag. For example, `ES` for +3166-1 alpha-2][9] country code for the region subtag. For example, `ES` for Spain or `MX` for Mexico. However, if I were working with Latin American Spanish (a group of countries), I -would need to specify Latin America's [UN M49][9] region code. For Latin +would need to specify Latin America's [UN M49][10] region code. For Latin America, its code is `419`. My lexical model would not suggest words that are common in Spain, but vulgar in Latin America, however it would predict words like "pupupsas" and "chuchitos", which are words that are uncommon in both Spain @@ -100,14 +104,16 @@ and Mexico. Another common UN M49 region code is `001` for the whole world. -Alphabetic region subtags are conventionally written in upper case. +> [!IMPORTANT] +> Alphabetic region subtags are conventionally written in upper case. [1]: https://en.wikipedia.org/wiki/IETF_language_tag [2]: https://en.wikipedia.org/wiki/ISO_639-1 [3]: https://en.wikipedia.org/wiki/ISO_639-3 -[4]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes -[5]: https://glottolog.org/glottolog/language -[6]: https://glottolog.org/resource/languoid/id/saan1246 -[7]: https://en.wikipedia.org/wiki/ISO_15924 -[8]: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 -[9]: https://en.wikipedia.org/wiki/UN_M49 +[4]: https://en.wikipedia.org/wiki/ISO_639 +[5]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes +[6]: https://glottolog.org/glottolog/language +[7]: https://glottolog.org/resource/languoid/id/saan1246 +[8]: https://en.wikipedia.org/wiki/ISO_15924 +[9]: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 +[10]: https://en.wikipedia.org/wiki/UN_M49 diff --git a/developer/docs/internal/developer-messaging.md b/developer/docs/internal/developer-messaging.md index d7fe3bb9d4..453230a733 100644 --- a/developer/docs/internal/developer-messaging.md +++ b/developer/docs/internal/developer-messaging.md @@ -102,7 +102,7 @@ Example: keys.ts \- compiler \- call site for error ```js static ERROR_InvalidScanCode = SevError | 0x0009; -static Error_InvalidScanCode = (o:{id: string, invalidCodeList: string}, x: ObjectWithMetadata) => mx( +static Error_InvalidScanCode = (o:{id: string, invalidCodeList: string}, compileContext:ObjectWithCompileContext) => mx( this.ERROR_InvalidScanCode, context, Form '${def(o.id)}' has invalid/unknown scancodes '${def(o.codes)}', `…additional markdown detail…` @@ -126,7 +126,7 @@ The `mx()` function does the following (note that `m()` is called first). ```js let evt = m(code, message, detail); // raw message -evt = LdmlCompilerMessages.offset(evt, x); // with offset +evt = LdmlCompilerMessages.offset(evt, compileContext); // with offset ``` Note: We could have: `ml(e:CompilerEvent, lineNumber?: number)` so that compilers with a line number could pass that in directly. diff --git a/developer/src/build.sh b/developer/src/build.sh index d01c6caeb8..6e23dc8c0a 100755 --- a/developer/src/build.sh +++ b/developer/src/build.sh @@ -2,10 +2,10 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" . "$KEYMAN_ROOT/developer/src/packages.inc.sh" @@ -66,7 +66,7 @@ builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- -if [[ $BUILDER_OS == win ]]; then +if builder_is_windows; then source "$KEYMAN_ROOT/resources/build/win/environment.inc.sh" fi diff --git a/developer/src/common/build.sh b/developer/src/common/build.sh index b37c2bc948..8ef5a4a1f2 100755 --- a/developer/src/common/build.sh +++ b/developer/src/common/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/developer/src/common/delphi/build.sh b/developer/src/common/delphi/build.sh index 63680a5624..907621194f 100755 --- a/developer/src/common/delphi/build.sh +++ b/developer/src/common/delphi/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/developer/src/common/web/build.sh b/developer/src/common/web/build.sh index 7ca90bd986..9be3a521fa 100755 --- a/developer/src/common/web/build.sh +++ b/developer/src/common/web/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/developer/src/common/web/test-helpers/build.sh b/developer/src/common/web/test-helpers/build.sh index c1664cfddd..98b8e0b184 100755 --- a/developer/src/common/web/test-helpers/build.sh +++ b/developer/src/common/web/test-helpers/build.sh @@ -2,10 +2,11 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" builder_describe "Keyman Developer unit test helpers" \ "@/developer/src/common/web/utils" \ @@ -20,6 +21,6 @@ builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- builder_run_action clean rm -rf ./build/ -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build tsc --build # builder_run_action test # no tests at this time diff --git a/developer/src/common/web/utils/build.sh b/developer/src/common/web/utils/build.sh index 251588e2f6..7c0f8f0ed9 100755 --- a/developer/src/common/web/utils/build.sh +++ b/developer/src/common/web/utils/build.sh @@ -2,10 +2,12 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" +. "$KEYMAN_ROOT/resources/build/typescript.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_describe "Build Keyman Developer web utility module" \ @@ -47,7 +49,7 @@ function do_build() { } builder_run_action clean rm -rf ./build/ -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build do_build -builder_run_action test builder_do_typescript_tests 45 +builder_run_action test typescript_run_eslint_mocha_tests 45 builder_run_action publish builder_publish_npm diff --git a/developer/src/common/web/utils/src/compiler-interfaces.ts b/developer/src/common/web/utils/src/compiler-interfaces.ts index 3cce4e75ef..74231aaf2b 100644 --- a/developer/src/common/web/utils/src/compiler-interfaces.ts +++ b/developer/src/common/web/utils/src/compiler-interfaces.ts @@ -1,5 +1,5 @@ import { CompilerCallbacks } from "./compiler-callbacks.js"; -import { ObjectWithMetadata } from "./symbol-utils.js"; +import { ObjectWithCompileContext } from '@keymanapp/common-types'; import { KeymanXMLReader, XML_FILENAME_SYMBOL } from "./xml-utils.js"; /** @@ -215,9 +215,9 @@ export class CompilerError { * @param x any object parsed from XML or with the XML_META_DATA_SYMBOL symbol copied over * @returns modified event object */ - public static setFromMetadata(event: CompilerEvent, x?: ObjectWithMetadata): CompilerEvent { - if(x) { - const metadata = KeymanXMLReader.getMetaData(x) || {}; + public static setFromMetadata(event: CompilerEvent, compileContext?: ObjectWithCompileContext): CompilerEvent { + if (compileContext) { + const metadata = KeymanXMLReader.getMetaData(compileContext) || {}; const offset = metadata?.startIndex; if (offset) { event.offset = offset; @@ -458,8 +458,8 @@ export function dedentCompilerMessageDetail(event: CompilerEvent) { * // it contains redundant info. * // 2. No code execution within the arrow function other than the 'mx' call, string interpolation, * // with `${def(o.property)}` as the max complexity of interpolation. - * static Error_InvalidScanCode = (o:{id: string, invalidCodeList: string}, x: ObjectWithMetadata) => mx( - * this.ERROR_InvalidScanCode, x, + * static Error_InvalidScanCode = (o:{id: string, invalidCodeList: string}, compileContext: ObjectWithCompileContext) => mx( + * this.ERROR_InvalidScanCode, compileContext, * `Form '${def(o.id)}' has invalid/unknown scancodes '${def(o.codes)}'`, * // Note: If detail is omitted, leave the trailing comma on the prior line to leave room for it * `…additional markdown detail…` @@ -474,7 +474,7 @@ export function dedentCompilerMessageDetail(event: CompilerEvent) { * @see CompilerMessageSpec * @returns the event */ -export function CompilerMessageObjectSpec(code: number, context: ObjectWithMetadata, message: string, detail?: string): CompilerEvent { +export function CompilerMessageObjectSpec(code: number, context: ObjectWithCompileContext, message: string, detail?: string): CompilerEvent { let evt = CompilerMessageSpec(code, message, detail); // constructs raw message evt = CompilerError.setFromMetadata(evt, context); // updates with offset from context return evt; diff --git a/developer/src/common/web/utils/src/developer-utils-messages.ts b/developer/src/common/web/utils/src/developer-utils-messages.ts index 6fd328802b..917af99e70 100644 --- a/developer/src/common/web/utils/src/developer-utils-messages.ts +++ b/developer/src/common/web/utils/src/developer-utils-messages.ts @@ -1,8 +1,9 @@ /* * Keyman is copyright (C) SIL Global. MIT License. */ -import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageDef as def, CompilerMessageSpec as m } from './compiler-interfaces.js'; +import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageDef as def, CompilerMessageSpec as m, CompilerMessageObjectSpec as mx } from './compiler-interfaces.js'; import { constants } from '@keymanapp/ldml-keyboard-constants'; +import { ObjectWithCompileContext } from '@keymanapp/common-types'; const DeveloperUtilsErrMask = CompilerErrorNamespace.DeveloperUtils; // const SevInfo = CompilerErrorSeverity.Info | DeveloperUtilsErrMask; @@ -14,8 +15,10 @@ const SevError = CompilerErrorSeverity.Error | DeveloperUtilsErrMask; export class DeveloperUtilsMessages { // structured Ajv validation error static ERROR_SchemaValidationError = SevError | 0x0001; - static Error_SchemaValidationError = (o:{instancePath:string, keyword:string, message: string, params: string}) => m(this.ERROR_SchemaValidationError, - `Error validating LDML XML file: ${def(o.instancePath)}: ${def(o.keyword)}: ${def(o.message)} ${def(o.params)}`); + static Error_SchemaValidationError = (o:{instancePath:string, keyword:string, message: string, params: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_SchemaValidationError, compileContext, + `Error validating LDML XML file: ${def(o.instancePath)}: ${def(o.keyword)}: ${def(o.message)} ${def(o.params)}`, + ); static ERROR_ImportInvalidBase = SevError | 0x0002; static Error_ImportInvalidBase = (o: { base: string, path: string, subtag: string }) => diff --git a/developer/src/common/web/utils/src/index.ts b/developer/src/common/web/utils/src/index.ts index c39afa8bff..a414139584 100644 --- a/developer/src/common/web/utils/src/index.ts +++ b/developer/src/common/web/utils/src/index.ts @@ -67,8 +67,8 @@ export { UrlSubpathCompilerCallback } from './utils/UrlSubpathCompilerCallback.j export { DeveloperUtilsMessages } from './developer-utils-messages.js'; export * as SourceFilenamePatterns from './source-filename-patterns.js'; -export { KeymanXMLType, KeymanXMLWriter, KeymanXMLReader, KeymanXMLMetadata, XML_FILENAME_SYMBOL } from './xml-utils.js'; -export { SymbolUtils, ObjectWithMetadata } from './symbol-utils.js'; +export { KeymanXMLType, KeymanXMLWriter, KeymanXMLReader, KeymanXMLMetadata, XML_FILENAME_SYMBOL, withOffset } from './xml-utils.js'; +export { SymbolUtils } from './symbol-utils.js'; export * as LineUtils from './line-utils.js'; export * as GitHubUrls from './github-urls.js'; export * as CloudUrls from './cloud-urls.js'; diff --git a/developer/src/common/web/utils/src/symbol-utils.ts b/developer/src/common/web/utils/src/symbol-utils.ts index 9404b404d8..a340344c9c 100644 --- a/developer/src/common/web/utils/src/symbol-utils.ts +++ b/developer/src/common/web/utils/src/symbol-utils.ts @@ -6,13 +6,7 @@ * Utilities for manipulating Symbol properties */ - -/** - * Any object with metadata, for example for line number errs. - * Defined as 'any' here to reduce noise on the client side. - * @see {@link KeymanXMLReader.getMetaData()} - */ -export type ObjectWithMetadata = any; +import { ObjectWithCompileContext } from '@keymanapp/common-types'; export class SymbolUtils { /** @@ -21,8 +15,8 @@ export class SymbolUtils { * @param from source for symbols * @returns the onto object */ - public static copySymbols(onto: T, from: ObjectWithMetadata): T { - const o = onto as ObjectWithMetadata; + public static copySymbols(onto: T, from: ObjectWithCompileContext): T { + const o = onto as ObjectWithCompileContext; for (const sym of Object.getOwnPropertySymbols(from)) { o[sym] = from[sym]; } diff --git a/developer/src/common/web/utils/src/types/ldml-keyboard/ldml-keyboard-xml-reader.ts b/developer/src/common/web/utils/src/types/ldml-keyboard/ldml-keyboard-xml-reader.ts index 0a31bc051b..f25b03582d 100644 --- a/developer/src/common/web/utils/src/types/ldml-keyboard/ldml-keyboard-xml-reader.ts +++ b/developer/src/common/web/utils/src/types/ldml-keyboard/ldml-keyboard-xml-reader.ts @@ -12,7 +12,7 @@ import { constants } from '@keymanapp/ldml-keyboard-constants'; import { LDMLKeyboardTestDataXMLSourceFile, LKTTest, LKTTests } from './ldml-keyboard-testdata-xml.js'; import boxXmlArray = util.boxXmlArray; import { LineFinderEventResolver } from '../../line-utils.js'; -import { XML_FILENAME_SYMBOL, KeymanXMLReader } from '../../xml-utils.js'; +import { XML_FILENAME_SYMBOL, KeymanXMLReader, findInstanceObject } from '../../xml-utils.js'; interface NameAndProps { '$'?: any; // content @@ -310,12 +310,13 @@ export class LDMLKeyboardXMLSourceFileReader implements EventResolver { public validate(source: LDMLKeyboardXMLSourceFile | LDMLKeyboardTestDataXMLSourceFile): boolean { if(!SchemaValidators.default.ldmlKeyboard3(source)) { for (const err of (SchemaValidators.default.ldmlKeyboard3).errors) { + const context = findInstanceObject(source, err?.instancePath?.split('/')); this.callbacks.reportMessage(DeveloperUtilsMessages.Error_SchemaValidationError({ instancePath: err.instancePath, keyword: err.keyword, message: err.message || 'Unknown AJV Error', // docs say 'message' is optional if 'messages:false' in options params: Object.entries(err.params || {}).sort().map(([k,v])=>`${k}="${v}"`).join(' '), - })); + }, context)); } return false; } diff --git a/developer/src/common/web/utils/src/xml-utils.ts b/developer/src/common/web/utils/src/xml-utils.ts index f83ca75dc7..9da6c00071 100644 --- a/developer/src/common/web/utils/src/xml-utils.ts +++ b/developer/src/common/web/utils/src/xml-utils.ts @@ -360,3 +360,40 @@ export class KeymanXMLWriter { } } +/** + * traverse an AJV instancePath and map to an object if possible + * @param source object tree root (contains the root object) + * @param path ajv split instancePath, such as '/keyboard3/layers/0'.split('/') + * @returns undefined if the path was not present, null if path went to something that wasn't an object, otherwise the compileContext object is returned. + */ +export function findInstanceObject(source: any, path: string[]) : any { + if(!path || !source || path.length == 0) { + return source; + } else if(path[0] == '') { + return findInstanceObject(source, path.slice(1)); + } else if(Array.isArray(source) || typeof source == 'object') { + const child = source[path[0]]; + if (child == undefined) return child; // nothing here + if (!child || typeof child == 'string') { + return source; // return the *parent* object if the child is empty (could be a property) + } + return findInstanceObject(child, path.slice(1)); + } else { + return null; + } +} + +/** + * Return an object simulating an XML object with an offset number + * For use in calling message functions + * @param c number for the offset setting + * @param x if set, this object will be used as the base object instead of {} + */ +export function withOffset(c: number, compileContext?: any) : KeymanXMLMetadata { + // set metadata on an empty object + const o = Object.assign({}, compileContext); + KeymanXMLReader.setMetaData(o, { + startIndex: c + }); + return o; +} diff --git a/developer/src/common/web/utils/test/ldml/ldml-keyboard-xml-reader.tests.ts b/developer/src/common/web/utils/test/ldml/ldml-keyboard-xml-reader.tests.ts index d09c1dfc41..286edd7958 100644 --- a/developer/src/common/web/utils/test/ldml/ldml-keyboard-xml-reader.tests.ts +++ b/developer/src/common/web/utils/test/ldml/ldml-keyboard-xml-reader.tests.ts @@ -8,7 +8,7 @@ import { testReaderCases } from '../helpers/reader-callback-test.js'; import CLDRScanToVkey = Constants.CLDRScanToVkey; import CLDRScanToKeyMap = Constants.CLDRScanToKeyMap; import USVirtualKeyCodes = Constants.USVirtualKeyCodes; -import { KeymanXMLReader, XML_FILENAME_SYMBOL } from '../../src/xml-utils.js'; +import { KeymanXMLReader, withOffset, XML_FILENAME_SYMBOL } from '../../src/xml-utils.js'; function pluckKeysFromKeybag(keys: LKKey[], ids: string[]) { return keys.filter(({id}) => ids.indexOf(id) !== -1); @@ -25,7 +25,7 @@ describe('ldml keyboard xml reader tests', function () { keyword: 'required', message: `must have required property 'info'`, params: 'missingProperty="info"', - })], + }, withOffset(39))], }, { subpath: 'invalid-conforms-to.xml', @@ -34,7 +34,7 @@ describe('ldml keyboard xml reader tests', function () { keyword: 'enum', message: `must be equal to one of the allowed values`, params: 'allowedValues="45,46"', // this has to be kept in sync with the DTD - })], + }, withOffset(39))], }, { subpath: 'import-minimal.xml', diff --git a/developer/src/common/web/utils/test/xml-utils.tests.ts b/developer/src/common/web/utils/test/xml-utils.tests.ts index 4b3df736b6..ec4f1ae80c 100644 --- a/developer/src/common/web/utils/test/xml-utils.tests.ts +++ b/developer/src/common/web/utils/test/xml-utils.tests.ts @@ -12,7 +12,7 @@ import { env } from 'node:process'; import { readFileSync, writeFileSync } from 'node:fs'; -import { KeymanXMLType, KeymanXMLReader, KeymanXMLWriter } from '../src/xml-utils.js'; +import { KeymanXMLType, KeymanXMLReader, KeymanXMLWriter, findInstanceObject } from '../src/xml-utils.js'; import { LineFinder } from '../src/line-utils.js'; import { makePathToFixture } from './helpers/index.js'; import { SymbolUtils } from '../src/symbol-utils.js'; @@ -198,5 +198,36 @@ describe(`XML Reader line number test`, () => { LineFinder.offsetToLineColumn( getMetaData(actual.keyboard3.transforms).startIndex, lines), { line: 8, column: 2 }); }); + describe('findInstanceObject test', () => { + const path0 = '/keyboard3/layers/0'; + const TARGET = Symbol("Looking for this!"); + it(`Should be able to parse ${path0}`, () => { + const o = { + keyboard3: { + layers: [ + TARGET, + ] + } + }; + assert.equal(findInstanceObject(o, path0.split('/')), TARGET); + }); + // path to property + const path1 = '/keyboard3/conformsTo'; + it(`Should be able to parse ${path1}`, () => { + const keyboard3 = { conformsTo: "1234"}; + const o = { + keyboard3, + }; + assert.equal(findInstanceObject(o, path1.split('/')), keyboard3); + }); + const path2 = '/keyboard3/bad/path'; + it(`Should be able to handle ${path2}`, () => { + const keyboard3 = { conformsTo: "1234"}; + const o = { + keyboard3, + }; + assert.equal(findInstanceObject(o, path2.split('/')), undefined); + }); + }); }); diff --git a/developer/src/ext/browse4folder/build.sh b/developer/src/ext/browse4folder/build.sh index d59e17ee71..cc86bd3427 100755 --- a/developer/src/ext/browse4folder/build.sh +++ b/developer/src/ext/browse4folder/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "browse4folder component" clean configure build test diff --git a/developer/src/ext/build.sh b/developer/src/ext/build.sh index 6dd7a576a1..fda45cef67 100755 --- a/developer/src/ext/build.sh +++ b/developer/src/ext/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/developer/src/ext/jedi/build.sh b/developer/src/ext/jedi/build.sh index d43ad7e1fb..226819d268 100755 --- a/developer/src/ext/jedi/build.sh +++ b/developer/src/ext/jedi/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/build-utils.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-basic.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE cd "$THIS_SCRIPT_PATH" diff --git a/developer/src/ext/mbcolor/build.sh b/developer/src/ext/mbcolor/build.sh index 7d1a2d174a..bbfc3a6fdc 100755 --- a/developer/src/ext/mbcolor/build.sh +++ b/developer/src/ext/mbcolor/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "mbcolor component" clean configure build test diff --git a/developer/src/ext/scfontcombobox/build.sh b/developer/src/ext/scfontcombobox/build.sh index 332f043875..72d0aec3fb 100755 --- a/developer/src/ext/scfontcombobox/build.sh +++ b/developer/src/ext/scfontcombobox/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "scfontcombobox component" clean configure build test diff --git a/developer/src/inst/build.sh b/developer/src/inst/build.sh index 8f5b722506..8b37048e1e 100755 --- a/developer/src/inst/build.sh +++ b/developer/src/inst/build.sh @@ -2,10 +2,10 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -source "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +source "$KEYMAN_ROOT/resources/build/utils.inc.sh" source "$KEYMAN_ROOT/resources/build/jq.inc.sh" builder_describe "Installation files for Keyman Developer" \ @@ -20,9 +20,9 @@ builder_describe_outputs \ builder_parse "$@" -. "$KEYMAN_ROOT/resources/build/win/environment.inc.sh" -. "$KEYMAN_ROOT/resources/build/win/wix.inc.sh" -. "$KEYMAN_ROOT/resources/build/win/zip.inc.sh" +. "${KEYMAN_ROOT}/resources/build/win/environment.inc.sh" +. "${KEYMAN_ROOT}/resources/build/win/wix.inc.sh" +. "${KEYMAN_ROOT}/resources/build/zip.inc.sh" # In dev environments, we'll hack the tier to alpha; CI sets this for us in real builds. if [[ -z ${KEYMAN_TIER+x} ]]; then @@ -51,7 +51,7 @@ function do_clean() { #------------------------------------------------------------------------------------------------------------------- function do_publish() { - verify-program-signatures + builder_if_release_build_level verify-program-signatures verify-node-installer-version "$KEYMAN_ROOT/common/windows/cef-checkout.sh" @@ -80,7 +80,7 @@ function do_publish() { copy-kmdev - verify-installer-signatures + builder_if_release_build_level verify-installer-signatures } function do_test() { @@ -233,7 +233,7 @@ function make-installer() { echo "Version=$KEYMAN_VERSION" >> setup.inf echo "MSIFileName=keymandeveloper.msi" >> setup.inf echo "Title=Keyman Developer ${KEYMAN_VERSION_WITH_TAG}" >>setup.inf - wzzip setup.zip keymandeveloper.msi setup.inf + add_zip_files setup.zip keymandeveloper.msi setup.inf cat "$DEVELOPER_PROGRAM/setup.exe" setup.zip > "keymandeveloper-$KEYMAN_VERSION.exe" wrap-signcode //d "Keyman Developer" "keymandeveloper-$KEYMAN_VERSION.exe" @@ -242,27 +242,24 @@ function make-installer() { # } -# TODO: rename this to keyman-developer-cli-$Version.zip -KMC_ZIP="$DEVELOPER_ROOT/release/$KEYMAN_VERSION/kmcomp-$KEYMAN_VERSION.zip" - function make-kmc-install-zip() { builder_heading make-kmc-install-zip copy-schemas - cd "$DEVELOPER_ROOT/bin" + ( + # shellcheck disable=2164 + cd "${DEVELOPER_ROOT}/bin" - wzzip -bd -bb0 "$KMC_ZIP" \ - kmconvert.exe \ - sentry.dll sentry.x64.dll \ - kmdecomp.exe \ - keyboard_info.schema.json \ - kmp.schema.json \ - keyman-touch-layout.spec.json keyman-touch-layout.clean.spec.json \ - xml/layoutbuilder/*.keyman-touch-layout \ - projects/* \ - server/* + # TODO: rename this to keyman-developer-cli-$Version.zip + local KMCOMP_ZIP="${DEVELOPER_ROOT}/release/${KEYMAN_VERSION}/kmcomp-${KEYMAN_VERSION}.zip" - cd "$THIS_SCRIPT_PATH" + add_zip_files "${KMCOMP_ZIP}" -q -r \ + kmconvert.exe \ + keyboard_info.schema.json \ + xml/layoutbuilder/*.keyman-touch-layout \ + projects/ \ + server/ + ) } # TODO: are these required? diff --git a/developer/src/kmanalyze/build.sh b/developer/src/kmanalyze/build.sh index 0e07044ffc..a72381557f 100755 --- a/developer/src/kmanalyze/build.sh +++ b/developer/src/kmanalyze/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Legacy keyboard source analysis tool" \ @@ -32,7 +32,7 @@ function do_build() { build_version.res vs_msbuild kmanalyze.vcxproj //t:Build "//p:Platform=Win32" cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM" - cp "$WIN32_TARGET_PATH/kmanalyze.pdb" "$DEVELOPER_DEBUGPATH" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmanalyze.pdb" "$DEVELOPER_DEBUGPATH" } function do_publish() { diff --git a/developer/src/kmc-analyze/build.sh b/developer/src/kmc-analyze/build.sh index ace83ffbbe..d54ed8927f 100755 --- a/developer/src/kmc-analyze/build.sh +++ b/developer/src/kmc-analyze/build.sh @@ -2,10 +2,12 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" +. "$KEYMAN_ROOT/resources/build/typescript.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_describe "Keyman Developer Compiler Analysis Tools" \ @@ -26,8 +28,8 @@ builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- builder_run_action clean rm -rf ./build/ -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build tsc --build builder_run_action api api-extractor run --local --verbose -builder_run_action test builder_do_typescript_tests 70 +builder_run_action test typescript_run_eslint_mocha_tests 70 builder_run_action publish builder_publish_npm diff --git a/developer/src/kmc-copy/build.sh b/developer/src/kmc-copy/build.sh index b681757be2..eb52215ec9 100755 --- a/developer/src/kmc-copy/build.sh +++ b/developer/src/kmc-copy/build.sh @@ -5,10 +5,12 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" +. "$KEYMAN_ROOT/resources/build/typescript.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_describe "Build Keyman kmc-copy module" \ @@ -32,13 +34,13 @@ builder_parse "$@" builder_run_action clean rm -rf ./build/ -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build tsc --build builder_run_action api api-extractor run --local --verbose # note: `export TEST_SAVE_ARTIFACTS=1` to save a copy of artifacts to temp path # note: `export TEST_SAVE_FIXTURES=1` to get a copy of cloud-based fixtures saved to online/ # TODO: -skip-full -builder_run_action test builder_do_typescript_tests 75 +builder_run_action test typescript_run_eslint_mocha_tests 75 builder_run_action publish builder_publish_npm diff --git a/developer/src/kmc-generate/build.sh b/developer/src/kmc-generate/build.sh index 7db46acd56..f1762f4ccf 100755 --- a/developer/src/kmc-generate/build.sh +++ b/developer/src/kmc-generate/build.sh @@ -5,10 +5,12 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" +. "$KEYMAN_ROOT/resources/build/typescript.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_describe "Build Keyman kmc-generate module" \ @@ -38,8 +40,8 @@ do_build() { } builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build do_build builder_run_action api api-extractor run --local --verbose -builder_run_action test builder_do_typescript_tests +builder_run_action test typescript_run_eslint_mocha_tests builder_run_action publish builder_publish_npm diff --git a/developer/src/kmc-keyboard-info/build.sh b/developer/src/kmc-keyboard-info/build.sh index 87902b6876..b22d973636 100755 --- a/developer/src/kmc-keyboard-info/build.sh +++ b/developer/src/kmc-keyboard-info/build.sh @@ -2,10 +2,12 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" +. "$KEYMAN_ROOT/resources/build/typescript.inc.sh" builder_describe "Build Keyman kmc keyboard-info Compiler module" \ "@/common/web/langtags" \ @@ -31,10 +33,10 @@ builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build tsc --build builder_run_action api api-extractor run --local --verbose -builder_run_action test builder_do_typescript_tests +builder_run_action test typescript_run_eslint_mocha_tests #------------------------------------------------------------------------------------------------------------------- diff --git a/developer/src/kmc-kmn/build.sh b/developer/src/kmc-kmn/build.sh index c6ed29b6f9..867f791bf9 100755 --- a/developer/src/kmc-kmn/build.sh +++ b/developer/src/kmc-kmn/build.sh @@ -5,10 +5,12 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" +. "$KEYMAN_ROOT/resources/build/typescript.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_describe "Keyman Developer Compiler Module for .kmn to .kmx" \ @@ -43,7 +45,7 @@ fi #------------------------------------------------------------------------------------------------------------------- if builder_start_action configure; then - verify_npm_setup + node_select_version_and_npm_ci builder_finish_action success configure fi @@ -63,7 +65,7 @@ function do_build() { function do_test() { copy_deps - builder_do_typescript_tests 80 + typescript_run_eslint_mocha_tests 80 } builder_run_action build do_build diff --git a/developer/src/kmc-kmn/src/compiler/compiler.ts b/developer/src/kmc-kmn/src/compiler/compiler.ts index 21985ad09a..403f721f90 100644 --- a/developer/src/kmc-kmn/src/compiler/compiler.ts +++ b/developer/src/kmc-kmn/src/compiler/compiler.ts @@ -6,7 +6,7 @@ TODO: implement additional interfaces: */ // TODO: rename wasm-host? -import { VisualKeyboard, KvkFileReader, LdmlKeyboardTypes, KeymanFileTypes, KvkFileWriter } from '@keymanapp/common-types'; +import { VisualKeyboard, KvkFileReader, LdmlKeyboardTypes, KeymanFileTypes, KvkFileWriter, ObjectWithCompileContext } from '@keymanapp/common-types'; import { CompilerCallbacks, CompilerEvent, CompilerOptions, KeymanCompiler, KeymanCompilerArtifacts, KeymanCompilerArtifactOptional, KeymanCompilerResult, KeymanCompilerArtifact, KvksFileReader, @@ -530,7 +530,7 @@ export class KmnCompiler implements KeymanCompiler, LdmlKeyboardTypes.UnicodeSet * @param rangeCount - number of ranges to allocate * @returns UnicodeSet accessor object, or null on failure */ - public parseUnicodeSet(pattern: string, rangeCount: number, x?: any) : LdmlKeyboardTypes.UnicodeSet | null { + public parseUnicodeSet(pattern: string, rangeCount: number, compileContext?: any) : LdmlKeyboardTypes.UnicodeSet | null { if(!this.verifyInitialized()) { /* c8 ignore next 2 */ // verifyInitialized will set a callback if needed @@ -563,7 +563,7 @@ export class KmnCompiler implements KeymanCompiler, LdmlKeyboardTypes.UnicodeSet // rc is negative: it's an error code. this.wasmExports.free(buf); // translate error code into callback - this.callbacks.reportMessage(getUnicodeSetError(rc, x)); + this.callbacks.reportMessage(getUnicodeSetError(rc, compileContext)); return null; } } @@ -571,7 +571,7 @@ export class KmnCompiler implements KeymanCompiler, LdmlKeyboardTypes.UnicodeSet /** * @internal */ - public sizeUnicodeSet(pattern: string, x?: any) : number { + public sizeUnicodeSet(pattern: string, compileContext?: any) : number { if(!this.verifyInitialized()) { /* c8 ignore next 2 */ return null; @@ -583,7 +583,7 @@ export class KmnCompiler implements KeymanCompiler, LdmlKeyboardTypes.UnicodeSet if (rc >= 0) { return rc; } else { - this.callbacks.reportMessage(getUnicodeSetError(rc, x)); + this.callbacks.reportMessage(getUnicodeSetError(rc, compileContext)); return -1; } } @@ -594,7 +594,7 @@ export class KmnCompiler implements KeymanCompiler, LdmlKeyboardTypes.UnicodeSet * @param rc parseUnicodeSet error code * @returns the compiler event */ -function getUnicodeSetError(rc: number, x?: any) : CompilerEvent { +function getUnicodeSetError(rc: number, compileContext?: ObjectWithCompileContext) : CompilerEvent { // from kmcmplib.h const KMCMP_ERROR_SYNTAX_ERR = -1; const KMCMP_ERROR_HAS_STRINGS = -2; @@ -602,16 +602,16 @@ function getUnicodeSetError(rc: number, x?: any) : CompilerEvent { const KMCMP_FATAL_OUT_OF_RANGE = -4; switch(rc) { case KMCMP_ERROR_SYNTAX_ERR: - return KmnCompilerMessages.Error_UnicodeSetSyntaxError(x); + return KmnCompilerMessages.Error_UnicodeSetSyntaxError(compileContext); case KMCMP_ERROR_HAS_STRINGS: - return KmnCompilerMessages.Error_UnicodeSetHasStrings(x); + return KmnCompilerMessages.Error_UnicodeSetHasStrings(compileContext); case KMCMP_ERROR_UNSUPPORTED_PROPERTY: - return KmnCompilerMessages.Error_UnicodeSetHasProperties(x); + return KmnCompilerMessages.Error_UnicodeSetHasProperties(compileContext); case KMCMP_FATAL_OUT_OF_RANGE: - return KmnCompilerMessages.Fatal_UnicodeSetOutOfRange(x); + return KmnCompilerMessages.Fatal_UnicodeSetOutOfRange(compileContext); default: /* c8 ignore next */ - return CompilerError.setFromMetadata(KmnCompilerMessages.Fatal_UnexpectedException({e: `Unexpected UnicodeSet error code ${rc}`}), x); + return CompilerError.setFromMetadata(KmnCompilerMessages.Fatal_UnexpectedException({e: `Unexpected UnicodeSet error code ${rc}`}), compileContext); } } diff --git a/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts b/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts index 3274a62ca1..46f573f253 100644 --- a/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts +++ b/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts @@ -1,7 +1,7 @@ import { KeyAddress } from "../kmw-compiler/validate-layout-file.js"; import { kmnfile } from "../kmw-compiler/compiler-globals.js"; -import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec as m, CompilerMessageObjectSpec as mx, CompilerMessageDef as def, CompilerMessageSpecWithException, KeymanUrls, ObjectWithMetadata } from "@keymanapp/developer-utils"; - +import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec as m, CompilerMessageObjectSpec as mx, CompilerMessageDef as def, CompilerMessageSpecWithException, KeymanUrls } from "@keymanapp/developer-utils"; +import { ObjectWithCompileContext } from "@keymanapp/common-types"; const Namespace = CompilerErrorNamespace.KmnCompiler; const SevInfo = CompilerErrorSeverity.Info | Namespace; const SevHint = CompilerErrorSeverity.Hint | Namespace; @@ -107,8 +107,8 @@ export class KmnCompilerMessages { ); static FATAL_UnicodeSetOutOfRange = SevFatal | 0x904; - static Fatal_UnicodeSetOutOfRange = (x?: ObjectWithMetadata) => mx( - this.FATAL_UnicodeSetOutOfRange, x, + static Fatal_UnicodeSetOutOfRange = (compileContext?: ObjectWithCompileContext) => mx( + this.FATAL_UnicodeSetOutOfRange, compileContext, `UnicodeSet buffer was too small`, `Raised when caller to UnicodeSet functions provides an invalid buffer. If you experience this error, it should be reported to the Keyman team for @@ -118,8 +118,8 @@ export class KmnCompilerMessages { // TODO: rename the following functions to Error_UsetHasStrings etc static ERROR_UnicodeSetHasStrings = SevError | 0x905; - static Error_UnicodeSetHasStrings = (x?: ObjectWithMetadata) => mx( - this.ERROR_UnicodeSetHasStrings, x, + static Error_UnicodeSetHasStrings = (compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_UnicodeSetHasStrings, compileContext, `uset contains strings, not allowed`, `The provided uset uses multi-character strings, (\`{}\` notation, e.g. \`[żġħ{ie}{għ}]\`. ). Although full UnicodeSets support strings, LDML @@ -130,8 +130,8 @@ export class KmnCompilerMessages { ); static ERROR_UnicodeSetHasProperties = SevError | 0x906; - static Error_UnicodeSetHasProperties = (x?: ObjectWithMetadata) => mx( - this.ERROR_UnicodeSetHasProperties, x, + static Error_UnicodeSetHasProperties = (compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_UnicodeSetHasProperties, compileContext, `uset contains properties, not allowed`, `The provided uset uses property notation (\`\\p{…}\` or \`[:…:]\`). LDML keyboards do not support Unicode properties in usets, because that would @@ -142,8 +142,8 @@ export class KmnCompilerMessages { ); static ERROR_UnicodeSetSyntaxError = SevError | 0x907; - static Error_UnicodeSetSyntaxError = (x?: ObjectWithMetadata) => mx( - this.ERROR_UnicodeSetSyntaxError, x, + static Error_UnicodeSetSyntaxError = (compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_UnicodeSetSyntaxError, compileContext, `uset had a Syntax Error while parsing`, `The provided uset has a syntax error and could not be parsed. Verify the format of the uset against the specification. diff --git a/developer/src/kmc-ldml/build.sh b/developer/src/kmc-ldml/build.sh index 689291b247..73763be4d5 100755 --- a/developer/src/kmc-ldml/build.sh +++ b/developer/src/kmc-ldml/build.sh @@ -5,10 +5,12 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" +. "$KEYMAN_ROOT/resources/build/typescript.inc.sh" builder_describe "Keyman kmc Keyboard Compiler module" \ "@/common/web/keyman-version" \ @@ -38,7 +40,7 @@ function do_clean() { } function do_configure() { - verify_npm_setup + node_select_version_and_npm_ci do_build_abnf } @@ -87,7 +89,7 @@ builder_run_action configure do_configure builder_run_action build do_build builder_run_action build-fixtures do_build_fixtures builder_run_action api api-extractor run --local --verbose -builder_run_action test builder_do_typescript_tests 90 +builder_run_action test typescript_run_eslint_mocha_tests 90 #------------------------------------------------------------------------------------------------------------------- diff --git a/developer/src/kmc-ldml/src/compiler/disp.ts b/developer/src/kmc-ldml/src/compiler/disp.ts index fd15c8ace7..b38ef72531 100644 --- a/developer/src/kmc-ldml/src/compiler/disp.ts +++ b/developer/src/kmc-ldml/src/compiler/disp.ts @@ -62,7 +62,7 @@ export class DispCompiler extends SectionCompiler { const result = new Disp(); // displayOptions - result.baseCharacter = sections.strs.allocString(this.keyboard3.displays?.displayOptions?.baseCharacter, { unescape: true, x: this.keyboard3?.displays?.displayOptions }); + result.baseCharacter = sections.strs.allocString(this.keyboard3.displays?.displayOptions?.baseCharacter, { unescape: true, compileContext:this.keyboard3?.displays?.displayOptions }); // displays result.disps = this.keyboard3.displays?.display.map(display => ({ @@ -70,13 +70,13 @@ export class DispCompiler extends SectionCompiler { stringVariables: true, markers: true, unescape: true, - x: display, + compileContext: display, }, sections), - id: sections.strs.allocString(display.keyId, { x: display }), // not escaped, not substituted + id: sections.strs.allocString(display.keyId, { compileContext: display }), // not escaped, not substituted display: sections.strs.allocString(display.display, { stringVariables: true, unescape: true, - x: display, + compileContext: display, }, sections), })) || []; // TODO-LDML: need coverage for the [] diff --git a/developer/src/kmc-ldml/src/compiler/empty-compiler.ts b/developer/src/kmc-ldml/src/compiler/empty-compiler.ts index 79015dca6b..a88e861f58 100644 --- a/developer/src/kmc-ldml/src/compiler/empty-compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/empty-compiler.ts @@ -1,7 +1,8 @@ import { SectionIdent, constants } from '@keymanapp/ldml-keyboard-constants'; import { SectionCompiler } from "./section-compiler.js"; import { util, KMXPlus, LdmlKeyboardTypes } from "@keymanapp/common-types"; -import { CompilerCallbacks, LDMLKeyboard, ObjectWithMetadata } from "@keymanapp/developer-utils"; +import { CompilerCallbacks, LDMLKeyboard } from "@keymanapp/developer-utils"; +import { ObjectWithCompileContext } from "@keymanapp/common-types"; import { VarsCompiler } from './vars.js'; import { LdmlCompilerMessages } from './ldml-compiler-messages.js'; @@ -34,7 +35,7 @@ export class StrsCompiler extends EmptyCompiler { const strs = section; /** attempt to find a context object for the string */ - function findContextForString(s: string): ObjectWithMetadata { + function findContextForString(s: string): ObjectWithCompileContext { // try exact match for(const str of strs.strings) { if (str.value == s) return str.context; diff --git a/developer/src/kmc-ldml/src/compiler/keys.ts b/developer/src/kmc-ldml/src/compiler/keys.ts index 3c890a5067..8656e7fbf2 100644 --- a/developer/src/kmc-ldml/src/compiler/keys.ts +++ b/developer/src/kmc-ldml/src/compiler/keys.ts @@ -324,16 +324,16 @@ export class KeysCompiler extends SectionCompiler { // allocate the in-memory const flicks: KeysFlicks = new KeysFlicks( - sections.strs.allocString(flickId, { x: flick }) + sections.strs.allocString(flickId, { compileContext: flick }) ); // add data from each segment for (const flickSegment of flick.flickSegment) { const { keyId, directions } = flickSegment; - const keyIdStr = sections.strs.allocString(keyId, { x: flickSegment }); + const keyIdStr = sections.strs.allocString(keyId, { compileContext: flickSegment }); const directionsList: ListItem = sections.list.allocListFromSpaces( directions, - { x: flickSegment }, + { compileContext: flickSegment }, sections); flicks.flicks.push({ directions: directionsList, @@ -370,20 +370,20 @@ export class KeysCompiler extends SectionCompiler { if (!!gap) { flags |= constants.keys_key_flags_gap; } - const id = sections.strs.allocString(key.id, { x: key }); + const id = sections.strs.allocString(key.id, { compileContext: key }); const longPress: ListItem = sections.list.allocListFromSpaces( - longPressKeyIds, { x: key }, + longPressKeyIds, { compileContext: key }, sections); const longPressDefault = sections.strs.allocString(longPressDefaultKeyId, - { x: key }, + { compileContext: key }, sections); const multiTap: ListItem = sections.list.allocListFromSpaces( multiTapKeyIds, - { x: key }, + { compileContext: key }, sections); - const keySwitch = sections.strs.allocString(layerId, { x: key }); // 'switch' is a reserved word + const keySwitch = sections.strs.allocString(layerId, { compileContext: key }); // 'switch' is a reserved word const toRaw = output; @@ -396,7 +396,7 @@ export class KeysCompiler extends SectionCompiler { unescape: true, singleOk: true, nfd: true, - x: key, + compileContext: key, }, sections); if (!to.isOneChar) { diff --git a/developer/src/kmc-ldml/src/compiler/layr.ts b/developer/src/kmc-ldml/src/compiler/layr.ts index 9dff22a5bf..baaf9282b5 100644 --- a/developer/src/kmc-ldml/src/compiler/layr.ts +++ b/developer/src/kmc-ldml/src/compiler/layr.ts @@ -60,7 +60,7 @@ export class LayrCompiler extends SectionCompiler { if (totalLayerCount === 0) { // TODO-LDML: does not validate touch layers yet // no layers seen anywhere valid = false; - this.callbacks.reportMessage(LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement(this.keyboard3?.layers[0])); + this.callbacks.reportMessage(LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement(this.keyboard3)); } return valid; } @@ -69,13 +69,13 @@ export class LayrCompiler extends SectionCompiler { const sect = new Layr(); sect.lists = this.keyboard3.layers.map((layers) => { - const hardware = sections.strs.allocString(layers.formId, {x:layers}); + const hardware = sections.strs.allocString(layers.formId, {compileContext: layers}); // Already validated in validate const layerEntries = []; for (const layer of layers.layer) { const rows = layer.row.map((row) => { const erow: LayrRow = { - keys: row.keys.trim().split(/[ \t]+/).map((id) => sections.strs.allocString(id, { x: row })), + keys: row.keys.trim().split(/[ \t]+/).map((id) => sections.strs.allocString(id, { compileContext: row })), }; // include linenumber info for row return SectionCompiler.copySymbols(erow, row); @@ -84,7 +84,7 @@ export class LayrCompiler extends SectionCompiler { // push a layer entry for each modifier set for (const mod of mods) { layerEntries.push({ - id: sections.strs.allocString(layer.id, {x:layer}), + id: sections.strs.allocString(layer.id, {compileContext: layer}), mod, rows, }); diff --git a/developer/src/kmc-ldml/src/compiler/ldml-compiler-messages.ts b/developer/src/kmc-ldml/src/compiler/ldml-compiler-messages.ts index f76e0d3b3a..1f879f475b 100644 --- a/developer/src/kmc-ldml/src/compiler/ldml-compiler-messages.ts +++ b/developer/src/kmc-ldml/src/compiler/ldml-compiler-messages.ts @@ -1,4 +1,5 @@ -import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageObjectSpec as mx, CompilerMessageSpec as m, CompilerMessageDef as def, ObjectWithMetadata } from '@keymanapp/developer-utils'; +import { ObjectWithCompileContext } from "@keymanapp/common-types"; +import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageObjectSpec as mx, CompilerMessageSpec as m, CompilerMessageDef as def } from '@keymanapp/developer-utils'; // const SevInfo = CompilerErrorSeverity.Info | CompilerErrorNamespace.LdmlKeyboardCompiler; const SevHint = CompilerErrorSeverity.Hint | CompilerErrorNamespace.LdmlKeyboardCompiler; const SevWarn = CompilerErrorSeverity.Warn | CompilerErrorNamespace.LdmlKeyboardCompiler; @@ -13,38 +14,38 @@ const SevErrorTransform = SevError | 0xF00; */ export class LdmlCompilerMessages { static HINT_NormalizationDisabled = SevHint | 0x0001; - static Hint_NormalizationDisabled = (x?: ObjectWithMetadata) => mx( - this.HINT_NormalizationDisabled, x, + static Hint_NormalizationDisabled = (compileContext?: ObjectWithCompileContext) => mx( + this.HINT_NormalizationDisabled, compileContext, `normalization=disabled is not recommended.`, ); static ERROR_InvalidLocale = SevError | 0x0002; - static Error_InvalidLocale = (o:{tag: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_InvalidLocale, x, + static Error_InvalidLocale = (o:{tag: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_InvalidLocale, compileContext, `Invalid BCP 47 locale form '${def(o.tag)}'`, ); static ERROR_HardwareLayerHasTooManyRows = SevError | 0x0003; - static Error_HardwareLayerHasTooManyRows = (x?: ObjectWithMetadata) => mx( - this.ERROR_HardwareLayerHasTooManyRows, x, + static Error_HardwareLayerHasTooManyRows = (compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_HardwareLayerHasTooManyRows, compileContext, `'hardware' layer has too many rows`, ); static ERROR_RowOnHardwareLayerHasTooManyKeys = SevError | 0x0004; - static Error_RowOnHardwareLayerHasTooManyKeys = (o: { row: number, hardware: string, modifiers: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_RowOnHardwareLayerHasTooManyKeys, x, + static Error_RowOnHardwareLayerHasTooManyKeys = (o: { row: number, hardware: string, modifiers: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_RowOnHardwareLayerHasTooManyKeys, compileContext, `Row #${def(o.row)} on 'hardware' ${def(o.hardware)} layer for modifier ${def(o.modifiers)} has too many keys`, ); static ERROR_KeyNotFoundInKeyBag = SevError | 0x0005; - static Error_KeyNotFoundInKeyBag = (o: { keyId: string, col: number, row: number, layer: string, form: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_KeyNotFoundInKeyBag, x, + static Error_KeyNotFoundInKeyBag = (o: { keyId: string, col: number, row: number, layer: string, form: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_KeyNotFoundInKeyBag, compileContext, `Key '${def(o.keyId)}' in position #${def(o.col)} on row #${def(o.row)} of layer ${def(o.layer)}, form '${def(o.form)}' not found in key bag`, ); static HINT_OneOrMoreRepeatedLocales = SevHint | 0x0006; - static Hint_OneOrMoreRepeatedLocales = (x?: ObjectWithMetadata) => mx( - this.HINT_OneOrMoreRepeatedLocales, x, + static Hint_OneOrMoreRepeatedLocales = (compileContext?: ObjectWithCompileContext) => mx( + this.HINT_OneOrMoreRepeatedLocales, compileContext, `After minimization, one or more locales is repeated and has been removed`, ); @@ -54,38 +55,38 @@ export class LdmlCompilerMessages { m(this.ERROR_InvalidFile, `The source file has an invalid structure: ${def(o.errorText)}`); static HINT_LocaleIsNotMinimalAndClean = SevHint | 0x0008; - static Hint_LocaleIsNotMinimalAndClean = (o:{sourceLocale: string, locale: string}, x?: ObjectWithMetadata) => mx( - this.HINT_LocaleIsNotMinimalAndClean, x, + static Hint_LocaleIsNotMinimalAndClean = (o:{sourceLocale: string, locale: string}, compileContext?: ObjectWithCompileContext) => mx( + this.HINT_LocaleIsNotMinimalAndClean, compileContext, `Locale '${def(o.sourceLocale)}' is not minimal or correctly formatted and should be '${def(o.locale)}'`, ); static ERROR_InvalidScanCode = SevError | 0x0009; - static Error_InvalidScanCode = (o: { codes: string, id: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_InvalidScanCode, x, + static Error_InvalidScanCode = (o: { codes: string, id: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_InvalidScanCode, compileContext, `Form '${def(o.id)}' has invalid/unknown scancodes '${def(o.codes)}'`, ); static WARN_CustomForm = SevWarn | 0x000A; - static Warn_CustomForm = (o: { id: string }, x?: ObjectWithMetadata) => mx( - this.WARN_CustomForm, x, + static Warn_CustomForm = (o: { id: string }, compileContext?: ObjectWithCompileContext) => mx( + this.WARN_CustomForm, compileContext, `Custom
element. Key layout may not be as expected.`, ); static ERROR_GestureKeyNotFoundInKeyBag = SevError | 0x000B; - static Error_GestureKeyNotFoundInKeyBag = (o:{keyId: string, parentKeyId: string, attribute: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_GestureKeyNotFoundInKeyBag, x, + static Error_GestureKeyNotFoundInKeyBag = (o:{keyId: string, parentKeyId: string, attribute: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_GestureKeyNotFoundInKeyBag, compileContext, `Key '${def(o.keyId)}' not found in key bag, referenced from other '${def(o.parentKeyId)}' in ${def(o.attribute)}`, ); static HINT_NoDisplayForMarker = SevHint | 0x000C; - static Hint_NoDisplayForMarker = (o: { id: string }, x?: ObjectWithMetadata) => mx( - this.HINT_NoDisplayForMarker, x, + static Hint_NoDisplayForMarker = (o: { id: string }, compileContext?: ObjectWithCompileContext) => mx( + this.HINT_NoDisplayForMarker, compileContext, `Key element with id "${def(o.id)}" has only marker output, but there is no matching display element by output or keyId. Keycap may be blank.`, ); static ERROR_InvalidVersion = SevError | 0x000D; - static Error_InvalidVersion = (o: { version: string; }, x?: ObjectWithMetadata) => mx( - this.ERROR_InvalidVersion, x, + static Error_InvalidVersion = (o: { version: string; }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_InvalidVersion, compileContext, `Version number '${def(o.version)}' must be a semantic version format string with 'major.minor.patch' components.`, `The version number in the LDML keyboard file must be a [semantic version](https://semver.org) (semver) string. This string has a format of three @@ -98,186 +99,186 @@ export class LdmlCompilerMessages { ); static ERROR_MustBeAtLeastOneLayerElement = SevError | 0x000E; - static Error_MustBeAtLeastOneLayerElement = (x?: ObjectWithMetadata) => mx( - this.ERROR_MustBeAtLeastOneLayerElement, x, + static Error_MustBeAtLeastOneLayerElement = (compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_MustBeAtLeastOneLayerElement, compileContext, `The source file must contain at least one layer element.`, ); static HINT_NoDisplayForSwitch = SevHint | 0x000F; - static Hint_NoDisplayForSwitch = (o: { id: string }, x?: ObjectWithMetadata) => mx( - this.HINT_NoDisplayForSwitch, x, + static Hint_NoDisplayForSwitch = (o: { id: string }, compileContext?: ObjectWithCompileContext) => mx( + this.HINT_NoDisplayForSwitch, compileContext, `Key element with id "${def(o.id)}" is a layer switch key, but there is no matching display element by keyId. Keycap may be blank.`, ); static ERROR_DisplayIsRepeated = SevError | 0x0010; - static Error_DisplayIsRepeated = (o:{display?: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_DisplayIsRepeated, x, + static Error_DisplayIsRepeated = (o:{display?: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_DisplayIsRepeated, compileContext, `display display='${def(o.display)}' refers to the same keyId or output as another entry.`, ); static ERROR_KeyMissingToGapOrSwitch = SevError | 0x0011; - static Error_KeyMissingToGapOrSwitch = (o:{keyId: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_KeyMissingToGapOrSwitch, x, + static Error_KeyMissingToGapOrSwitch = (o:{keyId: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_KeyMissingToGapOrSwitch, compileContext, `key id='${def(o.keyId)}' must have either output=, gap=, or layerId=.`, ); static ERROR_ExcessHardware = SevError | 0x0012; - static Error_ExcessHardware = (o:{formId: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_ExcessHardware, x, + static Error_ExcessHardware = (o:{formId: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_ExcessHardware, compileContext, `layers formId=${def(o.formId)}: Can only have one non-'touch' element`, ); static ERROR_InvalidHardware = SevError | 0x0013; - static Error_InvalidHardware = (o: { formId: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_InvalidHardware, x, + static Error_InvalidHardware = (o: { formId: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_InvalidHardware, compileContext, `layers has invalid value formId=${def(o.formId)}`, ); static ERROR_InvalidModifier = SevError | 0x0014; - static Error_InvalidModifier = (o:{modifiers: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_InvalidModifier, x, + static Error_InvalidModifier = (o:{modifiers: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_InvalidModifier, compileContext, `layer has invalid modifiers='${def(o.modifiers)}'`, ); static ERROR_MissingFlicks = SevError | 0x0015; - static Error_MissingFlicks = (o: {id: string, flickId: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_MissingFlicks, x, + static Error_MissingFlicks = (o: {id: string, flickId: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_MissingFlicks, compileContext, `key id=${def(o.id)} refers to missing flickId=${def(o.flickId)}`, ); static ERROR_DuplicateVariable = SevError | 0x0016; - static Error_DuplicateVariable = (o:{id: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_DuplicateVariable, x, + static Error_DuplicateVariable = (o:{id: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_DuplicateVariable, compileContext, `duplicate variable: id=${def(o.id)}`, ); // Not hit due to XML parsing static ERROR_InvalidTransformsType = SevError | 0x0018; - static Error_InvalidTransformsType = (o:{type: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_InvalidTransformsType, x, + static Error_InvalidTransformsType = (o:{type: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_InvalidTransformsType, compileContext, `Invalid transforms type: '${def(o.type)}'`, ); static ERROR_DuplicateTransformsType = SevError | 0x0019; - static Error_DuplicateTransformsType = (o:{type: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_DuplicateTransformsType, x, + static Error_DuplicateTransformsType = (o:{type: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_DuplicateTransformsType, compileContext, `Duplicate transforms type: '${def(o.type)}'`, ); static ERROR_MixedTransformGroup = SevError | 0x001A; - static Error_MixedTransformGroup = (x?: ObjectWithMetadata) => mx( - this.ERROR_MixedTransformGroup, x, + static Error_MixedTransformGroup = (compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_MixedTransformGroup, compileContext, `transformGroup cannot contain both reorder and transform elements`, ); static ERROR_EmptyTransformGroup = SevError | 0x001B; - static Error_EmptyTransformGroup = (x?: ObjectWithMetadata) => mx( - this.ERROR_EmptyTransformGroup, x, + static Error_EmptyTransformGroup = (compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_EmptyTransformGroup, compileContext, `transformGroup must have either reorder or transform elements`, ); static ERROR_MissingStringVariable = SevError | 0x001C; - static Error_MissingStringVariable = (o:{id: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_MissingStringVariable, x, + static Error_MissingStringVariable = (o:{id: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_MissingStringVariable, compileContext, `Reference to undefined string variable: \${${def(o.id)}}`, ); static ERROR_MissingSetVariable = SevError | 0x001D; - static Error_MissingSetVariable = (o:{id: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_MissingSetVariable, x, + static Error_MissingSetVariable = (o:{id: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_MissingSetVariable, compileContext, `Reference to undefined set variable: \$[${def(o.id)}]`, ); static ERROR_MissingUnicodeSetVariable = SevError | 0x001E; - static Error_MissingUnicodeSetVariable = (o:{id: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_MissingUnicodeSetVariable, x, + static Error_MissingUnicodeSetVariable = (o:{id: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_MissingUnicodeSetVariable, compileContext, `Reference to undefined UnicodeSet variable: \$[${def(o.id)}]`, ); static ERROR_NeedSpacesBetweenSetVariables = SevError | 0x001F; - static Error_NeedSpacesBetweenSetVariables = (o:{item: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_NeedSpacesBetweenSetVariables, x, + static Error_NeedSpacesBetweenSetVariables = (o:{item: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_NeedSpacesBetweenSetVariables, compileContext, `Need spaces between set variables: ${def(o.item)}`, ); static ERROR_CantReferenceSetFromUnicodeSet = SevError | 0x0020; - static Error_CantReferenceSetFromUnicodeSet = (o:{id: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_CantReferenceSetFromUnicodeSet, x, + static Error_CantReferenceSetFromUnicodeSet = (o:{id: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_CantReferenceSetFromUnicodeSet, compileContext, `Illegal use of set variable from within UnicodeSet: \$[${def(o.id)}]` ); static ERROR_MissingMarkers = SevError | 0x0021; - static Error_MissingMarkers = (o: { ids: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_MissingMarkers, x, + static Error_MissingMarkers = (o: { ids: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_MissingMarkers, compileContext, `Markers used for matching but not defined: ${def(o.ids)}` ); static ERROR_DisplayNeedsToOrId = SevError | 0x0022; - static Error_DisplayNeedsToOrId = (o:{display?: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_DisplayNeedsToOrId, x, + static Error_DisplayNeedsToOrId = (o:{display?: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_DisplayNeedsToOrId, compileContext, `display display='${def(o.display)}' needs output= or keyId=, but not both`, ); static HINT_PUACharacters = SevHint | 0x0023; - static Hint_PUACharacters = (o: { count: number, lowestCh: string }, x?: ObjectWithMetadata) => mx( - this.HINT_PUACharacters, x, + static Hint_PUACharacters = (o: { count: number, lowestCh: string }, compileContext?: ObjectWithCompileContext) => mx( + this.HINT_PUACharacters, compileContext, `File contains ${def(o.count)} PUA character(s), including ${def(o.lowestCh)}`, ); static WARN_UnassignedCharacters = SevWarn | 0x0024; - static Warn_UnassignedCharacters = (o: { count: number, lowestCh: string }, x?: ObjectWithMetadata) => mx( - this.WARN_UnassignedCharacters, x, + static Warn_UnassignedCharacters = (o: { count: number, lowestCh: string }, compileContext?: ObjectWithCompileContext) => mx( + this.WARN_UnassignedCharacters, compileContext, `File contains ${def(o.count)} unassigned character(s), including ${def(o.lowestCh)}`, ); static ERROR_IllegalCharacters = SevError | 0x0025; - static Error_IllegalCharacters = (o: { count: number, lowestCh: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_IllegalCharacters, x, + static Error_IllegalCharacters = (o: { count: number, lowestCh: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_IllegalCharacters, compileContext, `File contains ${def(o.count)} illegal character(s), including ${def(o.lowestCh) }`, ); static HINT_CharClassImplicitDenorm = SevHint | 0x0026; - static Hint_CharClassImplicitDenorm = (o: { lowestCh: number }, x?: ObjectWithMetadata) => mx( - this.HINT_CharClassImplicitDenorm, x, + static Hint_CharClassImplicitDenorm = (o: { lowestCh: number }, compileContext?: ObjectWithCompileContext) => mx( + this.HINT_CharClassImplicitDenorm, compileContext, `File has character classes which span non-NFD character(s), including ${def(o.lowestCh)}. These will not match any text.`, ); static WARN_CharClassExplicitDenorm = SevWarn | 0x0027; - static Warn_CharClassExplicitDenorm = (o: { lowestCh: number }, x?: ObjectWithMetadata) => mx( - this.WARN_CharClassExplicitDenorm, x, + static Warn_CharClassExplicitDenorm = (o: { lowestCh: number }, compileContext?: ObjectWithCompileContext) => mx( + this.WARN_CharClassExplicitDenorm, compileContext, `File has character classes which include non-NFD characters(s), including ${def(o.lowestCh)}. These will not match any text.`, ); // Available: 0x0028 static ERROR_InvalidVariableIdentifier = SevError | 0x0029; - static Error_InvalidVariableIdentifier = (o: { id: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_InvalidVariableIdentifier, x, + static Error_InvalidVariableIdentifier = (o: { id: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_InvalidVariableIdentifier, compileContext, `Invalid variable identifier "${def(o.id)}". Identifiers must be between 1 and 32 characters, and can use A-Z, a-z, 0-9, and _.`, ); static ERROR_InvalidMarkerIdentifier = SevError | 0x002A; - static Error_InvalidMarkerIdentifier = (o: { id: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_InvalidMarkerIdentifier, x, + static Error_InvalidMarkerIdentifier = (o: { id: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_InvalidMarkerIdentifier, compileContext, `Invalid marker identifier "\m{${def(o.id)}}". Identifiers must be between 1 and 32 characters, and can use A-Z, a-z, 0-9, and _.`, ); static WARN_StringDenorm = SevWarn | 0x002B; - static Warn_StringDenorm = (o: { s: string }, x?: ObjectWithMetadata) => mx( - this.WARN_StringDenorm, x, + static Warn_StringDenorm = (o: { s: string }, compileContext?: ObjectWithCompileContext) => mx( + this.WARN_StringDenorm, compileContext, `File contains string "${def(o.s)}" that is neither NFC nor NFD.`, ); static ERROR_DuplicateLayerWidth = SevError | 0x002C; - static Error_DuplicateLayerWidth = (o: { minDeviceWidth: number }, x?: ObjectWithMetadata) => mx( - this.ERROR_DuplicateLayerWidth, x, + static Error_DuplicateLayerWidth = (o: { minDeviceWidth: number }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_DuplicateLayerWidth, compileContext, `Two or more layers have minDeviceWidth=${def(o.minDeviceWidth)}`, `Touch layers must have distinct widths.` ); static ERROR_InvalidLayerWidth = SevError | 0x002D; - static Error_InvalidLayerWidth = (o: { minDeviceWidth: number }, x?: ObjectWithMetadata) => mx( - this.ERROR_InvalidLayerWidth, x, + static Error_InvalidLayerWidth = (o: { minDeviceWidth: number }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_InvalidLayerWidth, compileContext, `Invalid Layers minDeviceWidth=${def(o.minDeviceWidth)}`, `Width must be between 1-999 (millimeters), inclusive.` // sync with layr_max_minDeviceWidth / layr_max_maxDeviceWidth (from spec) ); @@ -285,8 +286,8 @@ export class LdmlCompilerMessages { // Available: 0x02E-0x2F static ERROR_InvalidQuadEscape = SevError | 0x0030; - static Error_InvalidQuadEscape = (o: { cp: string, recommended: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_InvalidQuadEscape, x, + static Error_InvalidQuadEscape = (o: { cp: string, recommended: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_InvalidQuadEscape, compileContext, `Invalid escape "${def(o.cp)}"`, `**Hint**: Use "${def(o.recommended)}"`, ); @@ -296,8 +297,8 @@ export class LdmlCompilerMessages { // This is a bit of a catch-all and represents messages bubbling up from the underlying regex engine static ERROR_UnparseableTransformFrom = SevErrorTransform | 0x00; - static Error_UnparseableTransformFrom = (o: { from: string, message: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_UnparseableTransformFrom, x, + static Error_UnparseableTransformFrom = (o: { from: string, message: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_UnparseableTransformFrom, compileContext, `Invalid transform from="${def(o.from)}": "${def(o.message)}"`, ); @@ -306,8 +307,8 @@ export class LdmlCompilerMessages { //------------------------------------------------------------------------------| static ERROR_IllegalTransformDollarsign = SevErrorTransform | 0x01; - static Error_IllegalTransformDollarsign = (o: { from: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_IllegalTransformDollarsign, x, + static Error_IllegalTransformDollarsign = (o: { from: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_IllegalTransformDollarsign, compileContext, `Invalid transform from="${def(o.from)}": Unescaped dollar-sign ($) is not valid transform syntax.`, ` **Hint**: Use \`\\$\` to match a literal dollar-sign. If this precedes a variable name, the variable name may not be valid (A-Z, a-z, 0-9, _, 32 @@ -315,36 +316,36 @@ export class LdmlCompilerMessages { `); static ERROR_TransformFromMatchesNothing = SevErrorTransform | 0x02; - static Error_TransformFromMatchesNothing = (o: { from: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_TransformFromMatchesNothing, x, + static Error_TransformFromMatchesNothing = (o: { from: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_TransformFromMatchesNothing, compileContext, `Invalid transfom from="${def(o.from)}": Matches an empty string.` ); static ERROR_IllegalTransformPlus = SevErrorTransform | 0x03; - static Error_IllegalTransformPlus = (o: { from: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_IllegalTransformPlus, x, + static Error_IllegalTransformPlus = (o: { from: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_IllegalTransformPlus, compileContext, `Invalid transform from="${def(o.from)}": Unescaped plus (+) is not valid transform syntax.`, ` **Hint**: Use \`\\+\` to match a literal plus. `); static ERROR_IllegalTransformAsterisk = SevErrorTransform | 0x04; - static Error_IllegalTransformAsterisk = (o: { from: string }, x?: ObjectWithMetadata) =>mx( - this.ERROR_IllegalTransformAsterisk, x, + static Error_IllegalTransformAsterisk = (o: { from: string }, compileContext?: ObjectWithCompileContext) =>mx( + this.ERROR_IllegalTransformAsterisk, compileContext, `Invalid transform from="${def(o.from)}": Unescaped asterisk (*) is not valid transform syntax.`, ` **Hint**: Use \`\\*\` to match a literal asterisk. `); static ERROR_IllegalTransformToUset = SevErrorTransform | 0x05; - static Error_IllegalTransformToUset = (o: { to: string }, x?: ObjectWithMetadata) => mx( - this.ERROR_IllegalTransformToUset, x, + static Error_IllegalTransformToUset = (o: { to: string }, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_IllegalTransformToUset, compileContext, `Invalid transform to="${def(o.to)}": Set variable (\\$[…]) cannot be used in 'to=' unless part of a map.`, ` **Hint**: If a map was meant, must use the form \`\`. `); static ERROR_UnparseableTransformTo = SevErrorTransform | 0x06; - static Error_UnparseableTransformTo = (o: {to: string, message: string}, x?: ObjectWithMetadata) => mx( - this.ERROR_UnparseableTransformTo, x, + static Error_UnparseableTransformTo = (o: {to: string, message: string}, compileContext?: ObjectWithCompileContext) => mx( + this.ERROR_UnparseableTransformTo, compileContext, `Invalid transform to="${def(o.to)}": "${def(o.message)}"`, ); } diff --git a/developer/src/kmc-ldml/src/compiler/loca.ts b/developer/src/kmc-ldml/src/compiler/loca.ts index 3cc222e613..d624dbaff1 100644 --- a/developer/src/kmc-ldml/src/compiler/loca.ts +++ b/developer/src/kmc-ldml/src/compiler/loca.ts @@ -1,6 +1,7 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; import { KMXPlus } from '@keymanapp/common-types'; -import { LDMLKeyboard, ObjectWithMetadata } from '@keymanapp/developer-utils'; +import { LDMLKeyboard } from '@keymanapp/developer-utils'; +import { ObjectWithCompileContext } from "@keymanapp/common-types"; import { SectionCompiler } from "./section-compiler.js"; import { LdmlCompilerMessages } from "./ldml-compiler-messages.js"; @@ -28,7 +29,7 @@ export class LocaCompiler extends SectionCompiler { * @param loc locale tag * @returns context metadata or null */ - private contextForLocale(loc: string) : ObjectWithMetadata { + private contextForLocale(loc: string) : ObjectWithCompileContext { if (loc == this.keyboard3.locale) return this.keyboard3; return this.keyboard3?.locales; } @@ -71,7 +72,7 @@ export class LocaCompiler extends SectionCompiler { // yet include `getCanonicalLocales` but node 16 does include it so we can // safely use it. Also well supported in modern browsers. const canonicalLocales = (Intl as any).getCanonicalLocales(locales) as string[]; - result.locales = canonicalLocales.map(locale => sections.strs.allocString(locale, {x: this.contextForLocale(locale)})); + result.locales = canonicalLocales.map(locale => sections.strs.allocString(locale, {compileContext: this.contextForLocale(locale)})); if(result.locales.length < locales.length) { this.callbacks.reportMessage(LdmlCompilerMessages.Hint_OneOrMoreRepeatedLocales(this.keyboard3?.locales)); diff --git a/developer/src/kmc-ldml/src/compiler/meta.ts b/developer/src/kmc-ldml/src/compiler/meta.ts index e6cd6371ee..eeb7f4f46c 100644 --- a/developer/src/kmc-ldml/src/compiler/meta.ts +++ b/developer/src/kmc-ldml/src/compiler/meta.ts @@ -54,17 +54,17 @@ export class MetaCompiler extends SectionCompiler { public compile(sections: DependencySections): Meta { const result = new Meta(); result.author = sections.strs.allocString(this.keyboard3.info?.author, - {x: this.keyboard3.info}); + {compileContext: this.keyboard3.info}); result.conform = sections.strs.allocString(this.keyboard3.conformsTo, - {x: this.keyboard3}); + {compileContext: this.keyboard3}); result.layout = sections.strs.allocString(this.keyboard3.info?.layout, - {x: this.keyboard3.info}); + {compileContext: this.keyboard3.info}); result.name = sections.strs.allocString(this.keyboard3.info?.name, - {x: this.keyboard3.info}); + {compileContext: this.keyboard3.info}); result.indicator = sections.strs.allocString(this.keyboard3.info?.indicator, - {x: this.keyboard3.info}); + {compileContext: this.keyboard3.info}); result.version = sections.strs.allocString(this.keyboard3.version?.number ?? "0.0.0", - {x: this.keyboard3.version}); + {compileContext: this.keyboard3.version}); result.settings = (this.keyboard3.settings?.normalization == "disabled" ? KeyboardSettings.normalizationDisabled : 0); return result; diff --git a/developer/src/kmc-ldml/src/compiler/substitution-tracker.ts b/developer/src/kmc-ldml/src/compiler/substitution-tracker.ts index 61daefa0cf..9282655a64 100644 --- a/developer/src/kmc-ldml/src/compiler/substitution-tracker.ts +++ b/developer/src/kmc-ldml/src/compiler/substitution-tracker.ts @@ -1,5 +1,5 @@ import { LdmlKeyboardTypes } from "@keymanapp/common-types"; -import { ObjectWithMetadata } from "@keymanapp/developer-utils"; +import { ObjectWithCompileContext } from '@keymanapp/common-types'; /** * Verb for SubstitutionTracker.add() @@ -15,7 +15,7 @@ export enum SubstitutionUse { variable, } -type SubstitutionSet = Map; +type SubstitutionSet = Map; /** Tracks usage of markers */ export class SubstitutionTracker { @@ -29,10 +29,10 @@ export class SubstitutionTracker { all: SubstitutionSet; constructor() { - this.emitted = new Map(); - this.consumed = new Map(); - this.matched = new Map(); - this.all = new Map(); + this.emitted = new Map(); + this.consumed = new Map(); + this.matched = new Map(); + this.all = new Map(); } /** @@ -40,33 +40,33 @@ export class SubstitutionTracker { * @param verb what kind of use we are adding * @param markers list of substitutions to add */ - add(verb: SubstitutionUse, markers: string[], x?: ObjectWithMetadata) { + add(verb: SubstitutionUse, markers: string[], compileContext?: ObjectWithCompileContext) { if (!markers.length) { return; // skip if empty } - x = x || {}; // need at least an empty object + compileContext = compileContext || {}; // need at least an empty object if (verb == SubstitutionUse.emit) { markers.forEach((m) => { - this.emitted.set(m, x); - this.all.set(m,x); + this.emitted.set(m, compileContext); + this.all.set(m, compileContext); }); } else if (verb == SubstitutionUse.consume) { markers.forEach((m) => { - this.consumed.set(m,x); - this.all.set(m,x); + this.consumed.set(m, compileContext); + this.all.set(m, compileContext); }); } else if (verb == SubstitutionUse.match) { markers.forEach((m) => { - this.matched.set(m,x); - this.all.set(m,x); + this.matched.set(m, compileContext); + this.all.set(m, compileContext); }); } else if (verb == SubstitutionUse.variable) { markers.forEach((m) => { // we don't know, so add it to all three - this.matched.set(m,x); - this.emitted.set(m,x); - this.consumed.set(m,x); - this.all.set(m,x); + this.matched.set(m, compileContext); + this.emitted.set(m, compileContext); + this.consumed.set(m, compileContext); + this.all.set(m, compileContext); }); /* c8 skip next 3 */ } else { @@ -77,22 +77,22 @@ export class SubstitutionTracker { /** rollup of several substitution types */ export class Substitutions { - addSetAndStringSubtitution(verb: SubstitutionUse, str?: string, x?: ObjectWithMetadata) { - this.set.add(verb, LdmlKeyboardTypes.VariableParser.allSetReferences(str), x); - this.addStringAndMarkerSubstitution(verb, str, x); + addSetAndStringSubtitution(verb: SubstitutionUse, str?: string, compileContext?: ObjectWithCompileContext) { + this.set.add(verb, LdmlKeyboardTypes.VariableParser.allSetReferences(str), compileContext); + this.addStringAndMarkerSubstitution(verb, str, compileContext); } /** add a string that can have string var substitutions or markers */ - addStringAndMarkerSubstitution(verb: SubstitutionUse, str?: string, x?: ObjectWithMetadata) { - this.addMarkers(verb, str, x); - this.addStringSubstitution(verb, str, x); + addStringAndMarkerSubstitution(verb: SubstitutionUse, str?: string, compileContext?: ObjectWithCompileContext) { + this.addMarkers(verb, str, compileContext); + this.addStringSubstitution(verb, str, compileContext); } - addStringSubstitution(verb: SubstitutionUse, str?: string, x?: ObjectWithMetadata) { - this.string.add(verb, LdmlKeyboardTypes.VariableParser.allStringReferences(str), x); + addStringSubstitution(verb: SubstitutionUse, str?: string, compileContext?: ObjectWithCompileContext) { + this.string.add(verb, LdmlKeyboardTypes.VariableParser.allStringReferences(str), compileContext); } /** add a string that's just markers */ - addMarkers(verb: SubstitutionUse, str?: string, x?: ObjectWithMetadata) { - this.markers.add(verb, LdmlKeyboardTypes.MarkerParser.allReferences(str), x); - LdmlKeyboardTypes.MarkerParser.allBrokenReferences(str).forEach(m => this.badMarkers.set(m, x)); + addMarkers(verb: SubstitutionUse, str?: string, compileContext?: ObjectWithCompileContext) { + this.markers.add(verb, LdmlKeyboardTypes.MarkerParser.allReferences(str), compileContext); + LdmlKeyboardTypes.MarkerParser.allBrokenReferences(str).forEach(m => this.badMarkers.set(m, compileContext)); } // all valid markers markers: SubstitutionTracker; @@ -110,6 +110,6 @@ export class Substitutions { this.set = new SubstitutionTracker(); this.string = new SubstitutionTracker(); this.uset = new SubstitutionTracker(); - this.badMarkers = new Map(); + this.badMarkers = new Map(); } } diff --git a/developer/src/kmc-ldml/src/compiler/tran.ts b/developer/src/kmc-ldml/src/compiler/tran.ts index 1cc9bd0289..0a3a781f6f 100644 --- a/developer/src/kmc-ldml/src/compiler/tran.ts +++ b/developer/src/kmc-ldml/src/compiler/tran.ts @@ -1,6 +1,7 @@ import { constants, SectionIdent } from "@keymanapp/ldml-keyboard-constants"; import { KMXPlus, LdmlKeyboardTypes, util } from '@keymanapp/common-types'; -import { CompilerCallbacks, LDMLKeyboard, ObjectWithMetadata } from "@keymanapp/developer-utils"; +import { CompilerCallbacks, LDMLKeyboard } from "@keymanapp/developer-utils"; +import { ObjectWithCompileContext } from "@keymanapp/common-types"; import { SectionCompiler } from "./section-compiler.js"; import Bksp = KMXPlus.Bksp; @@ -142,7 +143,7 @@ export abstract class TransformCompiler DTD - this.callbacks.reportMessage(LdmlCompilerMessages.Error_InvalidVariableIdentifier({id}, x)); + this.callbacks.reportMessage(LdmlCompilerMessages.Error_InvalidVariableIdentifier({id}, compileContext)); return false; } return true; @@ -65,11 +66,11 @@ export class VarsCompiler extends SectionCompiler { * @param id id * @param x context */ - function addId(id: string, x: LDMLKeyboard.Variable): void { + function addId(id: string, compileContext: LDMLKeyboard.Variable): void { if (allIds.has(id)) { dups.add(id); } else { - allIds.set(id, x); + allIds.set(id, compileContext); } } @@ -97,7 +98,7 @@ export class VarsCompiler extends SectionCompiler { // Sets for (const e of variables.set) { const { id, value } = e; - if(!this.validateIdentifier(id)) { + if(!this.validateIdentifier(id, e)) { valid = false; continue; } @@ -124,7 +125,7 @@ export class VarsCompiler extends SectionCompiler { // UnicodeSets for (const e of variables.uset) { const { id, value } = e; - if(!this.validateIdentifier(id)) { + if(!this.validateIdentifier(id, e)) { valid = false; continue; } @@ -270,7 +271,7 @@ export class VarsCompiler extends SectionCompiler { const allMarkers : string[] = Array.from(mt.all.keys()).filter(m => m !== LdmlKeyboardTypes.MarkerParser.ANY_MARKER_ID).sort(); result.markers = sections.list.allocList(allMarkers, { // pass the first object in the listr - x: mt.all.get(allMarkers[0]||'') + compileContext: mt.all.get(allMarkers[0]||'') // however, this is the string with the marker *id*. So, from the StrsCompiler // point of view, the value of this context would be if the marker ID had, say, // invalid Unicode in it for ERROR_IllegalCharacters - but, that wouldn't be a @@ -291,7 +292,7 @@ export class VarsCompiler extends SectionCompiler { let { value } = e; // fix any variables value = result.substituteStrings(value, sections); - result.strings.push(new StringVarItem(id, value, sections)); + result.strings.push(new StringVarItem(id, value, sections, e)); } addSet(result: Vars, e: LDMLKeyboard.LKSet, sections: DependencySections): void { const { id } = e; @@ -306,14 +307,14 @@ export class VarsCompiler extends SectionCompiler { // this is not 'forMatch', all variables are to be assumed as string literals, not regex // content. const cookedItems: string[] = rawItems.map(v => result.substituteMarkerString(v, false)); - result.sets.push(new SetVarItem(id, cookedItems, sections)); + result.sets.push(new SetVarItem(id, cookedItems, sections, e)); } addUnicodeSet(result: Vars, e: LDMLKeyboard.LKUSet, sections: DependencySections): void { const { id } = e; let { value } = e; value = result.substituteStrings(value, sections); value = result.substituteUnicodeSets(value, sections); - result.usets.push(new UnicodeSetItem(id, value, sections, sections.usetparser)); + result.usets.push(new UnicodeSetItem(id, value, sections, sections.usetparser, e)); } // routines for using/substituting variables have been moved to the Vars class and its // properties diff --git a/developer/src/kmc-ldml/test/disp.tests.ts b/developer/src/kmc-ldml/test/disp.tests.ts index 3bad95e339..8b17b35c55 100644 --- a/developer/src/kmc-ldml/test/disp.tests.ts +++ b/developer/src/kmc-ldml/test/disp.tests.ts @@ -1,11 +1,12 @@ import 'mocha'; import {assert} from 'chai'; import { DispCompiler } from '../src/compiler/disp.js'; -import { compilerTestCallbacks, loadSectionFixture, testCompilationCases, withOffset } from './helpers/index.js'; +import { compilerTestCallbacks, loadSectionFixture, testCompilationCases } from './helpers/index.js'; import { KMXPlus } from '@keymanapp/common-types'; import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js'; import Disp = KMXPlus.Disp; +import { withOffset } from '@keymanapp/developer-utils'; describe('disp', function () { this.slow(500); // 0.5 sec -- json schema validation takes a while diff --git a/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer.xml index 211961d616..4525675fe9 100644 --- a/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer.xml +++ b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer.xml @@ -8,7 +8,7 @@ - + diff --git a/developer/src/kmc-ldml/test/helpers/compareXml.ts b/developer/src/kmc-ldml/test/helpers/compareXml.ts index 057986bdb2..4082a40ce2 100644 --- a/developer/src/kmc-ldml/test/helpers/compareXml.ts +++ b/developer/src/kmc-ldml/test/helpers/compareXml.ts @@ -1,6 +1,7 @@ import {assert} from 'chai'; import {readFileSync} from 'node:fs'; import { KeymanXMLReader, SymbolUtils } from "@keymanapp/developer-utils"; +import { ObjectWithCompileContext } from '@keymanapp/common-types'; /** * @@ -8,10 +9,10 @@ import { KeymanXMLReader, SymbolUtils } from "@keymanapp/developer-utils"; * @param expect path to expected XML * @param mutator optional function that will be applied to the parsed object */ -export function compareXml(actual : string, expect: string, mutator?: (input: any) => any) { +export function compareXml(actual : string, expect: string, mutator?: (input: ObjectWithCompileContext) => ObjectWithCompileContext) { if (!mutator) { // no-op - mutator = (x: any) => x; + mutator = (compileContext: ObjectWithCompileContext) => compileContext; } const reader = new KeymanXMLReader('keyboard3'); diff --git a/developer/src/kmc-ldml/test/helpers/index.ts b/developer/src/kmc-ldml/test/helpers/index.ts index 967287cf87..be6216f9c1 100644 --- a/developer/src/kmc-ldml/test/helpers/index.ts +++ b/developer/src/kmc-ldml/test/helpers/index.ts @@ -8,7 +8,7 @@ import * as path from 'path'; import { fileURLToPath } from 'url'; import { SectionCompiler, SectionCompilerNew } from '../../src/compiler/section-compiler.js'; import { util, KMXPlus, LdmlKeyboardTypes } from '@keymanapp/common-types'; -import { CompilerEvent, compilerEventFormat, CompilerCallbacks, LDMLKeyboardXMLSourceFileReader, LDMLKeyboardTestDataXMLSourceFile, LDMLKeyboard, KeymanXMLMetadata, KeymanXMLReader, CompilerError } from "@keymanapp/developer-utils"; +import { CompilerEvent, compilerEventFormat, CompilerCallbacks, LDMLKeyboardXMLSourceFileReader, LDMLKeyboardTestDataXMLSourceFile, LDMLKeyboard, CompilerError } from "@keymanapp/developer-utils"; import { LdmlKeyboardCompiler } from '../../src/main.js'; // make sure main.js compiles import { assert } from 'chai'; import { KMXPlusMetadataCompiler } from '../../src/compiler/metadata-compiler.js'; @@ -358,17 +358,3 @@ export function hex_str(s?: string) : string { return [...s].map(ch => dontEscape.test(ch) ? ch : util.escapeRegexChar(ch)).join(''); } -/** - * Return an object simulating an XML object with an offset number - * For use in calling message functions - * @param c number for the offset setting - * @param x if set, this object will be used as the base object instead of {} - */ -export function withOffset(c: number, x?: any) : KeymanXMLMetadata { - // set metadata on an empty object - const o = Object.assign({}, x); - KeymanXMLReader.setMetaData(o, { - startIndex: c - }); - return o; -} diff --git a/developer/src/kmc-ldml/test/keys.tests.ts b/developer/src/kmc-ldml/test/keys.tests.ts index 3c3c842810..97171cf6fc 100644 --- a/developer/src/kmc-ldml/test/keys.tests.ts +++ b/developer/src/kmc-ldml/test/keys.tests.ts @@ -1,7 +1,7 @@ import 'mocha'; import { assert } from 'chai'; import { KeysCompiler } from '../src/compiler/keys.js'; -import { assertCodePoints, compilerTestCallbacks, loadSectionFixture, testCompilationCases, withOffset } from './helpers/index.js'; +import { assertCodePoints, compilerTestCallbacks, loadSectionFixture, testCompilationCases } from './helpers/index.js'; import { KMXPlus, Constants, LdmlKeyboardTypes } from '@keymanapp/common-types'; import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js'; import { constants } from '@keymanapp/ldml-keyboard-constants'; @@ -9,7 +9,7 @@ import { MetaCompiler } from '../src/compiler/meta.js'; const keysDependencies = [ ...BASIC_DEPENDENCIES, MetaCompiler ]; import Keys = KMXPlus.Keys; import { BASIC_DEPENDENCIES } from '../src/compiler/empty-compiler.js'; -import { LDMLKeyboard } from '@keymanapp/developer-utils'; +import { LDMLKeyboard, withOffset } from '@keymanapp/developer-utils'; const K = Constants.USVirtualKeyCodes; describe('keys', function () { diff --git a/developer/src/kmc-ldml/test/layr.tests.ts b/developer/src/kmc-ldml/test/layr.tests.ts index dc0334c0b1..64e4a3d373 100644 --- a/developer/src/kmc-ldml/test/layr.tests.ts +++ b/developer/src/kmc-ldml/test/layr.tests.ts @@ -2,12 +2,13 @@ import 'mocha'; import { assert } from 'chai'; import { LayrCompiler } from '../src/compiler/layr.js'; import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js'; -import { compilerTestCallbacks, testCompilationCases, withOffset } from './helpers/index.js'; +import { compilerTestCallbacks, testCompilationCases } from './helpers/index.js'; import { KMXPlus } from '@keymanapp/common-types'; import { constants } from '@keymanapp/ldml-keyboard-constants'; import Layr = KMXPlus.Layr; import LayrRow = KMXPlus.LayrRow; +import { withOffset } from '@keymanapp/developer-utils'; function allKeysOk(row : LayrRow, str : string, msg? : string) { const split = str.split(' '); @@ -107,13 +108,13 @@ describe('layr', function () { { // missing layer element subpath: 'sections/layr/invalid-missing-layer.xml', - errors: [LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement(withOffset(258))], + errors: [LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement(withOffset(40))], retainOffsetInMessages: true, }, { // missing layer element subpath: 'sections/layr/invalid-missing-layer2.xml', - errors: [LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement()], + errors: [LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement(withOffset(40))], retainOffsetInMessages: true, }, { diff --git a/developer/src/kmc-ldml/test/loca.tests.ts b/developer/src/kmc-ldml/test/loca.tests.ts index 287ee62b00..ffe4f656c7 100644 --- a/developer/src/kmc-ldml/test/loca.tests.ts +++ b/developer/src/kmc-ldml/test/loca.tests.ts @@ -1,11 +1,12 @@ import 'mocha'; import { assert } from 'chai'; import { LocaCompiler } from '../src/compiler/loca.js'; -import { compilerTestCallbacks, loadSectionFixture, withOffset } from './helpers/index.js'; +import { compilerTestCallbacks, loadSectionFixture } from './helpers/index.js'; import { KMXPlus } from '@keymanapp/common-types'; import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js'; import Loca = KMXPlus.Loca; +import { withOffset } from '@keymanapp/developer-utils'; describe('loca', function () { this.slow(500); // 0.5 sec -- json schema validation takes a while diff --git a/developer/src/kmc-ldml/test/messages.tests.ts b/developer/src/kmc-ldml/test/messages.tests.ts index a7d06006cb..60df66fb09 100644 --- a/developer/src/kmc-ldml/test/messages.tests.ts +++ b/developer/src/kmc-ldml/test/messages.tests.ts @@ -2,8 +2,7 @@ import 'mocha'; import {expect} from 'chai'; import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js'; import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; -import { CompilerErrorNamespace, CompilerEvent } from '@keymanapp/developer-utils'; -import { withOffset } from './helpers/index.js'; +import { CompilerErrorNamespace, CompilerEvent, withOffset } from '@keymanapp/developer-utils'; describe('LdmlCompilerMessages', function () { it('should have a valid LdmlCompilerMessages object', function() { diff --git a/developer/src/kmc-ldml/test/meta.tests.ts b/developer/src/kmc-ldml/test/meta.tests.ts index c744b3385e..beeed50915 100644 --- a/developer/src/kmc-ldml/test/meta.tests.ts +++ b/developer/src/kmc-ldml/test/meta.tests.ts @@ -1,13 +1,13 @@ import 'mocha'; import {assert} from 'chai'; import { MetaCompiler } from '../src/compiler/meta.js'; -import { compilerTestCallbacks, loadSectionFixture, withOffset } from './helpers/index.js'; +import { compilerTestCallbacks, loadSectionFixture } from './helpers/index.js'; import { KMXPlus } from '@keymanapp/common-types'; import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js'; import KeyboardSettings = KMXPlus.KeyboardSettings; import Meta = KMXPlus.Meta; -import { LDMLKeyboard } from '@keymanapp/developer-utils'; +import { LDMLKeyboard, withOffset } from '@keymanapp/developer-utils'; describe('meta', function () { this.slow(500); // 0.5 sec -- json schema validation takes a while diff --git a/developer/src/kmc-model-info/build.sh b/developer/src/kmc-model-info/build.sh index 766c9be413..816a195e00 100755 --- a/developer/src/kmc-model-info/build.sh +++ b/developer/src/kmc-model-info/build.sh @@ -2,10 +2,12 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" +. "$KEYMAN_ROOT/resources/build/typescript.inc.sh" builder_describe "Build Keyman kmc Lexical Model model-info Compiler module" \ "@/common/web/types" \ @@ -28,10 +30,10 @@ builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build tsc --build builder_run_action api api-extractor run --local --verbose -builder_run_action test builder_do_typescript_tests 55 +builder_run_action test typescript_run_eslint_mocha_tests 55 #------------------------------------------------------------------------------------------------------------------- diff --git a/developer/src/kmc-model/build.sh b/developer/src/kmc-model/build.sh index 9e7166f8b7..f2eef55cfc 100755 --- a/developer/src/kmc-model/build.sh +++ b/developer/src/kmc-model/build.sh @@ -2,10 +2,12 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" +. "$KEYMAN_ROOT/resources/build/typescript.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_describe "Keyman kmc Lexical Model Compiler module" \ @@ -36,9 +38,9 @@ function do_build() { } builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build do_build builder_run_action api api-extractor run --local --verbose -builder_run_action test builder_do_typescript_tests +builder_run_action test typescript_run_eslint_mocha_tests builder_run_action publish builder_publish_npm diff --git a/developer/src/kmc-package/build.sh b/developer/src/kmc-package/build.sh index 81f8b928c7..4b9379881a 100755 --- a/developer/src/kmc-package/build.sh +++ b/developer/src/kmc-package/build.sh @@ -5,10 +5,12 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" +. "$KEYMAN_ROOT/resources/build/typescript.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_describe "Build Keyman kmc Package Compiler module" \ @@ -33,8 +35,8 @@ builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build tsc --build builder_run_action api api-extractor run --local --verbose -builder_run_action test builder_do_typescript_tests +builder_run_action test typescript_run_eslint_mocha_tests builder_run_action publish builder_publish_npm diff --git a/developer/src/kmc/build.sh b/developer/src/kmc/build.sh index 15e8e18302..1644540651 100755 --- a/developer/src/kmc/build.sh +++ b/developer/src/kmc/build.sh @@ -5,11 +5,13 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" +. "$KEYMAN_ROOT/resources/build/typescript.inc.sh" . "$KEYMAN_ROOT/developer/src/packages.inc.sh" builder_describe "Build Keyman Keyboard Compiler kmc" \ @@ -54,7 +56,7 @@ function do_build() { #------------------------------------------------------------------------------------------------------------------- function do_test() { - builder_do_typescript_tests 50 + typescript_run_eslint_mocha_tests 50 ./test/command-line-tests.sh test } @@ -84,11 +86,14 @@ function do_bundle() { mkdir -p build/dist node build-bundler.js - sentry-cli sourcemaps inject \ - --org keyman \ - --project keyman-developer \ - --release "$KEYMAN_VERSION_GIT_TAG" \ - build/dist/ "${SOURCEMAP_PATHS[@]}" + if builder_is_ci_build && builder_is_ci_build_level_release; then + # Only inject sourcemaps for release builds + sentry-cli sourcemaps inject \ + --org keyman \ + --project keyman-developer \ + --release "$KEYMAN_VERSION_GIT_TAG" \ + build/dist/ "${SOURCEMAP_PATHS[@]}" + fi # Manually copy over kmcmplib module cp ../kmc-kmn/build/src/import/kmcmplib/wasm-host.wasm build/dist/ @@ -104,7 +109,7 @@ function do_bundle() { #------------------------------------------------------------------------------------------------------------------- builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo -builder_run_action configure verify_npm_setup +builder_run_action configure node_select_version_and_npm_ci builder_run_action build do_build builder_run_action test do_test builder_run_action api do_api diff --git a/developer/src/kmc/test/command-line-tests.sh b/developer/src/kmc/test/command-line-tests.sh index 74447afd41..41652bcdfd 100755 --- a/developer/src/kmc/test/command-line-tests.sh +++ b/developer/src/kmc/test/command-line-tests.sh @@ -5,11 +5,11 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE # . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" -# . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +# . "$KEYMAN_ROOT/resources/build/utils.inc.sh" # . "$KEYMAN_ROOT/developer/src/packages.inc.sh" builder_describe "Test Keyman Keyboard Compiler kmc command line e2e" \ diff --git a/developer/src/kmcmplib/build.sh b/developer/src/kmcmplib/build.sh index 77036cc990..952c0732b8 100755 --- a/developer/src/kmcmplib/build.sh +++ b/developer/src/kmcmplib/build.sh @@ -3,10 +3,10 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" . "$THIS_SCRIPT_PATH/checkout-keyboards.inc.sh" . "$THIS_SCRIPT_PATH/commands.inc.sh" diff --git a/developer/src/kmcmplib/commands.inc.sh b/developer/src/kmcmplib/commands.inc.sh index 280343853c..e2a5679ed6 100644 --- a/developer/src/kmcmplib/commands.inc.sh +++ b/developer/src/kmcmplib/commands.inc.sh @@ -126,7 +126,7 @@ do_command() { # ---------------------------------------------------------------------------- build_meson_cross_file_for_wasm() { - if [ $BUILDER_OS == win ]; then + if builder_is_windows; then local R=$(cygpath -w $(echo $EMSCRIPTEN_BASE) | sed 's_\\_\\\\_g') else local R=$(echo $EMSCRIPTEN_BASE | sed 's_/_\\/_g') @@ -140,4 +140,4 @@ should_do_full_test() { fi return 1 -} \ No newline at end of file +} diff --git a/developer/src/kmcmplib/meson.build b/developer/src/kmcmplib/meson.build index 0493c504f4..935f8496f1 100644 --- a/developer/src/kmcmplib/meson.build +++ b/developer/src/kmcmplib/meson.build @@ -8,7 +8,7 @@ project('kmcmplib', 'cpp', 'c', version: files('../../../VERSION.md'), license: 'MIT', default_options : ['buildtype=release', - 'cpp_std=c++14', + 'cpp_std=c++17', 'b_vscrt=static_from_buildtype', 'default_library=static', 'debug=true']) diff --git a/developer/src/kmcmplib/src/Compiler.cpp b/developer/src/kmcmplib/src/Compiler.cpp index 3b53453dce..ab6296609b 100644 --- a/developer/src/kmcmplib/src/Compiler.cpp +++ b/developer/src/kmcmplib/src/Compiler.cpp @@ -3863,17 +3863,68 @@ bool hasPreamble(std::u16string result) { return result.size() > 0 && result[0] == 0xFEFF; } +bool isValidUtf8(KMX_BYTE* str, int sz) { + int i = 0; + while (i < sz) { + int remaining = sz - i; + if (str[i] <= 0x7F) { + // ASCII + i++; + } else if ((str[i] & 0xE0) == 0xC0) { + // 2-byte sequence + if (remaining < 2 || + (str[i + 1] & 0xC0) != 0x80 || + str[i] == 0xC0 || // C0 and C1 are illegal values + str[i] == 0xC1) { + return false; + } + i += 2; + } else if ((str[i] & 0xF0) == 0xE0) { + // 3-byte sequence + if (remaining < 3 || + (str[i+1] & 0xC0) != 0x80 || + (str[i+2] & 0xC0) != 0x80) { + return false; + } + if (str[i] == 0xE0 && (str[i + 1] & 0xE0) == 0x80) { + return false; + } + if (str[i] == 0xED && (str[i + 1] & 0xE0) == 0xA0) { + return false; + } + i += 3; + } else if ((str[i] & 0xF8) == 0xF0) { + // 4-byte sequence + if (remaining < 4 || + (str[i+1] & 0xC0) != 0x80 || + (str[i+2] & 0xC0) != 0x80 || + (str[i+3] & 0xC0) != 0x80) { + return false; + } + if (str[i] == 0xF0 && (str[i + 1] & 0xF0) == 0x80) { + return false; + } + if (str[i] > 0xF4 || (str[i] == 0xF4 && str[i + 1] > 0x8F)) { + return false; + } + i += 4; + } else { + return false; + } + } + return true; +} + bool UTF16TempFromUTF8(KMX_BYTE* infile, int sz, KMX_BYTE** tempfile, int *sz16) { if(sz == 0) { return FALSE; } std::u16string result; - - try { - std::wstring_convert, char16_t> converter; - result = converter.from_bytes((char*)infile, (char*)infile+sz); - } catch(std::range_error& e) { + if (isValidUtf8(infile, sz)) { + std::string infileStr(reinterpret_cast(infile), sz); + result = u16string_from_string(infileStr); + } else { ReportCompilerMessage(KmnCompilerMessages::HINT_NonUnicodeFile); result.resize(sz); for(int i = 0; i < sz; i++) { diff --git a/developer/src/kmcmplib/tests/get-test-source.sh b/developer/src/kmcmplib/tests/get-test-source.sh index fe6cded2f1..af647ee311 100755 --- a/developer/src/kmcmplib/tests/get-test-source.sh +++ b/developer/src/kmcmplib/tests/get-test-source.sh @@ -3,7 +3,7 @@ # Finds all keyboards in the keyboards repo (passed in $1) that are valid source # keyboards (i.e. ///source/.kmn) # -# Called from meson.build, so this script does not use build-utils.sh. Do not run this +# Called from meson.build, so this script does not use builder-basic.inc.sh. Do not run this # script directly. # set -eu diff --git a/developer/src/kmcmplib/tests/gtest-compiler.tests.cpp b/developer/src/kmcmplib/tests/gtest-compiler.tests.cpp index 268d1d1f70..757767762f 100644 --- a/developer/src/kmcmplib/tests/gtest-compiler.tests.cpp +++ b/developer/src/kmcmplib/tests/gtest-compiler.tests.cpp @@ -24,6 +24,8 @@ KMX_DWORD GetRHS(PFILE_KEYBOARD fk, PKMX_WCHAR p, PKMX_WCHAR buf, int bufsize, i bool isIntegerWstring(PKMX_WCHAR p); bool hasPreamble(std::u16string result); KMX_DWORD ProcessKeyLineImpl(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnicode, PKMX_WCHAR pklIn, PKMX_WCHAR pklKey, PKMX_WCHAR pklOut); +extern bool UTF16TempFromUTF8(KMX_BYTE* infile, int sz, KMX_BYTE** tempfile, int* sz16); +extern bool isValidUtf8(KMX_BYTE* str, int sz); namespace kmcmp { extern int nErrors; @@ -2101,5 +2103,156 @@ TEST_F(CompilerTest, hasPreamble_test) { EXPECT_FALSE(hasPreamble(u"a\uFEFF")); } -// bool UTF16TempFromUTF8(KMX_BYTE* infile, int sz, KMX_BYTE** tempfile, int *sz16) // PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, KMX_DWORD dwSystemID) + +// Helper to convert UTF-16 buffer to std::u16string +static std::u16string buffer_to_u16string(const KMX_BYTE* buf, int sz16) { + std::u16string out; + for (int i = 0; i < sz16; i += 2) { + char16_t ch = buf[i] | (buf[i+1] << 8); + out.push_back(ch); + } + return out; +} + +TEST(UTF16TempFromUTF8, HandlesValidUTF8WithoutBOM) { + const char* utf8 = "abc \xE2\x82\xAC"; // "abc €" + int sz = strlen(utf8); + KMX_BYTE* tempfile = nullptr; + int sz16 = 0; + ASSERT_TRUE(UTF16TempFromUTF8((KMX_BYTE*)utf8, sz, &tempfile, &sz16)); + ASSERT_EQ(sz16, 10); // 5 UTF-16 code units * 2 bytes + std::u16string result = buffer_to_u16string(tempfile, sz16); + EXPECT_EQ(result, u"abc \u20AC"); + delete[] tempfile; +} + +TEST(UTF16TempFromUTF8, HandlesValidUTF8WithBOM) { + // UTF-8 BOM + "A" + const char utf8[] = { char(0xEF), char(0xBB), char(0xBF), 'A', 0 }; + int sz = 4; + KMX_BYTE* tempfile = nullptr; + int sz16 = 0; + ASSERT_TRUE(UTF16TempFromUTF8((KMX_BYTE*)utf8, sz, &tempfile, &sz16)); + ASSERT_EQ(sz16, 2); // Only 'A' (1 code unit) * 2 bytes + std::u16string result = buffer_to_u16string(tempfile, sz16); + EXPECT_EQ(result, u"A"); + delete[] tempfile; +} + +TEST(UTF16TempFromUTF8, HandlesInvalidUTF8FallbacksToCP1252) { + // 0x80 is invalid in UTF-8, should fallback to CP1252 (U+20AC) + const char invalid_utf8[] = { char(0x80), 0 }; + int sz = 1; + KMX_BYTE* tempfile = nullptr; + int sz16 = 0; + ASSERT_TRUE(UTF16TempFromUTF8((KMX_BYTE*)invalid_utf8, sz, &tempfile, &sz16)); + ASSERT_EQ(sz16, 2); + std::u16string result = buffer_to_u16string(tempfile, sz16); + EXPECT_EQ(result, u"\u20AC"); + delete[] tempfile; +} + +TEST(UTF16TempFromUTF8, HandlesEmptyInput) { + KMX_BYTE* tempfile = nullptr; + int sz16 = 0; + ASSERT_FALSE(UTF16TempFromUTF8(nullptr, 0, &tempfile, &sz16)); + // tempfile should remain nullptr, sz16 should be 0 + EXPECT_EQ(tempfile, nullptr); + EXPECT_EQ(sz16, 0); +} + +TEST(IsValidUtf8Test, ValidAscii) { + std::vector v = {'h', 'e', 'l', 'l', 'o'}; + EXPECT_TRUE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, Valid2Byte) { + std::vector v = {0xC2, 0xA2}; // U+00A2: ¢ + EXPECT_TRUE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, Valid3Byte) { + std::vector v = {0xE2, 0x82, 0xAC}; // U+20AC: € + EXPECT_TRUE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, Valid4Byte) { + std::vector v = {0xF0, 0x9F, 0x98, 0x80}; // U+1F600: 😀 + EXPECT_TRUE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, InvalidOverlongEncoding) { + std::vector v = {0xC1, 0x81}; // Overlong encoding for ASCII + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, InvalidMissingContinuation) { + std::vector v = {0xE2, 0x82}; // Missing one continuation byte + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, InvalidLoneContinuation) { + std::vector v = {0x80}; // Continuation byte without starter + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, Invalid4ByteOutOfRange) { + std::vector v = {0xF4, 0x90, 0x80, 0x80}; // > U+10FFFF + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, ValidMixedAsciiAndMultiByte) { + std::vector v = {'A', 0xC2, 0xA2, 0xE2, 0x82, 0xAC, 0xF0, 0x9F, 0x98, 0x80}; + EXPECT_TRUE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, Invalid2ByteBadSecondByte) { + std::vector v = {0xD2, 0x28}; + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, Invalid2ByteBadSecondByteTooHigh) { + std::vector v = {0xD2, 0xC8}; + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, Invalid3ByteBadSecondByte) { + std::vector v = {0xE2, 0x28, 0xA1}; + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, Invalid3ByteBadThirdByte) { + std::vector v = {0xE2, 0xA8, 0xD1}; + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, Invalid4ByteBadSecondByte) { + std::vector v = {0xF2, 0xC8, 0xB2, 0x88}; + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, Invalid4ByteBadThirdByte) { + std::vector v = {0xF2, 0xB8, 0x02, 0x88}; + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, Invalid4ByteBadFourthByte) { + std::vector v = {0xF2, 0xB8, 0xA2, 0xD8}; + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, InvalidByteC0) { + std::vector v = {0xC1}; + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, InvalidByteF5) { + std::vector v = {0xF5}; + EXPECT_FALSE(isValidUtf8(v.data(), v.size())); +} + +TEST(IsValidUtf8Test, ValidEmptyInput) { + std::vector v = {}; + EXPECT_TRUE(isValidUtf8(v.data(), v.size())); +} diff --git a/developer/src/kmcmplib/tests/prep.sh b/developer/src/kmcmplib/tests/prep.sh index 8ad9b81171..e00f2fc1fd 100755 --- a/developer/src/kmcmplib/tests/prep.sh +++ b/developer/src/kmcmplib/tests/prep.sh @@ -11,7 +11,7 @@ set -eu ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/build-utils.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-basic.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE cd "$THIS_SCRIPT_PATH" diff --git a/developer/src/kmconvert/build.sh b/developer/src/kmconvert/build.sh index 599379565f..e18977f05b 100755 --- a/developer/src/kmconvert/build.sh +++ b/developer/src/kmconvert/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Keyboard project generation and conversion tool" \ @@ -33,7 +33,7 @@ function do_build() { tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM" - cp "$WIN32_TARGET_PATH/kmconvert.dbg" "$DEVELOPER_DEBUGPATH" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmconvert.dbg" "$DEVELOPER_DEBUGPATH" rm -rf "$DEVELOPER_PROGRAM/projects/templates" mkdir -p "$DEVELOPER_PROGRAM/projects/templates" diff --git a/developer/src/kmdbrowserhost/build.sh b/developer/src/kmdbrowserhost/build.sh index b00a60735a..8996b17bc4 100755 --- a/developer/src/kmdbrowserhost/build.sh +++ b/developer/src/kmdbrowserhost/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Chromium browser host process for Keyman Developer" \ @@ -32,9 +32,7 @@ function do_build() { tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM" - if [[ -f "$WIN32_TARGET_PATH/kmdbrowserhost.dbg" ]]; then - cp "$WIN32_TARGET_PATH/kmdbrowserhost.dbg" "$DEVELOPER_DEBUGPATH" - fi + builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmdbrowserhost.dbg" "$DEVELOPER_DEBUGPATH" } function do_publish() { diff --git a/developer/src/kmdecomp/build.sh b/developer/src/kmdecomp/build.sh index a6b903fbc8..069e3e836a 100755 --- a/developer/src/kmdecomp/build.sh +++ b/developer/src/kmdecomp/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ @@ -33,7 +33,7 @@ function do_build() { build_version.res vs_msbuild kmdecomp.sln //t:Build "//p:Platform=Win32" cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM" - cp "$WIN32_TARGET_PATH/kmdecomp.pdb" "$DEVELOPER_DEBUGPATH" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmdecomp.pdb" "$DEVELOPER_DEBUGPATH" } function do_publish() { diff --git a/developer/src/samples/build.sh b/developer/src/samples/build.sh index 703a2691a8..c60602bf85 100755 --- a/developer/src/samples/build.sh +++ b/developer/src/samples/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/developer/src/samples/imsample/build.sh b/developer/src/samples/imsample/build.sh index 2c0c402e28..50447040ac 100755 --- a/developer/src/samples/imsample/build.sh +++ b/developer/src/samples/imsample/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ @@ -32,11 +32,11 @@ function do_build() { build_version.res vs_msbuild imsample.sln //t:Build "//p:Platform=Win32" cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM" - cp "$WIN32_TARGET_PATH/imsample.pdb" "$DEVELOPER_DEBUGPATH" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/imsample.pdb" "$DEVELOPER_DEBUGPATH" vs_msbuild imsample.sln //t:Build "//p:Platform=x64" cp "$X64_TARGET" "$DEVELOPER_PROGRAM" - cp "$X64_TARGET_PATH/imsample.x64.pdb" "$DEVELOPER_DEBUGPATH" + builder_if_release_build_level cp "$X64_TARGET_PATH/imsample.x64.pdb" "$DEVELOPER_DEBUGPATH" } # TODO diff --git a/developer/src/server/build.sh b/developer/src/server/build.sh index 6741bb68de..785fd496c0 100755 --- a/developer/src/server/build.sh +++ b/developer/src/server/build.sh @@ -3,10 +3,11 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/utils.inc.sh" +. "$KEYMAN_ROOT/resources/build/node.inc.sh" . "$KEYMAN_ROOT/resources/build/jq.inc.sh" builder_describe "Build Keyman Developer Server" \ @@ -49,7 +50,7 @@ function clean_server() { } function configure_server() { - verify_npm_setup + node_select_version_and_npm_ci # See https://github.com/bubenshchykov/ngrok/issues/254, https://github.com/bubenshchykov/ngrok/pull/255 # TODO: this is horrible; is there a way we can avoid this? rm -f "$KEYMAN_ROOT"/node_modules/ngrok/bin/ngrok diff --git a/developer/src/server/package.json b/developer/src/server/package.json index 99cd6bb260..37192ca6f3 100644 --- a/developer/src/server/package.json +++ b/developer/src/server/package.json @@ -13,7 +13,7 @@ "@sentry/node": "^7.57.0", "chalk": "^4.1.2", "express": "^4.20.0", - "multer": "^2.0.1", + "multer": "^2.0.2", "ngrok": "^5.0.0-beta.2", "open": "^8.4.0", "restructure": "^3.0.1", diff --git a/developer/src/server/src/version-data.ts b/developer/src/server/src/version-data.ts index 05f266c5c6..a082a65170 100644 --- a/developer/src/server/src/version-data.ts +++ b/developer/src/server/src/version-data.ts @@ -1,5 +1,5 @@ /* - Version information from build-utils.sh: + Version information from builder-basic.inc.sh: # KEYMAN_VERSION: Full current build version, e.g. "14.0.1" # [KEYMAN_VERSION_WIN: Full current build version for Windows, e.g. "14.0.1.0"] diff --git a/developer/src/setup/build.sh b/developer/src/setup/build.sh index 5dfe102363..1e4ab94a68 100755 --- a/developer/src/setup/build.sh +++ b/developer/src/setup/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ @@ -33,13 +33,8 @@ function do_build() { sentrytool_delphiprep "$WIN32_TARGET" setup.dpr tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM" - if [[ -f "$WIN32_TARGET_PATH/setup.dbg" ]]; then - cp "$WIN32_TARGET_PATH/setup.dbg" "$DEVELOPER_DEBUGPATH/devsetup.dbg" - fi - rm -f "$WIN32_TARGET_PATH/devsetup.dbg" - if [[ -f "$WIN32_TARGET_PATH/setup.dbg" ]]; then - mv "$WIN32_TARGET_PATH/setup.dbg" "$WIN32_TARGET_PATH/devsetup.dbg" - fi + builder_if_release_build_level cp "$WIN32_TARGET_PATH/setup.dbg" "$DEVELOPER_DEBUGPATH/devsetup.dbg" + builder_if_release_build_level mv "$WIN32_TARGET_PATH/setup.dbg" "$WIN32_TARGET_PATH/devsetup.dbg" } function do_publish() { diff --git a/developer/src/test/auto/build.sh b/developer/src/test/auto/build.sh index 043711e752..a2bca0e0dd 100755 --- a/developer/src/test/auto/build.sh +++ b/developer/src/test/auto/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/developer/src/test/auto/compile-supplementary-support/build.sh b/developer/src/test/auto/compile-supplementary-support/build.sh index b004605b1d..4f8acf40a9 100755 --- a/developer/src/test/auto/compile-supplementary-support/build.sh +++ b/developer/src/test/auto/compile-supplementary-support/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "test if KS=1 is in the right files, both in debug and non-debug versions" clean configure build test diff --git a/developer/src/test/auto/keyboard-js-info/build.sh b/developer/src/test/auto/keyboard-js-info/build.sh index 0a19929f4b..7bbf417681 100755 --- a/developer/src/test/auto/keyboard-js-info/build.sh +++ b/developer/src/test/auto/keyboard-js-info/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Test the read of basic keyboard info info from js files" clean configure build test diff --git a/developer/src/test/auto/kmcomp/build.sh b/developer/src/test/auto/kmcomp/build.sh index 7239010b06..8bfcd69149 100755 --- a/developer/src/test/auto/kmcomp/build.sh +++ b/developer/src/test/auto/kmcomp/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Test that kmc passes various compile tests" clean configure build test diff --git a/developer/src/test/auto/kmx-file-languages/build.sh b/developer/src/test/auto/kmx-file-languages/build.sh index 58ae1492ef..9bf1821450 100755 --- a/developer/src/test/auto/kmx-file-languages/build.sh +++ b/developer/src/test/auto/kmx-file-languages/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Test the read of language info from kmx files and translation from legacy ids to BCP47" clean configure build test diff --git a/developer/src/test/auto/model-ts-parser/build.sh b/developer/src/test/auto/model-ts-parser/build.sh index eaf5bb4819..e4e2208e56 100755 --- a/developer/src/test/auto/model-ts-parser/build.sh +++ b/developer/src/test/auto/model-ts-parser/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Test lexical model parser" clean configure build test diff --git a/developer/src/test/auto/package-info/build.sh b/developer/src/test/auto/package-info/build.sh index e67366b26e..32f9fbbbc4 100755 --- a/developer/src/test/auto/package-info/build.sh +++ b/developer/src/test/auto/package-info/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Test lexical model parser" clean configure build test diff --git a/developer/src/test/build.sh b/developer/src/test/build.sh index 48f3e69ac5..6546dbf752 100755 --- a/developer/src/test/build.sh +++ b/developer/src/test/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe \ diff --git a/developer/src/tike/build.sh b/developer/src/tike/build.sh index d0e5800fc3..0ba6c98642 100755 --- a/developer/src/tike/build.sh +++ b/developer/src/tike/build.sh @@ -2,7 +2,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE builder_describe "Build Keyman Developer IDE" \ @@ -76,11 +76,10 @@ function do_build() { cp kmlmp.cmd "$DEVELOPER_PROGRAM" cp kmc.cmd "$DEVELOPER_PROGRAM" cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_DLL" "$DEVELOPER_PROGRAM" - if [[ -f "$WIN32_TARGET_PATH/tike.dbg" ]]; then - cp "$WIN32_TARGET_PATH/tike.dbg" "$DEVELOPER_DEBUGPATH" - fi + builder_if_release_build_level cp "$WIN32_TARGET_PATH/tike.dbg" "$DEVELOPER_DEBUGPATH" + cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_DLL" "$WIN32_TARGET_PATH" - cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_PDB" "$WIN32_TARGET_PATH" + builder_if_release_build_level cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_PDB" "$WIN32_TARGET_PATH" cp "$KEYMAN_ROOT/common/windows/delphi/ext/sentry/sentry.dll" "$DEVELOPER_PROGRAM/" cp "$KEYMAN_ROOT/common/windows/delphi/ext/sentry/sentry.x64.dll" "$DEVELOPER_PROGRAM/" diff --git a/developer/src/tools/sentry-upload-difs.sh b/developer/src/tools/sentry-upload-difs.sh index 34ab4c60b0..b45387ec3e 100755 --- a/developer/src/tools/sentry-upload-difs.sh +++ b/developer/src/tools/sentry-upload-difs.sh @@ -14,7 +14,7 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" +. "${THIS_SCRIPT%/*}/../../../resources/build/builder-basic.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE cd "$KEYMAN_ROOT/developer" diff --git a/docs/build-bot.md b/docs/build-bot.md new file mode 100644 index 0000000000..642f285894 --- /dev/null +++ b/docs/build-bot.md @@ -0,0 +1,229 @@ +# Controlling builds in CI with the Keyman build bot + +The Keyman CI test infrastructure can be controlled by use of a `Build-bot:` +commit trailer, or a PR body `Build-bot:` trailer. This allows you to specify +what is built for any given platform. The primary purpose of the build bot is to +reduce the build agent load, but it can also be used to ensure that specific +artifacts are available for the purposes of testing. + +The Keyman build bot is only available for test builds on pull requests. It is +not used for any other builds, either test builds on target branches (master, +beta, stable-x.y), or for release builds. + +The default set of platform builds is determined by the files touched in the +pull request; see /resources/build/trigger-definitions.inc.sh. The default build +level for this set is 'build' (see [Build Level] section). This is known as the +'build set'. + +You may choose to increase or decrease what is built -- for example, for a +documentation-only change, you may decide that nothing needs to be built at all, +with: + +``` +Build-bot: skip +``` + +Or you may want to ensure that an artifact is built for Windows: + +``` +Build-bot: release:windows +``` + +## The Build-bot command + +The build bot is controlled through the `Build-bot` command, which can be put +into commit trailers or the PR body (not PR comments). The format of the command +is: + +``` +Build-bot: :[Platforms] ... +``` + +* `BuildLevel` can be `skip`, `build`, or `release`. See [Build Level] for more + details. +* `Platforms` can be omitted, in which case the command applies to all + previously-specified platforms (which is not equivalent to specifying `all` -- + if no platform is specified, it will only update platforms that were already + in the build set). If specified, it must be a comma-separated list, without + spaces, of one or more of the following platform identifiers: + + * `all`: apply to all platforms listed below + * `android` + * `developer` + * `ios` + * `linux` + * `mac` + * `web` + * `windows` + * `common`: build common components, on all three build platforms (note: + `release` is equivalent to `build` buildLevel for `common`) + * `core`: build Keyman Core, on all three build platforms, and also WASM + (note: `release` is equivalent to `build` buildLevel for `core`) + +The list of platforms can be found in the `available_platforms` variable in +trigger-definitions.inc.sh. + +## Build Level + +The build level specifies what we want to be run for a test build on a PR. + +There is a bit of nomenclature overlap with a 'release build'. A 'release' +buildLevel for a 'test build' is roughly equivalent to what is performed in a +'release build', however, a test build is only ever uploaded to test endpoints +(i.e. TestFlight, Play Store 'test' streams), and never to *.keyman.com, or to +other release distribution endpoints. + +The build level is controlled by the Build-bot commit trailer and PR body +Build-bot/Test-bot trailers. The default build level will be 'build'. + +For target branch (master, beta, stable-x.y) builds, the build level will always +be 'build', and Build-bot: commit trailers are ignored. + +### 'skip' build level + +Don't do a build at all. This would be appropriate for documentation PRs, for +example, or changes only to comments in source files. + +### 'build' build level + +Build the code and run unit tests, but don't create artifacts. What this looks +like will vary from platform to platform, but there are some common things we +won't do: +* we won't upload artifacts to TeamCity or *.keyman.com +* we won't upload artifacts to any endpoint such as Play Store +* we won't upload symbols to Sentry +* we won't codesign + +However, we _will_ still build an installer (skipping codesigning), as this is +part of the 'build' buildLevel rather than the 'release' buildLevel. The +installer will be thrown away for 'build' build level -- it will not be +available for download as an artifact. + +For a platform-specific example, on macOS we will also skip notarizing, as this +is costly and depends on external network resources, making it fragile. + +### 'release' build level + +A full test build will be run, roughly equivalent to a release build. We will do +the following steps: +* codesign (many platforms) and notarization (macOS) +* upload artifacts to TeamCity (all platforms) +* upload builds to TestFlight / Play Store 'test' endpoints (iOS/Android) +* upload symbols to Sentry + +For a 'release' build level: +* we won't upload artifacts to *.keyman.com +* we won't upload artifacts to any release endpoint such as Debian, + packages.sil.org, etc, or to the release areas for Play Store or App Store + +(uploading to *.keyman.com and to release endpoints happens in the release TC +build config/GHA) + +## Controlling the build bot with trailers + +The build bot respects commit trailers and trailers in the PR body. The commands +are cumulative and applied in order; the Build-bot trailer in the PR body is +applied after any trailers in commit messages. + +If no platform is specified, then the command applies to all platforms in the +current build set, overriding any previous Build-bot commands. Any platforms not +in the current build set will remain 'skipped'. + +If a platform is specified, then the command will apply only to that platform, +and the platform will be added to the build set if not already present (and +thus, subsequent Build-bot commands will be applied to this new platform as +well). + +It is important to note that the Build-bot trailers are read when builds are +triggered, which happens within 2 minutes of a PR being opened or commits being +pushed. Editing the PR body after the build trigger has run will have no effect +on existing builds, until another commit is pushed, or the test build trigger is +run manually from TeamCity. Thus, do not rely on editing the PR body after PR +creation to control the build bot; either include your build bot trailers in +commit messages, or include them in the PR body submitted when creating the PR. + +## Interactions with Test Bot + +The Build-bot has limited interactions with the Keyman test bot (aka +keymanapp-test-bot): if a 'User Testing' section is found in the PR body, the +default build level will be upgraded from 'build' to 'release'. + +Build bot trailers found in either the commits or in the PR body are applied +after the test bot command. + +WARNING: The build bot does not check PR comments for Test-bot commands or a +'User Testing' section. + +## Example Build-bot interactions + +Say we have a PR that touches `/android/build.sh`. The default build set will be +`(android:build)`. + +The PR body has a User Testing section: `# User Testing`. The build set is +upgraded to: `(android:release)`. + +The first commit includes a Build-bot command: `Build-bot: build:ios`. The build +set is now `(android:release ios:build)`. + +In a subsequent commit, the PR author decides that nothing needs to be built, +after all: `Build-bot: skip`. The build set is now `(android:skip ios:skip)`. +Note that other platforms are still 'skip' but not included in the build set. + +Finally, the PR author pushes another commit, with `Build-bot: release:windows`. +The build set is now: `(android:skip ios:skip windows:release)`. + +# FAQ + +* How do I specify commands for multiple platforms, e.g. building on Windows and Linux? + +``` +Build-bot: build:windows,linux +``` + +* If I modify a file that would cause a build on all platforms, does `Build-bot: + build android` then cause a build only on Android and skip all others? Or is + it only additive? + +The commands are additive. To skip all others, you would do: + +``` +Build-bot: skip +Build-bot: build:android +``` + +* What happens in the following scenario: I have a PR that I only want to build + on Android and I add `Build-bot: build android`. Later on I merge in the + changes from `master` and add another commit. If I still only want to build on + Android, do I have to add `Build-bot: build android` on my new commit again? + Or what happens with merges that touch other files which would cause builds + for other platforms? + +First, you would need to specify `skip` for all other platforms in order to only +build on Android: + +``` +Build-bot: skip +Build-bot: build:android +``` + +Then, the build bot scans all the commits in the PR, and additively builds the +final build set from the `Build-bot` commands from all the commits (up to 2000 +in one PR). Thus, earlier bot commands commits will continue to be honoured. +This makes it easier to apply merge commits, for example, or small fixups, +without needing to think about what needs to be built again. + +* How can I re-trigger builds after changing my build bot commands? + +In [TeamCity - Trigger Test Builds](https://build.palaso.org/buildConfiguration/Keyman_Test), +select a custom Run (small button attached to right of Run button), and in the +"Changes" tab, "Build branch" field, enter the PR number to re-run the builds. +Any test builds currently running against that PR will be cancelled and +restarted. + +Note: if you reduce the build set (with `skip`), and re-run builds in this way +without pushing a new commit, you will end up with stale status checks on the +latest commit in your PR for the builds that are now skipped on the new run, so +in this scenario it is safest to push a new commit with the updated build bot +commands. + +[Build Level]: #Build_Level \ No newline at end of file diff --git a/docs/builder.md b/docs/builder.md index c0b889ce9f..538bf783bd 100644 --- a/docs/builder.md +++ b/docs/builder.md @@ -1,4 +1,7 @@ -# Using the build-utils.sh builder functions +# Using the builder.inc.sh builder functions + +Note: see [resources/build/README.md](../resources/build/README.md) for +distinction on builder-basic.inc.sh and builder-full.inc.sh. The Keyman repository is standardising on bash scripts for builds. These may call project-specific builders, such as `tsc` for Typescript projects, `meson` @@ -43,7 +46,7 @@ set -eu ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "$(dirname "$THIS_SCRIPT")//resources/build/build-utils.sh" +. "$(dirname "$THIS_SCRIPT")//resources/build/builder-basic.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE # . "$KEYMAN_ROOT/.../foo.inc.sh" # any other includes, such as jq.inc.sh @@ -78,7 +81,7 @@ Builder scripts will inherit `set -eu` from builder.inc.sh: ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "$(dirname "$THIS_SCRIPT")//resources/build/build-utils.sh" +. "$(dirname "$THIS_SCRIPT")//resources/build/builder-basic.inc.sh" ## END STANDARD BUILD SCRIPT INCLUDE ``` @@ -100,7 +103,7 @@ search-and-replace) this section in the future as required. ## Any other includes -Once `build-utils.sh` has been included, the variable `$KEYMAN_ROOT` will be +Once `builder-basic.inc.sh` has been included, the variable `$KEYMAN_ROOT` will be available, so other include scripts should be sourced accordingly, for example: ```bash @@ -114,7 +117,7 @@ to `cd` at the start of your script. ## Standard environment -`build-utils.sh` will prepend `$KEYMAN_ROOT/node_modules/.bin` to the `PATH` +`builder-basic.inc.sh` will prepend `$KEYMAN_ROOT/node_modules/.bin` to the `PATH` variable to ensure that we run the correct versions of npm package commands, so there is no need to hard-code path references or add script wrappers to package.json (`npm run