mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-16 13:49:23 +00:00
chore(web): Merge branch 'master' into chore/web/feature-gestures-merge-master
This commit is contained in:
commit
5780da2cbf
61 changed files with 2309 additions and 1266 deletions
158
HISTORY.md
158
HISTORY.md
|
|
@ -1,5 +1,72 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 16.0.77 alpha 2022-10-10
|
||||
|
||||
* fix(web): possible error on change of context to a contextEditable (#7359)
|
||||
* chore(web): unit test polishing pass (#7381)
|
||||
|
||||
## 16.0.76 alpha 2022-10-06
|
||||
|
||||
* chore(linux): Cleanup unnecessary surrogate check (#7396)
|
||||
|
||||
## 16.0.75 alpha 2022-10-05
|
||||
|
||||
* chore(common): builder script maintenance (#7405)
|
||||
|
||||
## 16.0.74 alpha 2022-10-04
|
||||
|
||||
* chore(android/engine): Display Toast notifications on Sentry errors (#7390)
|
||||
* fix(android/engine): Dismiss subkeys on multi-touch (#7388)
|
||||
|
||||
## 16.0.73 alpha 2022-10-03
|
||||
|
||||
* fix(developer): handle invalid kps xml when loading project view (#7371)
|
||||
|
||||
## 16.0.72 alpha 2022-09-30
|
||||
|
||||
* fix(web): Only unhighlight suggestion if there's a pending one (#7383)
|
||||
* refactor(common): Split builder utilities to separate script (#7365)
|
||||
|
||||
## 16.0.71 alpha 2022-09-29
|
||||
|
||||
* fix(developer): generate valid js when using unquoted digits in stores (#7369)
|
||||
* fix(developer): ensure backslash in paths passed to kmcomp (#7370)
|
||||
* fix(web): Update spacebar caption when refreshing layout (#7348)
|
||||
* fix(common/web): mock deadkey handling after rules manipulating context (#7345)
|
||||
|
||||
## 16.0.70 alpha 2022-09-26
|
||||
|
||||
* fix(android): Add language name when installing default lexical-model (#7347)
|
||||
* chore(common): Add 15.0 stable entries to HISTORY.md :cherries: (#7350)
|
||||
|
||||
## 16.0.69 alpha 2022-09-21
|
||||
|
||||
* fix(android/engine): Check listview for setting keyboard selection (#7325)
|
||||
* refactor(common/web): reworks low-level module build scripts ️ (#7319)
|
||||
* refactor(common): `builder` script tweaks - "has action" vs "start action" ️ (#7333)
|
||||
* feat(common): builder trap-functionality testing ️ (#7334)
|
||||
* chore(common): builder extra params support (#7339)
|
||||
|
||||
## 16.0.68 alpha 2022-09-20
|
||||
|
||||
* chore(web): update `got` dependency for regression tests (#7315)
|
||||
* chore(developer): update multer dependency for server (#7314)
|
||||
* chore(developer): update node-windows-trayicon to update node-gyp to 9.1.0 (#7321)
|
||||
|
||||
## 16.0.67 alpha 2022-09-19
|
||||
|
||||
* fix(common/models): blocks full-text "corrections" ️ (#7241)
|
||||
* change(web): better fat-finger key weighting ️ (#7242)
|
||||
* fix(web): fixes unintended auto-acceptance of suggestion after reverting (#7305)
|
||||
* feat(common/models): wordbreaker customization (#7279)
|
||||
* fix(common/models): fixes reference dropped by git merge (#7313)
|
||||
* test(android): Add final keyboard to test K_ENTER rule (#7303)
|
||||
* chore(common): Update to Unicode 15.0 (#7302)
|
||||
* chore(common): update auto labeler configuration (#7316)
|
||||
* chore(common): make scripts executable and add pre-commit test (#7317)
|
||||
* chore(deps): bump nanoid and mocha (#7307)
|
||||
* chore(common): remove unused ncc dependency (#7318)
|
||||
|
||||
## 16.0.66 alpha 2022-09-17
|
||||
|
||||
* chore: improve auto labeling (#7288)
|
||||
|
|
@ -335,7 +402,96 @@
|
|||
|
||||
## 16.0.1 alpha 2022-03-02
|
||||
|
||||
* chore: move to 16.0 (#6318)
|
||||
* chore(common): move to 16.0 (#6318)
|
||||
|
||||
## 15.0.270 stable 2022-09-12
|
||||
|
||||
* chore(linux): Add support for Ubuntu 22.10 "Kinetic Kudu" (#7143)
|
||||
* chore(linux): Update debian changelog (#7144)
|
||||
* fix(android/engine): Cleanup list of subkeys when dismissing window (#7176)
|
||||
* chore(linux): Fix ibus-keyman.postinst script (#7208)
|
||||
* fix(windows): Delete both code units when deleting surrogate pairs in TSF-aware apps (#7256)
|
||||
* fix(developer): compiler mismatch on currentLine (#7191)
|
||||
* fix(developer): suppress repeated warnings about unreachable code (#7265)
|
||||
|
||||
## 15.0.269 stable 2022-08-29
|
||||
|
||||
* chore(linux): Update debian changelog (#7040)
|
||||
* feat(linux): Replace deprecated distutils :cherries: (#7052)
|
||||
* fix(developer): compiler emitting garbage for readonly groups (#7014)
|
||||
* chore: Change platform advocates per discussion (#7114)
|
||||
* fix(windows): remove saving and restoring context kbd options (#7107)
|
||||
* fix(windows): Add invalidate context action to non-updatable parse (#7108)
|
||||
* fix(android/engine): :cherries: Lower the max height for landscape orientation (#7128)
|
||||
|
||||
## 15.0.268 stable 2022-08-04
|
||||
|
||||
* chore(linux): Update debian changelog (#6995)
|
||||
* fix(developer): increase maximum file size for server (#7004)
|
||||
* fix(developer): rebuilding a model would not refresh it in server (#7016)
|
||||
* chore(linux): Add script to upload to debian (#7017)
|
||||
* fix(developer): stack overflow when compiling non-web keyboard (#7034)
|
||||
* fix(developer): prevent crash attempting to compile ansi keyboard (#7035)
|
||||
|
||||
## 15.0.267 stable 2022-07-26
|
||||
|
||||
* fix(developer): QR Code for Package Editor had wrong path (#6927)
|
||||
* fix(ios): ignore CFBundleShortVersionString (#6935)
|
||||
* fix(web): context-only rule effects, set(&layer) from physical keystrokes ️ (#6949)
|
||||
* chore(web): remove invalid warning msg (#6951)
|
||||
* fix(common): Fix `delete` :cherries: (#6966)
|
||||
* fix(linux): Another attempt at fixing postinst script :cherries: (#6961)
|
||||
* fix(linux): Fix uninstallation when using fcitx5 :cherries: (#6964)
|
||||
* fix(linux): Catch PermissionError exception :cherries: (#6969)
|
||||
* chore(linux): Update Debian changelog (#6972)
|
||||
* fix(developer): kmdecomp virtual character key output (#6945)
|
||||
* fix(developer): crash on exit when checking for updates (#6946)
|
||||
* fix(developer): crash when switching back a tab (#6947)
|
||||
* fix(developer): reduce timeouts if server shut down (#6948)
|
||||
* fix(android): rework longpress movement trigger (#6992)
|
||||
* chore(linux): Update changelog (#6987)
|
||||
* fix(developer): improve error message for multi-part key ids on <15.0 (#6990)
|
||||
|
||||
## 15.0.266 stable 2022-07-08
|
||||
|
||||
* fix(linux): Fix post-install script :cherries: (#6895)
|
||||
* fix(web): improve `console.error()` reporting (#6904)
|
||||
* fix(web): ncaps rules not matching on touch (#6913)
|
||||
|
||||
## 15.0.265 stable 2022-07-04
|
||||
|
||||
* fix(common): trigger builds correctly for stable builds (#6879)
|
||||
* fix(web): post-keystroke processing after use of pred. text selection (#6886)
|
||||
|
||||
## 15.0.264 stable 2022-07-01
|
||||
|
||||
* fix(ios): eliminates conditional height for banner image (#6857)
|
||||
* fix(web): clears repeating bksp on keyboard reload ️ (#6863)
|
||||
* fix(web): better stability during transient OSK load state (#6861)
|
||||
* fix(developer): readonly groups should never emit output (#6873)
|
||||
* fix(web): Maintain separate Caps Lock states for touch and physical (#6874)
|
||||
|
||||
## 15.0.263 stable 2022-06-24
|
||||
|
||||
* chore(ios): add manageAppVersionAndBuildNumber=false (#6837)
|
||||
|
||||
## 15.0.262 stable 2022-06-23
|
||||
|
||||
* fix(web): key preview styling, positioning issues (#6795)
|
||||
* chore(linux): Update Debian changelog (#6809)
|
||||
* fix(linux): Deal with non-existing files (#6812)
|
||||
* fix(linux): Catch KeyboardInterrupt (#6814)
|
||||
* fix(ios): blocks known non-error "error" report (#6816)
|
||||
* fix(web): fixes doc-keyboard generation (#6817)
|
||||
* chore(web): updates error-reporting lib uploader (pseudo- ) (#6819)
|
||||
|
||||
## 15.0.261 stable 2022-06-18
|
||||
|
||||
* fix(web): crash on custom modifier keys (#6789)
|
||||
|
||||
## 15.0.260 stable 2022-06-15
|
||||
|
||||
* chore(common): Release version 15.0
|
||||
|
||||
## 15.0.259 beta 2022-06-10
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
16.0.67
|
||||
16.0.78
|
||||
|
|
@ -165,6 +165,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
HashMap<String, String> lexicalModelInfo = new HashMap<String, String>();
|
||||
lexicalModelInfo.put(KMManager.KMKey_PackageID, defaultLexicalModel.getPackageID());
|
||||
lexicalModelInfo.put(KMManager.KMKey_LanguageID, defaultLexicalModel.getLanguageID());
|
||||
lexicalModelInfo.put(KMManager.KMKey_LanguageName, defaultLexicalModel.getLanguageName());
|
||||
lexicalModelInfo.put(KMManager.KMKey_LexicalModelID, defaultLexicalModel.getLexicalModelID());
|
||||
lexicalModelInfo.put(KMManager.KMKey_LexicalModelName, defaultLexicalModel.getLexicalModelName());
|
||||
lexicalModelInfo.put(KMManager.KMKey_LexicalModelVersion, defaultLexicalModel.getVersion());
|
||||
|
|
|
|||
|
|
@ -62,8 +62,7 @@ public class PackageActivity extends AppCompatActivity implements
|
|||
if (bundle != null) {
|
||||
kmpFile = new File(bundle.getString("kmpFile"));
|
||||
if (!kmpFile.exists()) {
|
||||
KMLog.LogError(TAG, kmpFile.getAbsolutePath() + " not found. Unable to extract");
|
||||
showErrorToast(context, getString(R.string.failed_to_extract));
|
||||
showErrorToast(kmpFile.getAbsolutePath() + " not found. Unable to extract");
|
||||
return;
|
||||
}
|
||||
installMode = KmpInstallMode.fromString(bundle.getString("installMode"));
|
||||
|
|
@ -82,27 +81,26 @@ public class PackageActivity extends AppCompatActivity implements
|
|||
if (pkgTarget.equals(PackageProcessor.PP_TARGET_LEXICAL_MODELS)) {
|
||||
kmpProcessor = new LexicalModelPackageProcessor(resourceRoot);
|
||||
} else if (!pkgTarget.equals(PackageProcessor.PP_TARGET_KEYBOARDS)) {
|
||||
showErrorToast(context, getString(R.string.no_targets_to_install));
|
||||
showErrorToast(getString(R.string.no_targets_to_install));
|
||||
return;
|
||||
}
|
||||
tempPackagePath = kmpProcessor.unzipKMP(kmpFile);
|
||||
|
||||
} catch (Exception e) {
|
||||
KMLog.LogException(TAG, "", e);
|
||||
showErrorToast(context, getString(R.string.failed_to_extract));
|
||||
showErrorToast(getString(R.string.failed_to_extract), e);
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject pkgInfo = kmpProcessor.loadPackageInfo(tempPackagePath);
|
||||
if (pkgInfo == null) {
|
||||
showErrorToast(context, getString(R.string.invalid_metadata));
|
||||
showErrorToast(getString(R.string.invalid_metadata));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check minimum keyboard version to ensure current version of Keyman supports the features
|
||||
String pkgMinimumKeyboardVersion = kmpProcessor.getPackageMinimumKeyboardVersion(pkgInfo);
|
||||
if (FileUtils.compareVersions(pkgMinimumKeyboardVersion, KMManager.getMajorVersion()) == FileUtils.VERSION_GREATER) {
|
||||
showErrorToast(context, getString(R.string.minimum_keyboard_version_not_supported));
|
||||
showErrorToast(getString(R.string.minimum_keyboard_version_not_supported));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -116,9 +114,9 @@ public class PackageActivity extends AppCompatActivity implements
|
|||
// Sanity check for keyboard packages
|
||||
if (pkgTarget.equals(PackageProcessor.PP_TARGET_KEYBOARDS)) {
|
||||
if (keyboardCount == 0) {
|
||||
showErrorToast(context, getString(R.string.no_new_touch_keyboards_to_install));
|
||||
showErrorToast(getString(R.string.no_new_touch_keyboards_to_install));
|
||||
} else if (languageCount == 0) {
|
||||
showErrorToast(context, getString(R.string.no_associated_languages));
|
||||
showErrorToast(getString(R.string.no_associated_languages));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -230,8 +228,12 @@ public class PackageActivity extends AppCompatActivity implements
|
|||
public void onLanguagesSelected(ArrayList<Keyboard> addKeyboardsList) {
|
||||
}
|
||||
|
||||
private void showErrorToast(Context context, String message) {
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
|
||||
private void showErrorToast(String message, Exception e) {
|
||||
if (e != null) {
|
||||
KMLog.LogException(TAG, message, e);
|
||||
} else {
|
||||
KMLog.LogError(TAG, message);
|
||||
}
|
||||
// Setting result to 1 so calling activity will finish too
|
||||
setResult(1);
|
||||
cleanup();
|
||||
|
|
@ -239,6 +241,10 @@ public class PackageActivity extends AppCompatActivity implements
|
|||
MainActivity.cleanupPackageInstall();
|
||||
}
|
||||
|
||||
private void showErrorToast(String message) {
|
||||
showErrorToast(message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the keyboard or lexical model package, and then notifies the corresponding listeners
|
||||
* @param context Context The activity context
|
||||
|
|
@ -282,7 +288,7 @@ public class PackageActivity extends AppCompatActivity implements
|
|||
cleanup();
|
||||
} else {
|
||||
// Use Toast so it will linger when PackageActivity finishes
|
||||
showErrorToast(context, getString(R.string.no_new_touch_keyboards_to_install));
|
||||
showErrorToast(getString(R.string.no_new_touch_keyboards_to_install));
|
||||
}
|
||||
} else if (pkgTarget.equals(PackageProcessor.PP_TARGET_LEXICAL_MODELS)) {
|
||||
List<Map<String, String>> installedLexicalModels =
|
||||
|
|
@ -305,7 +311,7 @@ public class PackageActivity extends AppCompatActivity implements
|
|||
cleanup();
|
||||
} else {
|
||||
// Use Toast so it will linger when PackageActivity finishes
|
||||
showErrorToast(context, getString(R.string.no_new_predictive_text_to_install));
|
||||
showErrorToast(getString(R.string.no_new_predictive_text_to_install));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -313,9 +319,9 @@ public class PackageActivity extends AppCompatActivity implements
|
|||
finish();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
KMLog.LogException(TAG, "", e);
|
||||
// Use Toast so it will linger when PackageActivity finishes
|
||||
showErrorToast(context, getString(R.string.no_targets_to_install));
|
||||
String msg = getString(R.string.no_targets_to_install);
|
||||
KMLog.LogException(TAG, msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,19 @@ public class BaseActivity extends AppCompatActivity {
|
|||
Toast.makeText(context, String.format(msg, args), duration).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Some classes aren't an AppCompatActivity and need this helper to send Toast notifications
|
||||
* @param defaultContext - the context to fallback if localeUpdatedContext is null
|
||||
* @param msg - Toast notification string
|
||||
* @param duration - length of the Toast notification (Toast.LENGTH_LONG or Toast.LENGTH_SHORT)
|
||||
*/
|
||||
public static void makeToast(Context defaultContext, String msg, int duration) {
|
||||
Context context = (localeUpdatedContext != null) ? localeUpdatedContext : defaultContext;
|
||||
if (context != null) {
|
||||
Toast.makeText(context, msg, duration).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context newBase) {
|
||||
// Override the app locale using the BCP 47 tag from shared preferences
|
||||
|
|
|
|||
|
|
@ -302,6 +302,11 @@ final class KMKeyboard extends WebView {
|
|||
// if active, allowing for smooth, integrated gesture control.
|
||||
subKeysWindow.getContentView().findViewById(R.id.grid).dispatchTouchEvent(event);
|
||||
} else {
|
||||
if (event.getPointerCount() > 1) {
|
||||
// Multiple points touch the screen at the same time, so dismiss any pending subkeys
|
||||
dismissKeyPreview(0);
|
||||
dismissSubKeysWindow();
|
||||
}
|
||||
gestureDetector.onTouchEvent(event);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -321,8 +321,10 @@ public final class KeyboardPickerActivity extends BaseActivity {
|
|||
}
|
||||
|
||||
private static void setSelection(int position) {
|
||||
listView.setItemChecked(position, true);
|
||||
listView.setSelection(position);
|
||||
if (listView != null) {
|
||||
listView.setItemChecked(position, true);
|
||||
listView.setSelection(position);
|
||||
}
|
||||
selectedIndex = position;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ public class CloudLexicalModelMetaDataDownloadCallback implements ICloudDownload
|
|||
private void processCloudResultForModel(Context aContext, MetaDataResult _r) {
|
||||
JSONArray lmData = _r.returnjson.jsonArray;
|
||||
if (lmData == null || lmData.length() == 0) {
|
||||
KMLog.LogError(TAG, "Error in lexical model metadata from api.keyman.com - zero or null");
|
||||
// Not an error if api.keyman.com returns empty array of associated lexical models
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ public final class FileUtils {
|
|||
if (tmpFile != null && tmpFile.exists()) {
|
||||
tmpFile.delete();
|
||||
}
|
||||
KMLog.LogError(TAG, "Could not download filename " + filename);
|
||||
KMLog.LogError(TAG, "Could not download filename " + destinationFilename);
|
||||
}
|
||||
|
||||
Connection.disconnect();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
package com.tavultesoft.kmea.util;
|
||||
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.tavultesoft.kmea.BaseActivity;
|
||||
|
||||
import io.sentry.Sentry;
|
||||
import io.sentry.SentryLevel;
|
||||
|
||||
|
|
@ -35,6 +39,8 @@ public final class KMLog {
|
|||
if (msg != null && !msg.isEmpty()) {
|
||||
Log.e(tag, msg);
|
||||
|
||||
BaseActivity.makeToast(null, msg, Toast.LENGTH_LONG);
|
||||
|
||||
if (Sentry.isEnabled()) {
|
||||
Sentry.captureMessage(msg, SentryLevel.ERROR);
|
||||
}
|
||||
|
|
@ -44,20 +50,22 @@ public final class KMLog {
|
|||
/**
|
||||
* Utility to log exceptions and send to Sentry
|
||||
* @param tag String of the caller
|
||||
* @param msg String of the exception message
|
||||
* @param msg String of the exception message (maybe localized)
|
||||
* @param e Throwable exception
|
||||
*/
|
||||
public static void LogException(String tag, String msg, Throwable e) {
|
||||
String errorMsg = "";
|
||||
if (msg != null && !msg.isEmpty()) {
|
||||
Log.e(tag, msg + "\n" + e);
|
||||
errorMsg = msg + "\n" + e;
|
||||
} else if (e != null) {
|
||||
Log.e(tag, e.getMessage(), e);
|
||||
errorMsg = e.getMessage();
|
||||
}
|
||||
Log.e(tag, errorMsg, e);
|
||||
|
||||
BaseActivity.makeToast(null, errorMsg, Toast.LENGTH_LONG);
|
||||
|
||||
if (Sentry.isEnabled()) {
|
||||
if (msg != null && !msg.isEmpty()) {
|
||||
Sentry.addBreadcrumb(msg);
|
||||
}
|
||||
Sentry.addBreadcrumb(errorMsg);
|
||||
Sentry.captureException(e);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardEventLi
|
|||
HashMap<String, String>lexicalModelInfo = new HashMap<String, String>();
|
||||
lexicalModelInfo.put(KMManager.KMKey_PackageID, "example.ta.wordlist");
|
||||
lexicalModelInfo.put(KMManager.KMKey_LanguageID, "ta");
|
||||
lexicalModelInfo.put(KMManager.KMKey_LanguageName, "Tamil");
|
||||
lexicalModelInfo.put(KMManager.KMKey_LexicalModelID, "example.ta.wordlist");
|
||||
lexicalModelInfo.put(KMManager.KMKey_LexicalModelVersion, "1.0");
|
||||
KMManager.addLexicalModel(context, lexicalModelInfo);
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven
|
|||
HashMap<String, String>lexicalModelInfo = new HashMap<String, String>();
|
||||
lexicalModelInfo.put(KMManager.KMKey_PackageID, "example.ta.wordlist");
|
||||
lexicalModelInfo.put(KMManager.KMKey_LanguageID, "ta");
|
||||
lexicalModelInfo.put(KMManager.KMKey_LanguageName, "Tamil");
|
||||
lexicalModelInfo.put(KMManager.KMKey_LexicalModelID, "example.ta.wordlist");
|
||||
lexicalModelInfo.put(KMManager.KMKey_LexicalModelVersion, "1.0");
|
||||
KMManager.addLexicalModel(this, lexicalModelInfo);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
"@keymanapp/resources-gosh": "*",
|
||||
"@types/node": "^10.17.21",
|
||||
"chai": "^4.3.4",
|
||||
"karma": "^6.3.4",
|
||||
"karma": "^6.4.1",
|
||||
"karma-browserstack-launcher": "^1.6.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ display_usage ( ) {
|
|||
FLAGS=
|
||||
CI_REPORTING=0
|
||||
FETCH_DEPS=true
|
||||
CHAINING_FLAGS=-skip-package-install
|
||||
CHAINING_FLAGS=
|
||||
|
||||
# Parse args
|
||||
while [[ $# -gt 0 ]] ; do
|
||||
|
|
@ -43,7 +43,7 @@ while [[ $# -gt 0 ]] ; do
|
|||
;;
|
||||
-CI)
|
||||
CI_REPORTING=1
|
||||
CHAINING_FLAGS="$CHAINING_FLAGS -CI"
|
||||
CHAINING_FLAGS="$CHAINING_FLAGS --ci"
|
||||
;;
|
||||
-skip-package-install|-S)
|
||||
FETCH_DEPS=false
|
||||
|
|
@ -79,7 +79,7 @@ fi
|
|||
|
||||
# First, run tests on the keyboard processor.
|
||||
pushd "$KEYMAN_ROOT/common/web/keyboard-processor"
|
||||
./test.sh $CHAINING_FLAGS || fail "Tests failed by dependencies; aborting integration tests."
|
||||
./build.sh test $CHAINING_FLAGS || fail "Tests failed by dependencies; aborting integration tests."
|
||||
popd
|
||||
|
||||
# Build the leaf-style, bundled version of input-processor for use in testing.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ The Keyboard Processor module is an internal component of KeymanWeb, seen within
|
|||
* Builds will call `npm install` to automatically install further necessary build dependencies.
|
||||
|
||||
* Linux users can run the following to update to LTS version of nodejs
|
||||
|
||||
|
||||
```
|
||||
sudo apt-get install python-software-properties
|
||||
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
||||
|
|
@ -18,4 +18,4 @@ sudo apt-get install nodejs
|
|||
|
||||
**********************************************************************
|
||||
|
||||
The build script may be found at src/build.sh.
|
||||
The main build script may be found in this folder at ./build.sh.
|
||||
|
|
|
|||
81
common/web/keyboard-processor/build.sh
Executable file
81
common/web/keyboard-processor/build.sh
Executable file
|
|
@ -0,0 +1,81 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Compile KeymanWeb's 'keyboard-processor' module, one of the components of Web's 'core' module.
|
||||
#
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "$(dirname "$THIS_SCRIPT")/../../../resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
|
||||
|
||||
# This script runs from its own folder
|
||||
cd "$THIS_SCRIPT_PATH"
|
||||
|
||||
################################ Main script ################################
|
||||
|
||||
# Ensures color var use in `builder_describe`'s argument respects the specified
|
||||
# --color/--no-color option.
|
||||
builder_check_color "$@"
|
||||
|
||||
builder_describe \
|
||||
"Compiles the web-oriented utility function module." \
|
||||
configure \
|
||||
clean \
|
||||
build \
|
||||
test \
|
||||
"--ci For use with action ${BUILDER_TERM_START}test${BUILDER_TERM_END} - emits CI-friendly test reports"
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
# START - Script parameter configuration
|
||||
REPORT_STYLE=local # Default setting.
|
||||
|
||||
if builder_has_option --ci; then
|
||||
REPORT_STYLE=ci
|
||||
|
||||
echo "Replacing user-friendly test reports with CI-friendly versions."
|
||||
fi
|
||||
|
||||
# END - Script parameter configuration
|
||||
|
||||
if builder_start_action configure; then
|
||||
verify_npm_setup
|
||||
|
||||
"$KEYMAN_ROOT/common/web/keyman-version/build.sh"
|
||||
|
||||
builder_finish_action success configure
|
||||
fi
|
||||
|
||||
if builder_start_action clean; then
|
||||
npm run clean
|
||||
builder_finish_action success clean
|
||||
fi
|
||||
|
||||
if builder_start_action build; then
|
||||
npm run tsc -- --build "$THIS_SCRIPT_PATH/src/tsconfig.json"
|
||||
builder_finish_action success build
|
||||
fi
|
||||
|
||||
if builder_start_action test; then
|
||||
# Build test dependency
|
||||
pushd "$KEYMAN_ROOT/common/web/recorder"
|
||||
./build.sh
|
||||
popd
|
||||
|
||||
npm run tsc -- --build "$THIS_SCRIPT_PATH/src/tsconfig.bundled.json"
|
||||
|
||||
echo_heading "Running Keyboard Processor test suite"
|
||||
|
||||
FLAGS=
|
||||
if [ $REPORT_STYLE == ci ]; then
|
||||
FLAGS="$FLAGS --reporter mocha-teamcity-reporter"
|
||||
fi
|
||||
|
||||
npm run mocha -- --recursive $FLAGS ./tests/cases/
|
||||
|
||||
builder_finish_action success test
|
||||
fi
|
||||
|
|
@ -26,6 +26,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"tsc": "tsc",
|
||||
"clean": "tsc -b --clean src/tsconfig.json && tsc -b --clean src/tsconfig.bundled.json",
|
||||
"test": "gosh ./test.sh",
|
||||
"mocha": "mocha"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Compile KeymanWeb's 'keyboard-processor' module, one of the components of Web's 'core' module.
|
||||
#
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "$(dirname "$THIS_SCRIPT")/../../../../resources/build/build-utils.sh"
|
||||
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
# This script runs from its own folder
|
||||
cd "$(dirname "$THIS_SCRIPT")"
|
||||
|
||||
display_usage ( ) {
|
||||
echo "build.sh [-skip-package-install | -S] [-test]"
|
||||
echo
|
||||
echo " -skip-package-install (or -S) skips the `npm install` dependency check."
|
||||
echo " Intended for use when this script is called by another build script."
|
||||
echo ""
|
||||
echo " If more than one target is specified, the last one will take precedence."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Establish default build parameters
|
||||
set_default_vars ( ) {
|
||||
FETCH_DEPS=true
|
||||
# We need to build keyman-version with a script for now
|
||||
"$KEYMAN_ROOT/common/web/keyman-version/build.sh" || fail "Could not build keyman-version"
|
||||
}
|
||||
|
||||
set_default_vars
|
||||
|
||||
# Parse args
|
||||
while [[ $# -gt 0 ]] ; do
|
||||
key="$1"
|
||||
case $key in
|
||||
-skip-package-install|-S)
|
||||
set_default_vars
|
||||
FETCH_DEPS=false
|
||||
;;
|
||||
esac
|
||||
shift # past argument
|
||||
done
|
||||
|
||||
if [ $FETCH_DEPS = true ]; then
|
||||
verify_npm_setup
|
||||
fi
|
||||
|
||||
# Compile web's `keyboard-processor` module.
|
||||
npm run tsc -- -b src/tsconfig.json || fail "Failed to compile the web/keyboard-processor module."
|
||||
|
|
@ -439,12 +439,14 @@ namespace com.keyman.text {
|
|||
if(dn > this.caretIndex) {
|
||||
dn = this.caretIndex;
|
||||
}
|
||||
this.adjustDeadkeys(-dn);
|
||||
this.text = this.text.kmwSubstr(0, this.caretIndex - dn) + this.getTextAfterCaret();
|
||||
this.caretIndex -= dn;
|
||||
}
|
||||
}
|
||||
|
||||
insertTextBeforeCaret(s: string): void {
|
||||
this.adjustDeadkeys(s._kmwLength());
|
||||
this.text = this.getTextBeforeCaret() + s + this.getTextAfterCaret();
|
||||
this.caretIndex += s.kmwLength();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
# Include useful testing resource functions
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "$(dirname "$THIS_SCRIPT")/../../../resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
|
||||
|
||||
# We should work within the script's directory, not the one we were called in.
|
||||
cd "$THIS_SCRIPT_PATH"
|
||||
|
||||
# A simple utility script to facilitate unit-testing for the LM Layer.
|
||||
# It's rigged to be callable by NPM to facilitate testing during development when in other folders.
|
||||
|
||||
display_usage ( ) {
|
||||
echo "test.sh [-skip-package-install] [-CI] [ -? | -h | -help]"
|
||||
echo " -CI to perform continuous-integration friendly tests and reporting formatted for TeamCity"
|
||||
echo " -? | -h | -help to display this help information"
|
||||
echo " -skip-package-install to bypass refreshing dependencies. Useful when called by scripts that pre-fetch"
|
||||
echo ""
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Defaults
|
||||
FLAGS=
|
||||
CI_REPORTING=0
|
||||
FETCH_DEPS=true
|
||||
|
||||
# Parse args
|
||||
while [[ $# -gt 0 ]] ; do
|
||||
key="$1"
|
||||
case $key in
|
||||
-skip-package-install|-S)
|
||||
FETCH_DEPS=false
|
||||
;;
|
||||
-h|-help|-\?)
|
||||
display_usage
|
||||
exit
|
||||
;;
|
||||
-CI)
|
||||
CI_REPORTING=1
|
||||
;;
|
||||
esac
|
||||
shift # past argument
|
||||
done
|
||||
|
||||
if [ $FETCH_DEPS = true ]; then
|
||||
verify_npm_setup
|
||||
fi
|
||||
|
||||
test-headless ( ) {
|
||||
if (( CI_REPORTING )); then
|
||||
FLAGS="$FLAGS --reporter mocha-teamcity-reporter"
|
||||
fi
|
||||
|
||||
npm run mocha -- --recursive $FLAGS ./tests/cases/
|
||||
}
|
||||
|
||||
# Build test dependency
|
||||
pushd "$KEYMAN_ROOT/common/web/recorder/src"
|
||||
./build.sh -skip-package-install || fail "recorder-core compilation failed."
|
||||
popd
|
||||
|
||||
# Compile web's `keyboard-processor` module."
|
||||
npm run tsc -- -b src/tsconfig.bundled.json || fail "Failed to compile the testing version of the core/web/keyboard-processor module."
|
||||
|
||||
# Run headless (browserless) tests.
|
||||
echo_heading "Running Keyboard Processor test suite"
|
||||
test-headless || fail "Keyboard Processor tests failed!"
|
||||
|
||||
|
|
@ -25,18 +25,18 @@ builder_parse "$@"
|
|||
# TODO: build if out-of-date if test is specified
|
||||
# TODO: configure if npm has not been run, and build is specified
|
||||
|
||||
if builder_has_action configure; then
|
||||
if builder_start_action configure; then
|
||||
verify_npm_setup
|
||||
builder_report success configure
|
||||
builder_finish_action success configure
|
||||
fi
|
||||
|
||||
if builder_has_action clean; then
|
||||
if builder_start_action clean; then
|
||||
npm run clean
|
||||
rm -f ./version.inc.ts
|
||||
builder_report success clean
|
||||
builder_finish_action success clean
|
||||
fi
|
||||
|
||||
if builder_has_action build; then
|
||||
if builder_start_action build; then
|
||||
# Generate index.ts
|
||||
echo "
|
||||
// Generated by common/web/keyman-version/build.sh
|
||||
|
|
@ -57,6 +57,6 @@ if builder_has_action build; then
|
|||
" > ./version.inc.ts
|
||||
|
||||
# Build
|
||||
npm run build -- $builder_verbose || die "Could not build worker."
|
||||
builder_report success build
|
||||
npm run build -- $builder_verbose
|
||||
builder_finish_action success build
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -83,9 +83,9 @@ builder_parse "$@"
|
|||
# TODO: build if out-of-date if test is specified
|
||||
# TODO: configure if npm has not been run, and build is specified
|
||||
|
||||
if builder_has_action configure; then
|
||||
if builder_start_action configure; then
|
||||
verify_npm_setup
|
||||
builder_report success configure
|
||||
builder_finish_action success configure
|
||||
fi
|
||||
|
||||
# We always need to clean first because the wrapping function
|
||||
|
|
@ -95,12 +95,16 @@ fi
|
|||
# of typescript, we need to avoid this!
|
||||
# TODO: we should try and rework this to avoid the need to manually wrap
|
||||
|
||||
if builder_has_action clean || builder_has_action build >/dev/null; then
|
||||
if builder_start_action clean; then
|
||||
npm run clean
|
||||
builder_report success clean
|
||||
builder_finish_action success clean
|
||||
fi
|
||||
|
||||
if builder_has_action build; then
|
||||
if builder_start_action build; then
|
||||
if ! builder_has_action clean; then
|
||||
npm run clean
|
||||
fi
|
||||
|
||||
# Ensure keyman-version is properly build (requires build script)
|
||||
"$KEYMAN_ROOT/common/web/keyman-version/build.sh" || fail "Could not build keyman-version"
|
||||
|
||||
|
|
@ -115,10 +119,10 @@ if builder_has_action build; then
|
|||
wrap-worker-code LMLayerWorkerCode "${WORKER_OUTPUT}/intermediate.js" > "${WORKER_OUTPUT_FILENAME}" || die
|
||||
cp "${WORKER_OUTPUT_FILENAME}" "${WORKER_TEST_BUNDLE_TARGET_FILENAME}" || die
|
||||
|
||||
builder_report success build
|
||||
builder_finish_action success build
|
||||
fi
|
||||
|
||||
if builder_has_action test; then
|
||||
if builder_start_action test; then
|
||||
npm test || fail "Tests failed"
|
||||
builder_report success test
|
||||
builder_finish_action success test
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
"@keymanapp/resources-gosh": "*",
|
||||
"@types/node": "^10.17.21",
|
||||
"chai": "^4.3.4",
|
||||
"karma": "^6.3.4",
|
||||
"karma": "^6.4.1",
|
||||
"karma-browserstack-launcher": "^1.6.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
|
|
|
|||
78
common/web/recorder/build.sh
Executable file
78
common/web/recorder/build.sh
Executable file
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Compiles development-related KeymanWeb resources for use with developing/running tests.
|
||||
# - the Recorder module (for engine tests)
|
||||
# - the DOM module (for touch-alias and element-interface tests)
|
||||
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "$(dirname "$THIS_SCRIPT")/../../../resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
|
||||
|
||||
################################ Main script ################################
|
||||
|
||||
|
||||
builder_describe \
|
||||
"Compiles the web-oriented utility function module." \
|
||||
configure \
|
||||
clean \
|
||||
build \
|
||||
":module Builds recorder-core module" \
|
||||
":proctor Builds headless-testing, node-oriented 'proctor' component"
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
# START - Script parameter configuration
|
||||
REPORT_STYLE="local" # Default setting.
|
||||
|
||||
if builder_has_option --ci; then
|
||||
REPORT_STYLE="ci"
|
||||
|
||||
echo "Replacing user-friendly test reports with CI-friendly versions."
|
||||
fi
|
||||
|
||||
# END - Script parameter configuration
|
||||
|
||||
function do_configure() {
|
||||
verify_npm_setup
|
||||
"$KEYMAN_ROOT/common/web/keyman-version/build.sh"
|
||||
}
|
||||
|
||||
if builder_start_action configure :module; then
|
||||
do_configure
|
||||
builder_finish_action success configure :module
|
||||
fi
|
||||
|
||||
if builder_start_action configure :proctor; then
|
||||
if builder_has_action configure :module; then
|
||||
echo "Configuration already completed in configure:module; skipping."
|
||||
else
|
||||
do_configure
|
||||
fi
|
||||
builder_finish_action success configure :proctor
|
||||
fi
|
||||
|
||||
if builder_start_action clean :module; then
|
||||
npm run tsc -- -b --clean "$THIS_SCRIPT_PATH/src/tsconfig.json"
|
||||
builder_finish_action success clean :module
|
||||
fi
|
||||
|
||||
if builder_start_action clean :proctor; then
|
||||
npm run tsc -- -b --clean "$THIS_SCRIPT_PATH/src/nodeProctor.tsconfig.json"
|
||||
builder_finish_action success clean :proctor
|
||||
fi
|
||||
|
||||
if builder_start_action build :module; then
|
||||
npm run tsc -- --build "$THIS_SCRIPT_PATH/src/tsconfig.json"
|
||||
builder_finish_action success build :module
|
||||
fi
|
||||
|
||||
if builder_start_action build :proctor; then
|
||||
npm run tsc -- --build "$THIS_SCRIPT_PATH/src/nodeProctor.tsconfig.json"
|
||||
builder_finish_action success build :proctor
|
||||
fi
|
||||
|
|
@ -3,7 +3,8 @@
|
|||
"description": "Core classes used to develop KeymanWeb test cases based on keystrokes",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"tsc": "tsc"
|
||||
"tsc": "tsc",
|
||||
"clean": "tsc -b --clean src/tsconfig.json && tsc -b --clean src/nodeProctor.tsconfig.json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Compiles development-related KeymanWeb resources for use with developing/running tests.
|
||||
# - the Recorder module (for engine tests)
|
||||
# - the DOM module (for touch-alias and element-interface tests)
|
||||
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "$(dirname "$THIS_SCRIPT")/../../../../resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
|
||||
|
||||
display_usage ( ) {
|
||||
echo "build.sh [-skip-package-install]"
|
||||
echo
|
||||
echo " -skip-package-install skips the `npm install` dependency check."
|
||||
echo " (or -S) Intended for use when this script is called by another build script."
|
||||
echo ""
|
||||
echo " If more than one target is specified, the last one will take precedence."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Establish default build parameters
|
||||
set_default_vars ( ) {
|
||||
FETCH_DEPS=true
|
||||
}
|
||||
|
||||
set_default_vars
|
||||
|
||||
# Parse args
|
||||
while [[ $# -gt 0 ]] ; do
|
||||
key="$1"
|
||||
case $key in
|
||||
-skip-package-install|-S)
|
||||
set_default_vars
|
||||
FETCH_DEPS=false
|
||||
;;
|
||||
esac
|
||||
shift # past argument
|
||||
done
|
||||
|
||||
if [ $FETCH_DEPS = true ]; then
|
||||
verify_npm_setup
|
||||
# We need to build keyman-version and lm-worker with a script for now
|
||||
"$KEYMAN_ROOT/common/web/keyman-version/build.sh" || fail "Could not build keyman-version"
|
||||
fi
|
||||
|
||||
npm run tsc -- --build "$THIS_SCRIPT_PATH/tsconfig.json"
|
||||
if [ $? -ne 0 ]; then
|
||||
fail "KeymanWeb recorder-core compilation failed."
|
||||
fi
|
||||
|
||||
npm run tsc -- --build "$THIS_SCRIPT_PATH/nodeProctor.tsconfig.json"
|
||||
if [ $? -ne 0 ]; then
|
||||
fail "Node-based unit-test Proctor compilation failed."
|
||||
fi
|
||||
|
|
@ -11,41 +11,28 @@ THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BA
|
|||
|
||||
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
|
||||
|
||||
display_usage ( ) {
|
||||
echo "build.sh [-skip-package-install]"
|
||||
echo
|
||||
echo " -skip-package-install skips the \`npm install\` dependency check."
|
||||
echo " (or -S) Intended for use when this script is called by another build script."
|
||||
echo ""
|
||||
echo " If more than one target is specified, the last one will take precedence."
|
||||
exit 1
|
||||
}
|
||||
################################ Main script ################################
|
||||
|
||||
# Establish default build parameters
|
||||
set_default_vars ( ) {
|
||||
FETCH_DEPS=true
|
||||
# We need to build keyman-version and lm-worker with a script for now
|
||||
"$KEYMAN_ROOT/common/web/keyman-version/build.sh" || fail "Could not build keyman-version"
|
||||
}
|
||||
builder_describe \
|
||||
"Compiles the web-oriented utility function module." \
|
||||
configure clean build
|
||||
|
||||
set_default_vars
|
||||
builder_parse "$@"
|
||||
|
||||
# Parse args
|
||||
while [[ $# -gt 0 ]] ; do
|
||||
key="$1"
|
||||
case $key in
|
||||
-skip-package-install|-S)
|
||||
FETCH_DEPS=false
|
||||
;;
|
||||
esac
|
||||
shift # past argument
|
||||
done
|
||||
if builder_start_action configure; then
|
||||
verify_npm_setup
|
||||
|
||||
if [ "$FETCH_DEPS" = true ]; then
|
||||
verify_npm_setup
|
||||
"$KEYMAN_ROOT/common/web/keyman-version/build.sh"
|
||||
|
||||
builder_finish_action success configure
|
||||
fi
|
||||
|
||||
npm run tsc -- --build "$THIS_SCRIPT_PATH/tsconfig.json"
|
||||
if [ $? -ne 0 ]; then
|
||||
fail "Utility-function package compilation failed."
|
||||
if builder_start_action clean; then
|
||||
npm run clean
|
||||
builder_finish_action success clean
|
||||
fi
|
||||
|
||||
if builder_start_action build; then
|
||||
npm run tsc -- --build "$THIS_SCRIPT_PATH/tsconfig.json"
|
||||
builder_finish_action success build
|
||||
fi
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
"types": "./build/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "gosh ./build.sh",
|
||||
"clean": "tsc -b --clean",
|
||||
"tsc": "tsc"
|
||||
},
|
||||
"repository": {
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ uses
|
|||
function CompileKeyboard(FInFile, FOutFile: string; FDebug, FWarnAsError: Boolean): Boolean; forward; // I4706
|
||||
function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion: Boolean): Boolean; forward;
|
||||
//function CompilerMessage(line: Integer; msgcode: LongWord; text: PAnsiChar): Integer; stdcall; forward;
|
||||
procedure FixupPathSlashes(var path: string); forward;
|
||||
|
||||
procedure Run;
|
||||
var
|
||||
|
|
@ -269,6 +270,14 @@ begin
|
|||
try
|
||||
if not FSilent then writeln('');
|
||||
|
||||
FixupPathSlashes(FParamDebugFile);
|
||||
FixupPathSlashes(FParamTarget);
|
||||
FixupPathSlashes(FParamInfile);
|
||||
FixupPathSlashes(FParamOutfile);
|
||||
FixupPathSlashes(FParamInfile2);
|
||||
FixupPathSlashes(FInstallerMSI);
|
||||
FixupPathSlashes(FJsonSchemaPath);
|
||||
|
||||
if FParamDebugfile <> '' then
|
||||
begin
|
||||
hOutfile := CreateFile(PChar(FParamDebugfile), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0);
|
||||
|
|
@ -347,6 +356,10 @@ begin
|
|||
else TProjectLogConsole.Instance.Log(plsFailure, FInFile, 'Keyboard '+FInFile+' could not be compiled.', 0, 0);
|
||||
end;
|
||||
|
||||
procedure FixupPathSlashes(var path: string);
|
||||
begin
|
||||
path := path.Replace('/', '\', [rfReplaceAll]);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@
|
|||
"@sentry/node": "^6.16.1",
|
||||
"chalk": "^4.1.2",
|
||||
"express": "^4.17.2",
|
||||
"multer": "^1.4.4",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"ngrok": "^4.2.2",
|
||||
"open": "^8.4.0",
|
||||
"ws": "^8.3.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"hetrodo-node-hide-console-window-napi": "keymanapp/hetrodo-node-hide-console-window-napi#keyman-15.0",
|
||||
"node-windows-trayicon": "keymanapp/node-windows-trayicon#keyman-15.0"
|
||||
"node-windows-trayicon": "keymanapp/node-windows-trayicon#keyman-16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@keymanapp/resources-gosh": "*",
|
||||
|
|
|
|||
|
|
@ -1467,7 +1467,7 @@ begin
|
|||
9: Result := '\t';
|
||||
10: Result := '\n';
|
||||
13: Result := '\r';
|
||||
else Result := '\x'+IntToHex(ch,1);
|
||||
else Result := '\x'+IntToHex(ch,2);
|
||||
end;
|
||||
end
|
||||
else
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ interface
|
|||
|
||||
uses
|
||||
System.SysUtils,
|
||||
Xml.XMLDom,
|
||||
Xml.XMLIntf,
|
||||
|
||||
KPSFile,
|
||||
|
|
@ -108,7 +109,19 @@ begin
|
|||
pack := TKPSFile.Create;
|
||||
try
|
||||
pack.FileName := FileName;
|
||||
pack.LoadXML;
|
||||
try
|
||||
pack.LoadXML;
|
||||
except
|
||||
// ignore errors in the xml; will reduce metadata visible to the user
|
||||
on E:EDOMParseError do
|
||||
begin
|
||||
Exit;
|
||||
end;
|
||||
on E:DOMException do
|
||||
begin
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
for i := 0 to pack.Files.Count - 1 do
|
||||
if Project.Files.IndexOfFileName(pack.Files[i].FileName) < 0 then
|
||||
CreateProjectFile(Project, pack.Files[i].FileName, Self);
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ struct _IBusKeymanEngine {
|
|||
gchar *ldmlfile;
|
||||
gchar *kb_name;
|
||||
gchar *char_buffer;
|
||||
gunichar firstsurrogate;
|
||||
gboolean lctrl_pressed;
|
||||
gboolean rctrl_pressed;
|
||||
gboolean lalt_pressed;
|
||||
|
|
@ -223,7 +222,6 @@ static void reset_context(IBusEngine *engine)
|
|||
km_kbp_context *context;
|
||||
|
||||
g_message("reset_context");
|
||||
keyman->firstsurrogate = 0;
|
||||
context = km_kbp_state_context(keyman->state);
|
||||
if ((engine->client_capabilities & IBUS_CAP_SURROUNDING_TEXT) != 0)
|
||||
{
|
||||
|
|
@ -313,7 +311,6 @@ ibus_keyman_engine_constructor(
|
|||
|
||||
keyman->kb_name = NULL;
|
||||
keyman->ldmlfile = NULL;
|
||||
keyman->firstsurrogate = 0;
|
||||
keyman->lalt_pressed = FALSE;
|
||||
keyman->lctrl_pressed = FALSE;
|
||||
keyman->ralt_pressed = FALSE;
|
||||
|
|
@ -505,44 +502,25 @@ process_unicode_char_action(
|
|||
IBusKeymanEngine *keyman,
|
||||
const km_kbp_action_item *action_item
|
||||
) {
|
||||
if (g_unichar_type(action_item->character) == G_UNICODE_SURROGATE) {
|
||||
if (keyman->firstsurrogate == 0) {
|
||||
keyman->firstsurrogate = action_item->character;
|
||||
g_message("first surrogate %d", keyman->firstsurrogate);
|
||||
} else {
|
||||
glong items_read, items_written;
|
||||
gunichar2 utf16_pair[2] = {keyman->firstsurrogate, action_item->character};
|
||||
gchar *utf8_pair = g_utf16_to_utf8(utf16_pair, 2, &items_read, &items_written, NULL);
|
||||
if (keyman->char_buffer == NULL) {
|
||||
keyman->char_buffer = utf8_pair;
|
||||
} else {
|
||||
gchar *new_buffer = g_strjoin("", keyman->char_buffer, utf8_pair, NULL);
|
||||
g_free(keyman->char_buffer);
|
||||
g_free(utf8_pair);
|
||||
keyman->char_buffer = new_buffer;
|
||||
}
|
||||
keyman->firstsurrogate = 0;
|
||||
}
|
||||
g_assert(g_unichar_type(action_item->character) != G_UNICODE_SURROGATE);
|
||||
gchar *utf8 = (gchar *)g_new0(gchar, 12);
|
||||
gint numbytes = g_unichar_to_utf8(action_item->character, utf8);
|
||||
if (numbytes > 12) {
|
||||
g_error("g_unichar_to_utf8 overflowing buffer");
|
||||
g_free(utf8);
|
||||
} else {
|
||||
gchar *utf8 = (gchar *)g_new0(gchar, 12);
|
||||
gint numbytes = g_unichar_to_utf8(action_item->character, utf8);
|
||||
if (numbytes > 12) {
|
||||
g_error("g_unichar_to_utf8 overflowing buffer");
|
||||
g_free(utf8);
|
||||
g_message("unichar:U+%04x, bytes:%d, string:%s", action_item->character, numbytes, utf8);
|
||||
if (keyman->char_buffer == NULL) {
|
||||
g_message("setting buffer to converted unichar");
|
||||
keyman->char_buffer = utf8;
|
||||
} else {
|
||||
g_message("unichar:U+%04x, bytes:%d, string:%s", action_item->character, numbytes, utf8);
|
||||
if (keyman->char_buffer == NULL) {
|
||||
g_message("setting buffer to converted unichar");
|
||||
keyman->char_buffer = utf8;
|
||||
} else {
|
||||
g_message("appending converted unichar to CHAR buffer");
|
||||
gchar *new_buffer = g_strjoin("", keyman->char_buffer, utf8, NULL);
|
||||
g_free(keyman->char_buffer);
|
||||
g_free(utf8);
|
||||
keyman->char_buffer = new_buffer;
|
||||
}
|
||||
g_message("CHAR buffer is now %s", keyman->char_buffer);
|
||||
g_message("appending converted unichar to CHAR buffer");
|
||||
gchar *new_buffer = g_strjoin("", keyman->char_buffer, utf8, NULL);
|
||||
g_free(keyman->char_buffer);
|
||||
g_free(utf8);
|
||||
keyman->char_buffer = new_buffer;
|
||||
}
|
||||
g_message("CHAR buffer is now %s", keyman->char_buffer);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -671,7 +649,6 @@ process_capslock_action(
|
|||
}
|
||||
|
||||
static gboolean process_end_action(IBusKeymanEngine *keyman) {
|
||||
keyman->firstsurrogate = 0;
|
||||
if (keyman->char_buffer != NULL) {
|
||||
ibus_keyman_engine_commit_string(keyman, keyman->char_buffer);
|
||||
g_free(keyman->char_buffer);
|
||||
|
|
|
|||
311
package-lock.json
generated
311
package-lock.json
generated
|
|
@ -170,7 +170,7 @@
|
|||
"@keymanapp/resources-gosh": "*",
|
||||
"@types/node": "^10.17.21",
|
||||
"chai": "^4.3.4",
|
||||
"karma": "^6.3.4",
|
||||
"karma": "^6.4.1",
|
||||
"karma-browserstack-launcher": "^1.6.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
|
|
@ -325,7 +325,7 @@
|
|||
"@keymanapp/resources-gosh": "*",
|
||||
"@types/node": "^10.17.21",
|
||||
"chai": "^4.3.4",
|
||||
"karma": "^6.3.4",
|
||||
"karma": "^6.4.1",
|
||||
"karma-browserstack-launcher": "^1.6.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
|
|
@ -573,7 +573,7 @@
|
|||
"@sentry/node": "^6.16.1",
|
||||
"chalk": "^4.1.2",
|
||||
"express": "^4.17.2",
|
||||
"multer": "^1.4.4",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"ngrok": "^4.2.2",
|
||||
"open": "^8.4.0",
|
||||
"ws": "^8.3.0"
|
||||
|
|
@ -596,7 +596,7 @@
|
|||
},
|
||||
"optionalDependencies": {
|
||||
"hetrodo-node-hide-console-window-napi": "keymanapp/hetrodo-node-hide-console-window-napi#keyman-15.0",
|
||||
"node-windows-trayicon": "keymanapp/node-windows-trayicon#keyman-15.0"
|
||||
"node-windows-trayicon": "keymanapp/node-windows-trayicon#keyman-16.0"
|
||||
}
|
||||
},
|
||||
"developer/src/server/node_modules/@types/mocha": {
|
||||
|
|
@ -611,6 +611,42 @@
|
|||
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
|
||||
"dev": true
|
||||
},
|
||||
"developer/src/server/node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||
"dependencies": {
|
||||
"streamsearch": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
}
|
||||
},
|
||||
"developer/src/server/node_modules/multer": {
|
||||
"version": "1.4.5-lts.1",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
|
||||
"integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^1.0.0",
|
||||
"concat-stream": "^1.5.2",
|
||||
"mkdirp": "^0.5.4",
|
||||
"object-assign": "^4.1.1",
|
||||
"type-is": "^1.6.4",
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"developer/src/server/node_modules/streamsearch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"developer/src/server/node_modules/ts-node": {
|
||||
"version": "10.8.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.1.tgz",
|
||||
|
|
@ -1970,39 +2006,6 @@
|
|||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
||||
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
|
||||
"dependencies": {
|
||||
"dicer": "0.2.5",
|
||||
"readable-stream": "1.1.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/busboy/node_modules/isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||
},
|
||||
"node_modules/busboy/node_modules/readable-stream": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.1",
|
||||
"isarray": "0.0.1",
|
||||
"string_decoder": "~0.10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/busboy/node_modules/string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
|
|
@ -2133,9 +2136,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/cliui/node_modules/ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
|
@ -2553,39 +2556,6 @@
|
|||
"integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/dicer": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
|
||||
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
|
||||
"dependencies": {
|
||||
"readable-stream": "1.1.x",
|
||||
"streamsearch": "0.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dicer/node_modules/isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||
},
|
||||
"node_modules/dicer/node_modules/readable-stream": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.1",
|
||||
"isarray": "0.0.1",
|
||||
"string_decoder": "~0.10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/dicer/node_modules/string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
|
||||
|
|
@ -3763,9 +3733,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/karma": {
|
||||
"version": "6.3.17",
|
||||
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.17.tgz",
|
||||
"integrity": "sha512-2TfjHwrRExC8yHoWlPBULyaLwAFmXmxQrcuFImt/JsAsSZu1uOWTZ1ZsWjqQtWpHLiatJOHL5jFjXSJIgCd01g==",
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz",
|
||||
"integrity": "sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@colors/colors": "1.5.0",
|
||||
|
|
@ -3787,7 +3757,7 @@
|
|||
"qjobs": "^1.2.0",
|
||||
"range-parser": "^1.2.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"socket.io": "^4.2.0",
|
||||
"socket.io": "^4.4.1",
|
||||
"source-map": "^0.6.1",
|
||||
"tmp": "^0.2.1",
|
||||
"ua-parser-js": "^0.7.30",
|
||||
|
|
@ -4788,24 +4758,6 @@
|
|||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/multer": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz",
|
||||
"integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^0.2.11",
|
||||
"concat-stream": "^1.5.2",
|
||||
"mkdirp": "^0.5.4",
|
||||
"object-assign": "^4.1.1",
|
||||
"on-finished": "^2.3.0",
|
||||
"type-is": "^1.6.4",
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
|
|
@ -5901,14 +5853,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/streamsearch": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
||||
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
|
|
@ -6468,9 +6412,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
|
@ -6663,9 +6607,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
|
@ -6971,7 +6915,7 @@
|
|||
"@sentry/cli": "2.2.0",
|
||||
"chai": "^4.3.4",
|
||||
"google-closure-compiler-java": "^20200224.0.0",
|
||||
"karma": "^6.3.4",
|
||||
"karma": "^6.4.1",
|
||||
"karma-browserstack-launcher": "^1.6.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
|
|
@ -7238,9 +7182,9 @@
|
|||
"express": "^4.17.2",
|
||||
"hetrodo-node-hide-console-window-napi": "keymanapp/hetrodo-node-hide-console-window-napi#keyman-15.0",
|
||||
"mocha": "^10.0.0",
|
||||
"multer": "^1.4.4",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"ngrok": "^4.2.2",
|
||||
"node-windows-trayicon": "keymanapp/node-windows-trayicon#keyman-15.0",
|
||||
"node-windows-trayicon": "keymanapp/node-windows-trayicon#keyman-16.0",
|
||||
"open": "^8.4.0",
|
||||
"ts-node": "^10.4.0",
|
||||
"tsc-watch": "^4.5.0",
|
||||
|
|
@ -7260,6 +7204,33 @@
|
|||
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
|
||||
"dev": true
|
||||
},
|
||||
"busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||
"requires": {
|
||||
"streamsearch": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"multer": {
|
||||
"version": "1.4.5-lts.1",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
|
||||
"integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
|
||||
"requires": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^1.0.0",
|
||||
"concat-stream": "^1.5.2",
|
||||
"mkdirp": "^0.5.4",
|
||||
"object-assign": "^4.1.1",
|
||||
"type-is": "^1.6.4",
|
||||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"streamsearch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "10.8.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.1.tgz",
|
||||
|
|
@ -7457,7 +7428,7 @@
|
|||
"@types/node": "^10.17.21",
|
||||
"chai": "^4.3.4",
|
||||
"es6-shim": "^0.35.5",
|
||||
"karma": "^6.3.4",
|
||||
"karma": "^6.4.1",
|
||||
"karma-browserstack-launcher": "^1.6.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
|
|
@ -7505,7 +7476,7 @@
|
|||
"@types/node": "^10.17.21",
|
||||
"chai": "^4.3.4",
|
||||
"es6-shim": "^0.35.5",
|
||||
"karma": "^6.3.4",
|
||||
"karma": "^6.4.1",
|
||||
"karma-browserstack-launcher": "^1.6.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
|
|
@ -8646,38 +8617,6 @@
|
|||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
},
|
||||
"busboy": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
||||
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
|
||||
"requires": {
|
||||
"dicer": "0.2.5",
|
||||
"readable-stream": "1.1.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.1",
|
||||
"isarray": "0.0.1",
|
||||
"string_decoder": "~0.10.x"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||
}
|
||||
}
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
|
|
@ -8776,9 +8715,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
|
|
@ -9102,38 +9041,6 @@
|
|||
"integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
|
||||
"dev": true
|
||||
},
|
||||
"dicer": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
|
||||
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
|
||||
"requires": {
|
||||
"readable-stream": "1.1.x",
|
||||
"streamsearch": "0.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.1",
|
||||
"isarray": "0.0.1",
|
||||
"string_decoder": "~0.10.x"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
||||
}
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
|
||||
|
|
@ -10056,9 +9963,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"karma": {
|
||||
"version": "6.3.17",
|
||||
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.17.tgz",
|
||||
"integrity": "sha512-2TfjHwrRExC8yHoWlPBULyaLwAFmXmxQrcuFImt/JsAsSZu1uOWTZ1ZsWjqQtWpHLiatJOHL5jFjXSJIgCd01g==",
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz",
|
||||
"integrity": "sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@colors/colors": "1.5.0",
|
||||
|
|
@ -10080,7 +9987,7 @@
|
|||
"qjobs": "^1.2.0",
|
||||
"range-parser": "^1.2.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"socket.io": "^4.2.0",
|
||||
"socket.io": "^4.4.1",
|
||||
"source-map": "^0.6.1",
|
||||
"tmp": "^0.2.1",
|
||||
"ua-parser-js": "^0.7.30",
|
||||
|
|
@ -10315,7 +10222,7 @@
|
|||
"chai": "^4.3.4",
|
||||
"eventemitter3": "^4.0.0",
|
||||
"google-closure-compiler-java": "^20200224.0.0",
|
||||
"karma": "^6.3.4",
|
||||
"karma": "^6.4.1",
|
||||
"karma-browserstack-launcher": "^1.6.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
|
|
@ -10888,21 +10795,6 @@
|
|||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"multer": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz",
|
||||
"integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==",
|
||||
"requires": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^0.2.11",
|
||||
"concat-stream": "^1.5.2",
|
||||
"mkdirp": "^0.5.4",
|
||||
"object-assign": "^4.1.1",
|
||||
"on-finished": "^2.3.0",
|
||||
"type-is": "^1.6.4",
|
||||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
|
|
@ -11739,11 +11631,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"streamsearch": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
||||
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
|
|
@ -12163,9 +12050,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
|
|
@ -12259,9 +12146,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ including this file...
|
|||
|
||||
## Various tools
|
||||
|
||||
### build-utils.sh
|
||||
|
||||
See [build-utils.md](build-utils.md)
|
||||
|
||||
### vswhere
|
||||
|
||||
vswhere.exe is used by Keyman Core to select a Visual Studio version on Windows.
|
||||
|
|
|
|||
587
resources/build/build-utils.md
Normal file
587
resources/build/build-utils.md
Normal file
|
|
@ -0,0 +1,587 @@
|
|||
# Using the build-utils.sh builder functions
|
||||
|
||||
The Keyman repository is standardising on bash scripts for builds. These may
|
||||
call project-specific builders, such as `tsc` for Typescript projects, `meson`
|
||||
for our cross-platform C++ projects, `xcodebuild` on macOS and iOS projects,
|
||||
`gradle` for Android, `nmake` in our Windows builds, or worse, but each project
|
||||
should also have a `build.sh` script in its root.
|
||||
|
||||
We have standardised on parameters and structure for `build.sh` scripts. The
|
||||
objectives are:
|
||||
|
||||
1. to be consistent in use of script parameters across all platforms and
|
||||
projects
|
||||
2. to be self-documenting in usage (`--help` should always tell you all you need
|
||||
to know)
|
||||
3. for the scripts to be easily readable, coherent, and straightforward for
|
||||
anyone involved in the project to maintain.
|
||||
|
||||
* [Jump to API definitions](#builder-api-functions-and-variables)
|
||||
|
||||
---
|
||||
|
||||
# Anatomy of a build script
|
||||
|
||||
A build script is made up of three sections:
|
||||
|
||||
* [Prologue](#build-script-prologue)
|
||||
* [Definition](#defining-build-script-parameters)
|
||||
* [Processing actions](#build-script-actions)
|
||||
|
||||
# Build script prologue
|
||||
|
||||
A build script should always start with the following prologue:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# <short description of the script purpose>
|
||||
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "$(dirname "$THIS_SCRIPT")/<relative-path-to-repo-root>/resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
# any other includes, such as jq.inc.sh
|
||||
|
||||
################################ Main script ################################
|
||||
```
|
||||
|
||||
This prologue ensures that we have a consistent environment. Explaining each section:
|
||||
|
||||
## Shebang
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
```
|
||||
|
||||
We use the `/usr/bin/env` prefix to ensure that we get the right version of bash
|
||||
on macOS (installed via homebrew, rather than the obsolete system-provided one).
|
||||
This also works fine on Linux, git bash on Windows, and WSL.
|
||||
|
||||
## Bash options (`set -eu`)
|
||||
|
||||
We use `set -eu` throughout:
|
||||
|
||||
* `-e` to exit on any statement failure
|
||||
* `-u` to abort on unset variable use (usually coming from typos)
|
||||
|
||||
## Standard build script include
|
||||
|
||||
```bash
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "$(dirname "$THIS_SCRIPT")/<relative-path-to-repo-root>/resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
```
|
||||
|
||||
This somewhat unwieldy incantation handles all our build environments, with
|
||||
`greadlink` necessary on macOS (again installed with homebrew) due to the
|
||||
included macOS `readlink` struggling with canonicalization of symbolic links.
|
||||
The intent is to get a good solid consistent path for the script so that we can
|
||||
safely include the build script, no matter what `pwd` is when the script is run.
|
||||
|
||||
The only modification permissible in this block is the
|
||||
`<relative-path-to-repo-root>` text which will be a series of `../` paths taking
|
||||
us to the repository root from the location of the script itself.
|
||||
|
||||
Inclusion of other scripts should be kept outside this standard build script
|
||||
include section, as we may programatically update (a.ka. global
|
||||
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
|
||||
available, so other include scripts should be sourced accordingly, for example:
|
||||
|
||||
```bash
|
||||
. "$KEYMAN_ROOT/resources/build/jq.inc.sh"
|
||||
```
|
||||
|
||||
## Split
|
||||
|
||||
The comment line splitting the prologue from the body of the script is optional,
|
||||
but makes the script easy to scan!
|
||||
|
||||
```bash
|
||||
################################ Main script ################################
|
||||
```
|
||||
|
||||
# Defining build script parameters
|
||||
|
||||
The build script should use the `builder` functions and variables to process its
|
||||
command line and control its run.
|
||||
|
||||
Build scripts can define **targets**, **actions**, and **options**, which are
|
||||
parameters passed in to the script when it is run by a user or called by
|
||||
another script:
|
||||
|
||||
* **targets**: these are the expected outputs of the build script. A target is
|
||||
prefixed with a `:`, for example `:app`. If no target is defined for a script,
|
||||
then the default target `:project` is used.
|
||||
|
||||
* **actions**: these are the various actions that a build script can take, such
|
||||
as `clean`, or `build`. If no action is passed in to on a given script
|
||||
invocation, then the default action is `build` (unless the script defines an
|
||||
alternative default).
|
||||
|
||||
* **options**: these are possible additional options that can be passed to the
|
||||
script to modify the behavior of the script. All options should be prefixed
|
||||
with `--`, such as `--debug`, and a shorthand single letter form may also be
|
||||
optionally provided, such as `-d`.
|
||||
|
||||
Note that when we call scripts from other scripts, particularly in CI, we
|
||||
should always use the longhand form; the shorthand form is for convenience on
|
||||
the command line only.
|
||||
|
||||
Be judicious in use of options; a common one will be `--debug` to do a debug
|
||||
build, but overuse of options will make scripts hard to use.
|
||||
|
||||
Options can be used to provide additional data, by including `=<varname>` in
|
||||
their definition. Otherwise, they are treated as a boolean.
|
||||
|
||||
The first step in your script is to describe the available parameters, using
|
||||
[`builder_describe`], for example:
|
||||
|
||||
```bash
|
||||
builder_describe \
|
||||
"Tests the build-utils.sh builder functions. This is merely an example." \
|
||||
clean \
|
||||
build \
|
||||
test \
|
||||
"install Installs something on the local system" \
|
||||
:app \
|
||||
":engine The internal engine for the app" \
|
||||
"--power,-p Use powerful mode" \
|
||||
"--zoom,-z Use zoom mode" \
|
||||
"--feature=FOO Enable feature foo"
|
||||
```
|
||||
|
||||
After describing the available parameters, you need to pass the command line
|
||||
parameters in for parsing and validation:
|
||||
|
||||
```bash
|
||||
builder_parse "$@"
|
||||
```
|
||||
|
||||
If any parameters are invalid, the script will be terminated by
|
||||
[`builder_parse`](#builderparse-function) with an error and will print the
|
||||
script usage help using [`builder_display_usage`](#builderdisplayusage-function).
|
||||
|
||||
# Build script actions
|
||||
|
||||
Then, check each of the potential actions, in the order that they should be run,
|
||||
for example:
|
||||
|
||||
```bash
|
||||
if builder_start_action configure; then
|
||||
verify_npm_setup
|
||||
builder_finish_action success configure
|
||||
fi
|
||||
|
||||
if builder_start_action clean; then
|
||||
npm run clean
|
||||
rm -f ./version.inc.ts
|
||||
builder_finish_action success clean
|
||||
fi
|
||||
|
||||
if builder_start_action build; then
|
||||
npm run build -- $builder_verbose
|
||||
builder_finish_action success build
|
||||
fi
|
||||
```
|
||||
|
||||
Each step is run separately, is started with [`builder_start_action`], and
|
||||
finishes with [`builder_finish_action`]. If a build step is complex, it may be
|
||||
worthwhile splitting it into a separate function or even a separate script
|
||||
include.
|
||||
|
||||
Use the longer form of `if ...; then` rather than the shorter `[ ... ] && `
|
||||
pattern, for consistency and readability.
|
||||
|
||||
# Standard builder parameters
|
||||
|
||||
The following parameters are pre-defined and should not be overridden:
|
||||
|
||||
* `--help`, `-h`: displays help on using this script
|
||||
* `--color`: forces on ANSI color output for the script
|
||||
* `--no-color`: forces off ANSI color output for the script
|
||||
* `--verbose`, `-v`: verbose mode, sets the [`$builder_verbose`] variable
|
||||
|
||||
# Builder API functions and variables
|
||||
|
||||
|
||||
## `builder_check_color` function
|
||||
|
||||
If you wish to provide [formatting variables] in your [`builder_describe`] call, you
|
||||
will need to use `builder_check_color` first. This function takes the same
|
||||
parameters as [`builder_parse`].
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
builder_check_color "$@"
|
||||
builder_describe "sample" \
|
||||
"--ci For use with action ${BUILDER_TERM_START}test${BUILDER_TERM_END} - emits CI-friendly test reports"
|
||||
```
|
||||
|
||||
|
||||
## `builder_describe` function
|
||||
|
||||
Describes a build script, defines available parameters and their meanings. Use
|
||||
together with `builder_parse` to process input parameters.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
builder_describe description param_desc...
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
* `description`: A short description of what the script does
|
||||
* `param_desc`: Space separated name and description of parameter.
|
||||
|
||||
### Description
|
||||
|
||||
The `param_desc` parameter has two components: first, the parameter definition,
|
||||
and second, an optional description for the parameter. The parameter definition
|
||||
must not include any spaces, and the description, if included, must be preceded
|
||||
by at least one space. This means that the parameters should be surrounded by
|
||||
quote marks so that they are treated as a single parameter, for example:
|
||||
|
||||
```bash
|
||||
builder_describe "Sample script" \
|
||||
":app the app" \
|
||||
configure \
|
||||
build \
|
||||
test \
|
||||
"--print-errors,-p Print errors"
|
||||
```
|
||||
|
||||
Or, a shorthand version for a simple script:
|
||||
|
||||
```bash
|
||||
builder_describe "Build version module" clean configure build test
|
||||
```
|
||||
|
||||
Each `param_desc` parameter defines a **target**, **action**, or **option**. All
|
||||
parameters passed on the command line in a call to the script (prior to `--`,
|
||||
see [`$builder_extra_params`] variable) must match one of the parameters defined
|
||||
here.
|
||||
|
||||
**Targets** are defined by including a `:` prefix, for example:
|
||||
|
||||
```bash
|
||||
builder_describe "Sample script" :engine ":proxy the proxy module"
|
||||
```
|
||||
|
||||
There are several predefined targets. These will not be available to users of
|
||||
your script unless you include them in the `builder_describe` call, but when
|
||||
used, they have default descriptions, which can be used instead of adding your
|
||||
own in the call:
|
||||
* `:project`: `"this project"`
|
||||
* `:app`: `"main app"`
|
||||
* `:engine`: `"engine module"`
|
||||
* `:module`: `"this module"`
|
||||
* `:tools`: `"build tools for this project"`
|
||||
|
||||
**Actions** are defined as single words, for example:
|
||||
|
||||
```bash
|
||||
builder_describe "Sample script build "install installs app on local system"
|
||||
```
|
||||
|
||||
There are several predefined actions. Again, these will not be available to
|
||||
users unless you include them in the call, but they do have default
|
||||
descriptions:
|
||||
* `clean`: `"remove build/ folder and build artifacts"`
|
||||
* `configure`: `"install dependencies, e.g. npm"`
|
||||
* `build`: `"build target(s)"`
|
||||
* `test`: `"run automated tests"`
|
||||
|
||||
The default action will be `build`, unless overridden by using the `+` suffix on
|
||||
a definition:
|
||||
|
||||
```bash
|
||||
builder_describe "Testing script" clean test+
|
||||
```
|
||||
|
||||
**Options** are defined by including a `--` prefix, for example:
|
||||
|
||||
```bash
|
||||
builder_describe "Sample script" \
|
||||
--debug,-d \
|
||||
"--out-path,-o=OUT_PATH Specify output path"
|
||||
```
|
||||
|
||||
A shorthand form may optionally be provided by appending `,-x` to the parameter
|
||||
definition, where `x` is a one letter shorthand form. Currently, shorthand forms
|
||||
may not be combined when invoking the script -- each must be passed separately.
|
||||
Ensure that you do not include a space after the comma.
|
||||
|
||||
By default, an option will be treated as a boolean. It can be tested with
|
||||
[`builder_has_option`]. If you need to pass additional data, then the
|
||||
`=<variable>` format specifies an environment variable where the additional data
|
||||
will be stored. When using this format, it is good to use [`builder_has_option`]
|
||||
to test for the presence of the parameter before attempting to use the variable.
|
||||
|
||||
**Note:** although the definition uses `=` to define the variable, when invoking
|
||||
script, the value should be passed in as a separate parameter.
|
||||
|
||||
There is one option with a predefined description: `--debug`. When including
|
||||
this, you should use `--debug,-d` to enable the shorthand form.
|
||||
|
||||
Note that you should not include any of the [standard builder parameters] here.
|
||||
|
||||
|
||||
## `builder_display_usage` function
|
||||
|
||||
Prints the help for the script, constructed from the [`builder_describe`]
|
||||
parameters, so must be called after `builder_describe`.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
builder_describe "sample" clean build test
|
||||
builder_display_usage
|
||||
```
|
||||
|
||||
|
||||
## `$builder_extra_params` variable
|
||||
|
||||
If a build script needs to be able to pass arbitrary additional parameters onto
|
||||
another tool, for example, to a test runner, then the `--` parameter can be
|
||||
used, for example:
|
||||
|
||||
```bash
|
||||
./build.sh test -- test-window-color --verbose
|
||||
```
|
||||
|
||||
These two additional parameters will be available in the `$builder_extra_params`
|
||||
array variable, which can then be used in a call to the tool, using the `${var[@]}`
|
||||
array expansion format:
|
||||
|
||||
```bash
|
||||
npm test -- "${builder_extra_params[@]}"
|
||||
```
|
||||
|
||||
|
||||
## `builder_finish_action` function
|
||||
|
||||
Finishes an action sequence. Should always be paired with [`builder_start_action`].
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
if builder_start_action action:target; then
|
||||
# ... do the action
|
||||
if something_failed; then
|
||||
builder_finish_action "yeah, something failed" action:target
|
||||
exit 1
|
||||
fi
|
||||
builder_finish_action success action:target
|
||||
fi
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
* **result**: Result or message -- `success`, `failure`, or a more detailed
|
||||
failure message
|
||||
* **action**: Action to test
|
||||
* **:target**: Target to test
|
||||
|
||||
These last two parameters can optionally be space separated.
|
||||
|
||||
### Description
|
||||
In normal circumstances, `builder_finish_action` will then print a corresponding
|
||||
message:
|
||||
|
||||
```
|
||||
## [common/web/keyman-version] action:target completed successfully
|
||||
```
|
||||
|
||||
When errors arise, a failure message will be printed, and the script will abort
|
||||
with a non-zero exit code:
|
||||
|
||||
```
|
||||
## [common/web/keyman-version] action:target failed with message: yeah, something failed
|
||||
```
|
||||
|
||||
|
||||
## `builder_has_action` function
|
||||
|
||||
This is similar to [`builder_start_action`], testing whether the script
|
||||
invocation included a specific action, but does not start the action, and thus
|
||||
does not print any messages to the console.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
if builder_has_action action:target; then
|
||||
# ...
|
||||
fi
|
||||
```
|
||||
|
||||
See [`builder_start_action`] for more details.
|
||||
|
||||
|
||||
## `builder_has_option` function
|
||||
|
||||
Tests if an option has been passed in the script invocation. The option must be
|
||||
defined in [`builder_describe`].
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
if builder_has_option --option; then
|
||||
# ...
|
||||
fi
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
* **--option**: The option to test. Must be the longhand form, and must be
|
||||
prefixed with `--`.
|
||||
|
||||
### Description
|
||||
|
||||
When testing for presence of options that take additional data, the additional
|
||||
data variable will only be set if the option is passed in the script invocation.
|
||||
So `builder_has_option` is a clean way to test for the presence of the option in
|
||||
this case too:
|
||||
|
||||
```bash
|
||||
builder_describe "Sample" "--path=OUT_PATH"
|
||||
builder_parse "$@"
|
||||
|
||||
if builder_has_option --path; then
|
||||
echo "The output path is $OUT_PATH"
|
||||
fi
|
||||
```
|
||||
|
||||
|
||||
## `builder_parse` function
|
||||
|
||||
Initializes a build.sh script, parses command line. Will abort the script if
|
||||
invalid parameters are passed in. Use together with [`builder_describe`], which
|
||||
sets up the possible command line parameters.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
builder_parse "$@"
|
||||
```
|
||||
|
||||
### Description
|
||||
|
||||
Generally, you will always pass `"$@"` as the parameter for this call, to pass
|
||||
all the command line parameters from the script invocation, with automatically
|
||||
correct quoting and escaping.
|
||||
|
||||
|
||||
## `builder_start_action` function
|
||||
|
||||
Starts an action and prints a message to the console, if the user has provided
|
||||
the action in the script invocation.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
if builder_start_action action:target; then
|
||||
# ... do the action
|
||||
fi
|
||||
|
||||
if builder_start_action action :target; then
|
||||
# ... do the action
|
||||
fi
|
||||
|
||||
if builder_start_action action; then
|
||||
# ... do the action for default target (:project)
|
||||
fi
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
* **action** Action to test
|
||||
* **:target** Target to test
|
||||
|
||||
These two parameters can optionally be space separated.
|
||||
|
||||
### Description
|
||||
|
||||
`builder_start_action` will only return `0` if the user passes that action as a
|
||||
parameter when invoking the script. If the user has passed that action in, or
|
||||
the action is the default (when no actions are provided), then the function will
|
||||
also print a log message indicating that the action has started, for example:
|
||||
|
||||
```
|
||||
## [common/web/keyman-version] build:project starting...
|
||||
```
|
||||
|
||||
|
||||
## `builder_use_color` function
|
||||
|
||||
This will normally be managed internally by build-utils, but can be manually
|
||||
overridden with:
|
||||
|
||||
```bash
|
||||
builder_use_color true
|
||||
# or
|
||||
builder_use_color false
|
||||
```
|
||||
|
||||
## `$builder_verbose` variable
|
||||
|
||||
This standard variable will be set to `"--verbose"`, if the `--verbose` or `-v`
|
||||
parameter is passed on the command line, and otherwise will be set to `""`.
|
||||
|
||||
### Usage
|
||||
|
||||
For example, can be used to pass `--verbose` to another app:
|
||||
|
||||
```bash
|
||||
npm test -- $builder_verbose
|
||||
|
||||
# Can also be used like a standard option:
|
||||
if builder_has_option --verbose; then
|
||||
# ...
|
||||
fi
|
||||
```
|
||||
|
||||
## Formatting variables
|
||||
|
||||
These helper variables define ANSI color escapes when running in color mode, and
|
||||
resolve either to empty string (for `$COLOR_*`), or equivalent plain-text forms
|
||||
(for `$BUILDER_TERM_*`) when running without color:
|
||||
|
||||
* `$BUILDER_TERM_START`: Use blue to start definition of a term for builder
|
||||
documentation (or `<` in plain-text)
|
||||
* `$BUILDER_TERM_END`: Return to standard color to finish definition of a term
|
||||
for builder documentation (or `>` in plain-text)
|
||||
* `$COLOR_RED`: Red (error)
|
||||
* `$COLOR_GREEN`: Green (success)
|
||||
* `$COLOR_BLUE`: Blue (heading / informational)
|
||||
* `$COLOR_YELLOW`: Yellow (warning)
|
||||
* `$COLOR_RESET`: Back to default (light grey/white)
|
||||
* `$HEADING_SETMARK`: Add a setmark, e.g. with VSCode
|
||||
<https://code.visualstudio.com/updates/v1_69#_setmark-sequence-support>
|
||||
|
||||
|
||||
[standard builder parameters]: #standard-builder-parameters
|
||||
[`builder_check_color`]: #buildercheckcolor-function
|
||||
[`builder_describe`]: #builderdescribe-function
|
||||
[`builder_display_usage`]: #builderdisplayusage-function
|
||||
[`$builder_extra_params`]: #builderextraparams-variable
|
||||
[`builder_finish_action`]: #builderfinishaction-function
|
||||
[`builder_has_action`]: #builderhasaction-function
|
||||
[`builder_has_option`]: #builderhasoption-function
|
||||
[`builder_parse`]: #builderparse-function
|
||||
[`builder_start_action`]: #builderstartaction-function
|
||||
[`builder_use_color`]: #builderusecolor-function
|
||||
[`$builder_verbose`]: #builderverbose-variable
|
||||
[formatting variables]: #formatting-variables
|
||||
|
|
@ -32,23 +32,12 @@
|
|||
#
|
||||
SHLVL=0
|
||||
|
||||
# Setup variable for calling script's path and name
|
||||
if [ ! -z ${THIS_SCRIPT+x} ]; then
|
||||
THIS_SCRIPT_PATH="$(dirname "$THIS_SCRIPT")"
|
||||
readonly THIS_SCRIPT_PATH
|
||||
THIS_SCRIPT_NAME="$(basename "$THIS_SCRIPT")"
|
||||
readonly THIS_SCRIPT_NAME
|
||||
fi
|
||||
|
||||
function die () {
|
||||
# TODO: consolidate this with fail() from shellHelperFunctions.sh
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
function die() {
|
||||
# TODO: consolidate this with fail() from shellHelperFunctions.sh
|
||||
builder_die "$*"
|
||||
}
|
||||
|
||||
function findRepositoryRoot() {
|
||||
function findKeymanRoot() {
|
||||
# See https://stackoverflow.com/questions/59895/how-to-get-the-source-directory-of-a-bash-script-from-within-the-script-itself
|
||||
# None of the answers are 100% correct for cross-platform
|
||||
# On macOS, requires coreutils (`brew install coreutils`)
|
||||
|
|
@ -180,9 +169,12 @@ function findShouldSentryRelease() {
|
|||
esac
|
||||
}
|
||||
|
||||
findRepositoryRoot
|
||||
findKeymanRoot
|
||||
findTier
|
||||
findVersion
|
||||
|
||||
# Source builder_script
|
||||
. "$KEYMAN_ROOT/resources/builder.inc.sh"
|
||||
# printVersionUtilsDebug
|
||||
printBuildNumberForTeamCity
|
||||
|
||||
|
|
@ -271,486 +263,6 @@ replaceVersionStrings_Mkver() {
|
|||
" "$infile" > "$outfile"
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Standard build script functions for managing command line, actions and targets
|
||||
################################################################################
|
||||
|
||||
# The following allows coloring of warning and error lines, but only works if there's a
|
||||
# terminal attached, so not on the build machine.
|
||||
|
||||
# Overrides default colorization of logging; can be used in command-line with
|
||||
# --color or --no-color, or overridden as necessary on a per-script basis.
|
||||
#
|
||||
# Parameters
|
||||
# 1: use_color true or false
|
||||
builder_use_color() {
|
||||
if $1; then
|
||||
COLOR_RED=$(tput setaf 1)
|
||||
COLOR_GREEN=$(tput setaf 2)
|
||||
COLOR_BLUE=$(tput setaf 4)
|
||||
COLOR_YELLOW=$(tput setaf 3)
|
||||
COLOR_RESET=$(tput sgr0)
|
||||
# e.g. VSCode https://code.visualstudio.com/updates/v1_69#_setmark-sequence-support
|
||||
HEADING_SETMARK='\x1b]1337;SetMark\x07'
|
||||
else
|
||||
COLOR_RED=
|
||||
COLOR_GREEN=
|
||||
COLOR_BLUE=
|
||||
COLOR_YELLOW=
|
||||
COLOR_RESET=
|
||||
HEADING_SETMARK=
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -n "$TERM" ]] && [[ "$TERM" != "dumb" ]] && [[ "$TERM" != "unknown" ]]; then
|
||||
builder_use_color true
|
||||
else
|
||||
builder_use_color false
|
||||
fi
|
||||
|
||||
####################################################################################
|
||||
#
|
||||
# builder_ functions for standard build script parameter and process management
|
||||
#
|
||||
####################################################################################
|
||||
|
||||
#
|
||||
# builder_ names are reserved.
|
||||
# _builder_ names are internal use and subject to change
|
||||
#
|
||||
_builder_debug=false
|
||||
|
||||
# returns 0 if first parameter is in the array passed as second parameter
|
||||
#
|
||||
# Usage:
|
||||
# if _builder_item_in_array "item" "${array[@]}"; then ...; fi
|
||||
# Parameters:
|
||||
# 1: item item to search for in array
|
||||
# 2: array bash array, e.g. array=(one two three)
|
||||
_builder_item_in_array() {
|
||||
local e match="$1"
|
||||
shift
|
||||
[[ -z "$match" ]] && return 1
|
||||
for e; do [[ "$e" == "$match" ]] && return 0; done
|
||||
return 1
|
||||
}
|
||||
|
||||
_builder_item_is_target() {
|
||||
local item="$1"
|
||||
[[ $item =~ ^: ]] && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# Returns 0 if the user has asked to perform action on target on the command line
|
||||
#
|
||||
# Usage:
|
||||
# if build_has_action action[:target]; then ...; fi
|
||||
# Parameters:
|
||||
# 1: action name of action
|
||||
# 2: :target name of target, :-prefixed, as part of first param or space separated ok
|
||||
# Example:
|
||||
# if build_has_action build :app; then
|
||||
# if build_has_action build:app; then
|
||||
#
|
||||
builder_has_action() {
|
||||
local action="$1" target
|
||||
|
||||
if [[ $action =~ : ]]; then
|
||||
IFS=: read -r action target <<< $action
|
||||
target=:$target
|
||||
elif [[ -z ${2+x} ]]; then
|
||||
target=:project
|
||||
else
|
||||
target="$2"
|
||||
fi
|
||||
|
||||
if _builder_item_in_array "$action$target" "${_builder_chosen_action_targets[@]}"; then
|
||||
echo "${COLOR_BLUE}## $action$target starting...${COLOR_RESET}"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
# Returns 0 if the user has --option on the command line
|
||||
#
|
||||
# Usage:
|
||||
# if build_has_option option; then ...; fi
|
||||
# Parameters:
|
||||
# 1: option name of option, i.e. --option
|
||||
# Example:
|
||||
# if build_has_option --debug; then
|
||||
#
|
||||
builder_has_option() {
|
||||
local option="$1"
|
||||
|
||||
if _builder_item_in_array "$option" "${_builder_chosen_options[@]}"; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
_builder_trim() {
|
||||
local var="$*"
|
||||
# remove leading whitespace characters
|
||||
var="${var#"${var%%[![:space:]]*}"}"
|
||||
# remove trailing whitespace characters
|
||||
var="${var%"${var##*[![:space:]]}"}"
|
||||
printf '%s' "$var"
|
||||
}
|
||||
|
||||
#
|
||||
# Describes a build script, defines available parameters
|
||||
# and their meanings. Use together with builder_parse
|
||||
# to process input parameters
|
||||
#
|
||||
# Usage:
|
||||
# builder_describe description param_desc...
|
||||
# Parameters:
|
||||
# description A short description of what the script does
|
||||
# param_desc Space separated name and description of parameter, e.g.
|
||||
# "build Builds the target"
|
||||
# May be repeated to describe all parameters
|
||||
#
|
||||
# There are three types of parameters that may be specified:
|
||||
#
|
||||
# * Option, param_desc format: "--option[,-o][=var] [One line description]"
|
||||
# All options must have a longhand form with two prefix hyphens,
|
||||
# e.g. --option. The ",-o" shorthand form is optional. When testing if
|
||||
# the option is set with `builder_has_option``, always use the longhand
|
||||
# form.
|
||||
#
|
||||
# if =var is specified, then the next parameter will be a variable stored
|
||||
# in $var for that option. e.g. --option=opt means $opt will have the value
|
||||
# 'foo' when the script is called for --option foo.
|
||||
#
|
||||
# * Action, param_desc format: "action [One line description]"
|
||||
# Actions must be a single word, lower case. To specify an action
|
||||
# as the default, append a '+' to the action name, e.g.
|
||||
# "test+ Test the project". If there is no default specified, then
|
||||
# it will be 'build'
|
||||
#
|
||||
# * Target, param_desc format: ":target [One line description]"
|
||||
# A target always starts with colon, e.g. :project.
|
||||
#
|
||||
builder_describe() {
|
||||
_builder_description="$1"
|
||||
_builder_actions=()
|
||||
_builder_targets=()
|
||||
_builder_options=()
|
||||
_builder_default_action=build
|
||||
declare -A -g _builder_params
|
||||
declare -A -g _builder_options_short
|
||||
declare -A -g _builder_options_var
|
||||
shift
|
||||
# describe each target, action, and option possibility
|
||||
while [[ $# -gt 0 ]]; do
|
||||
local key="$1"
|
||||
local value="$(echo "$key" | cut -d" " -f 1 -)"
|
||||
local description=
|
||||
if [[ $key =~ [[:space:]] ]]; then
|
||||
description=$(_builder_trim "$(echo "$key" | cut -d" " -f 2- -)")
|
||||
fi
|
||||
|
||||
if [[ $value =~ ^: ]]; then
|
||||
# Parameter is a target
|
||||
_builder_targets+=($value)
|
||||
elif [[ $value =~ ^-- ]]; then
|
||||
# Parameter is an option
|
||||
# Look for a shorthand version of the option
|
||||
local option_var=
|
||||
if [[ $value =~ = ]]; then
|
||||
option_var="$(echo "$value" | cut -d= -f 2 -)"
|
||||
value="$(echo "$value" | cut -d= -f 1 -)"
|
||||
fi
|
||||
|
||||
if [[ $value =~ , ]]; then
|
||||
local option_long="$(echo "$value" | cut -d, -f 1 -)"
|
||||
local option_short="$(echo "$value" | cut -d, -f 2 -)"
|
||||
_builder_options+=($option_long)
|
||||
_builder_options_short[$option_short]="$option_long"
|
||||
if [[ ! -z "$option_var" ]]; then
|
||||
_builder_options_var[$option_long]="$option_var"
|
||||
fi
|
||||
value="$option_long, $option_short"
|
||||
else
|
||||
_builder_options+=($value)
|
||||
if [[ ! -z "$option_var" ]]; then
|
||||
_builder_options_var[$value]="$option_var"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -z $option_var ]]; then
|
||||
value="$value $option_var"
|
||||
fi
|
||||
else
|
||||
# Parameter is an action
|
||||
if [[ $value =~ \+$ ]]; then
|
||||
# If the action name has a '+' suffix then it is the default action
|
||||
value=${value//+}
|
||||
_builder_default_action=$value
|
||||
fi
|
||||
_builder_actions+=($value)
|
||||
fi
|
||||
|
||||
if [[ -z "${description}" ]]; then
|
||||
description=$(_builder_get_default_description "$value")
|
||||
fi
|
||||
_builder_params[${value}]="$description"
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
# We'll always add a :project if no target is specified
|
||||
if (( ! ${#_builder_targets[@]} )); then
|
||||
_builder_targets+=(:project)
|
||||
_builder_params[\:project]=$(_builder_get_default_description ":project")
|
||||
fi
|
||||
}
|
||||
|
||||
_builder_get_default_description() {
|
||||
local description=
|
||||
local value="$1"
|
||||
# default descriptions for common build actions, targets, and options
|
||||
case "$value" in
|
||||
clean) description="remove build/ folder and build artifacts" ;;
|
||||
configure) description="install dependencies, e.g. npm" ;;
|
||||
build) description="build target(s)" ;;
|
||||
test) description="run automated tests" ;;
|
||||
:project) description="this project" ;;
|
||||
:app) description="main app" ;;
|
||||
:engine) description="engine module" ;;
|
||||
:module) description="this module" ;;
|
||||
:tools) description="build tools for this project" ;;
|
||||
--debug) description="debug build" ;;
|
||||
esac
|
||||
echo "$description"
|
||||
}
|
||||
|
||||
_builder_parameter_error() {
|
||||
local program="$1"
|
||||
local type="$2"
|
||||
local param="$3"
|
||||
echo "$COLOR_RED$program: invalid $type: $param$COLOR_RESET"
|
||||
echo
|
||||
builder_display_usage
|
||||
exit 64
|
||||
|
||||
}
|
||||
|
||||
# Initializes a build.sh script, parses command line. Will abort the script if
|
||||
# invalid parameters are passed in. Use together with builder_describe which
|
||||
# sets up the possible command line parameters
|
||||
#
|
||||
# Usage:
|
||||
# builder_parse "$@"
|
||||
# Parameters
|
||||
# 1: $@ command-line arguments
|
||||
builder_parse() {
|
||||
builder_verbose=
|
||||
_builder_chosen_action_targets=()
|
||||
_builder_chosen_options=()
|
||||
|
||||
# Process command-line arguments
|
||||
while [[ $# -gt 0 ]] ; do
|
||||
local key="$1"
|
||||
local action=
|
||||
local target=
|
||||
local e has_action has_target has_option longhand_option
|
||||
|
||||
if [[ $key =~ : ]]; then
|
||||
IFS=: read -r action target <<< $key
|
||||
target=:$target
|
||||
else
|
||||
action="$key"
|
||||
target=
|
||||
fi
|
||||
|
||||
_builder_item_in_array "$action" "${_builder_actions[@]}" && has_action=1 || has_action=0
|
||||
_builder_item_in_array "$target" "${_builder_targets[@]}" && has_target=1 || has_target=0
|
||||
|
||||
# Expand short -o to --option in options lookup
|
||||
if [[ ! -z ${_builder_options_short[$key]+x} ]]; then
|
||||
key=${_builder_options_short[$key]}
|
||||
fi
|
||||
_builder_item_in_array "$key" "${_builder_options[@]}" && has_option=1 || has_option=0
|
||||
|
||||
if (( has_action )) && (( has_target )); then
|
||||
# apply the selected action and selected target
|
||||
_builder_chosen_action_targets+=("$key")
|
||||
elif (( has_action )); then
|
||||
# apply the selected action to all targets
|
||||
if [[ ! -z $target ]]; then
|
||||
# A target was specified but is not valid
|
||||
_builder_parameter_error "$0" target "$target"
|
||||
fi
|
||||
|
||||
for e in "${_builder_targets[@]}"; do
|
||||
_builder_chosen_action_targets+=("$action$e")
|
||||
done
|
||||
elif (( has_target )); then
|
||||
# apply the default action to the selected target
|
||||
|
||||
if [[ ! -z $action ]]; then
|
||||
# An action was specified but is not valid
|
||||
_builder_parameter_error "$0" action "$action"
|
||||
fi
|
||||
|
||||
_builder_chosen_action_targets+=("$_builder_default_action$target")
|
||||
elif (( has_option )); then
|
||||
_builder_chosen_options+=("$key")
|
||||
if [[ ! -z ${_builder_options_var[$key]+x} ]]; then
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
_builder_parameter_error "$0" parameter "$key"
|
||||
fi
|
||||
# Set the variable associated with this option to the next parameter value
|
||||
# A little bit of hoop jumping here to avoid issues with cygwin paths being
|
||||
# corrupted too early in the game
|
||||
local varname=${_builder_options_var[$key]}
|
||||
declare -g $varname="$1"
|
||||
fi
|
||||
|
||||
else
|
||||
case "$key" in
|
||||
--help|-h)
|
||||
builder_display_usage
|
||||
exit 0
|
||||
;;
|
||||
--color)
|
||||
builder_use_color true
|
||||
;;
|
||||
--no-color)
|
||||
builder_use_color false
|
||||
;;
|
||||
--verbose|-v)
|
||||
_builder_chosen_options+=(--verbose)
|
||||
builder_verbose=--verbose
|
||||
;;
|
||||
*)
|
||||
_builder_parameter_error "$0" parameter "$key"
|
||||
esac
|
||||
fi
|
||||
shift # past the processed argument
|
||||
done
|
||||
|
||||
if (( ! ${#_builder_chosen_action_targets[@]} )); then
|
||||
for e in "${_builder_targets[@]}"; do
|
||||
_builder_chosen_action_targets+=("$_builder_default_action$e")
|
||||
done
|
||||
fi
|
||||
|
||||
if $_builder_debug; then
|
||||
echo "[DEBUG] Selected actions and targets:"
|
||||
for e in "${_builder_chosen_action_targets[@]}"; do
|
||||
echo "* $e"
|
||||
done
|
||||
echo
|
||||
echo "[DEBUG] Selected options:"
|
||||
for e in "${_builder_chosen_options[@]}"; do
|
||||
echo "* $e"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
_builder_pad() {
|
||||
local count=$1
|
||||
local text1=$2
|
||||
local text2=$3
|
||||
local fmt="%-${count}s%s\n"
|
||||
printf $fmt "$text1" "$text2"
|
||||
}
|
||||
|
||||
builder_display_usage() {
|
||||
local e program description
|
||||
|
||||
# Minimum padding is 12 characters, increase this if necessary
|
||||
# if you add other, longer, global options (like --verbose)
|
||||
local width=12
|
||||
|
||||
for e in "${!_builder_params[@]}"; do
|
||||
if (( ${#e} > $width )); then
|
||||
width=${#e}
|
||||
fi
|
||||
done
|
||||
|
||||
width=$((width + 6))
|
||||
|
||||
program="$(basename "$0")"
|
||||
if [[ ! -z ${_builder_description+x} ]]; then
|
||||
echo "$program: $_builder_description"
|
||||
echo
|
||||
fi
|
||||
|
||||
echo "Usage: $program [options...] [action][:target]..."
|
||||
echo
|
||||
echo "Actions: "
|
||||
|
||||
for e in "${_builder_actions[@]}"; do
|
||||
if [[ -v _builder_params[$e] ]]; then
|
||||
description="${_builder_params[$e]}"
|
||||
else
|
||||
description=$(_builder_get_default_description "$e")
|
||||
fi
|
||||
_builder_pad $width " $e" "$description"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Targets: "
|
||||
|
||||
for e in "${_builder_targets[@]}"; do
|
||||
if [[ -v _builder_params[$e] ]]; then
|
||||
description="${_builder_params[$e]}"
|
||||
else
|
||||
description=$(_builder_get_default_description "$e")
|
||||
fi
|
||||
_builder_pad $width " $e" "$description"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Options: "
|
||||
|
||||
for e in "${!_builder_params[@]}"; do
|
||||
if [[ $e =~ ^-- ]]; then
|
||||
_builder_pad $width " $e" "${_builder_params[$e]}"
|
||||
fi
|
||||
done
|
||||
|
||||
_builder_pad $width " --verbose, -v" "Verbose logging"
|
||||
_builder_pad $width " --color" "Force colorized output"
|
||||
_builder_pad $width " --no-color" "Never use colorized output"
|
||||
_builder_pad $width " --help, -h" "Show this help"
|
||||
local c1="${COLOR_BLUE:=<}"
|
||||
local c0="${COLOR_RESET:=>}"
|
||||
echo
|
||||
echo "* Specify ${c1}action:target${c0} to run a specific ${c1}action${c0} against a specific ${c1}:target${c0}."
|
||||
echo "* If ${c1}action${c0} is specified without a ${c1}target${c0} suffix, it will be applied to all ${c1}:target${c0}s."
|
||||
echo "* If ${c1}:target${c0} is specified without an ${c1}action${c0} prefix, ${c1}$_builder_default_action:target${c0} will be inferred."
|
||||
echo "* If no ${c1}action${c0}, ${c1}:target${c0}, or ${c1}action:target${c0} entries are specified, ${c1}$_builder_default_action${c0} will run on all ${c1}:target${c0}s."
|
||||
echo
|
||||
}
|
||||
|
||||
builder_report() {
|
||||
local result="$1"
|
||||
local action="$2" target
|
||||
|
||||
if [[ $action =~ : ]]; then
|
||||
IFS=: read -r action target <<< $action
|
||||
target=:$target
|
||||
elif [[ -z ${3+x} ]]; then
|
||||
target=:project
|
||||
else
|
||||
target="$3"
|
||||
fi
|
||||
|
||||
if [ $result == success ]; then
|
||||
echo "${COLOR_GREEN}## $action$target completed successfully${COLOR_RESET}"
|
||||
else
|
||||
echo "${COLOR_RED}## $action$target failed. Result: $result${COLOR_RESET}"
|
||||
fi
|
||||
}
|
||||
|
||||
set_keyman_standard_build_path() {
|
||||
PATH="$KEYMAN_ROOT/node_modules/.bin:$PATH"
|
||||
}
|
||||
|
|
|
|||
175
resources/build/tests/build-utils-traps.test.sh
Executable file
175
resources/build/tests/build-utils-traps.test.sh
Executable file
|
|
@ -0,0 +1,175 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "$(dirname "$THIS_SCRIPT")/../build-utils.sh"
|
||||
# END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
|
||||
|
||||
##############
|
||||
|
||||
CHECK="${COLOR_GREEN}✔${COLOR_RESET}" # ✔
|
||||
CROSS="${COLOR_RED}❌${COLOR_RESET}" # ❌
|
||||
|
||||
builder_describe \
|
||||
"Provides actions useful for build-utils.sh trap-functionality unit testing.\nOnly one action may be set at a time." \
|
||||
"error" \
|
||||
"error-in-function" \
|
||||
"incomplete"
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
###
|
||||
|
||||
HR="------------------------------------------------"
|
||||
|
||||
active_test=
|
||||
error-test-matcher() {
|
||||
ERR_CODE=$?
|
||||
ALL_PASS=true
|
||||
|
||||
echo "$HR"
|
||||
|
||||
if [[ $ERR_CODE != 0 ]]; then
|
||||
printf "${CHECK} Error reported to calling script\n"
|
||||
else
|
||||
printf "${CROSS} Error not reported to calling script\n"
|
||||
ALL_PASS=false
|
||||
fi
|
||||
|
||||
# output_logs will be accessible here. The report makes the most intuitive sense
|
||||
# when presented here.
|
||||
|
||||
# Verify output logs have the expected output message.
|
||||
|
||||
if [[ "${output_logs:-}" =~ "raise-build-error:project failed" ]]; then
|
||||
printf "${CHECK} Expected build-failure report was emitted\n"
|
||||
else
|
||||
printf "${CROSS} Expected build-failure report was not emitted\n"
|
||||
ALL_PASS=false
|
||||
fi
|
||||
|
||||
# Let's not re-trap ourselves from the trap!
|
||||
trap - err exit
|
||||
|
||||
if [[ $ALL_PASS == true ]]; then
|
||||
builder_finish_action success $active_test
|
||||
exit 0
|
||||
else
|
||||
builder_finish_action failure $active_test
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Note: if this test is run, no other ones after it may execute!
|
||||
if builder_start_action error; then
|
||||
# clear base traps
|
||||
trap - err
|
||||
trap - exit
|
||||
|
||||
active_test=error
|
||||
|
||||
echo "$HR"
|
||||
|
||||
# The `|| true` disables any trap shenanigans.
|
||||
# Needed to capture the log messages.
|
||||
output_logs=`$THIS_SCRIPT_PATH/trap-test-builds.sh raise-build-error` || true
|
||||
|
||||
trap error-test-matcher err exit
|
||||
|
||||
# Relies on the `trap` - no further commands outside the handler will be processed.
|
||||
# Also emits the log messages (but we can't capture them here)
|
||||
$THIS_SCRIPT_PATH/trap-test-builds.sh raise-build-error
|
||||
|
||||
# In case the expected error isn't emitted. Will emit the report-failure.
|
||||
error-test-matcher
|
||||
fi
|
||||
|
||||
function-with-error() {
|
||||
$THIS_SCRIPT_PATH/trap-test-builds.sh raise-build-error
|
||||
}
|
||||
|
||||
# Note: if this test is run, no other ones after it may execute!
|
||||
if builder_start_action error-in-function; then
|
||||
# clear base traps
|
||||
trap - err
|
||||
trap - exit
|
||||
|
||||
active_test=error-in-function
|
||||
|
||||
echo "$HR"
|
||||
|
||||
# The `|| true` disables any trap shenanigans.
|
||||
# Needed to capture the log messages.
|
||||
output_logs=`function-with-error` || true
|
||||
|
||||
trap error-test-matcher err exit
|
||||
|
||||
# Relies on the `trap` - no further commands outside the handler will be processed.
|
||||
# Also emits the log messages (but we can't capture them here)
|
||||
function-with-error
|
||||
|
||||
# In case the expected error isn't emitted. Will emit the report-failure.
|
||||
error-test-matcher
|
||||
fi
|
||||
|
||||
########
|
||||
|
||||
warning-test-matcher() {
|
||||
ERR_CODE=$?
|
||||
ALL_PASS=true
|
||||
|
||||
echo "$HR"
|
||||
|
||||
if [[ $ERR_CODE == 0 ]]; then
|
||||
printf "${CHECK} Error not reported to calling script\n"
|
||||
else
|
||||
printf "${CROSS} Error reported to calling script\n"
|
||||
ALL_PASS=false
|
||||
fi
|
||||
|
||||
# output_logs will be accessible here. The report makes the most intuitive sense
|
||||
# when presented here.
|
||||
|
||||
# Verify output logs have the expected output message.
|
||||
|
||||
if [[ "${output_logs:-}" =~ "unreported-action:project never reported" ]]; then
|
||||
printf "${CHECK} Expected warning report was emitted\n"
|
||||
else
|
||||
printf "${CROSS} Expected warning report was not emitted\n"
|
||||
ALL_PASS=false
|
||||
fi
|
||||
|
||||
# Let's not re-trap ourselves from the trap!
|
||||
trap - err exit
|
||||
|
||||
if [[ $ALL_PASS == true ]]; then
|
||||
builder_finish_action success incomplete
|
||||
exit 0
|
||||
else
|
||||
builder_finish_action failure incomplete
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if builder_start_action incomplete; then
|
||||
# clear base traps
|
||||
trap - err
|
||||
trap - exit
|
||||
|
||||
echo "$HR"
|
||||
|
||||
# The `|| true` disables any trap shenanigans.
|
||||
output_logs=`$THIS_SCRIPT_PATH/trap-test-builds.sh unreported-action` || true
|
||||
|
||||
trap warning-test-matcher err
|
||||
|
||||
# Relies on the `trap` - no further commands outside the handler will be processed.
|
||||
$THIS_SCRIPT_PATH/trap-test-builds.sh unreported-action
|
||||
|
||||
warning-test-matcher
|
||||
fi
|
||||
|
|
@ -5,7 +5,7 @@ set -eu
|
|||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "$(dirname "$THIS_SCRIPT")/build-utils.sh"
|
||||
. "$(dirname "$THIS_SCRIPT")/../build-utils.sh"
|
||||
# END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
|
||||
|
|
@ -18,9 +18,9 @@ if [[ "${_builder_chosen_action_targets[@]}" != "build:project" ]]; then
|
|||
fail " Test: builder_parse, shorthand form 'build' should give us 'build:project"
|
||||
fi
|
||||
|
||||
if builder_has_action build; then
|
||||
if builder_start_action build; then
|
||||
echo "building project"
|
||||
builder_report success build
|
||||
builder_finish_action success build
|
||||
else
|
||||
fail "FAIL: should have matched action build for :project"
|
||||
fi
|
||||
|
|
@ -54,35 +54,35 @@ if [[ $_builder_default_action != "default" ]]; then
|
|||
fi
|
||||
|
||||
# Shorthand form where we don't have a :target (default is ":project")
|
||||
if builder_has_action build; then
|
||||
if builder_start_action build; then
|
||||
echo "building project"
|
||||
builder_report success build
|
||||
builder_finish_action success build
|
||||
else
|
||||
fail "FAIL: should have matched action build for :project"
|
||||
fi
|
||||
|
||||
if builder_has_action clean :app; then
|
||||
if builder_start_action clean :app; then
|
||||
echo "Cleaning <clean :app>"
|
||||
builder_report success clean :app
|
||||
builder_finish_action success clean :app
|
||||
else
|
||||
fail "FAIL: should have matched action clean for :app"
|
||||
fi
|
||||
|
||||
if builder_has_action clean:app; then
|
||||
if builder_start_action clean:app; then
|
||||
echo "Cleaning <clean:app>"
|
||||
builder_report success clean:app
|
||||
builder_finish_action success clean:app
|
||||
else
|
||||
fail "FAIL: should have matched action clean for :app"
|
||||
fi
|
||||
|
||||
if builder_has_action build :app; then
|
||||
if builder_start_action build :app; then
|
||||
echo "Building app"
|
||||
builder_report success build :app
|
||||
builder_finish_action success build :app
|
||||
else
|
||||
fail "FAIL: should have matched action build for :app"
|
||||
fi
|
||||
|
||||
if builder_has_action build :module; then
|
||||
if builder_start_action build :module; then
|
||||
fail "FAIL: should not have matched action build for :module"
|
||||
fi
|
||||
|
||||
|
|
@ -142,13 +142,35 @@ if builder_has_option --feature; then
|
|||
if [[ $FOO == xyzzy ]]; then
|
||||
echo "PASS: --feature option variable \$FOO has expected value 'xyzzy'"
|
||||
else
|
||||
echo "FAIL: --feature option variable \$FOO had value '$FOO' but should have had 'xyzzy'"
|
||||
fail "FAIL: --feature option variable \$FOO had value '$FOO' but should have had 'xyzzy'"
|
||||
fi
|
||||
else
|
||||
echo "FAIL: --feature option not found"
|
||||
fi
|
||||
|
||||
builder_parse -- one two "three four five"
|
||||
if [[ ${builder_extra_params[0]} != "one" ]]; then
|
||||
fail "FAIL: -- extra parameter 'one' not found"
|
||||
fi
|
||||
if [[ ${builder_extra_params[1]} != "two" ]]; then
|
||||
fail "FAIL: -- extra parameter 'two' not found"
|
||||
fi
|
||||
if [[ ${builder_extra_params[2]} != "three four five" ]]; then
|
||||
fail "FAIL: -- extra parameter 'three four five' not found"
|
||||
fi
|
||||
|
||||
# Run tests based in separate scripts to facilitate their operation
|
||||
|
||||
# Due to the nature of the build-utils-traps tests, only one may be
|
||||
# specified at a time; each ends with an `exit`.
|
||||
echo "Running separate tests"
|
||||
$THIS_SCRIPT_PATH/build-utils-traps.test.sh error
|
||||
$THIS_SCRIPT_PATH/build-utils-traps.test.sh error-in-function
|
||||
$THIS_SCRIPT_PATH/build-utils-traps.test.sh incomplete
|
||||
echo "Fin"
|
||||
|
||||
# Finally, run with --help so we can see what it looks like
|
||||
# Note: calls `exit`, so no further tests may be defined.
|
||||
|
||||
echo "${COLOR_BLUE}## Testing --help${COLOR_RESET}"
|
||||
|
||||
25
resources/build/tests/trap-test-builds.sh
Executable file
25
resources/build/tests/trap-test-builds.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "$(dirname "$THIS_SCRIPT")/../build-utils.sh"
|
||||
# END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe \
|
||||
"Error-generating delegate script" \
|
||||
"raise-build-error+" \
|
||||
"unreported-action"
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
if builder_start_action raise-build-error; then
|
||||
echo "Emulating build script error => failure report"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if builder_start_action unreported-action; then
|
||||
echo "Leaving action 'success' unreported; should trigger trap message"
|
||||
fi
|
||||
702
resources/builder.inc.sh
Executable file
702
resources/builder.inc.sh
Executable file
|
|
@ -0,0 +1,702 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# This script contains utilities for builder_script calls
|
||||
#
|
||||
# * builder_ functions and variables are defined here.
|
||||
# * REPO_ROOT defines the top level of this repository
|
||||
# * THIS_SCRIPT_PATH defines the full path of the running script
|
||||
# * THIS_SCRIPT_NAME defines the basename of the running script
|
||||
# * THIS_SCRIPT_IDENTIFIER defines the repo-relative path of the running script
|
||||
# * _builder_ functions and variables are internal use only for builder.inc.sh, and
|
||||
# subject to change at any time. Do not use them in other scripts.
|
||||
# * Note: the running script is the top-level script that includes either
|
||||
# builder.inc.sh directly, or, just in the Keyman repo, via build-utils.sh.
|
||||
#
|
||||
|
||||
# _builder_init is called internally at the bottom of this file after we have
|
||||
# all function declarations in place.
|
||||
function _builder_init() {
|
||||
_builder_findRepoRoot
|
||||
_builder_setBuildScriptIdentifiers
|
||||
|
||||
if [[ -n "$TERM" ]] && [[ "$TERM" != "dumb" ]] && [[ "$TERM" != "unknown" ]]; then
|
||||
builder_use_color true
|
||||
else
|
||||
builder_use_color false
|
||||
fi
|
||||
}
|
||||
|
||||
function _builder_findRepoRoot() {
|
||||
# See https://stackoverflow.com/questions/59895/how-to-get-the-source-directory-of-a-bash-script-from-within-the-script-itself
|
||||
# None of the answers are 100% correct for cross-platform
|
||||
# On macOS, requires coreutils (`brew install coreutils`)
|
||||
local SCRIPT=$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")
|
||||
REPO_ROOT=$(dirname $(dirname "$SCRIPT"))
|
||||
readonly REPO_ROOT
|
||||
}
|
||||
|
||||
# Used to build script-related build variables useful for referencing the calling script
|
||||
# and for prefixing builder_finish_action outputs in order to more clearly identify the calling
|
||||
# script.
|
||||
#
|
||||
# Assumes that THIS_SCRIPT has been set, typically like this:
|
||||
#
|
||||
# ## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# # adjust relative paths as necessary
|
||||
# THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
|
||||
# . "$(dirname "$THIS_SCRIPT")/resources/builder.inc.sh"
|
||||
# ## END STANDARD BUILD SCRIPT INCLUDE
|
||||
function _builder_setBuildScriptIdentifiers() {
|
||||
if [ ! -z ${THIS_SCRIPT+x} ]; then
|
||||
THIS_SCRIPT_PATH="$(dirname "$THIS_SCRIPT")"
|
||||
readonly THIS_SCRIPT_PATH
|
||||
THIS_SCRIPT_NAME="$(basename "$THIS_SCRIPT")"
|
||||
readonly THIS_SCRIPT_NAME
|
||||
# Leaves only the part of the path based upon REPO_ROOT.
|
||||
THIS_SCRIPT_IDENTIFIER=${THIS_SCRIPT_PATH#"$REPO_ROOT/"}
|
||||
readonly THIS_SCRIPT_IDENTIFIER
|
||||
else
|
||||
echo "Warning: THIS_SCRIPT not defined; builder.inc.sh has not been sourced with standard script include."
|
||||
fi
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Standard build script functions for managing command line, actions and targets
|
||||
################################################################################
|
||||
|
||||
# The following allows coloring of warning and error lines, but only works if there's a
|
||||
# terminal attached, so not on the build machine.
|
||||
|
||||
# Overrides default colorization of logging; can be used in command-line with
|
||||
# --color or --no-color, or overridden as necessary on a per-script basis.
|
||||
#
|
||||
# Parameters
|
||||
# 1: use_color true or false
|
||||
builder_use_color() {
|
||||
if $1; then
|
||||
COLOR_RED=$(tput setaf 1)
|
||||
COLOR_GREEN=$(tput setaf 2)
|
||||
COLOR_YELLOW=$(tput setaf 3)
|
||||
COLOR_BLUE=$(tput setaf 4)
|
||||
COLOR_PURPLE=$(tput setaf 5)
|
||||
COLOR_TEAL=$(tput setaf 6)
|
||||
COLOR_WHITE=$(tput setaf 7)
|
||||
COLOR_GREY=$(tput setaf 8)
|
||||
COLOR_RESET=$(tput sgr0)
|
||||
# e.g. VSCode https://code.visualstudio.com/updates/v1_69#_setmark-sequence-support
|
||||
HEADING_SETMARK='\x1b]1337;SetMark\x07'
|
||||
|
||||
# Used by `builder_display_usage` when marking special terms (actions, targets, options)
|
||||
# in the plain-text description area.
|
||||
BUILDER_TERM_START="$COLOR_BLUE"
|
||||
BUILDER_TERM_END="$COLOR_RESET"
|
||||
else
|
||||
COLOR_RED=
|
||||
COLOR_GREEN=
|
||||
COLOR_YELLOW=
|
||||
COLOR_BLUE=
|
||||
COLOR_PURPLE=
|
||||
COLOR_TEAL=
|
||||
COLOR_WHITE=
|
||||
COLOR_GREY=
|
||||
COLOR_RESET=
|
||||
HEADING_SETMARK=
|
||||
BUILDER_TERM_START="<"
|
||||
BUILDER_TERM_END=">"
|
||||
fi
|
||||
}
|
||||
|
||||
function builder_die() {
|
||||
echo
|
||||
echo "${COLOR_RED}$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
####################################################################################
|
||||
#
|
||||
# builder_ functions for standard build script parameter and process management
|
||||
#
|
||||
####################################################################################
|
||||
|
||||
#
|
||||
# builder_ names are reserved.
|
||||
# _builder_ names are internal use and subject to change
|
||||
#
|
||||
_builder_debug=false
|
||||
|
||||
#
|
||||
# builder_extra_params: string containing all parameters after '--'
|
||||
#
|
||||
builder_extra_params=()
|
||||
|
||||
# returns 0 if first parameter is in the array passed as second parameter
|
||||
#
|
||||
# Usage:
|
||||
# if _builder_item_in_array "item" "${array[@]}"; then ...; fi
|
||||
# Parameters:
|
||||
# 1: item item to search for in array
|
||||
# 2: array bash array, e.g. array=(one two three)
|
||||
_builder_item_in_array() {
|
||||
local e match="$1"
|
||||
shift
|
||||
[[ -z "$match" ]] && return 1
|
||||
for e; do [[ "$e" == "$match" ]] && return 0; done
|
||||
return 1
|
||||
}
|
||||
|
||||
_builder_item_is_target() {
|
||||
local item="$1"
|
||||
[[ $item =~ ^: ]] && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
function _builder_warn_if_incomplete() {
|
||||
if [ -n "${_builder_current_action}" ]; then
|
||||
local scope="[$THIS_SCRIPT_IDENTIFIER] "
|
||||
echo "${COLOR_YELLOW}## ${scope}Warning - $_builder_current_action never reported success or failure${COLOR_RESET}"
|
||||
# exit 1 # If we wanted this scenario to result in a forced build-script fail.
|
||||
fi
|
||||
|
||||
# Since we've already warned about this once, we'll clear the variable to prevent repetitions.
|
||||
_builder_current_action=
|
||||
}
|
||||
|
||||
# Used by a `trap` statement later to facilitate auto-reporting failures on error detection
|
||||
# without obscuring failure exit/error codes.
|
||||
_builder_failure_trap() {
|
||||
local trappedExitCode=$?
|
||||
local action target
|
||||
|
||||
# Since 'exit' is also trapped, we can also handle end-of-script incomplete actions.
|
||||
if [[ $trappedExitCode == 0 ]]; then
|
||||
# While there weren't errors, were there any actions that never reported success or failure?
|
||||
_builder_warn_if_incomplete
|
||||
return
|
||||
fi
|
||||
|
||||
# If we've reached this point, we're here because an error occurred.
|
||||
|
||||
# Iterate across currently-active actions and report their failures.
|
||||
if [ -n "${_builder_current_action}" ]; then
|
||||
action="${_builder_current_action}"
|
||||
if [[ $action =~ : ]]; then
|
||||
IFS=: read -r action target <<< $action
|
||||
target=:$target
|
||||
else
|
||||
target=:project
|
||||
fi
|
||||
|
||||
builder_finish_action failure $action $target
|
||||
|
||||
# Make 100% sure that the exit code chains fully.
|
||||
# Without this, nested scripts have failed to chain errors from npm calls past the script
|
||||
# that directly executed the failed npm command.
|
||||
exit $trappedExitCode
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Builds the standardized `action:target` string for the specified action-target
|
||||
# pairing and also returns 0 if the user has asked to perform it on the command
|
||||
# line. Otherwise, returns 0 and sets an empty string in place of the matched
|
||||
# pair.
|
||||
#
|
||||
# The string will be set as `_builder_matched_action`, which is for
|
||||
# builder.inc.sh internal use, used by `builder_start_action`.
|
||||
#
|
||||
# Usage:
|
||||
# if build_has_action action[:target]; then ...; fi
|
||||
# Parameters:
|
||||
# 1: action name of action
|
||||
# 2: :target name of target, :-prefixed, as part of first param or space separated ok
|
||||
# Example:
|
||||
# if builder_has_action build :app; then # or build:app, that's fine too.
|
||||
builder_has_action() {
|
||||
local action="$1" target
|
||||
|
||||
if [[ $action =~ : ]]; then
|
||||
IFS=: read -r action target <<< $action
|
||||
target=:$target
|
||||
elif [[ -z ${2+x} ]]; then
|
||||
target=:project
|
||||
else
|
||||
target="$2"
|
||||
fi
|
||||
|
||||
if _builder_item_in_array "$action$target" "${_builder_chosen_action_targets[@]}"; then
|
||||
# To avoid WET re-processing of the $action$target string set
|
||||
_builder_matched_action="$action$target"
|
||||
return 0
|
||||
else
|
||||
_builder_matched_action=
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Returns 0 if the user has asked to perform action on target on the command line, and
|
||||
# then starts the action. Should be paired with builder_finish_action
|
||||
#
|
||||
# Usage:
|
||||
# if builder_start_action action[:target]; then ...; fi
|
||||
# Parameters:
|
||||
# 1: action name of action
|
||||
# 2: :target name of target, :-prefixed, as part of first param or space separated ok
|
||||
# Example:
|
||||
# if builder_start_action build :app; then
|
||||
# if builder_start_action build:app; then
|
||||
#
|
||||
builder_start_action() {
|
||||
local scope="[$THIS_SCRIPT_IDENTIFIER] "
|
||||
|
||||
if builder_has_action $@; then
|
||||
echo "${COLOR_BLUE}## $scope$_builder_matched_action starting...${COLOR_RESET}"
|
||||
if [ -n "${_builder_current_action}" ]; then
|
||||
_builder_warn_if_incomplete
|
||||
fi
|
||||
_builder_current_action="$_builder_matched_action"
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Returns 0 if the user has --option on the command line
|
||||
#
|
||||
# Usage:
|
||||
# if build_has_option option; then ...; fi
|
||||
# Parameters:
|
||||
# 1: option name of option, i.e. --option
|
||||
# Example:
|
||||
# if build_has_option --debug; then
|
||||
#
|
||||
builder_has_option() {
|
||||
local option="$1"
|
||||
|
||||
if _builder_item_in_array "$option" "${_builder_chosen_options[@]}"; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
_builder_trim() {
|
||||
local var="$*"
|
||||
# remove leading whitespace characters
|
||||
var="${var#"${var%%[![:space:]]*}"}"
|
||||
# remove trailing whitespace characters
|
||||
var="${var%"${var##*[![:space:]]}"}"
|
||||
printf '%s' "$var"
|
||||
}
|
||||
|
||||
#
|
||||
# Describes a build script, defines available parameters and their meanings. Use
|
||||
# together with `builder_parse` to process input parameters.
|
||||
#
|
||||
# Usage:
|
||||
# builder_describe description param_desc...
|
||||
# Parameters:
|
||||
# description A short description of what the script does
|
||||
# param_desc Space separated name and description of parameter, e.g.
|
||||
# "build Builds the target"
|
||||
# May be repeated to describe all parameters
|
||||
#
|
||||
# There are three types of parameters that may be specified:
|
||||
#
|
||||
# * Option, param_desc format: "--option[,-o][=var] [One line description]"
|
||||
# All options must have a longhand form with two prefix hyphens,
|
||||
# e.g. --option. The ",-o" shorthand form is optional. When testing if
|
||||
# the option is set with `builder_has_option``, always use the longhand
|
||||
# form.
|
||||
#
|
||||
# if =var is specified, then the next parameter will be a variable stored
|
||||
# in $var for that option. e.g. --option=opt means $opt will have the value
|
||||
# 'foo' when the script is called for --option foo.
|
||||
#
|
||||
# * Action, param_desc format: "action [One line description]"
|
||||
# Actions must be a single word, lower case. To specify an action
|
||||
# as the default, append a '+' to the action name, e.g.
|
||||
# "test+ Test the project". If there is no default specified, then
|
||||
# it will be 'build'
|
||||
#
|
||||
# * Target, param_desc format: ":target [One line description]"
|
||||
# A target always starts with colon, e.g. :project.
|
||||
#
|
||||
builder_describe() {
|
||||
_builder_description="$1"
|
||||
_builder_actions=()
|
||||
_builder_targets=()
|
||||
_builder_options=()
|
||||
_builder_default_action=build
|
||||
declare -A -g _builder_params
|
||||
declare -A -g _builder_options_short
|
||||
declare -A -g _builder_options_var
|
||||
shift
|
||||
# describe each target, action, and option possibility
|
||||
while [[ $# -gt 0 ]]; do
|
||||
local key="$1"
|
||||
local value="$(echo "$key" | cut -d" " -f 1 -)"
|
||||
local description=
|
||||
if [[ $key =~ [[:space:]] ]]; then
|
||||
description=$(_builder_trim "$(echo "$key" | cut -d" " -f 2- -)")
|
||||
fi
|
||||
|
||||
if [[ $value =~ ^: ]]; then
|
||||
# Parameter is a target
|
||||
_builder_targets+=($value)
|
||||
elif [[ $value =~ ^-- ]]; then
|
||||
# Parameter is an option
|
||||
# Look for a shorthand version of the option
|
||||
local option_var=
|
||||
if [[ $value =~ = ]]; then
|
||||
option_var="$(echo "$value" | cut -d= -f 2 -)"
|
||||
value="$(echo "$value" | cut -d= -f 1 -)"
|
||||
fi
|
||||
|
||||
if [[ $value =~ , ]]; then
|
||||
local option_long="$(echo "$value" | cut -d, -f 1 -)"
|
||||
local option_short="$(echo "$value" | cut -d, -f 2 -)"
|
||||
_builder_options+=($option_long)
|
||||
_builder_options_short[$option_short]="$option_long"
|
||||
if [[ ! -z "$option_var" ]]; then
|
||||
_builder_options_var[$option_long]="$option_var"
|
||||
fi
|
||||
value="$option_long, $option_short"
|
||||
else
|
||||
_builder_options+=($value)
|
||||
if [[ ! -z "$option_var" ]]; then
|
||||
_builder_options_var[$value]="$option_var"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -z $option_var ]]; then
|
||||
value="$value $option_var"
|
||||
fi
|
||||
else
|
||||
# Parameter is an action
|
||||
if [[ $value =~ \+$ ]]; then
|
||||
# If the action name has a '+' suffix then it is the default action
|
||||
value=${value//+}
|
||||
_builder_default_action=$value
|
||||
fi
|
||||
_builder_actions+=($value)
|
||||
fi
|
||||
|
||||
if [[ -z "${description}" ]]; then
|
||||
description=$(_builder_get_default_description "$value")
|
||||
fi
|
||||
_builder_params[${value}]="$description"
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
# We'll always add a :project if no target is specified
|
||||
if (( ! ${#_builder_targets[@]} )); then
|
||||
_builder_targets+=(:project)
|
||||
_builder_params[\:project]=$(_builder_get_default_description ":project")
|
||||
fi
|
||||
}
|
||||
|
||||
_builder_get_default_description() {
|
||||
local description=
|
||||
local value="$1"
|
||||
# default descriptions for common build actions, targets, and options
|
||||
case "$value" in
|
||||
clean) description="remove build/ folder and build artifacts" ;;
|
||||
configure) description="install dependencies, e.g. npm" ;;
|
||||
build) description="build target(s)" ;;
|
||||
test) description="run automated tests" ;;
|
||||
:project) description="this project" ;;
|
||||
:app) description="main app" ;;
|
||||
:engine) description="engine module" ;;
|
||||
:module) description="this module" ;;
|
||||
:tools) description="build tools for this project" ;;
|
||||
--debug) description="debug build" ;;
|
||||
esac
|
||||
echo "$description"
|
||||
}
|
||||
|
||||
_builder_parameter_error() {
|
||||
local program="$1"
|
||||
local type="$2"
|
||||
local param="$3"
|
||||
echo "$COLOR_RED$program: invalid $type: $param$COLOR_RESET"
|
||||
echo
|
||||
builder_display_usage
|
||||
exit 64
|
||||
|
||||
}
|
||||
|
||||
# Pre-initializes the color setting based on the options specified to a
|
||||
# a build.sh script, parsing the command line to do so. This is only
|
||||
# needed if said script wishes to use this script's defined colors while
|
||||
# respecting the options provided by the script's caller.
|
||||
#
|
||||
# Usage:
|
||||
# builder_check_color "$@"
|
||||
# Parameters
|
||||
# 1: $@ all command-line arguments (as with builder_parse)
|
||||
builder_check_color() {
|
||||
# Process command-line arguments
|
||||
while [[ $# -gt 0 ]] ; do
|
||||
local key="$1"
|
||||
|
||||
case "$key" in
|
||||
--color)
|
||||
builder_use_color true
|
||||
;;
|
||||
--no-color)
|
||||
builder_use_color false
|
||||
;;
|
||||
esac
|
||||
shift # past the processed argument
|
||||
done
|
||||
}
|
||||
|
||||
# Initializes a build.sh script, parses command line. Will abort the script if
|
||||
# invalid parameters are passed in. Use together with builder_describe which
|
||||
# sets up the possible command line parameters
|
||||
#
|
||||
# Usage:
|
||||
# builder_parse "$@"
|
||||
# Parameters
|
||||
# 1: $@ command-line arguments
|
||||
builder_parse() {
|
||||
builder_verbose=
|
||||
builder_extra_params=()
|
||||
_builder_chosen_action_targets=()
|
||||
_builder_chosen_options=()
|
||||
_builder_current_action=
|
||||
|
||||
# Process command-line arguments
|
||||
while [[ $# -gt 0 ]] ; do
|
||||
local key="$1"
|
||||
local action=
|
||||
local target=
|
||||
local e has_action has_target has_option longhand_option
|
||||
|
||||
if [[ $key == "--" ]]; then
|
||||
shift
|
||||
builder_extra_params=("$@")
|
||||
break
|
||||
fi
|
||||
|
||||
if [[ $key =~ : ]]; then
|
||||
IFS=: read -r action target <<< $key
|
||||
target=:$target
|
||||
else
|
||||
action="$key"
|
||||
target=
|
||||
fi
|
||||
|
||||
_builder_item_in_array "$action" "${_builder_actions[@]}" && has_action=1 || has_action=0
|
||||
_builder_item_in_array "$target" "${_builder_targets[@]}" && has_target=1 || has_target=0
|
||||
|
||||
# Expand short -o to --option in options lookup
|
||||
if [[ ! -z ${_builder_options_short[$key]+x} ]]; then
|
||||
key=${_builder_options_short[$key]}
|
||||
fi
|
||||
_builder_item_in_array "$key" "${_builder_options[@]}" && has_option=1 || has_option=0
|
||||
|
||||
if (( has_action )) && (( has_target )); then
|
||||
# apply the selected action and selected target
|
||||
_builder_chosen_action_targets+=("$key")
|
||||
elif (( has_action )); then
|
||||
# apply the selected action to all targets
|
||||
if [[ ! -z $target ]]; then
|
||||
# A target was specified but is not valid
|
||||
_builder_parameter_error "$0" target "$target"
|
||||
fi
|
||||
|
||||
for e in "${_builder_targets[@]}"; do
|
||||
_builder_chosen_action_targets+=("$action$e")
|
||||
done
|
||||
elif (( has_target )); then
|
||||
# apply the default action to the selected target
|
||||
|
||||
if [[ ! -z $action ]]; then
|
||||
# An action was specified but is not valid
|
||||
_builder_parameter_error "$0" action "$action"
|
||||
fi
|
||||
|
||||
_builder_chosen_action_targets+=("$_builder_default_action$target")
|
||||
elif (( has_option )); then
|
||||
_builder_chosen_options+=("$key")
|
||||
if [[ ! -z ${_builder_options_var[$key]+x} ]]; then
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
_builder_parameter_error "$0" parameter "$key"
|
||||
fi
|
||||
# Set the variable associated with this option to the next parameter value
|
||||
# A little bit of hoop jumping here to avoid issues with cygwin paths being
|
||||
# corrupted too early in the game
|
||||
local varname=${_builder_options_var[$key]}
|
||||
declare -g $varname="$1"
|
||||
fi
|
||||
|
||||
else
|
||||
case "$key" in
|
||||
--help|-h)
|
||||
builder_display_usage
|
||||
exit 0
|
||||
;;
|
||||
--color)
|
||||
builder_use_color true
|
||||
;;
|
||||
--no-color)
|
||||
builder_use_color false
|
||||
;;
|
||||
--verbose|-v)
|
||||
_builder_chosen_options+=(--verbose)
|
||||
builder_verbose=--verbose
|
||||
;;
|
||||
*)
|
||||
_builder_parameter_error "$0" parameter "$key"
|
||||
esac
|
||||
fi
|
||||
shift # past the processed argument
|
||||
done
|
||||
|
||||
if (( ! ${#_builder_chosen_action_targets[@]} )); then
|
||||
for e in "${_builder_targets[@]}"; do
|
||||
_builder_chosen_action_targets+=("$_builder_default_action$e")
|
||||
done
|
||||
fi
|
||||
|
||||
if $_builder_debug; then
|
||||
echo "[DEBUG] Selected actions and targets:"
|
||||
for e in "${_builder_chosen_action_targets[@]}"; do
|
||||
echo "* $e"
|
||||
done
|
||||
echo
|
||||
echo "[DEBUG] Selected options:"
|
||||
for e in "${_builder_chosen_options[@]}"; do
|
||||
echo "* $e"
|
||||
done
|
||||
fi
|
||||
|
||||
# Now that we've successfully parsed options adhering to the _builder spec, we may activate our
|
||||
# action_failure and action_hanging traps. (We don't want them active on scripts not yet using
|
||||
# said script.)
|
||||
#
|
||||
# Note: if an error occurs within a script's function in a `set -e` script, it becomes an exit
|
||||
# instead for the function's caller. So, we need both `err` and `exit` here.
|
||||
# See https://medium.com/@dirk.avery/the-bash-trap-trap-ce6083f36700.
|
||||
trap _builder_failure_trap err exit
|
||||
}
|
||||
|
||||
_builder_pad() {
|
||||
local count=$1
|
||||
local text1=$2
|
||||
local text2=$3
|
||||
local fmt="%-${count}s%s\n"
|
||||
printf $fmt "$text1" "$text2"
|
||||
}
|
||||
|
||||
builder_display_usage() {
|
||||
local e program description
|
||||
|
||||
# Minimum padding is 12 characters, increase this if necessary
|
||||
# if you add other, longer, global options (like --verbose)
|
||||
local width=12
|
||||
|
||||
for e in "${!_builder_params[@]}"; do
|
||||
if (( ${#e} > $width )); then
|
||||
width=${#e}
|
||||
fi
|
||||
done
|
||||
|
||||
width=$((width + 6))
|
||||
|
||||
program="$(basename "$0")"
|
||||
if [[ ! -z ${_builder_description+x} ]]; then
|
||||
echo "$program: $_builder_description"
|
||||
echo
|
||||
fi
|
||||
|
||||
echo "Usage: $program [options...] [action][:target]..."
|
||||
echo
|
||||
echo "Actions: "
|
||||
|
||||
for e in "${_builder_actions[@]}"; do
|
||||
if [[ -v _builder_params[$e] ]]; then
|
||||
description="${_builder_params[$e]}"
|
||||
else
|
||||
description=$(_builder_get_default_description "$e")
|
||||
fi
|
||||
_builder_pad $width " $e" "$description"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Targets: "
|
||||
|
||||
for e in "${_builder_targets[@]}"; do
|
||||
if [[ -v _builder_params[$e] ]]; then
|
||||
description="${_builder_params[$e]}"
|
||||
else
|
||||
description=$(_builder_get_default_description "$e")
|
||||
fi
|
||||
_builder_pad $width " $e" "$description"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Options: "
|
||||
|
||||
for e in "${!_builder_params[@]}"; do
|
||||
if [[ $e =~ ^-- ]]; then
|
||||
_builder_pad $width " $e" "${_builder_params[$e]}"
|
||||
fi
|
||||
done
|
||||
|
||||
_builder_pad $width " --verbose, -v" "Verbose logging"
|
||||
_builder_pad $width " --color" "Force colorized output"
|
||||
_builder_pad $width " --no-color" "Never use colorized output"
|
||||
_builder_pad $width " --help, -h" "Show this help"
|
||||
|
||||
# Defined in `builder_use_color`; this assumes that said func has been called.
|
||||
local c1=$BUILDER_TERM_START
|
||||
local c0=$BUILDER_TERM_END
|
||||
echo
|
||||
echo "* Specify ${c1}action:target${c0} to run a specific ${c1}action${c0} against a specific ${c1}:target${c0}."
|
||||
echo "* If ${c1}action${c0} is specified without a ${c1}target${c0} suffix, it will be applied to all ${c1}:target${c0}s."
|
||||
echo "* If ${c1}:target${c0} is specified without an ${c1}action${c0} prefix, ${c1}$_builder_default_action:target${c0} will be inferred."
|
||||
echo "* If no ${c1}action${c0}, ${c1}:target${c0}, or ${c1}action:target${c0} entries are specified, ${c1}$_builder_default_action${c0} will run on all ${c1}:target${c0}s."
|
||||
echo
|
||||
}
|
||||
|
||||
builder_finish_action() {
|
||||
local result="$1"
|
||||
local action="$2" target
|
||||
|
||||
if [[ $action =~ : ]]; then
|
||||
IFS=: read -r action target <<< $action
|
||||
target=:$target
|
||||
elif [[ -z ${3+x} ]]; then
|
||||
target=:project
|
||||
else
|
||||
target="$3"
|
||||
fi
|
||||
|
||||
local scope="[$THIS_SCRIPT_IDENTIFIER] "
|
||||
|
||||
if [[ "$action$target" == "${_builder_current_action}" ]]; then
|
||||
if [[ $result == success ]]; then
|
||||
echo "${COLOR_GREEN}## $scope$action$target completed successfully${COLOR_RESET}"
|
||||
elif [[ $result == failure ]]; then
|
||||
echo "${COLOR_RED}## $scope$action$target failed${COLOR_RESET}"
|
||||
else
|
||||
echo "${COLOR_RED}## $scope$action$target failed with message: $result${COLOR_RESET}"
|
||||
fi
|
||||
|
||||
# Remove $action$target from the array; it is no longer a current action
|
||||
_builder_current_action=
|
||||
else
|
||||
echo "${COLOR_YELLOW}## Warning: reporting result of $action$target but the action was never started!${COLOR_RESET}"
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Initialize builder once all functions are declared
|
||||
#
|
||||
_builder_init
|
||||
|
|
@ -21,12 +21,12 @@ builder_describe \
|
|||
"build (default) builds bulk_renderer to ../release/renderer/"
|
||||
builder_parse "$@"
|
||||
|
||||
if builder_has_action configure; then
|
||||
if builder_start_action configure; then
|
||||
verify_npm_setup
|
||||
builder_report success configure
|
||||
builder_finish_action success configure
|
||||
fi
|
||||
|
||||
if builder_has_action build; then
|
||||
if builder_start_action build; then
|
||||
tsc --build "$THIS_SCRIPT_PATH/tsconfig.json" $builder_verbose
|
||||
builder_report success build
|
||||
builder_finish_action success build
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
"@sentry/cli": "2.2.0",
|
||||
"chai": "^4.3.4",
|
||||
"google-closure-compiler-java": "^20200224.0.0",
|
||||
"karma": "^6.3.4",
|
||||
"karma": "^6.4.1",
|
||||
"karma-browserstack-launcher": "^1.6.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
|
|
|
|||
|
|
@ -405,7 +405,7 @@ namespace com.keyman.dom {
|
|||
doChangeEvent(_target: HTMLElement) {
|
||||
if(DOMEventHandlers.states.changed) {
|
||||
let event = new Event('change', {"bubbles": true, "cancelable": false});
|
||||
|
||||
|
||||
// Ensure that touch-aliased elements fire as if from the aliased element.
|
||||
if(_target['base'] && _target['base']['kmw_ip']) {
|
||||
_target = _target['base'];
|
||||
|
|
@ -772,7 +772,7 @@ namespace com.keyman.dom {
|
|||
|
||||
const x = touch.screenX;
|
||||
const y = touch.screenY;
|
||||
|
||||
|
||||
// Allow content of input elements to be dragged horizontally or vertically
|
||||
if(typeof this.firstTouch == 'undefined' || this.firstTouch == null) {
|
||||
this.firstTouch={x:x,y:y};
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ namespace com.keyman.dom.targets {
|
|||
|
||||
getTextAfterCaret(): string {
|
||||
if(!this.hasSelection()) {
|
||||
return;
|
||||
return '';
|
||||
}
|
||||
|
||||
let caret = this.getCarets().end;
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ namespace com.keyman.dom.targets {
|
|||
|
||||
getTextAfterCaret(): string {
|
||||
if(!this.hasSelection()) {
|
||||
return;
|
||||
return '';
|
||||
}
|
||||
|
||||
let caret = this.getCarets().end;
|
||||
|
|
|
|||
|
|
@ -499,6 +499,8 @@ namespace com.keyman.osk {
|
|||
bs.width = bs.maxWidth = this.computedWidth + 'px';
|
||||
bs.height = bs.maxHeight = this.computedHeight + 'px';
|
||||
|
||||
// Ensure that the layer's spacebar is properly captioned.
|
||||
this.vkbd.showLanguage();
|
||||
} else {
|
||||
const bs = this._Box.style;
|
||||
bs.width = 'auto';
|
||||
|
|
|
|||
|
|
@ -275,7 +275,9 @@ namespace com.keyman.osk {
|
|||
|
||||
// Cancel (but do not execute) pending key if neither a popup key or the base key
|
||||
if(t == null || t.id.indexOf('popup') < 0) {
|
||||
this.highlight(this.pendingTarget,false);
|
||||
if (this.pendingTarget) {
|
||||
this.highlight(this.pendingTarget,false);
|
||||
}
|
||||
this.clearHolds();
|
||||
this.pendingTarget = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,8 +87,7 @@
|
|||
<div id='DynamicInputs'><h3>Inputs:</h3></div>
|
||||
<div id='DynamicTextareas'><h3>Textareas:</h3></div>
|
||||
<div id='DynamicIFrames'><h3>IFrames:</h3>
|
||||
<p><em>Note:</em> The iframe section should not actually attach/enable for touch devices
|
||||
and does not support <code>setKeyboardForControl</code>.</p>
|
||||
<p><em>Note:</em> The iframe section does not support <code>setKeyboardForControl</code>.</p>
|
||||
</div>
|
||||
<div id='DynamicDesignFrames'><h3>Design-mode IFrames:</h3>
|
||||
<p><em>Note:</em> The iframe section should not actually attach/enable for touch devices
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Regression Tests
|
||||
|
||||
This folder contains tools to run automated regression tests on Keyman Engine for Web
|
||||
This folder contains tools to run automated regression tests on Keyman Engine for Web
|
||||
and Keyman Developer. The intent is to validate changes to the compiler and the engine
|
||||
against the set of available keyboards in the Keyman keyboards repository from
|
||||
https://github.com/keymanapp/keyboards.
|
||||
|
|
@ -10,7 +10,7 @@ the `kmanalyze` program which is not yet available outside the keyman source rep
|
|||
is the only Windows dependency (the `kmcomp` compiler is also used, but this runs under
|
||||
WINE and is available in binary form outside the source repo).
|
||||
|
||||
The test suite runs in Node.js and launches Chrome for the tests. In the future, the
|
||||
The test suite runs in Node.js and launches Chrome for the tests. In the future, the
|
||||
tests may be able to be run in native Node.js.
|
||||
|
||||
## Configuration
|
||||
|
|
@ -35,7 +35,7 @@ the keyman repository, for example:
|
|||
- regression-tests
|
||||
|
||||
Keyman Engine for Web, Keyman Developer (kmcomp, kmcmpdll, kmanalyze) must be built
|
||||
prior to running the tests. (If not testing source versions, only kmanalyze is
|
||||
prior to running the tests. (If not testing source versions, only kmanalyze is
|
||||
required). See the build documentation for each project for details; summary below:
|
||||
|
||||
To build KeymanWeb (bash):
|
||||
|
|
@ -45,12 +45,12 @@ To build KeymanWeb (bash):
|
|||
|
||||
To build just the Keyman Developer tools required from the Windows source (cmd):
|
||||
|
||||
cd windows\src
|
||||
make -DNOUI ext global buildtools developer
|
||||
cd developer\src
|
||||
nmake
|
||||
|
||||
### Known Failures
|
||||
|
||||
As there are some known failures, these are listed in `src/known-failures.js`.
|
||||
As there are some known failures, these are listed in `src/known-failures.js`.
|
||||
As the bugs that impact these keyboards are addressed, they should be progressively
|
||||
removed from the file. Keyboards listed in this file will still be tested and
|
||||
report errors, but they will not fail the test suite overall.
|
||||
|
|
@ -62,7 +62,7 @@ tests that run with node + Karma + Mocha + Chai.
|
|||
|
||||
### Manual Tests
|
||||
|
||||
`node interactive.js` will start a web server listening on http://localhost:1337/.
|
||||
`node interactive.js` will start a web server listening on http://localhost:1337/.
|
||||
Navigating to this address will allow you to load and test a given keyboard from the
|
||||
keyboards repository. This assumes that the keyboards are already available and that
|
||||
the .tests have already been built.
|
||||
|
|
@ -72,7 +72,7 @@ tools than for intensive testing.
|
|||
|
||||
### Automated Tests
|
||||
|
||||
`node test.js` runs the entire test suite. There are a number of command line
|
||||
`node test.js` runs the entire test suite. There are a number of command line
|
||||
parameters available:
|
||||
|
||||
$ node test.js -h
|
||||
|
|
@ -89,15 +89,15 @@ parameters available:
|
|||
-l, --log-all-failures Log all test failures to console, not just the first failure for each keyboard
|
||||
-h, --help output usage information
|
||||
|
||||
When the entire test suite is run, the regression test compares the latest version of
|
||||
When the entire test suite is run, the regression test compares the latest version of
|
||||
the compiler from the source repository, together with the latest version of Keyman
|
||||
Engine for Web from the source repository, against the latest stable releases of
|
||||
Engine for Web from the source repository, against the latest stable releases of
|
||||
each, downloaded from https://downloads.keyman.com/
|
||||
|
||||
These two versions are referenced as `source` and `stable` respectively. You can
|
||||
also specify exact stable, beta or alpha versions for download by specifying a
|
||||
version number in the `-c` and `-e` parameters. You can test as many versions as
|
||||
you like at a time, but beware: the number of permutations tested increases as
|
||||
you like at a time, but beware: the number of permutations tested increases as
|
||||
the square of tested versions.
|
||||
|
||||
The continuous integration configuration currently uses `node test.js -l`.
|
||||
|
|
@ -126,14 +126,14 @@ The test builder generates a `tests-generated.js` file for running Karma and the
|
|||
## Background
|
||||
|
||||
`kmanalyze` generates a .tests file for a given keyboard, from analysis of a
|
||||
corresponding .kmx file (a future update will use the intermediate in-memory
|
||||
corresponding .kmx file (a future update will use the intermediate in-memory
|
||||
.kmx file used by the compiler to ensure we access only the web rules). It
|
||||
does not currently support deadkeys, recursive groups, or web-specific rules
|
||||
(lines prefixed with `$keymanweb:`). Nor does it support keyboard or system
|
||||
options. It will generate a single test for each store referenced in an `any`
|
||||
statement, rather than a test for each character in the store.
|
||||
|
||||
.tests files and .results files are stored in a `tests/` folder for each
|
||||
.tests files and .results files are stored in a `tests/` folder for each
|
||||
keyboard in the repository.
|
||||
|
||||
The test suite runs tests against the physical keyboard ruleset for keyboards.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
"commander": "^2.19.0",
|
||||
"express": "^4.16.4",
|
||||
"fs-extra": "^7.0.1",
|
||||
"got": "^9.6.0",
|
||||
"got": "^11.8.5",
|
||||
"jasmine-core": "^3.3.0",
|
||||
"karma": "^6.3.16",
|
||||
"karma-chai": "^0.1.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// test-runner
|
||||
|
||||
const KEYBOARDS_RELATIVE_PATH = "/keyboards/";
|
||||
const KEYBOARDS_RELATIVE_PATH = "/keyboards/";
|
||||
const TEST_BATCH_SIZE = 100; // or Infinity
|
||||
|
||||
var receiver;
|
||||
|
|
@ -17,7 +17,7 @@ var windowLoad = new Promise(function(resolve, reject) {
|
|||
receiver = document.getElementById('receiver');
|
||||
if(!receiver) {
|
||||
// When running tests, we dynamically create the receiver
|
||||
receiver = document.createElement('input');
|
||||
receiver = document.createElement('input');
|
||||
document.body.appendChild(receiver);
|
||||
let d0 = document.createElement('div');
|
||||
d0.id = 'progressWindow';
|
||||
|
|
@ -51,15 +51,15 @@ function chunk(arr, chunkSize) {
|
|||
}
|
||||
|
||||
var testRunner = {
|
||||
modCodes: keyman.osk.modifierCodes,
|
||||
keyCodes: keyman.osk.keyCodes,
|
||||
modCodes: com.keyman.text.Codes.modifierCodes,
|
||||
keyCodes: com.keyman.text.Codes.keyCodes,
|
||||
keyboards: {},
|
||||
max: 0,
|
||||
|
||||
/**
|
||||
* Loads a .tests file for the given keyboard, and the corresponding keyboard. Both the
|
||||
* Loads a .tests file for the given keyboard, and the corresponding keyboard. Both the
|
||||
* keyboard and the tests file are injected via script into the document.
|
||||
*
|
||||
*
|
||||
* @param {string} locator a string in the form of shortname/id, e.g. k/kayan, sil/sil_ipa
|
||||
* @returns {Promise} A promise fulfilled when the keyboard and the tests file are ready
|
||||
*/
|
||||
|
|
@ -102,7 +102,7 @@ var testRunner = {
|
|||
|
||||
/**
|
||||
* Load a tests file and a keyboard from the keyboards repository
|
||||
* @param {string} locator
|
||||
* @param {string} locator
|
||||
*/
|
||||
loadTests: function(locator) {
|
||||
console.log('Loading tests + keyboard for '+locator);
|
||||
|
|
@ -130,7 +130,7 @@ var testRunner = {
|
|||
};
|
||||
// The KeymanWeb global will always exist, even when we otherwise change API.
|
||||
KeymanWeb.registerStub(stub);
|
||||
let k = testRunner.keyboards[id];
|
||||
let k = testRunner.keyboards[id];
|
||||
keyman.setKeyboardForControl(receiver, id, k.keyboard.languages[0].id);
|
||||
document.body.focus();
|
||||
receiver.focus();
|
||||
|
|
@ -185,7 +185,7 @@ var testRunner = {
|
|||
};
|
||||
http.open('POST', '/save-results');
|
||||
http.setRequestHeader('Content-Type', 'application/json');
|
||||
http.send(JSON.stringify(json));
|
||||
http.send(JSON.stringify(json));
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ var testRunner = {
|
|||
|
||||
/**
|
||||
* Called by a .tests file to register a set of input tests
|
||||
* @param {object} data
|
||||
* @param {object} data
|
||||
*/
|
||||
register: function(data) {
|
||||
let keys = Object.keys(data.inputTests);
|
||||
|
|
@ -240,7 +240,7 @@ var testRunner = {
|
|||
runTests: function(keyboardId) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
this.keyboards[keyboardId].results = {};
|
||||
|
||||
|
||||
console.log('-- Running '+this.keyboards[keyboardId].inputTests.length+' tests for '+keyboardId+'.');
|
||||
var chunkSize = TEST_BATCH_SIZE;
|
||||
|
||||
|
|
@ -291,7 +291,7 @@ var testRunner = {
|
|||
e.LisVirtualKeyCode = true;
|
||||
e.LisVirtualKey = true;
|
||||
e.vkCode = test.key;
|
||||
|
||||
|
||||
// Keyman 14 changes the processKeystroke interface
|
||||
e.device = keyman.util.device.coreSpec;
|
||||
if(keyman.core) {
|
||||
|
|
@ -319,7 +319,7 @@ var testRunner = {
|
|||
initProgress: function(max) {
|
||||
this.max = max;
|
||||
this.progressPosition = document.getElementById('progressPosition');
|
||||
if(this.progressPosition) this.progressPosition.style.width = '0px';
|
||||
if(this.progressPosition) this.progressPosition.style.width = '0px';
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -327,7 +327,7 @@ var testRunner = {
|
|||
* @param {number} len Current position of progress bar
|
||||
*/
|
||||
updateProgress: function(len) {
|
||||
if(this.progressPosition) this.progressPosition.style.width = Math.round(len / this.max * 100).toString() + '%';
|
||||
if(this.progressPosition) this.progressPosition.style.width = Math.round(len / this.max * 100).toString() + '%';
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ program
|
|||
.version('0.1')
|
||||
.option('-c, --compiler-versions [versions]', 'Specify compiler version(s) to test. Can specify "stable", "source" or a specific version number.', list, ['stable','source'])
|
||||
.option('-e, --engine-versions [versions]', 'Specify KeymanWeb engine version(s) to test. Can specify "stable", "source" or a specific version number.', list, ['stable','source'])
|
||||
.option('-k, --keyboards [keyboards]', 'Builds and tests specific keyboard source files. If -k is not specified, then test all keyboards in the keyboards repository.', list, [])
|
||||
.option('-k, --keyboards [keyboards]', 'Builds and tests specific keyboard source files (e.g. k/khmer_angkor). If -k is not specified, then test all keyboards in the keyboards repository.', list, [])
|
||||
.option('-f, --fail-fast', "Don't attempt to continue tests after the first keyboard test fails")
|
||||
.option('-d, --debug', "Build keyboard with debug symbols")
|
||||
.option('--deep', "Compare all version combinations against base version, instead of just one; only valid when comparing 1 version of each against base")
|
||||
|
|
@ -57,8 +57,8 @@ const bash = process.platform == 'win32'
|
|||
//TODO: support testing standalone keyboards that are not in the repo
|
||||
|
||||
const compilerDestPath = path.join(config.KEYBOARDS_ROOT, 'tools');
|
||||
const kmcompDestPath = path.join(compilerDestPath, 'kmcomp.exe');
|
||||
const kmcmpdllDestPath = path.join(compilerDestPath, 'kmcmpdll.dll');
|
||||
const kmcompDestPath = path.join(compilerDestPath, 'kmcomp', 'kmcomp.exe');
|
||||
const kmcmpdllDestPath = path.join(compilerDestPath, 'kmcomp', 'kmcmpdll.dll');
|
||||
|
||||
let testedCompilerVersions = [], testedEngineVersions = [], firstCompile = true;
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ process.on('unhandledRejection', (reason, p) => {
|
|||
});
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param items An array of items.
|
||||
* @param fn A function that accepts an item from the array and returns a promise.
|
||||
* @returns {Promise}
|
||||
|
|
@ -95,7 +95,7 @@ function fail(msg, code) {
|
|||
if(program.failFast) process.exit();
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Clean the keyboards repo -- unless we are skipping analysis
|
||||
// This removes the build/ and tests/ folders
|
||||
//
|
||||
|
|
@ -107,27 +107,31 @@ if(!program.skipAnalysis) {
|
|||
let cleanKeyboard = function(keyboard) {
|
||||
keyboard = keyboard ? config.KEYBOARDS_GROUP+'/'+keyboard : config.KEYBOARDS_GROUP;
|
||||
return util.runProcess(
|
||||
`${bash.command}`,
|
||||
[].concat(bash.params, ['build.sh', '-c', keyboard]),
|
||||
`${bash.command}`,
|
||||
[].concat(bash.params, ['build.sh', '-c', keyboard]),
|
||||
{ cwd: config.KEYBOARDS_ROOT });
|
||||
};
|
||||
|
||||
cleanKeyboards = (program.keyboards.length)
|
||||
|
||||
cleanKeyboards = (program.keyboards.length)
|
||||
? forEachPromise(program.keyboards, cleanKeyboard)
|
||||
: cleanKeyboard('');
|
||||
}
|
||||
}
|
||||
|
||||
cleanKeyboards.then(() => {
|
||||
fs.renameSync(kmcompDestPath, kmcompDestPath + '.bak');
|
||||
fs.renameSync(kmcmpdllDestPath, kmcmpdllDestPath + '.bak');
|
||||
|
||||
|
||||
process.on('exit', () => {
|
||||
console.log('Restoring original compiler files');
|
||||
fs.renameSync(kmcompDestPath + '.bak', kmcompDestPath);
|
||||
fs.renameSync(kmcmpdllDestPath + '.bak', kmcmpdllDestPath);
|
||||
if(fs.existsSync(kmcompDestPath + '.bak')) {
|
||||
fs.renameSync(kmcompDestPath + '.bak', kmcompDestPath);
|
||||
}
|
||||
if(fs.existsSync(kmcmpdllDestPath + '.bak')) {
|
||||
fs.renameSync(kmcmpdllDestPath + '.bak', kmcmpdllDestPath);
|
||||
}
|
||||
});
|
||||
}).then(() => forEachPromise(program.compilerVersions, version => {
|
||||
//
|
||||
//
|
||||
// Compile phase - get the compiler
|
||||
//
|
||||
|
||||
|
|
@ -141,17 +145,18 @@ cleanKeyboards.then(() => {
|
|||
console.error('Source build of compiler is only available on Windows.');
|
||||
reject();
|
||||
}
|
||||
fs.copyFileSync(path.join(config.KEYMAN_REPO_BASE_RELATIVE_PATH, 'windows', 'bin', 'developer', 'kmcomp.exe'), kmcompDestPath);
|
||||
fs.copyFileSync(path.join(config.KEYMAN_REPO_BASE_RELATIVE_PATH, 'windows', 'bin', 'developer', 'kmcmpdll.dll'), kmcmpdllDestPath);
|
||||
fs.copyFileSync(path.join(config.KEYMAN_REPO_BASE_RELATIVE_PATH, 'developer', 'bin', 'kmcomp.exe'), kmcompDestPath);
|
||||
fs.copyFileSync(path.join(config.KEYMAN_REPO_BASE_RELATIVE_PATH, 'developer', 'bin', 'kmcmpdll.dll'), kmcmpdllDestPath);
|
||||
resolve();
|
||||
});
|
||||
break;
|
||||
case 'stable':
|
||||
getCompiler = got('https://downloads.keyman.com/api/version/developer/2.0', { json: true })
|
||||
getCompiler = got('https://downloads.keyman.com/api/version/developer/2.0')
|
||||
.json()
|
||||
.then(response => {
|
||||
compilerVersion = response.body.developer.stable.version;
|
||||
compilerVersion = response.developer.stable.version;
|
||||
console.log('Downloading compiler version '+compilerVersion);
|
||||
return got(`https://downloads.keyman.com/developer/stable/${compilerVersion}/kmcomp-${compilerVersion}.zip`, { encoding: null });
|
||||
return got(`https://downloads.keyman.com/developer/stable/${compilerVersion}/kmcomp-${compilerVersion}.zip`, { responseType: 'buffer' });
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Unzipping compiler');
|
||||
|
|
@ -163,7 +168,7 @@ cleanKeyboards.then(() => {
|
|||
default:
|
||||
console.log('Downloading specified compiler version '+version);
|
||||
compilerVersion = version;
|
||||
getCompiler = got(`https://downloads.keyman.com/developer/stable/${version}/kmcomp-${version}.zip`, { encoding: null })
|
||||
getCompiler = got(`https://downloads.keyman.com/developer/stable/${version}/kmcomp-${version}.zip`, { responseType: 'buffer' })
|
||||
.then(response => {
|
||||
console.log('Unzipping compiler');
|
||||
let zip = new AdmZip(response.body);
|
||||
|
|
@ -190,11 +195,11 @@ cleanKeyboards.then(() => {
|
|||
console.log('building keyboard '+keyboard);
|
||||
|
||||
return util.runProcess(
|
||||
`${bash.command}`,
|
||||
[].concat(bash.params, ['build.sh'], program.debug?['-d']:[], [/*TODO: waiting on keyboards repo support for this param: '-T', 'kmn',*/ keyboard]),
|
||||
`${bash.command}`,
|
||||
[].concat(bash.params, ['build.sh'], program.debug?['-d']:[], [/*TODO: waiting on keyboards repo support for this param: '-T', 'kmn',*/ keyboard]),
|
||||
{ cwd: config.KEYBOARDS_ROOT });
|
||||
};
|
||||
|
||||
|
||||
return (program.keyboards.length) ? forEachPromise(program.keyboards, buildKeyboard) : buildKeyboard('');
|
||||
});
|
||||
|
||||
|
|
@ -208,7 +213,7 @@ cleanKeyboards.then(() => {
|
|||
firstCompile = false;
|
||||
let analyzeKeyboard = function(keyboard) {
|
||||
const locator = keyboard.shortname+'/'+keyboard.id;
|
||||
|
||||
|
||||
const kmx = path.join(config.KEYBOARDS_ROOT, config.KEYBOARDS_GROUP, locator, 'build', keyboard.id+'.kmx');
|
||||
const testsPath = path.join(config.KEYBOARDS_ROOT, config.KEYBOARDS_GROUP, locator, 'tests');
|
||||
const tests = path.join(testsPath, keyboard.id+'.tests');
|
||||
|
|
@ -225,11 +230,11 @@ cleanKeyboards.then(() => {
|
|||
|
||||
console.log(`Building test cases for ${locator}`);
|
||||
// TODO: Find kmanalyze outside the repo. This forces Windows-dependence right now
|
||||
return util.runProcess('../../../windows/bin/developer/kmanalyze.exe', [kmx, tests], {}, true);
|
||||
return util.runProcess('../../../developer/bin/kmanalyze.exe', [kmx, tests], {}, true);
|
||||
};
|
||||
|
||||
let keyboards = program.keyboards.length
|
||||
? program.keyboards.map((locator) => util.parseLocator(locator))
|
||||
|
||||
let keyboards = program.keyboards.length
|
||||
? program.keyboards.map((locator) => util.parseLocator(locator))
|
||||
: util.getKeyboardFolders(KEYBOARDS_ROOT, false);
|
||||
return forEachPromise(keyboards, analyzeKeyboard);
|
||||
}
|
||||
|
|
@ -267,11 +272,12 @@ cleanKeyboards.then(() => {
|
|||
});
|
||||
break;
|
||||
case 'stable':
|
||||
getEngine = got('https://downloads.keyman.com/api/version/web/2.0', { json: true })
|
||||
getEngine = got('https://downloads.keyman.com/api/version/web/2.0')
|
||||
.json()
|
||||
.then(response => {
|
||||
engineVersion = response.body.web.stable.version;
|
||||
engineVersion = response.web.stable.version;
|
||||
console.log('Downloading engine version '+engineVersion);
|
||||
return got(`https://downloads.keyman.com/web/stable/${engineVersion}/keymanweb-${engineVersion}.zip`, { encoding: null });
|
||||
return got(`https://downloads.keyman.com/web/stable/${engineVersion}/keymanweb-${engineVersion}.zip`, { responseType: 'buffer' });
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Unzipping engine');
|
||||
|
|
@ -283,7 +289,7 @@ cleanKeyboards.then(() => {
|
|||
default:
|
||||
console.log('Downloading specified engine version '+version0);
|
||||
engineVersion = version0;
|
||||
getEngine = got(`https://downloads.keyman.com/web/stable/${version0}/keymanweb-${version0}.zip`, { encoding: null })
|
||||
getEngine = got(`https://downloads.keyman.com/web/stable/${version0}/keymanweb-${version0}.zip`, { responseType: 'buffer' })
|
||||
.then(response => {
|
||||
console.log('Unzipping engine');
|
||||
// Assuming engine files we want are at unminified/ in the zip
|
||||
|
|
@ -292,8 +298,8 @@ cleanKeyboards.then(() => {
|
|||
});
|
||||
}
|
||||
|
||||
return getEngine.then(() => {
|
||||
console.log('Testing compiler version '+compilerVersion+', engine version '+engineVersion);
|
||||
return getEngine.then(() => {
|
||||
console.log('Testing compiler version '+compilerVersion+', engine version '+engineVersion);
|
||||
|
||||
if(testedEngineVersions.indexOf(engineVersion) < 0) {
|
||||
testedEngineVersions.push(engineVersion);
|
||||
|
|
@ -302,9 +308,9 @@ cleanKeyboards.then(() => {
|
|||
return util.runProcess(
|
||||
`node`,
|
||||
[].concat(
|
||||
['test-builder.js',
|
||||
'--compiler-version', compilerVersion,
|
||||
'--engine-version', engineVersion],
|
||||
['test-builder.js',
|
||||
'--compiler-version', compilerVersion,
|
||||
'--engine-version', engineVersion],
|
||||
program.keyboards.length ? ['--keyboards', program.keyboards.join(',')] : []
|
||||
)
|
||||
);
|
||||
|
|
@ -330,8 +336,8 @@ cleanKeyboards.then(() => {
|
|||
// Validate each of the test files against the first tested compiler+engine version
|
||||
|
||||
let localFailCount = 0;
|
||||
const localFail = knownFailures.hasOwnProperty([keyboard.id]) ?
|
||||
(msg) => { if(++localFailCount == 1) console.warn(`WARN: Not failing test because ${keyboard.id} is in known-failures.`); console.warn(`WARN: ${msg}`); } :
|
||||
const localFail = knownFailures.hasOwnProperty([keyboard.id]) ?
|
||||
(msg) => { if(++localFailCount == 1) console.warn(`WARN: Not failing test because ${keyboard.id} is in known-failures.`); console.warn(`WARN: ${msg}`); } :
|
||||
fail;
|
||||
|
||||
try {
|
||||
|
|
@ -370,8 +376,8 @@ cleanKeyboards.then(() => {
|
|||
for(let k in baseResultJSON) {
|
||||
if(resultJSON[k] !== baseResultJSON[k]) {
|
||||
if(++errors == 1 || program.logAllFailures) {
|
||||
let
|
||||
ix = k.toString(),
|
||||
let
|
||||
ix = k.toString(),
|
||||
whitespace = ' '.repeat(prefix.length + ix.length + 6),
|
||||
input = `${testsJSON.inputTests[k].context ? `"${testsJSON.inputTests[k].context}" ` : ""}+ ${keyname(testsJSON.inputTests[k].modifier, testsJSON.inputTests[k].key)}`;
|
||||
console.error(`${prefix}[${ix}]: expected: ${input} > "${baseResultJSON[k]}"`);
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ readonly ENGINE_TEST_OUTPUT
|
|||
# (Facilitates automated setup for build agents.)
|
||||
PATH="../../node_modules/.bin:$PATH"
|
||||
|
||||
pushd "$KEYMAN_ROOT/common/web/recorder/src"
|
||||
./build.sh -skip-package-install || fail "recorder-core compilation failed."
|
||||
pushd "$KEYMAN_ROOT/common/web/recorder"
|
||||
./build.sh || fail "recorder-core compilation failed."
|
||||
popd
|
||||
|
||||
compiler="npm run tsc --"
|
||||
|
|
|
|||
|
|
@ -1455,7 +1455,7 @@ describe('Element Input/Output Interfacing', function() {
|
|||
});
|
||||
|
||||
it('correctly maintains deadkeys', function() {
|
||||
InterfaceTests.Tests.deadkeyMaintenance(InterfaceTests.TouchAlias);
|
||||
InterfaceTests.Tests.deadkeyMaintenance(InterfaceTests.Mock);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -63,13 +63,13 @@ describe('Engine - Browser Interactions', function() {
|
|||
fixture.cleanup();
|
||||
});
|
||||
|
||||
it('Backing up and restoring (loadStore/saveStore)', function(done) {
|
||||
it('Backing up and restoring (loadStore/saveStore)', function() {
|
||||
// Keyboard's default value is 0, corresponding to "no foo."
|
||||
var keyboardID = "options_with_save";
|
||||
var prefixedKeyboardID = "Keyboard_" + keyboardID;
|
||||
var storeName = "foo";
|
||||
|
||||
keyman.setActiveKeyboard(keyboardID, 'en').then(function() {
|
||||
return keyman.setActiveKeyboard(keyboardID, 'en').then(function() {
|
||||
// Alas, saveStore itself requires the keyboard to be active!
|
||||
KeymanWeb.saveStore(storeName, 1);
|
||||
|
||||
|
|
@ -79,45 +79,42 @@ describe('Engine - Browser Interactions', function() {
|
|||
|
||||
// Reload the keyboard so that we can test its loaded value.
|
||||
keyman.removeKeyboards(keyboardID, true);
|
||||
|
||||
// Now we can reload the keyboard and run the test.
|
||||
var remainderOfTest = function() {
|
||||
// This requires proper storage to a cookie, as we'll be on a new instance of the same keyboard.
|
||||
var value = KeymanWeb.loadStore(prefixedKeyboardID, storeName, 0);
|
||||
assert.equal(value, 1, "Did not properly save and reload variable store setting");
|
||||
|
||||
KeymanWeb.saveStore(storeName, 0);
|
||||
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
return loadKeyboardFromJSON("/keyboards/options_with_save.json", testconfig.timeouts.scriptLoad)
|
||||
.then(() => {
|
||||
return keyman.setActiveKeyboard(keyboardID, 'en');
|
||||
}).then(remainderOfTest);
|
||||
}).then(() => {
|
||||
return loadKeyboardFromJSON("/keyboards/options_with_save.json", testconfig.timeouts.scriptLoad);
|
||||
}).then(() => {
|
||||
return keyman.setActiveKeyboard(keyboardID, 'en');
|
||||
}).then(() => {
|
||||
// This requires proper storage to a cookie, as we'll be on a new instance of the same keyboard.
|
||||
var value = KeymanWeb.loadStore(prefixedKeyboardID, storeName, 0);
|
||||
assert.equal(value, 1, "Did not properly save and reload variable store setting");
|
||||
}).finally(() => {
|
||||
KeymanWeb.saveStore(storeName, 0);
|
||||
});
|
||||
});
|
||||
|
||||
it("Multiple-sequence check", function(done) {
|
||||
it("Multiple-sequence check", function() {
|
||||
this.timeout(testconfig.timeouts.standard + testconfig.timeouts.scriptLoad * 3);
|
||||
var keyboardID = "options_with_save";
|
||||
var storeName = "foo";
|
||||
|
||||
keyman.setActiveKeyboard(keyboardID, 'en').then(function() {
|
||||
return keyman.setActiveKeyboard(keyboardID, 'en').then(function() {
|
||||
KeymanWeb.saveStore(storeName, 1);
|
||||
keyman.removeKeyboards(keyboardID, true);
|
||||
|
||||
var finalCheck = function() {
|
||||
// Reset the keyboard... again.
|
||||
keyman.removeKeyboards(keyboardID, true);
|
||||
|
||||
// Second test: expects option to still be "off" b/c cookies.
|
||||
runKeyboardTestFromJSON('/engine_tests/options_with_save_2.json', {usingOSK: false}, done, assert.equal, testconfig.timeouts.scriptLoad);
|
||||
};
|
||||
|
||||
}).then(() => {
|
||||
// First test: expects option to be "on" from cookie-init setting, emitting "foo.", then turning option "off".
|
||||
runKeyboardTestFromJSON('/engine_tests/options_with_save_1.json', {usingOSK: false}, finalCheck, assert.equal, testconfig.timeouts.scriptLoad);
|
||||
return runKeyboardTestFromJSON('/engine_tests/options_with_save_1.json',
|
||||
{usingOSK: false},
|
||||
assert.equal,
|
||||
testconfig.timeouts.scriptLoad)
|
||||
}).then(() => {
|
||||
// Reset the keyboard... again.
|
||||
keyman.removeKeyboards(keyboardID, true);
|
||||
|
||||
// Second test: expects option to still be "off" b/c cookies.
|
||||
return runKeyboardTestFromJSON('/engine_tests/options_with_save_2.json',
|
||||
{usingOSK: false},
|
||||
assert.equal,
|
||||
testconfig.timeouts.scriptLoad);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -183,12 +180,12 @@ describe('Engine - Browser Interactions', function() {
|
|||
describe('Sequence Simulation Checks', function() {
|
||||
this.timeout(testconfig.timeouts.scriptLoad);
|
||||
|
||||
it('Keyboard simulation', function(done) {
|
||||
runKeyboardTestFromJSON('/engine_tests/basic_lao_simulation.json', {usingOSK: false}, done, assert.equal, testconfig.timeouts.scriptLoad);
|
||||
it('Keyboard simulation', function() {
|
||||
return runKeyboardTestFromJSON('/engine_tests/basic_lao_simulation.json', {usingOSK: false}, assert.equal, testconfig.timeouts.scriptLoad);
|
||||
});
|
||||
|
||||
it('OSK simulation', function(done) {
|
||||
runKeyboardTestFromJSON('/engine_tests/basic_lao_simulation.json', {usingOSK: true}, done, assert.equal, testconfig.timeouts.scriptLoad);
|
||||
it('OSK simulation', function() {
|
||||
return runKeyboardTestFromJSON('/engine_tests/basic_lao_simulation.json', {usingOSK: true}, assert.equal, testconfig.timeouts.scriptLoad);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
@ -217,10 +214,10 @@ describe('Unmatched Final Groups', function() {
|
|||
fixture.cleanup();
|
||||
});
|
||||
|
||||
it('matches rule from early group AND performs default behavior', function(done) {
|
||||
it('matches rule from early group AND performs default behavior', function() {
|
||||
// While a TAB-oriented version would be nice, it's much harder to write the test
|
||||
// to detect change in last input element.
|
||||
runKeyboardTestFromJSON('/engine_tests/ghp_enter.json', {usingOSK: true}, done, assert.equal, testconfig.timeouts.scriptLoad);
|
||||
return runKeyboardTestFromJSON('/engine_tests/ghp_enter.json', {usingOSK: true}, assert.equal, testconfig.timeouts.scriptLoad);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -246,59 +243,56 @@ describe('Engine - Browser Interactions', function() {
|
|||
it('Local', function() {
|
||||
this.timeout(testconfig.timeouts.scriptLoad);
|
||||
|
||||
var test_callback = function() {
|
||||
return loadKeyboardFromJSON("/keyboards/lao_2008_basic.json",
|
||||
testconfig.timeouts.scriptLoad).then(function() {
|
||||
assert.isNotNull(keyman.getKeyboard("lao_2008_basic", "lo"), "Keyboard stub was not registered!");
|
||||
assert.equal(keyman.getActiveKeyboard(), "Keyboard_lao_2008_basic", "Keyboard not set automatically!");
|
||||
keyman.removeKeyboards('lao_2008_basic');
|
||||
assert.equal(keyman.getActiveKeyboard(), '', "Keyboard not removed correctly!");
|
||||
}
|
||||
|
||||
return loadKeyboardFromJSON("/keyboards/lao_2008_basic.json", testconfig.timeouts.scriptLoad)
|
||||
.then(test_callback);
|
||||
});
|
||||
});
|
||||
|
||||
it('Automatically sets first available keyboard', function() {
|
||||
this.timeout(2 * testconfig.timeouts.scriptLoad);
|
||||
|
||||
var test_callback = function() {
|
||||
assert.isNotNull(keyman.getKeyboard("lao_2008_basic", "lo"), "Keyboard stub was not registered!");
|
||||
assert.equal(keyman.getActiveKeyboard(), "Keyboard_lao_2008_basic", "Keyboard not set automatically!");
|
||||
keyman.removeKeyboards('lao_2008_basic');
|
||||
assert.equal(keyman.getActiveKeyboard(), '', "Keyboard not removed correctly!");
|
||||
}
|
||||
return loadKeyboardFromJSON("/keyboards/lao_2008_basic.json",
|
||||
testconfig.timeouts.scriptLoad,
|
||||
{passive: true}).then(() => {
|
||||
// Because we're loading the keyboard 'passively', KMW's setActiveKeyboard function is auto-called
|
||||
// on the stub-add. That specific call (for first keyboard auto-activation) is outside of KMW's
|
||||
// current Promise chain, so we can't _directly_ rely on a KMW Promise to test it.
|
||||
return new Promise((resolve) => {
|
||||
let hasResolved = false;
|
||||
// So, we give KMW the time needed for auto-activation to happen, polling a bit actively so that we don't
|
||||
// wait unnecessarily long after it occurs.
|
||||
let absoluteTimer = window.setTimeout(() => {
|
||||
if(!hasResolved) {
|
||||
resolve();
|
||||
hasResolved = true;
|
||||
}
|
||||
|
||||
window.clearTimeout(intervalTimer);
|
||||
}, testconfig.timeouts.scriptLoad);
|
||||
|
||||
let intervalTimer = window.setInterval(() => {
|
||||
if(keyman.getActiveKeyboard() != '') {
|
||||
window.clearTimeout(intervalTimer);
|
||||
window.clearTimeout(absoluteTimer);
|
||||
|
||||
return loadKeyboardFromJSON("/keyboards/lao_2008_basic.json", testconfig.timeouts.scriptLoad, {passive: true})
|
||||
.then(() => {
|
||||
// Because we're loading the keyboard 'passively', KMW's setActiveKeyboard function is auto-called
|
||||
// on the stub-add. That specific call (for first keyboard auto-activation) is outside of KMW's
|
||||
// current Promise chain, so we can't _directly_ rely on a KMW Promise to test it.
|
||||
return new Promise((resolve) => {
|
||||
let hasResolved = false;
|
||||
// So, we give KMW the time needed for auto-activation to happen, polling a bit actively so that we don't
|
||||
// wait unnecessarily long after it occurs.
|
||||
let absoluteTimer = window.setTimeout(() => {
|
||||
if(!hasResolved) {
|
||||
resolve();
|
||||
hasResolved = true;
|
||||
}
|
||||
|
||||
window.clearTimeout(intervalTimer);
|
||||
}, testconfig.timeouts.scriptLoad);
|
||||
|
||||
let intervalTimer = window.setInterval(() => {
|
||||
if(keyman.getActiveKeyboard() != '') {
|
||||
window.clearTimeout(intervalTimer);
|
||||
window.clearTimeout(absoluteTimer);
|
||||
|
||||
if(!hasResolved) {
|
||||
resolve();
|
||||
hasResolved = true;
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
// Once this delay-Promise resolves successfully (either way)...
|
||||
}).then(test_callback); // THEN we run our checks.
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
// Once this delay-Promise resolves successfully (either way)...
|
||||
}).then(function() {
|
||||
assert.isNotNull(keyman.getKeyboard("lao_2008_basic", "lo"), "Keyboard stub was not registered!");
|
||||
assert.equal(keyman.getActiveKeyboard(), "Keyboard_lao_2008_basic", "Keyboard not set automatically!");
|
||||
keyman.removeKeyboards('lao_2008_basic');
|
||||
assert.equal(keyman.getActiveKeyboard(), '', "Keyboard not removed correctly!");
|
||||
}); // THEN we run our checks.
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -24,23 +24,24 @@ describe('Engine - Chirality', function() {
|
|||
fixture.cleanup();
|
||||
});
|
||||
|
||||
it('Keyboard + OSK simulation', function(done) {
|
||||
it('Keyboard + OSK simulation', function() {
|
||||
this.timeout(testconfig.timeouts.scriptLoad * (testconfig.mobile ? 1 : 2));
|
||||
/* Interestingly, this still works on iOS, probably because we're able to force-set
|
||||
* the 'location' property in the simulated event on mobile devices, even when iOS neglects to
|
||||
* set it for real events.
|
||||
*/
|
||||
runKeyboardTestFromJSON('/engine_tests/chirality.json', {usingOSK: false}, function() {
|
||||
/* We only really care to test the 'desktop' OSK because of how it directly models the modifier keys.
|
||||
*
|
||||
* The 'phone' and 'layout' versions take shortcuts that bypass any tricky chiral logic;
|
||||
* a better test for those would be to ensure the touch OSK is constructed properly.
|
||||
*/
|
||||
if(!testconfig.mobile) {
|
||||
runKeyboardTestFromJSON('/engine_tests/chirality.json', {usingOSK: true}, done, assert.equal, testconfig.timeouts.scriptLoad);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}, assert.equal, testconfig.timeouts.scriptLoad);
|
||||
return runKeyboardTestFromJSON('/engine_tests/chirality.json',
|
||||
{usingOSK: false},
|
||||
assert.equal,
|
||||
testconfig.timeouts.scriptLoad).then(() => {
|
||||
/* We only really care to test the 'desktop' OSK because of how it directly models the modifier keys.
|
||||
*
|
||||
* The 'phone' and 'layout' versions take shortcuts that bypass any tricky chiral logic;
|
||||
* a better test for those would be to ensure the touch OSK is constructed properly.
|
||||
*/
|
||||
if(!testconfig.mobile) {
|
||||
return runKeyboardTestFromJSON('/engine_tests/chirality.json', {usingOSK: true}, assert.equal, testconfig.timeouts.scriptLoad);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -19,16 +19,14 @@ describe('Event Management', function() {
|
|||
teardownKMW();
|
||||
});
|
||||
|
||||
it('Keystroke-based onChange event generation', function(done) {
|
||||
it('Keystroke-based onChange event generation', function() {
|
||||
var simple_A = {"type":"key","key":"a","code":"KeyA","keyCode":65,"modifierSet":0,"location":0};
|
||||
var event = new KMWRecorder.PhysicalInputEventSpec(simple_A);
|
||||
|
||||
var ele = document.getElementById("input");
|
||||
var aliasing = false;
|
||||
|
||||
ele.onchange = function() {
|
||||
ele.onchange = null;
|
||||
done();
|
||||
}
|
||||
|
||||
if(ele['kmw_ip']) {
|
||||
|
|
@ -43,14 +41,15 @@ describe('Event Management', function() {
|
|||
let eventDriver = new KMWRecorder.BrowserDriver(ele);
|
||||
eventDriver.simulateEvent(event);
|
||||
|
||||
var focusEvent = new FocusEvent('blur', {relatedTarget: ele});
|
||||
let focusEvent = new FocusEvent('blur', {relatedTarget: ele});
|
||||
ele.dispatchEvent(focusEvent);
|
||||
|
||||
if(focusEvent) {
|
||||
ele.dispatchEvent(focusEvent);
|
||||
}
|
||||
// Asserts that the handler is called. As the handler clears itself, it will only
|
||||
// remain set if it hasn't been called.
|
||||
assert.isNull(ele.onchange, '`onchange` handler was not called');
|
||||
});
|
||||
|
||||
it('OSK-based onChange event generation', function(done) {
|
||||
it('OSK-based onChange event generation', function() {
|
||||
var simple_A = {"type":"osk","keyID":"default-K_A"};
|
||||
var event = new KMWRecorder.OSKInputEventSpec(simple_A);
|
||||
|
||||
|
|
@ -59,7 +58,6 @@ describe('Event Management', function() {
|
|||
|
||||
ele.onchange = function() {
|
||||
ele.onchange = null;
|
||||
done();
|
||||
}
|
||||
|
||||
if(ele['kmw_ip']) {
|
||||
|
|
@ -74,27 +72,15 @@ describe('Event Management', function() {
|
|||
let eventDriver = new KMWRecorder.BrowserDriver(ele);
|
||||
eventDriver.simulateEvent(event);
|
||||
|
||||
var focusEvent;
|
||||
let focusEvent = new FocusEvent('blur', {relatedTarget: ele});
|
||||
ele.dispatchEvent(focusEvent);
|
||||
|
||||
if(typeof FocusEvent == 'function') {
|
||||
focusEvent = new FocusEvent('blur', {relatedTarget: ele});
|
||||
} else {
|
||||
focusEvent = document.createEvent("FocusEvent");
|
||||
focusEvent.initFocusEvent("blur", true, false, ele.ownerDocument.defaultView, 0, ele);
|
||||
}
|
||||
|
||||
if(focusEvent)
|
||||
ele.dispatchEvent(focusEvent);
|
||||
// Asserts that the handler is called. As the handler clears itself, it will only
|
||||
// remain set if it hasn't been called.
|
||||
assert.isNull(ele.onchange, '`onchange` handler was not called');
|
||||
});
|
||||
|
||||
it('Keystroke-based onInput event generation', function(done) {
|
||||
// Not all browsers support InputEvent. Bypass the test for these.
|
||||
if(typeof InputEvent != 'function') {
|
||||
console.log("InputEvent not supported.");
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
it('Keystroke-based onInput event generation', function() {
|
||||
var simple_A = {"type":"key","key":"a","code":"KeyA","keyCode":65,"modifierSet":0,"location":0};
|
||||
var event = new KMWRecorder.PhysicalInputEventSpec(simple_A);
|
||||
|
||||
|
|
@ -106,9 +92,6 @@ describe('Event Management', function() {
|
|||
|
||||
ele.addEventListener("input", function() {
|
||||
counterObj.i++;
|
||||
if(counterObj.i == fin) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
if(ele['kmw_ip']) {
|
||||
|
|
@ -120,16 +103,11 @@ describe('Event Management', function() {
|
|||
eventDriver.simulateEvent(event);
|
||||
eventDriver.simulateEvent(event);
|
||||
eventDriver.simulateEvent(event);
|
||||
|
||||
assert.equal(counterObj.i, fin, "Event handler not called the expected number of times");
|
||||
});
|
||||
|
||||
it('OSK-based onInput event generation', function(done) {
|
||||
// Not all browsers support InputEvent. Bypass the test for these.
|
||||
if(typeof InputEvent != 'function') {
|
||||
console.log("InputEvent not supported.");
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
it('OSK-based onInput event generation', function() {
|
||||
var simple_A = {"type":"osk","keyID":"default-K_A"};
|
||||
var event = new KMWRecorder.OSKInputEventSpec(simple_A);
|
||||
|
||||
|
|
@ -141,9 +119,6 @@ describe('Event Management', function() {
|
|||
|
||||
ele.addEventListener("input", function() {
|
||||
counterObj.i++;
|
||||
if(counterObj.i == fin) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
if(ele['kmw_ip']) {
|
||||
|
|
@ -155,5 +130,7 @@ describe('Event Management', function() {
|
|||
eventDriver.simulateEvent(event);
|
||||
eventDriver.simulateEvent(event);
|
||||
eventDriver.simulateEvent(event);
|
||||
|
||||
assert.equal(counterObj.i, fin, "Event handler not called the expected number of times");
|
||||
});
|
||||
});
|
||||
|
|
@ -5,15 +5,6 @@ describe('Text Selection', function() {
|
|||
|
||||
/* Utility functions */
|
||||
|
||||
function supportsInputEvent(done) {
|
||||
if(typeof InputEvent != 'function') {
|
||||
console.log("InputEvent not supported.");
|
||||
done();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function setupElement(ele) {
|
||||
if(ele['kmw_ip']) {
|
||||
ele = ele['kmw_ip'];
|
||||
|
|
@ -46,12 +37,6 @@ describe('Text Selection', function() {
|
|||
return new KMWRecorder.BrowserDriver(setupElement(ele));
|
||||
}
|
||||
|
||||
|
||||
if(!supportsInputEvent()) {
|
||||
console.log('Don\'t run Text Selection tests on browsers that don\'t support Input event');
|
||||
return;
|
||||
}
|
||||
|
||||
var device = new com.keyman.Device();
|
||||
device.detect();
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ get_browser_set_for_OS ( ) {
|
|||
if [ $os_id = "mac" ]; then
|
||||
BROWSERS="--browsers Firefox,Chrome,Safari"
|
||||
elif [ $os_id = "win" ]; then
|
||||
BROWSERS="--browsers Firefox,Chrome,IE,Edge"
|
||||
BROWSERS="--browsers Firefox,Chrome,Edge"
|
||||
else
|
||||
BROWSERS="--browsers Firefox,Chrome"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -170,15 +170,15 @@ function runLoadedKeyboardTest(testDef, device, usingOSK, assertCallback) {
|
|||
testDef.test(proctor);
|
||||
}
|
||||
|
||||
function runKeyboardTestFromJSON(jsonPath, params, callback, assertCallback, timeout) {
|
||||
function runKeyboardTestFromJSON(jsonPath, params, assertCallback, timeout) {
|
||||
var testSpec = new KMWRecorder.KeyboardTest(fixture.load(jsonPath, true));
|
||||
let device = new com.keyman.Device();
|
||||
device.detect();
|
||||
|
||||
loadKeyboardStub(testSpec.keyboard, timeout).then(() => {
|
||||
return loadKeyboardStub(testSpec.keyboard, timeout).then(() => {
|
||||
runLoadedKeyboardTest(testSpec, device.coreSpec, params.usingOSK, assertCallback);
|
||||
}).finally(() => {
|
||||
keyman.removeKeyboards(testSpec.keyboard.id);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue