Merge branch 'feat/common/kmx-plus-osk-embed-support' into feat/developer/reserve-space-for-kmxplus

This commit is contained in:
Marc Durdin 2025-11-20 15:16:21 +01:00 committed by GitHub
commit 3e1c8ed04e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
79 changed files with 1057 additions and 7069 deletions

View file

@ -31,6 +31,7 @@ env:
STATUS_CONTEXT: 'Ubuntu Packaging'
DEBFULLNAME: 'Keyman GHA packager'
DEBEMAIL: 'support@keyman.com'
_builder_timings: false
jobs:
sourcepackage:
@ -135,7 +136,7 @@ jobs:
strategy:
fail-fast: true
matrix:
dist: [jammy, noble, plucky]
dist: [jammy, noble, plucky, questing]
steps:
- name: Checkout
@ -161,7 +162,7 @@ jobs:
strategy:
fail-fast: true
matrix:
dist: [questing]
dist: [devel]
steps:
- name: Checkout
@ -185,6 +186,23 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Free disk space
run: |
# Free disk space by removing some large directories that we don't use (#15171)
# see https://dev.to/mathio/squeezing-disk-space-from-github-actions-runners-an-engineers-guide-3pjg
# Remove Android SDKs (frees 12 GB on Ubuntu 24.04)
sudo rm -rf /usr/local/lib/android
# Remove Haskell (GHC) (frees 6.4 GB)
sudo rm -rf /usr/local/.ghcup
# Remove CodeQL and other toolcaches (frees 5.8 GB)
sudo rm -rf /opt/hostedtoolcache
# Remove .NET SDKs (frees 4.0 GB)
sudo rm -rf /usr/share/dotnet
- name: Download Source Artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:

View file

@ -1,5 +1,40 @@
# Keyman Version History
## 19.0.163 alpha 2025-11-17
* chore(linux): Update debian changelog (#15133)
* fix(windows): handle exceptions when walking stack in exception handler (#15150)
* fix(windows): disable OutputDebugString from DllMain (#15151)
* change(windows): include map2pdb generation for relevant Delphi projects (#14799)
* change(windows): use circular buffer for logging (#15163)
* fix(windows): handle forward slash for KEYMAN_ROOT env var (#15156)
* maint(mac): handle hdiutil detach failure correctly in make-km-dmg.sh (#15176)
## 19.0.162 alpha 2025-11-14
* chore(linux): use `devel` for next Ubuntu release (#15157)
* maint(resources): add build timing report to builder (#15114)
* maint(resources): add `builder_launch` function (#15129)
* fix(web): improve determination of default path (#15159)
* maint(linux): free disk space before running autopkgtests (#15172)
## 19.0.161 alpha 2025-11-13
* docs(android): Document disclaimer of Keyman NOT containing adware/spyware (#15136)
## 19.0.160 alpha 2025-11-12
* fix(android): Change Alert Dialog instead of Toast if package missing keyboard and lexical-model files (#15098)
* chore(linux): add support for Ubuntu 26.04 LTS Resolute (#15137)
## 19.0.159 alpha 2025-11-11
* maint(core): fix option defaults for Meson >= 1.1 (#15131)
## 19.0.158 alpha 2025-11-10
* fix(web): add unit tests for DeadkeyTracker and fix a few bugs (#15113)
## 19.0.157 alpha 2025-11-06
* chore(common): Consolidate platformtest.js keyboard to `/common/test/keyboards/` (#15034)

View file

@ -1 +1 @@
19.0.158
19.0.164

View file

@ -85,7 +85,8 @@ public class PackageActivity extends BaseActivity implements
if (pkgTarget.equals(PackageProcessor.PP_TARGET_LEXICAL_MODELS)) {
kmpProcessor = new LexicalModelPackageProcessor(resourceRoot);
} else if (!pkgTarget.equals(PackageProcessor.PP_TARGET_KEYBOARDS)) {
showErrorToast(getString(R.string.no_targets_to_install));
// Not using showErrorToast to avoid cluttering Sentry with noise
showErrorDialog(context, pkgId, getString(R.string.no_targets_to_install));
return;
}
tempPackagePath = kmpProcessor.unzipKMP(kmpFile);

View file

@ -0,0 +1,28 @@
---
title: Does Keyman include adware or spyware?
---
No!
We never have and never will include adware or spyware components.
When [enabling Keyman as a system keyboard](../start/enabling-system-keyboard), Android displays the following warning, which is the same message that is displayed for any keyboard app that is installed onto an Android device.
![](../android_images/attention.png)
> Attention: This input method may be able to collect all the text you
> type, including personal data like passwords and credit card numbers.
> It comes from the app Keyman. Use this input method? Cancel / OK
The Keyman app does not record the keystrokes you type and does not send keystrokes off-device, unlike many other keyboard apps. All predictive text suggestions are entirely on your device.
Keyman is an open source application, and you are most welcome to inspect the source code at https://github.com/keymanapp/keyman.
## Checking for Updates
Keyman does contact the keyman.com website periodically to check for updates for the keyboards and predictive text dictionaries you have installed.
In this process, the only information communicated is the current version of Keyman and the version of the keyboards and dictionaries.
## Crash Report Information
When enabled, the Keyman app will send crash reporting information to [Sentry](https://keyman.sentry.io). No personally identifiable information or keyboard strokes are recorded. This can be disabled in the Keyman [settings menu](../basic/config/).

View file

@ -5,5 +5,6 @@ title: About Keyman for Android
* [Welcome to Keyman](welcome)
* [What's New](whatsnew)
* [System Requirements](system-requirements)
* [Does Keyman include adware or spyware?](adware)
* [Data Privacy Policy](privacy)
* [Version History](history)

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -6,6 +6,7 @@ title: Keyman for Android 19.0 Help
* [Welcome to Keyman](about/welcome)
* [What's New](about/whatsnew)
* [System Requirements](about/system-requirements)
* [Does Keyman include adware or spyware?](about/adware)
* [Data Privacy Policy](about/privacy)
* [Version History](about/history)

View file

@ -8,7 +8,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
builder_describe \
"Keyman common third party Delphi components" \
clean configure build test reset \
:cef4delphi :dcpcrypt :jwa :sentry :tds2dbg
:cef4delphi :dcpcrypt :jwa :sentry
builder_parse "$@"
builder_run_child_actions clean configure build test

View file

@ -10,6 +10,8 @@ uses
Winapi.Windows,
jwaimagehlp,
jwantstatus,
jwawinbase,
sentry;
@ -229,20 +231,41 @@ var
Skip: Integer;
LastMask: WORD;
begin
// Floating point state may be broken here, so let's mask it out and continue
// We'll restore state afterwards
LastMask := Get8087CW;
Set8087CW_Threadsafe($1332);
try
if (ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_ACCESS_VIOLATION) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_ILLEGAL_INSTRUCTION) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_PRIV_INSTRUCTION) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_POSSIBLE_DEADLOCK) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_IN_PAGE_ERROR) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> EXCEPTION_STACK_OVERFLOW) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> STATUS_INVALID_PARAMETER) and
(ExceptionInfo.ExceptionRecord.ExceptionCode <> cDelphiException) then
begin
// These are the most common exceptions we wish to catch; we won't get
// call stacks for any other exception types, but we will still log
// important exceptions
Exit(0);
end;
if ExceptionInfo.ExceptionRecord.ExceptionCode = cDelphiException
then Skip := DELPHI_FRAMES_TO_SKIP
else Skip := 0;
CaptureStackTraceForException(ExceptionInfo.ExceptionRecord.ExceptionAddress, ExceptionInfo, Skip);
// Floating point state may be broken here, so let's mask it out and continue
// We'll restore state afterwards
LastMask := Get8087CW;
Set8087CW_Threadsafe($1332);
// Restore FP state
Set8087CW_Threadsafe(LastMask);
if ExceptionInfo.ExceptionRecord.ExceptionCode = cDelphiException
then Skip := DELPHI_FRAMES_TO_SKIP
else Skip := 0;
CaptureStackTraceForException(ExceptionInfo.ExceptionRecord.ExceptionAddress, ExceptionInfo, Skip);
Result := 0; //EXCEPTION_CONTINUE_SEARCH;
// Restore FP state
Set8087CW_Threadsafe(LastMask);
Result := 0; //EXCEPTION_CONTINUE_SEARCH;
except
// Silently handle errors here because we can't do anything with them;
// we'll just get no stack
Result := 0;
end;
end;
constructor TSentryClient.Create(AOptions: TSentryClientOptions; const ALogger: string; AFlags: TSentryClientFlags);

View file

@ -24,10 +24,8 @@ function do_test() {
delphi_msbuild SentryClientTest.dproj "//p:Platform=Win32"
delphi_msbuild SentryClientVclTest.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET_PATH/SentryClientTest.exe" -dpr SentryClientTest.dpr
sentrytool_delphiprep "$WIN32_TARGET_PATH/SentryClientVclTest.exe" -dpr SentryClientVclTest.dpr
tds2dbg "$WIN32_TARGET_PATH/SentryClientTest.exe"
tds2dbg "$WIN32_TARGET_PATH/SentryClientVclTest.exe"
do_map2pdb "$WIN32_TARGET_PATH/SentryClientTest.map" "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/SentryClientVclTest.map" "$WIN32_TARGET"
sentry-cli upload-dif -p keyman-windows --wait --include-sources .

View file

@ -1,49 +0,0 @@
Keyman Third Party Package Readme: Tds2Dbg
===========================================
Name: Tds2Dbg
Purpose: Tds2Dbg converts .tds to .dbg files
Source: https://sourceforge.net/projects/tds2dbg/
Version: 2014-12-22
License: ISC (MIT compatible)
Updated for: N/A
Documentation: https://sourceforge.net/projects/tds2dbg/
Download location: https://sourceforge.net/projects/tds2dbg/
Licensed For: Unlimited developers
Include Paths: N/A
Installation notes
------------------
Download from site above.
Build notes
-----------
tds2dbg is used for building .dbg files from .tds files so that Delphi
projects can be debugged in WinDbg/Visual Studio and with dump files.
This project builds with Borland C++ 5.5 and not other compilers;
while it may build successfully with other compilers, there appear to
be alignment issues. Hence, the executable is included in the repository
to avoid requiring another build tool.
License - ISC
-------------
Copyright (c) 2009-2010, Bjarne Juul Pasgaard
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice, this permission notice and the following disclaimer
appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View file

@ -1 +0,0 @@
/bin/

View file

@ -1,8 +0,0 @@
Get the free Borland C++ Builder 5.5 from
https://downloads.embarcadero.com/free/c_builder
Ensure that the location of bcc32.exe is in your path
and type "make.exe" in a command prompt with the
current working directory set to the directory
holder the sources for tds2dbg.

View file

@ -1,12 +0,0 @@
tds2dbg 0.2.0
* Added support for conversion of public and static symbols.
* Corrected padding and alignment.
* Preliminary support for conversion of types. Still incomplete.
* Can now convert a non-PE CodeView NB09 file to a PE CodeView file.
* Can now generate bare CodeView files (i.e. no PE header).
tds2dbg 0.1.0
* Initial version

View file

@ -1,15 +0,0 @@
Copyright (c) 2009-2010, Bjarne Juul Pasgaard
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice, this permission notice and the following disclaimer
appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View file

@ -1,22 +0,0 @@
info:
echo The following builds require Borland C++ compiler to be installed and in the path; see Readme.txt for details.
echo So, the tds2dbg.bin is kept in the repo in order to make builds simpler
BCC55DIR=c:\borland\bcc55
tds2dbg.exe: main.cpp tds2dbg.cpp fileutils.cpp log.cpp
# $(CL) /Fe$< /EHsc $**
path $(BCC55DIR)\bin;$(PATH)
$(BCC55DIR)\bin\bcc32 -y -v -L$(BCC55DIR)\Lib -I$(BCC55DIR)\Include -e$< $**
test.exe: testa.cpp
# $(CL) /Fe$< /EHsc $**
$(BCC55DIR)\bin\bcc32 -y -v -M -L$(BCC55DIR)\Lib -I$(BCC55DIR)\Include -e$< $**
clean:
-del /Q tds2dbg.obj fileutils.obj log.obj main.obj tds2dbg.tds
-del /Q testa.obj test.exe test.tds test.dbg
-rmdir /S /Q html
doc:
doxygen tds2dbg.dcf

View file

@ -1,64 +0,0 @@
NAME
tds2dbg - Borland TDS debug symbol file to Microsft DBG debug symbol file
converter.
SYNOPSIS
tds2dbg [ [-h|--help] | [-t|--types] [-s|--statics] [--] program.exe ]
DESCRIPTION
tds2dbg will read the Borland debug symbol file associated with the
Microsoft Windows executable program.exe (program.tds) and create a
corresponding Microsoft DBG debug symbol file (program.dbg). Additionally,
it will modify a flag in program.exe to signal that debug symbols are
available externally to the executable.
The dbg files can be used with, say, windbg available from the
Microsoft Debugging Tools at the Microsoft Windows Hardware Developer
Central (http://www.microsoft.com/whdc).
OPTIONS
-h, --help Prints a help text. Running tds2dbg without arguments
will do the same.
-v, --verbose Each occurence of this option increases verbosity.
-q, --quiet Operate silently.
-t, --types Convert types (incomplete feature).
-s, --statics Treat statics as publics.
-b, --barecv Write bare code view data. No PE header is added.
KNOWN ISSUES
tds2dbg is currently only able to insert source code references, public
symbols, and static symbols into the generated DBG file. Debug information
on local variables is partially generated, but no debugger seems to be
able to use this information. Additionally, conversion of types is not
fully supported and is only done on request (the -t option).
VERSION
This is the documentation for tds2dbg version 0.2.0
AUTHOR
Copyright (C) 2009-2010, Bjarne Juul Pasgaard
LICENSE
Copyright (C) 2009-2010, Bjarne Juul Pasgaard
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice, this permission notice and the following disclaimer
appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View file

@ -1,34 +0,0 @@
#!/usr/bin/env bash
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
# TODO: move this out of ext and into tools
builder_describe "tds2dbg component" clean configure build test
builder_parse "$@"
#-------------------------------------------------------------------------------------------------------------------
source "$KEYMAN_ROOT/resources/build/win/environment.inc.sh"
builder_describe_outputs \
configure:project /resources/build/win/delphi_environment_generated.inc.sh \
build:project "$WIN32_TARGET_PATH/tds2dbg.exe"
#-------------------------------------------------------------------------------------------------------------------
function do_build() {
mkdir -p "$WIN32_TARGET_PATH"
cp tds2dbg.bin "$WIN32_TARGET_PATH/tds2dbg.exe"
mkdir -p "$KEYMAN_ROOT/common/windows/bin/tools"
cp "$WIN32_TARGET_PATH/tds2dbg.exe" "$KEYMAN_ROOT/common/windows/bin/tools/"
}
builder_run_action clean:project clean_windows_project_files
builder_run_action configure:project configure_windows_build_environment
builder_run_action build:project do_build
# builder_run_action test:project do_test

View file

@ -1,158 +0,0 @@
// Copyright (c) 2009-2010, Bjarne Juul Pasgaard
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice, this permission notice and the following disclaimer
// appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/// @file fileutils.cpp File utilities
#include "fileutils.h"
FileWrapper::FileWrapper(FILE* f) : m_f(f)
{
}
unsigned int
FileWrapper::tell() const {
return ftell(m_f);
}
void
FileWrapper::seek(unsigned int pos) throw(std::runtime_error) {
if(fseek(m_f,pos,SEEK_SET)) {
throw std::runtime_error("Failed to set read/write position");
}
}
void
FileWrapper::skip(unsigned int amount) throw(std::runtime_error) {
if(fseek(m_f,amount,SEEK_CUR)) {
throw std::runtime_error("Failed to advance read/write position");
}
}
void
FileWrapper::write1bLpfName(const std::string& name) throw(std::runtime_error)
{
unsigned char len = (unsigned char) (name.length());
if((sizeof len) != fwrite(&len,1,(sizeof len),m_f)) {
throw std::runtime_error("Failed to write length field of string");
}
if((unsigned int)(len+1) != fwrite(name.c_str(),1,len+1,m_f)) {
throw std::runtime_error("Failed to write string contents");
}
}
RecordReader::RecordReader(FileWrapper& tdsFile, unsigned int recSz) :
m_tdsFile(tdsFile)
{
m_recPos = tdsFile.tell();
m_endPos = m_recPos + recSz;
}
RecordWriter::~RecordWriter()
{
WriteQCont::iterator it = m_writeQ.begin();
while(m_writeQ.end() != it) {
delete *it++;
}
}
void
RecordWriter::commit(FileWrapper& f) const throw(std::runtime_error)
{
WriteQCont::const_iterator it = m_writeQ.begin();
while(m_writeQ.end() != it) {
(*it++)->commit(f);
}
}
unsigned int
RecordWriter::length() const
{
unsigned int len = 0;
WriteQCont::const_iterator it = m_writeQ.begin();
while(m_writeQ.end() != it) {
len += (*it++)->length();
}
return len;
}
void
RecordWriter::write1bLpfName(const std::string& name)
{
unsigned char len = (unsigned char) (name.length());
this->write(len);
this->write(*(name.c_str()),len+1);
}
RecordWriter::WriteQueueElement::~WriteQueueElement()
{
}
unsigned int
RecordWriter::CopiedData::length() const
{
return m_data.size();
}
void
RecordWriter::CopiedData::commit(FileWrapper& f) const throw(std::runtime_error)
{
f.write(m_data.front(),m_data.size());
}
RecordWriter::WriteQueueElement*
RecordWriter::CopiedData::append(const unsigned char* src, unsigned int len)
{
m_data.reserve(m_data.size()+len);
while(len--) {
m_data.push_back(*src++);
}
return 0;
}
RecordWriter::ReferencedData::
ReferencedData(const unsigned char* src, unsigned int len)
: m_src(src), m_len(len)
{
}
unsigned int
RecordWriter::ReferencedData::length() const
{
return m_len;
}
void
RecordWriter::ReferencedData::commit(FileWrapper& f) const
throw(std::runtime_error)
{
f.write(*m_src,m_len);
}
RecordWriter::WriteQueueElement*
RecordWriter::ReferencedData::append(const unsigned char* src, unsigned int len)
{
CopiedData* c = new CopiedData();
c->append(src,len);
return c;
}
// Local Variables:
// mode:c++
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:

View file

@ -1,564 +0,0 @@
// Copyright (c) 2009-2010, Bjarne Juul Pasgaard
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice, this permission notice and the following disclaimer
// appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/// @file fileutils.h File utilities
#ifndef FILEUTILS_H
#define FILEUTILS_H
#include <stdio.h>
#include <vector>
/// @brief Base class for file access wrappers.
class FileWrapper {
public:
/// @brief Constructor.
///
/// @param f The file stream being wrapped. Ownership not transferred.
FileWrapper(FILE* f);
/// @brief Returns the current read/write position.
/// @return The current write position.
unsigned int tell() const;
/// @brief Sets the current read/write position.
///
/// @param pos The new (absolute) write position.
///
/// @throw runtime_error If an error occured.
void seek(unsigned int pos) throw(std::runtime_error);
/// @brief Skips a number of bytes from the input stream.
///
/// @param amount The number of bytes to skip.
///
/// @throw runtime_error If an error occured.
void skip(unsigned int amount) throw(std::runtime_error);
/// @brief Reads a sequence of structures from the current read
/// position and throws an exception if end of file is
/// reached prematurely.
///
/// @param dst The 1st structure to hold the data read in.
/// @param num The number of structures to read.
///
/// @tparam The class of the structures to read.
///
/// @throw runtime_error If not all bytes could be read.
template <class T>
void read(T& dst, unsigned int num = 1) throw(std::runtime_error) {
unsigned int n = fread(&dst,1,num*sizeof(T),m_f);
if(num*sizeof(T) != n) {
throw std::runtime_error("Unexpected end of file");
}
}
/// @brief Attempts to reads a sequence of structures from the
/// current read position.
///
/// @param dst The 1st structure to hold the data read in.
/// @param num The number of structures to read.
///
/// @return The number of bytes actually read.
///
/// @tparam The class of the structures to read.
template <class T>
unsigned int tryRead(T& dst, unsigned int num = 1) {
return fread(&dst,1,num*sizeof(T),m_f);
}
/// @brief Writes a sequence of bytes to the current write position.
///
/// @param src Reference to the 1st structure to be written.
/// @param num The number of instances to write.
///
/// @return The number of bytes actually written.
///
/// @tparam T Class of the structure to write.
///
/// @throw runtime_error If an error occured.
template <class T>
unsigned int
write(const T& src, unsigned int num = 1) throw(std::runtime_error) {
unsigned int n = fwrite(&src,1,num*sizeof(T),m_f);
if(n != num*sizeof(T)) {
throw std::runtime_error("Write error in write()");
}
return n;
}
/// @brief Writes multiple copies of the same structure starting from the
/// current write position.
///
/// @param src Reference to the structure to be written.
/// @param num The number of copies to write.
///
/// @return The number of bytes actually written.
///
/// @tparam T Class of the structure to write.
///
/// @throw runtime_error If an error occured.
template <class T>
unsigned int
writeRep(const T& src, unsigned int num = 1) throw(std::runtime_error);
/// @brief Writes a (1-byte) length-prefixed name to the current
/// write position.
///
/// @param name The name to write.
///
/// @throw runtime_error If an error occured.
void write1bLpfName(const std::string& name) throw(std::runtime_error);
protected:
FILE* m_f; ///< The file stream.
};
template <class T>
unsigned int
FileWrapper::writeRep(const T& src, unsigned int num)
throw(std::runtime_error)
{
unsigned int n = 0;
while(num--) {
unsigned int m = fwrite(&src,1,sizeof(T),m_f);
if(sizeof(T) != m) {
throw std::runtime_error("Write error in writeRep()");
}
n += m;
}
return n;
}
/// @brief Reads a record sequentially from a file while keeping track
/// of the number of bytes available.
class RecordReader {
public:
/// @brief Constructor.
///
/// @param tdsFile The file to read from.
/// @param recSz The number of bytes in the record.
RecordReader(FileWrapper& tdsFile, unsigned int recSz);
/// @brief Returns the current read position.
///
/// @return The current (absolute) read position.
unsigned int tell() {
return m_tdsFile.tell();
}
/// @brief Determines whether an end-of-record condition is met.
///
/// @retval true if all bytes have been read.
/// @retval false otherwise.
bool eor() const {
return (m_tdsFile.tell() >= m_endPos);
}
/// @brief Sets the current absolute read position.
///
/// @param pos The new (absolute) read position.
///
/// @throw runtime_error In case of errors.
void seek(unsigned int pos) throw(std::runtime_error) {
m_tdsFile.seek(pos);
}
/// @brief Skips a number of bytes from the input stream.
///
/// @param amount The number of bytes to skip.
///
/// @throw runtime_error In case of errors.
void skip(unsigned int amount) throw(std::runtime_error) {
m_tdsFile.skip(amount);
}
/// @brief Reads a sequence of structures from the current read
/// position in the TDS file.
///
/// @param dst The 1st structure to hold the data read in.
/// @param num The number of structures to read.
///
/// @throw runtime_error In case of errors.
template <class T>
void read(T& dst, unsigned int num = 1) throw(std::runtime_error) {
m_tdsFile.read(dst,num);
}
protected:
FileWrapper& m_tdsFile; ///< The file to read from.
unsigned int m_recPos; ///< The position of the current record.
unsigned int m_endPos; ///< The position after the end of the record list.
};
/// @brief Registers data for later writing to a file while counting
/// the number of bytes to be written.
class RecordWriter {
public:
/// @brief Destructor.
~RecordWriter();
/// @brief Commits the data registered to a file at the current write
/// position of the file.
///
/// @param file The file to write to.
///
/// @throw runtime_error In case of errors.
void commit(FileWrapper& f) const throw(std::runtime_error);
/// @brief Returns the number of bytes registered for later writing.
///
/// @return The number of bytes registered.
unsigned int length() const;
/// @brief Appends a sequence of structures to the tail of the
/// write queue.
///
/// The data provided is copied into the internal write queue.
///
/// @param dst The 1st structure that holds the data to be written.
/// @param num The number of structures to write.
///
/// @return The number of bytes written.
template <class T>
unsigned int write(const T& src, unsigned int num = 1) {
unsigned int n = sizeof(T)*num;
WriteQueueElement* e;
if(0 == m_writeQ.size()) {
e = new CopiedData();
m_writeQ.push_back(e);
}
e = m_writeQ.back()->append(reinterpret_cast<const unsigned char*>(&src),n);
if(e) {
// New element created, append it to the queue.
m_writeQ.push_back(e);
}
return n;
}
/// @brief Writes multiple copies of the same structure starting from the
/// current write position.
///
/// @param src Reference to the structure to be written.
/// @param num The number of copies to write.
///
/// @return The number of bytes actually written.
///
/// @tparam T Class of the structure to write.
template <class T>
unsigned int writeRep(const T& src, unsigned int num = 1) {
unsigned int n = 0;
while(num--) {
n+= this->write(src);
}
return n;
}
/// @brief Appends a sequence of structures to the tail of the
/// write queue.
///
/// This menthod appends a reference to the data to the write
/// queue. The data provided is not copied.
///
/// @param dst The 1st structure that holds the data to be written.
/// @param num The number of structures to write.
///
/// @return The number of bytes written.
template <class T>
unsigned int delayedWrite(const T& src, unsigned int num = 1) {
unsigned int n = sizeof(T)*num;
m_writeQ.
push_back(new
ReferencedData(reinterpret_cast<unsigned char*>(&src),n));
return n;
}
/// @brief Appends a (1-byte) length-prefixed name to the write queue.
///
/// @param name The name to write.
void write1bLpfName(const std::string& name);
protected:
/// @brief Base class for write queue data.
class WriteQueueElement
{
public:
/// @brief Destructor.
virtual ~WriteQueueElement();
/// @brief Returns the number of bytes registered within this element.
///
/// @return The number of bytes registered within this element.
virtual unsigned int length() const = 0;
/// @brief Commits the data in this element to the current write
/// position of a file.
///
/// @param f The file to write to.
///
/// @throw runtime_error In case of errors.
virtual void commit(FileWrapper& f) const throw(std::runtime_error) = 0;
/// @brief Appends a copy of a sequence of bytes to the current
/// element.
///
/// @param src The first byte to copy.
/// @param len The number of bytes to copy.
///
/// @return 0 if the bytes were appended to the current element, or
/// a pointer to a new element that received the copied bytes.
virtual
WriteQueueElement* append(const unsigned char* src, unsigned int len) = 0;
};
/// @brief Data copied into the write queue.
class CopiedData : public WriteQueueElement
{
public:
// Overridden base class method.
unsigned int length() const;
// Overridden base class method.
void commit(FileWrapper& f) const throw(std::runtime_error);
// Overridden base class method.
WriteQueueElement* append(const unsigned char* src, unsigned int len);
protected:
std::vector<unsigned char> m_data;
};
/// @brief Data referenced from the write queue.
class ReferencedData : public WriteQueueElement
{
public:
// Overridden base class method.
unsigned int length() const;
// Overridden base class method.
void commit(FileWrapper& f) const throw(std::runtime_error);
// Overridden base class method.
WriteQueueElement* append(const unsigned char* src, unsigned int len);
/// @brief Constructor.
///
/// @param src The first byte to reference.
/// @param len The number of subsequent bytes to reference.
ReferencedData(const unsigned char* src, unsigned int len);
protected:
const unsigned char* m_src; ///< The first byte referenced.
unsigned int m_len; ///< The number of bytes referenced.
};
protected:
/// @brief Container underlying the internal write queue.
typedef std::vector<WriteQueueElement*> WriteQCont;
/// @brief The internal write queue.
WriteQCont m_writeQ;
};
/// @brief Postpones writing of a record until committed.
///
/// @note It is important to create an instance of this class before writing
/// any records that should follow it (in file order).
///
/// @tparam R The record structure.
template <class R>
class PpRecordWriter {
public:
/// @brief Constructor.
///
/// @param dbgFile The DBG file to write to.
/// @param rec The record to be written when committed to.
PpRecordWriter(FileWrapper& dbgFile, R& rec) :
m_dbgFile(dbgFile), m_rec(rec), m_len(0)
{
m_recPos = dbgFile.tell();
dbgFile.skip(sizeof(R));
}
/// @brief Commits to the record.
///
/// This method will write out the record.
///
/// @throw runtime_error In case of errors.
void commit() throw(std::runtime_error) {
unsigned int oldPos = m_dbgFile.tell();
m_dbgFile.seek(m_recPos);
m_len = m_dbgFile.write(m_rec);
m_dbgFile.seek(oldPos);
}
/// @brief Returns the number of bytes written.
/// @return The number of bytes written.
unsigned int length() const {
return m_len;
}
protected:
/// @brief The DBG file to write to.
FileWrapper& m_dbgFile;
/// @breif The record to write.
R& m_rec;
/// @brief File position of the record.
unsigned int m_recPos;
/// @brief Number of bytes written.
unsigned int m_len;
};
/// @brief Postpones writing of a header with a length field to be
/// computed until committed.
///
/// @tparam H The header structure.
/// @tparam L The type of the length field.
template <class H, class L>
class HeaderWithLen {
public:
/// @brief Constructor.
///
/// @param hdr The header to be written when committed to.
/// Must remain available during the entire lifetime of the
/// object created.
/// @param lenFld The length field to update. Must remaining available
/// during the entire lifetime of the object created.
HeaderWithLen(H& hdr, L& lenFld) : m_hdr(hdr), m_lenFld(lenFld)
{
}
/// @brief Writes the header and the data registered by a RecordWriter.
///
/// This method will write out the header with an updated length field
/// as well as the data registered by the RecordWriter.
/// The length field will be computed as the size of the header, plus the
/// size of the data written by the RecordWriter, minus the size of the
/// length field itself.
///
/// @param f The file to write to.
/// @param w The record writer that was used to write the data committed to.
///
/// @throw runtime_error In case of errors.
void
commit(FileWrapper& f, const RecordWriter& w) throw(std::runtime_error){
m_lenFld = sizeof(H) + w.length()-sizeof(L);
f.write(m_hdr);
w.commit(f);
}
/// @brief Returns the number of bytes (to be) written by the record writer
/// and the header writer itself.
///
/// @return The number of bytes (to be) written.
unsigned int length(const RecordWriter& w) const {
return sizeof(H) + w.length();
}
protected:
/// @brief The header to write.
H& m_hdr;
/// @brief The length field to update.
L& m_lenFld;
};
/// @brief Writes a sequence of bytes from one file object to another.
///
/// @param dst Destination file object.
/// @param src Source file object.
/// @param amount Number of bytes to copy.
///
/// @tparam D Class of the destination object.
/// @tparam S Class of the source object.
///
/// @throw runtime_error In case of errors.
template <class D, class S>
void
copyFileContent(D& dst, S& src, unsigned int amount = 1)
throw(std::runtime_error)
{
unsigned char buf[512];
unsigned int toRead = (sizeof buf);
while(amount) {
if(toRead>amount) {
toRead = amount;
}
src.read(buf[0],toRead);
amount -= toRead;
dst.write(buf[0],toRead);
}
}
/// @brief Appends the contents of one file to another.
///
/// @param dst Destination file object.
/// @param src Source file object.
///
/// @tparam D Class of the destination object.
/// @tparam S Class of the source object.
///
/// @throw runtime_error In case of errors.
template <class D, class S>
void
copyFile(D& dst, S& src) throw(std::runtime_error)
{
unsigned char buf[512];
unsigned int bytesRead;
do {
bytesRead = src.tryRead(buf[0],(sizeof buf));
dst.write(buf[0],bytesRead);
} while((sizeof buf) == bytesRead);
}
#endif
// Local Variables:
// mode:c++
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:

View file

@ -1,44 +0,0 @@
// Copyright (c) 2009-2010, Bjarne Juul Pasgaard
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice, this permission notice and the following disclaimer
// appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/// @file log.cpp Log utilitites
#include "log.h"
std::ostream& Log::operator()(unsigned int verbosity)
{
if(verbosity<m_verbosity) {
return std::cout;
} else {
return m_nullStream;
}
}
Log::NullStreamBuf::NullStreamBuf()
{
}
Log& mylog()
{
static Log _log;
return _log;
}
// Local Variables:
// mode:c++
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:

View file

@ -1,82 +0,0 @@
// Copyright (c) 2009-2010, Bjarne Juul Pasgaard
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice, this permission notice and the following disclaimer
// appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/// @file log.h Log utilities
#ifndef LOG_H
#define LOG_H
#include <iostream>
/// @brief Logger class.
class Log
{
public:
/// @brief Constructor.
Log() : m_verbosity(0), m_nullStream(&m_nullStreamBuf) {}
/// @brief Sets the level of verbosity
///
/// @param verbosity New level of verbosity. 0 means silence.
void setVerbosity(unsigned int verbosity) { m_verbosity = verbosity; }
/// @brief Returns a reference to a stream handling the requested level
/// of verbosity.
///
/// @param verbosity The requested level of verbosity.
std::ostream& operator()(unsigned int verbosity = 0);
protected:
/// @brief Helper class implementing a dummy stream buffer that
/// discards everything sent to it.
///
/// @ingroup HMILoggingFeatures
class NullStreamBuf : public std::streambuf
{
public:
/// @brief Constructor
NullStreamBuf();
};
protected:
/// @brief The current level of verbosity.
unsigned int m_verbosity;
/// @brief Supporting stream buffer for m_nullStream.
NullStreamBuf m_nullStreamBuf;
/// @brief Internal stream used as a null-stream that discards
/// everything streamed to it.
std::ostream m_nullStream;
};
/// @brief Accesser for the global Log instance.
///
/// @return Reference to the global log.
Log& mylog();
#endif LOG_H
// Local Variables:
// mode:c++
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:

View file

@ -1,115 +0,0 @@
// Copyright (c) 2009-2010, Bjarne Juul Pasgaard
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice, this permission notice and the following disclaimer
// appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "tds2dbg.h"
#include <iostream>
#include <string>
#include <locale>
void printUsage(const char* argv0)
{
std::cerr
<< "Usage: " << argv0
<< " [-h|--help] [-t|--types] [-s|--statics] [--] exefilenamewithextension\n"
"Options:\n"
" -h, --help Prints this message\n"
" -v, --verbose Increase verbosity\n"
" -q, --quiet Operate silently\n"
" -t, --types Convert types (incomplete feature)\n"
" -s, --statics Treat statics as publics\n"
" -b, --barecv Write bare code view data\n"
<< std::endl;
}
int main(int argc, char* argv[])
{
int argNo = 1;
unsigned int verbosity = 1;
bool convertTypes = false;
bool staticsAsPublics = false;
bool bareCodeView = false;
while(argc > 1) {
char* a = argv[argNo];
if('-' != *a || 0==strcmp("--",a)) {
break;
} else if(0==strcmp("-h",a) || 0==strcmp("--help",a)) {
printUsage(argv[0]);
return 0;
} else if(0==strcmp("--verbose",a) || 0==strcmp("-v",a)) {
if(verbosity) ++verbosity;
} else if(0==strcmp("--quiet",a) || 0==strcmp("-q",a)) {
verbosity = 0;
} else if(0==strcmp("--types",a) || 0==strcmp("-t",a)) {
convertTypes = true;
} else if(0==strcmp("--statics",a) || 0==strcmp("-s",a)) {
staticsAsPublics = true;
} else if(0==strcmp("--barecv",a) || 0==strcmp("-b",a)) {
bareCodeView = true;
} else {
std::cerr << "Unknown option " << a << std::endl;
printUsage(argv[0]);
return 1;
}
--argc;
++argNo;
}
if(2 != argc) {
printUsage(argv[0]);
return 1;
}
std::string exeFile(argv[argNo]);
std::string::size_type dotP = exeFile.rfind('.');
if(std::string::npos == dotP) {
std::cerr
<< "Argument exe file name must be specified with extension"
<< std::endl;
return 2;
}
std::string tdsFile(exeFile.substr(0,dotP+1));
std::string dbgFile(tdsFile);
const std::ctype<char>& ctf =
std::use_facet<std::ctype<char> >(std::locale());
std::string::size_type ep = dotP;
while(exeFile.length() != ep) {
exeFile[ep] = ctf.tolower(exeFile[ep]);
++ep;
}
std::string ext(exeFile.substr(dotP+1));
if(!ext.compare("tds") || !ext.compare("dbg")) {
std::cerr
<< "Executable file cannot have extension .tds or .dbg"
<< std::endl;
return 1;
}
tdsFile.append("tds");
dbgFile.append("dbg");
return tds2dbg(exeFile.c_str(),tdsFile.c_str(),dbgFile.c_str(),
verbosity,convertTypes,staticsAsPublics,bareCodeView);
}
// Local Variables:
// mode:c++
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,38 +0,0 @@
// Copyright (c) 2009-2010, Bjarne Juul Pasgaard
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice, this permission notice and the following disclaimer
// appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifndef TDS2DBGH
#define TDS2DBGH
/// @brief Converts an exe and a tds file to a dbg file.
///
/// @param exeFileName Name of EXE file.
/// @param tdsFileName Name of TDS file.
/// @param dbgFileName Name of DBG file.
/// @param verbosity Verbosity level. 0 means silence.
/// @param doConvTypes Should types be converted?
/// @param staticsAsPublics Treat statics as publics?
/// @param bareCodeView Write bare code view (no header).
///
/// @todo doConvTypes == true is incomplete.
int tds2dbg(const char* exeFileName,
const char* tdsFileName,
const char* dbgFileName,
unsigned int verbosity,
bool doConvTypes,
bool staticsAsPublics,
bool bareCodeView);
#endif

View file

@ -1,31 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.1000
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tds2dbg", "tds2dbg.vcxproj", "{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Debug|x64.ActiveCfg = Debug|x64
{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Debug|x64.Build.0 = Debug|x64
{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Debug|x86.ActiveCfg = Debug|Win32
{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Debug|x86.Build.0 = Debug|Win32
{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Release|x64.ActiveCfg = Release|x64
{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Release|x64.Build.0 = Release|x64
{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Release|x86.ActiveCfg = Release|Win32
{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {50FB6E2C-4FEF-4828-A315-1017276BF47A}
EndGlobalSection
EndGlobal

View file

@ -1,119 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="fileutils.cpp" />
<ClCompile Include="log.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="tds2dbg.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="fileutils.h" />
<ClInclude Include="log.h" />
<ClInclude Include="tds2dbg.h" />
<ClInclude Include="tdscvstructs.h" />
</ItemGroup>
<ItemGroup>
<None Include="tds2dbg.bin" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="fileutils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="log.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tds2dbg.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="fileutils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="log.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="tds2dbg.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="tdscvstructs.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="tds2dbg.bin">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
</Project>

File diff suppressed because it is too large Load diff

View file

@ -1,27 +0,0 @@
#include <stdio.h>
#include <windows.h>
static int a = 1;
int main(int argc, char* argv[])
{
(void) argc; (void) argv;
int b = a+1;
printf("%i %i\n",a,b);
while(a<0xf0000000) {
++a;
printf("%i\n",a++);
Sleep(1000);
}
return 0;
}
// Local Variables:
// mode:c++
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:

View file

@ -395,7 +395,7 @@ var
begin
// On developer machines, if we are running within the source repo, then use
// those paths
keyman_root := GetEnvironmentVariable('KEYMAN_ROOT');
keyman_root := GetEnvironmentVariable('KEYMAN_ROOT').Replace('/', '\');
if (keyman_root <> '') and SameText(keyman_root, ParamStr(0).Substring(0, keyman_root.Length)) then
begin
// Source repo, bin folder

View file

@ -83,7 +83,7 @@ if "!COMMAND!" == "configure" (
if "%1" == "--no-tests" (
meson setup build\!ARCH!\!BUILDTYPE! !STATIC_LIBRARY! --buildtype !BUILDTYPE! -Dkeyman_core_tests=false --werror %2 %3 %4 %5 %6 %7 %8 %9 || exit !errorlevel!
) else (
meson setup build\!ARCH!\!BUILDTYPE! !STATIC_LIBRARY! --buildtype !BUILDTYPE! --werror %1 %2 %3 %4 %5 %6 %7 %8 %9 || exit !errorlevel!
meson setup build\!ARCH!\!BUILDTYPE! !STATIC_LIBRARY! --buildtype !BUILDTYPE! -Dkeyman_core_tests=true --werror %1 %2 %3 %4 %5 %6 %7 %8 %9 || exit !errorlevel!
)
shift
)

View file

@ -79,7 +79,7 @@ builder_parse "$@"
# if we don't plan to run them, for example when doing a dependency build
# in CI
#
MESON_OPTION_keyman_core_tests=
MESON_OPTION_keyman_core_tests="-Dkeyman_core_tests=true"
BUILD_BAT_keyman_core_tests=
if builder_is_dep_build || builder_has_option --no-tests; then

View file

@ -53,7 +53,7 @@ function do_build() {
function do_test() {
typescript_run_eslint_mocha_tests 50
./test/command-line-tests.sh test
builder_launch /developer/src/kmc/test/command-line-tests.sh test
}
#-------------------------------------------------------------------------------------------------------------------

View file

@ -29,11 +29,10 @@ function do_build() {
build_manifest.res
run_in_vs_env rc icons.rc
delphi_msbuild kmconvert.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" kmconvert.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/kmconvert.map" "$WIN32_TARGET"
cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmconvert.dbg" "$DEVELOPER_DEBUGPATH"
cp_if_exists "$WIN32_TARGET_PATH/kmconvert.pdb" "$DEVELOPER_DEBUGPATH"
rm -rf "$DEVELOPER_PROGRAM/projects/templates"
mkdir -p "$DEVELOPER_PROGRAM/projects/templates"
@ -53,7 +52,12 @@ function do_publish() {
wrap-signcode //d "Keyman Developer" "$DEVELOPER_PROGRAM/kmconvert.exe"
wrap-symstore "$DEVELOPER_PROGRAM/kmconvert.exe" //t keyman-developer
wrap-symstore "$DEVELOPER_DEBUGPATH/kmconvert.dbg" //t keyman-developer
wrap-symstore "$DEVELOPER_DEBUGPATH/kmconvert.pdb" //t keyman-developer
}
function do_install() {
cp "$DEVELOPER_PROGRAM/kmconvert.exe" "$INSTALLPATH_KEYMANDEVELOPER/kmconvert.exe"
cp_if_exists "$DEVELOPER_DEBUGPATH/kmconvert.pdb" "$INSTALLPATH_KEYMANDEVELOPER/kmconvert.pdb"
}
builder_run_action clean:project clean_windows_project_files
@ -61,5 +65,5 @@ builder_run_action configure:project configure_windows_build_environment
builder_run_action build:project do_build
builder_run_action test:project do_test
builder_run_action publish:project do_publish
builder_run_action install:project cp "$DEVELOPER_PROGRAM/kmconvert.exe" "$INSTALLPATH_KEYMANDEVELOPER/kmconvert.exe"
builder_run_action install:project do_install
builder_run_action edit:project start kmconvert.dproj

View file

@ -28,11 +28,10 @@ function do_build() {
build_version.res
build_manifest.res
delphi_msbuild kmdbrowserhost.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" kmdbrowserhost.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/kmdbrowserhost.map" "$WIN32_TARGET"
cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmdbrowserhost.dbg" "$DEVELOPER_DEBUGPATH"
cp_if_exists "$WIN32_TARGET_PATH/kmdbrowserhost.pdb" "$DEVELOPER_DEBUGPATH"
}
function do_publish() {
@ -41,7 +40,12 @@ function do_publish() {
wrap-signcode //d "Keyman Developer" "$DEVELOPER_PROGRAM/kmdbrowserhost.exe"
wrap-symstore "$DEVELOPER_PROGRAM/kmdbrowserhost.exe" //t keyman-developer
wrap-symstore "$DEVELOPER_DEBUGPATH/kmdbrowserhost.dbg" //t keyman-developer
wrap-symstore "$DEVELOPER_DEBUGPATH/kmdbrowserhost.pdb" //t keyman-developer
}
function do_install() {
cp "$DEVELOPER_PROGRAM/kmdbrowserhost.exe" "$INSTALLPATH_KEYMANDEVELOPER/kmdbrowserhost.exe"
cp_if_exists "$DEVELOPER_DEBUGPATH/kmdbrowserhost.pdb" "$INSTALLPATH_KEYMANDEVELOPER/kmdbrowserhost.pdb"
}
builder_run_action clean:project clean_windows_project_files
@ -49,6 +53,6 @@ builder_run_action configure:project configure_windows_build_environment
builder_run_action build:project do_build
# builder_run_action test:project do_test
builder_run_action publish:project do_publish
builder_run_action install:project cp "$DEVELOPER_PROGRAM/kmdbrowserhost.exe" "$INSTALLPATH_KEYMANDEVELOPER/kmdbrowserhost.exe"
builder_run_action install:project do_install
builder_run_action edit:project start kmdbrowserhost.dproj

View file

@ -30,11 +30,32 @@ function do_build() {
build_version.res
build_manifest.res
delphi_msbuild setup.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" setup.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/setup.map" "$WIN32_TARGET"
#
# The installer needs to be a multiple of 512 bytes in order for the SFX code
# to find the ZIP header. After map2pdb appends its header, the file may not
# be the right size. So take the file size of setup.exe and generate a nul
# file that would expand it to nearest 512 bytes, and append it to the
# executable. This will make the file 512 bytes larger if its size is already
# on a 512 byte boundary.
#
local file_size expansion_size expansion_file
file_size=$(stat -c %s "$WIN32_TARGET")
expansion_size=$(( 512 - ( $file_size % 512 ) ))
expansion_file="$(mktemp)"
dd if=/dev/zero of="${expansion_file}" bs=1 count="${expansion_size}"
cat "$WIN32_TARGET" "$expansion_file" > "$WIN32_TARGET.out"
mv -f "$WIN32_TARGET.out" "$WIN32_TARGET"
rm -f "${expansion_file}"
cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/setup.dbg" "$DEVELOPER_DEBUGPATH/devsetup.dbg"
builder_if_release_build_level mv "$WIN32_TARGET_PATH/setup.dbg" "$WIN32_TARGET_PATH/devsetup.dbg"
cp_if_exists "$WIN32_TARGET_PATH/setup.pdb" "$DEVELOPER_DEBUGPATH/devsetup.pdb"
# builder_if_release_build_level cp "$WIN32_TARGET_PATH/setup.pdb" "$DEVELOPER_DEBUGPATH/devsetup.pdb"
# builder_if_release_build_level mv "$WIN32_TARGET_PATH/setup.pdb" "$WIN32_TARGET_PATH/devsetup.pdb"
}
function do_publish() {
@ -42,7 +63,7 @@ function do_publish() {
# Not signing here because we need to sign the bundled installer instead
# wrap-signcode /d "Keyman Developer Setup" $(DEVELOPER_PROGRAM)\setup.exe
wrap-symstore "$DEVELOPER_PROGRAM/setup.exe" //t keyman-developer
wrap-symstore "$DEVELOPER_DEBUGPATH/setup.dbg" //t keyman-developer
wrap-symstore "$DEVELOPER_DEBUGPATH/setup.pdb" //t keyman-developer
}
builder_run_action clean:project clean_windows_project_files

View file

@ -68,16 +68,15 @@ function do_build() {
cp -R "$DEVELOPER_ROOT/src/tike/xml/"* "$DEVELOPER_PROGRAM/xml/"
delphi_msbuild tike.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" tike.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/tike.map" "$WIN32_TARGET"
cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM"
cp_if_exists "$WIN32_TARGET_PATH/tike.pdb" "$DEVELOPER_DEBUGPATH"
cp kmc.cmd "$DEVELOPER_PROGRAM"
cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_DLL" "$DEVELOPER_PROGRAM"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/tike.dbg" "$DEVELOPER_DEBUGPATH"
cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_DLL" "$WIN32_TARGET_PATH"
builder_if_release_build_level cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_PDB" "$WIN32_TARGET_PATH"
builder_if_release_build_level cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_PDB" "$DEVELOPER_DEBUGPATH"
cp "$KEYMAN_ROOT/common/windows/delphi/ext/sentry/sentry.dll" "$DEVELOPER_PROGRAM/"
cp "$KEYMAN_ROOT/common/windows/delphi/ext/sentry/sentry.x64.dll" "$DEVELOPER_PROGRAM/"
@ -96,11 +95,12 @@ function do_publish() {
wrap-signcode //d "Keyman Developer" "$DEVELOPER_PROGRAM/sentry.x64.dll"
wrap-signcode //d "Keyman Developer" "$DEVELOPER_PROGRAM/crashpad_handler.exe"
wrap-symstore "$DEVELOPER_PROGRAM/tike.exe" //t keyman-developer
wrap-symstore "$DEVELOPER_DEBUGPATH/tike.dbg" //t keyman-developer
wrap-symstore "$DEVELOPER_DEBUGPATH/tike.pdb" //t keyman-developer
}
function do_install() {
cp "$DEVELOPER_PROGRAM/tike.exe" "$INSTALLPATH_KEYMANDEVELOPER/tike.exe"
cp_if_exists "$DEVELOPER_DEBUGPATH/tike.pdb" "$INSTALLPATH_KEYMANDEVELOPER/tike.pdb"
cp "$DEVELOPER_PROGRAM/$KEYMANCORE_DLL" "$INSTALLPATH_KEYMANDEVELOPER/$KEYMANCORE_DLL"
}

View file

@ -345,7 +345,7 @@ SETX KEYMAN_CEF4DELPHI_ROOT "c:\Projects\keyman\CEF4Delphi_Binary"
```ps1
# Elevated PowerShell
choco install 7zip html-help-workshop
choco install wixtoolset --version=3.11.1
choco install wixtoolset --version=3.14.1
git clone https://github.com/keymanapp/CEF4Delphi_Binary C:\Projects\keyman\CEF4Delphi_Binary
```

View file

@ -1038,6 +1038,28 @@ fi
--------------------------------------------------------------------------------
## `builder_launch` function
Starts a child script build, passing current build dependency, inheritable
options, and timing status through as parameters. This should be used rather
than calling the build script directly, to avoid multiple builds of
dependencies.
Do not use builder standard options such as `--deps` or `--debug`.
### Parameters
* 1: `script` path to script, relative to root of repo
* 2+: `parameters` action(s), target(s), parameters for the child script to run
### Example
```bash
builder_launch core/build.sh configure,build:wasm --no-tests
```
--------------------------------------------------------------------------------
## `builder_parse` function
Initializes a build.sh script, parses command line. Will abort the script if

View file

@ -1,3 +1,9 @@
keyman (18.0.244-1) unstable; urgency=medium
* New upstream release.
-- Eberhard Beilharz <eb1@sil.org> Mon, 10 Nov 2025 17:06:33 +0100
keyman (18.0.243-1) unstable; urgency=medium
* replace dbus-x11 dependency with default-dbus-session-bus | dbus-session-bus

View file

@ -6,6 +6,9 @@
set -eu
shopt -s inherit_errexit
# Avoid timing reports as we don't have node in this script
export _builder_timings=false
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"

View file

@ -33,7 +33,7 @@ else
fi
echo "ppa: ${ppa}"
distributions="${DIST:-jammy noble plucky questing}"
distributions="${DIST:-jammy noble plucky questing resolute}"
packageversion="${PACKAGEVERSION:-1~sil1}"
BASEDIR=$(pwd)

View file

@ -192,12 +192,17 @@ builder_echo info "Detaching \"$WORKING_COPY_OF_IMAGE\""
DETACH_SUCCESS=0
DETACH_FORCE=
while (( DETACH_SUCCESS < 10 )); do
DETACH_RESULT=0
hdiutil detach $STAGING_DIR $VERBOSITY $DETACH_FORCE || DETACH_RESULT=$?
if [[ $DETACH_RESULT != 0 || -d "$STAGING_DIR" ]] ; then
if hdiutil detach $STAGING_DIR $VERBOSITY $DETACH_FORCE; then
DETACH_RESULT=0
echo "hdiutil detach reported success"
else
DETACH_RESULT=$?
echo "hdiutil detach reported failure code ${DETACH_RESULT}"
fi
if [[ $DETACH_RESULT != 0 ]] || [[ -d "$STAGING_DIR" ]] ; then
(( DETACH_SUCCESS++ ))
echo "Failed to unmount: \"$STAGING_DIR\" on attempt #$DETACH_SUCCESS. Waiting 5 seconds to try again."
if (( DETACH_SUCCESS > 5 )); then
if (( DETACH_SUCCESS == 6 )); then
echo " Note: Detach failed first five times, so now attempting with '-force'"
DETACH_FORCE=-force
fi

View file

@ -30,7 +30,7 @@ KEYMAN_MIN_VERSION_NODE_MAJOR=20 # node version source of truth is
KEYMAN_MIN_VERSION_NPM=10.5.1 # 10.5.0 has bug, discussed in #10350
KEYMAN_MIN_VERSION_EMSCRIPTEN=3.1.58 # Use KEYMAN_USE_EMSDK to automatically update to this version
KEYMAN_MIN_VERSION_VISUAL_STUDIO=2022 # Visual Studio 2022, see /docs/build/windows.md for workloads and components
KEYMAN_MIN_VERSION_MESON=1.0.0
KEYMAN_MIN_VERSION_MESON=1.0.0 # TODO: rename meson_options.txt to meson.options when updating to >= 1.1, see #15127.
KEYMAN_VERSION_GRADLE=8.12 # See /android/KMEA/gradle/wrapper/gradle-wrapper.properties

View file

@ -2,6 +2,9 @@
set -eu
# Avoid timing reports in unit tests
export _builder_timings=false
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
@ -243,7 +246,7 @@ if [[ $KEYMAN_VERSION_ENVIRONMENT == "local" ]]; then
expected="$(builder_echo grey "Local build environment detected: setting --debug")"$'\n'"$(builder_echo setmark "test.sh parameters: <--feature xyzzy --bar abc --baz def test --debug>")"
fi
if [[ "${parse_output[*]}" != "${expected}" ]]; then
builder_die "FAIL: Wrong output for '--feature xyzzy --bar abc --baz def test':\n Actual : ${parse_output[*]}\n Expected: ${expected}"
builder_die "FAIL: Wrong output for '--feature xyzzy --bar abc --baz def test':\n Actual : '${parse_output[*]}'\n Expected: '${expected}'"
fi
#----------------------------------------------------------------------
@ -300,12 +303,12 @@ function builder_echo_start_end_tests() {
TEAMCITY_GIT_PATH=""
_builder_debug_internal=false
_builder_is_child=0
expected=""
expected="$(builder_echo heading "description")"
result=$(builder_echo start "foo" "description")
if [[ "${result[*]}" != "${expected}" ]]; then
builder_die "FAIL: Wrong output for 'builder_echo start foo description' (no debug, is child):\n Actual : ${result[*]}\n Expected: ${expected}"
fi
expected=""
expected="$(builder_echo success "description")"
result=$(builder_echo end "foo" success "description")
if [[ "${result[*]}" != "${expected}" ]]; then
builder_die "FAIL: Wrong output for 'builder_echo end foo success description' (no debug, is child):\n Actual : ${result[*]}\n Expected: ${expected}"
@ -436,7 +439,7 @@ echo
# Finally, run with --help so we can see what it looks like; note:
# builder_parse calls `exit 0` on a --help run, so running in a subshell
echo -e "${COLOR_BLUE}## Testing --help${COLOR_RESET}"
builder_parse --no-color --help
builder_parse --no-color --help --no-timings
) || builder_die "FAIL: builder-parse unexpectedly returned failure code $?"
echo -e "${COLOR_GREEN}======================================================${COLOR_RESET}"

View file

@ -30,5 +30,4 @@ DEVELOPER_DEBUGPATH="$DEVELOPER_ROOT/debug"
# Delphi build tool macros
SENTRYTOOL="$COMMON_ROOT/tools/sentrytool/$WIN32_TARGET_PATH/sentrytool.exe"
TDS2DBG="$KEYMAN_ROOT/common/windows/bin/tools/tds2dbg"
DEVTOOLS="$COMMON_ROOT/tools/devtools/$WIN32_TARGET_PATH/devtools.exe"

View file

@ -72,23 +72,25 @@ run_in_delphi_env() {
)
}
sentrytool_delphiprep() {
if builder_is_ci_build && builder_is_ci_build_level_build; then
builder_echo "Skipping sentrytool_delphiprep - buildLevel=build: $@"
return 0
#
# If map2pdb is on the path, then run it. Note: we do not include it in the repo
# because it can have false positives in malware scans. map2pdb is used to
# generate .pdb files for Delphi-generated executables, which can then be used
# in debuggers such as WinDbg, or profilers such as VTune.
#
do_map2pdb() {
if hash map2pdb 2>/dev/null; then
map2pdb "$1" -bind:"$2"
fi
local EXE_PATH="$1"
local DPR_PATH="$2"
(
builder_echo heading "### delphi: sentrytool_delphiprep $1 $2"
source "$KEYMAN_ROOT/resources/build/win/delphi_environment.inc.sh"
"$SENTRYTOOL" delphiprep -r "$KEYMAN_ROOT" -i "$DELPHIINCLUDES" "$(cygpath -w "$EXE_PATH")" -dpr "$DPR_PATH"
)
}
tds2dbg() {
builder_if_release_build_level "$TDS2DBG" "$@"
#
# If the first parameter is a file that exists, do the copy, otherwise, no-op.
#
cp_if_exists() {
if [[ -f "$1" ]]; then
cp "$@"
fi
}
delphi_msbuild() {

View file

@ -1,5 +1,5 @@
WIXPATH="$ProgramFilesx86/WiX Toolset v3.11/bin"
WIXPATH="$ProgramFilesx86/WiX Toolset v3.14/bin"
# !IFDEF LINT
WIXLIGHTLINT=()

View file

@ -30,6 +30,11 @@ set -eu
#
SHLVL=0
# A regex of reserved builder parameters, must be kept in sync with
# the matching of builder parameters in _builder_parse_expanded_parameters
_builder_reserved_parameters='^(--help|-h|--color|--no-color|--verbose|-v|--timings|--no-timings|--debug|--release|--deps|--no-deps|--force-deps|--builder-dep-parent|--builder-child|--builder-report-dependencies|--builder-completion-describe|--offline|--builder-ignore-unknown-options)$'
# _builder_init is called internally at the bottom of this file after we have
# all function declarations in place.
function _builder_init() {
@ -180,7 +185,7 @@ function builder_heading() {
builder_echo() {
local color=white message= mark= block= action= do_output=true test=
local color=white message= mark= block= action= test=
local echo_target=echo
if [[ $# -gt 1 ]]; then
@ -194,9 +199,6 @@ builder_echo() {
shift 2
action="start"
color="heading"
if ! builder_is_running_on_teamcity && builder_is_child_build; then
do_output=${_builder_debug_internal:-false}
fi
elif [[ $1 == "end" ]] || [[ $1 == "endTest" ]]; then
# builder_echo end block status message
test="$1"
@ -204,9 +206,6 @@ builder_echo() {
color="$3"
shift 3
action="end"
if [[ "${color}" != "error" ]] && ! builder_is_running_on_teamcity && builder_is_child_build; then
do_output=${_builder_debug_internal:-false}
fi
fi
fi
message="$*"
@ -219,30 +218,28 @@ builder_echo() {
fi
fi
if ${do_output}; then
if [[ ! -z ${COLOR_RED+x} ]]; then
case $color in
white) color="$COLOR_WHITE" ;;
grey) color="$COLOR_GREY" ;;
green|success) color="$COLOR_GREEN" ;;
blue|heading) color="$COLOR_BLUE" ;;
yellow|warning) color="$COLOR_YELLOW" ;;
red|error) color="$COLOR_RED"; echo_target=_builder_error_echo ;;
purple) color="$COLOR_PURPLE" ;;
brightwhite) color="$COLOR_BRIGHTWHITE" ;;
teal|debug) color="$COLOR_TEAL" ;;
setmark) mark="$HEADING_SETMARK" color="$COLOR_PURPLE" ;;
esac
if [[ ! -z ${COLOR_RED+x} ]]; then
case $color in
white) color="$COLOR_WHITE" ;;
grey) color="$COLOR_GREY" ;;
green|success) color="$COLOR_GREEN" ;;
blue|heading) color="$COLOR_BLUE" ;;
yellow|warning) color="$COLOR_YELLOW" ;;
red|error) color="$COLOR_RED"; echo_target=_builder_error_echo ;;
purple) color="$COLOR_PURPLE" ;;
brightwhite) color="$COLOR_BRIGHTWHITE" ;;
teal|debug) color="$COLOR_TEAL" ;;
setmark) mark="$HEADING_SETMARK" color="$COLOR_PURPLE" ;;
esac
if builder_is_dep_build; then
$echo_target -e "$mark$COLOR_GREY[$THIS_SCRIPT_IDENTIFIER]$COLOR_RESET $color$message$COLOR_RESET"
else
$echo_target -e "$mark$BUILDER_BOLD$COLOR_BRIGHT_WHITE[$THIS_SCRIPT_IDENTIFIER]$COLOR_RESET $color$message$COLOR_RESET"
fi
if builder_is_dep_build; then
$echo_target -e "$mark$COLOR_GREY[$THIS_SCRIPT_IDENTIFIER]$COLOR_RESET $color$message$COLOR_RESET"
else
# Cope with the case of pre-init message and just emit plain text
$echo_target -e "$message"
$echo_target -e "$mark$BUILDER_BOLD$COLOR_BRIGHT_WHITE[$THIS_SCRIPT_IDENTIFIER]$COLOR_RESET $color$message$COLOR_RESET"
fi
else
# Cope with the case of pre-init message and just emit plain text
$echo_target -e "$message"
fi
if [[ "${action}" == "end" ]] && builder_is_running_on_teamcity; then
@ -377,7 +374,7 @@ _builder_item_is_target() {
function _builder_warn_if_incomplete() {
if [ -n "${_builder_current_action}" ]; then
builder_echo warning "$_builder_current_action never reported success or failure"
builder_echo warning "WARNING: $_builder_current_action never reported success or failure"
# exit 1 # If we wanted this scenario to result in a forced build-script fail.
fi
@ -392,6 +389,7 @@ _builder_failure_trap() {
local action target
_builder_cleanup_deps
_builder_timing_cleanup_and_report $trappedExitCode
# Since 'exit' is also trapped, we can also handle end-of-script incomplete actions.
if [[ $trappedExitCode == 0 ]]; then
@ -440,20 +438,70 @@ _builder_cleanup_deps() {
# Child scripts
#------------------------------------------------------------------------------------------
_builder_execute_child() {
local action=$1
local target=$2
#
# Starts a child script build, passing current build dependency, inheritable
# options, and timing status through as parameters. This should be used rather
# than calling the build script directly, to avoid multiple builds of
# dependencies.
#
# Do not use builder standard options such as `--deps` or `--debug`.
#
# ### Parameters
#
# * 1: `script` path to script, relative to root of repo
# * 2+: `parameters` action(s), target(s), parameters for the child script to run
#
# ### Example
#
# ```bash
# builder_launch core/build.sh configure,build:wasm --no-tests
# ```
#
builder_launch() {
local script="$1"
local param
shift
if [[ ! "$script" =~ ^/(.+) ]]; then
builder_die "Error: builder_launch: script path must start with /, and is relative to repo root"
fi
for param in "$@"; do
if [[ "${param}" =~ ${_builder_reserved_parameters} ]]; then
builder_die "Error: builder_launch: reserved parameter '${param}' used"
fi
done
builder_echo grey "## script '$script $*' launched..."
_builder_execute_child_script "${KEYMAN_ROOT}${script}" "$@"
builder_echo grey "## script '$script $*' completed"
}
_builder_execute_child() {
local action="$1"
local target="$2"
local script="$THIS_SCRIPT_PATH/${_builder_target_paths[$target]}/build.sh"
builder_echo start "$action$target" "## $action$target starting..."
builder_echo grey "## child $action$target starting..."
_builder_execute_child_script "$script" "$action"
builder_echo grey "## child $action$target completed successfully"
}
# ### Parameters
#
# * 1: `script` path to script
# * 2+: `parameters` action(s), target(s), parameters for the child script to run
_builder_execute_child_script() {
local script="$1"
# subsequent parameters passed as $@/$* below
shift
# Build array of specified inheritable options
local child_options=()
local opt
for opt in "${_builder_options_inheritable[@]}"; do
if builder_has_option $opt; then
child_options+=($opt)
if builder_has_option "${opt}"; then
child_options+=("${opt}")
fi
done
@ -469,16 +517,14 @@ _builder_execute_child() {
--builder-child \
$_builder_build_deps \
$dep_flag "$dep_module" \
$action \
${child_options[@]} \
"$@" \
"${child_options[@]}" \
$builder_verbose \
$builder_debug \
$_builder_offline \
&& (
builder_echo end "$action$target" success "## $action$target completed successfully"
) || (
|| (
result=$?
builder_echo end "$action$target" error "## $action$target failed with exit code $result"
builder_echo error "## child script '$script $*' failed with exit code $result"
exit $result
) || exit $? # Required due to above subshell masking exit
}
@ -679,6 +725,9 @@ builder_start_action() {
fi
_builder_current_action="${_builder_matched_action}"
# Note: we include the action's dependencies in timing
_builder_timing_start "${_builder_matched_action}"
# Build dependencies as required
_builder_do_build_deps "${_builder_matched_action}"
return 0
@ -687,6 +736,139 @@ builder_start_action() {
fi
}
###################################################################################################
# Timing
###################################################################################################
_builder_timing_setup() {
if $_builder_timings; then
export _builder_timing_report_file=`mktemp`
_builder_timing_start "build.sh:top"
fi
}
_builder_timing_start() {
local context="$1"
if [[ ! -z "${_builder_timing_report_file:+x}" ]]; then
echo "START $THIS_SCRIPT_IDENTIFIER:$context $(date +%s.%N)" >> "$_builder_timing_report_file"
fi
}
_builder_timing_stop() {
local context="$1"
if [[ ! -z "${_builder_timing_report_file:+x}" ]]; then
echo "STOP $THIS_SCRIPT_IDENTIFIER:$context $(date +%s.%N)" >> "$_builder_timing_report_file"
fi
}
#
# Removes temporary `_builder_timing_report_file` file when top-level build
# script finishes and reports on timings if they were requested.
#
_builder_timing_cleanup_and_report() {
local build_exit_code="$1"
if builder_is_dep_build || builder_is_child_build || [[ -z ${_builder_timing_report_file:+x} ]]; then
return 0
fi
if [[ "$build_exit_code" != 0 ]]; then
# Hide the timings report if build failed, but still cleanup
_builder_timings=false
fi
if $_builder_timings; then
_builder_timing_stop "build.sh:top"
if ! command -v node > /dev/null; then
builder_warn "WARNING: node.js not found, skipping timings report"
else
builder_heading "## Build timings"
# for simplicity of distribution, we embed the node.js script here
# shellcheck disable=SC2016
node -e '
const fs = require("node:fs");
const process = require("node:process");
let
COLOR_RED="", COLOR_GREEN="", COLOR_YELLOW="", COLOR_BLUE="", COLOR_PURPLE="", COLOR_TEAL="",
COLOR_WHITE="", COLOR_BRIGHT_WHITE="", COLOR_GREY="", COLOR_RESET="", BUILDER_BOLD="";
if(process.stdout.isTTY) {
COLOR_RED="\x1b[31m"; COLOR_GREEN="\x1b[32m"; COLOR_YELLOW="\x1b[33m"; COLOR_BLUE="\x1b[34m";
COLOR_PURPLE="\x1b[35m"; COLOR_TEAL="\x1b[36m"; COLOR_WHITE="\x1b[38;5;252m";
COLOR_BRIGHT_WHITE="\x1b[38;5;255m"; COLOR_GREY="\x1b[90m"; COLOR_RESET="\x1b(B\x1b[m";
BUILDER_BOLD="\x1b[1m";
}
const log = fs.readFileSync(process.argv[process.argv.length-1], "utf8").split("\n");
let top = null;
const modules = {};
const tree = [];
for(const line of log) {
if(line.trim() == "") continue;
const [event, area, time] = line.split(" ");
const [module, action, target, sub] = area.split(":");
if(event == "START") {
const mod = {area, module, action, target: target ? ":"+target : "", sub: sub ?? "", start:parseFloat(time), stop:null, modules:[]};
if(!top) top = mod;
modules[area] = mod;
if(tree.length) {
tree[tree.length-1].modules.push(mod);
}
tree.push(mod);
} else if(event == "STOP") {
if(tree.length) {
const mod = tree.pop();
if(mod.area != area) {
console.error(`Expected ${area} to equal ${mod.area}`);
}
}
const mod = modules[area];
if(!mod) {
console.error(`START not found for ${area}`);
} else {
mod.stop = parseFloat(time);
}
} else {
console.error(`Unrecognized line: "${line}"`);
}
}
if(tree.length) {
console.error(`Expected tree to be empty`);
console.dir(tree);
}
if(!Object.keys(modules).length || !top) {
console.error(`Expected report to be non-empty`);
process.exit(0);
}
// Write out the tree with Pretty Printing
printModule("", top);
function printModule(indent, mod) {
const duration = Math.round((mod.stop - mod.start) * 10) / 10;
console.log(`${indent}${BUILDER_BOLD}[${mod.module}]${COLOR_RESET} ${COLOR_GREEN}${mod.action}${mod.target}${COLOR_RESET} ${mod.sub} ${duration}`);
for(const submod of mod.modules) {
printModule(indent+" ", submod);
}
}
' "${_builder_timing_report_file}"
fi
fi
rm -f "$_builder_timing_report_file"
_builder_timing_report_file=
}
###################################################################################################
#
# Returns 0 if the user has --option on the command line
#
@ -1096,6 +1278,7 @@ _builder_get_default_description() {
:engine) description="engine module" ;;
:module) description="this module" ;;
:tools) description="build tools for this project" ;;
--timings) description="report on timings" ;;
--debug) description="debug build" ;;
--release) description="release build (prevents --debug in local-env builds)" ;;
esac
@ -1318,6 +1501,16 @@ builder_parse() {
_builder_parse_expanded_parameters() {
_builder_build_deps=--deps
builder_verbose=
if [[ -z "${_builder_timings+x}" ]]; then
# Only set _builder_timings if not already set
if builder_is_ci_build; then
_builder_timings=true
else
_builder_timings=false
fi
export _builder_timings
fi
builder_debug=
local is_release=false
local _params=($@)
@ -1354,7 +1547,7 @@ _builder_parse_expanded_parameters() {
action=$new_action
;;
*)
builder_warn "Parameter $action has $? matches, could mean any of {$new_action}"
builder_warn "ERROR: Parameter $action has $? matches, could mean any of {$new_action}"
exit 1
;;
esac
@ -1365,7 +1558,7 @@ _builder_parse_expanded_parameters() {
target=$new_target
;;
*)
builder_warn "Parameter $target has $? matches, could mean any of {$new_target}"
builder_warn "ERROR: Parameter $target has $? matches, could mean any of {$new_target}"
exit 1
;;
esac
@ -1423,6 +1616,7 @@ _builder_parse_expanded_parameters() {
fi
else
# See also _builder_reserved_parameters
case "$key" in
--help|-h)
builder_display_usage
@ -1438,6 +1632,12 @@ _builder_parse_expanded_parameters() {
_builder_chosen_options+=(--verbose)
builder_verbose=--verbose
;;
--timings)
_builder_timings=true
;;
--no-timings)
_builder_timings=false
;;
--debug)
_builder_chosen_options+=(--debug)
builder_debug=--debug
@ -1529,18 +1729,23 @@ _builder_parse_expanded_parameters() {
fi
if builder_is_dep_build; then
if [[ -z ${_builder_deps_built+x} ]]; then
builder_die "FATAL ERROR: Expected '_builder_deps_built' variable to be set"
fi
_builder_verify_expected_sub_process_variables
elif builder_is_child_build; then
if [[ -z ${_builder_deps_built+x} ]]; then
builder_die "FATAL ERROR: Expected '_builder_deps_built' variable to be set"
fi
_builder_verify_expected_sub_process_variables
else
# For now, we'll leave this warning disabled, but may re-enable in #15130
# if [[ ! -z "${_builder_deps_built+x}" ]]; then
# builder_warn "WARNING: Child build '${THIS_SCRIPT_IDENTIFIER}' instantiated without using builder_launch"
# fi
# This is a top-level invocation, so we want to track which dependencies
# have been built, so they don't get built multiple times.
export _builder_deps_built=`mktemp`
# We will also track timings
_builder_timing_setup
# Per #11106, local builds use --debug by default.
# Second condition prevents the block (and message) from executing when --debug is already specified explicitly.
if [[ ${KEYMAN_VERSION_ENVIRONMENT:-} == "local" ]] && [[ $builder_debug != --debug ]] && ! $is_release; then
@ -1575,6 +1780,17 @@ _builder_parse_expanded_parameters() {
fi
}
_builder_verify_expected_sub_process_variables() {
if [[ -z ${_builder_deps_built+x} ]]; then
builder_die "FATAL ERROR: Expected '_builder_deps_built' variable to be set"
fi
if $_builder_timings; then
if [[ -z ${_builder_timing_report_file+x} ]]; then
builder_die "FATAL ERROR: Expected '_builder_timing_report_file' variable to be set"
fi
fi
}
#
# Returns 0 (true) if --builder-ignore-unknown-options flag has been specified
# in the command line
@ -1596,21 +1812,14 @@ _builder_completion_describe() {
echo -n "; "
printf '%s ' "${_builder_targets[@]}"
echo -n "; "
# Remove all '+' suffixes from options; they're a config on the option, not part
# of the actual option text itself.
local _builder_opts=()
for e in "${!_builder_params[@]}"; do
if [[ $e =~ ^-- ]]; then
_builder_opts+=(${e%+*})
fi
done
# Add default options
_builder_opts+=( --verbose --debug --color --no-color --offline --help )
local opts=("${_builder_options[@]}")
# Add default options; see also _builder_reserved_parameters
opts+=( --verbose --debug --release --color --no-color --offline --help --timings --no-timings )
if builder_has_dependencies; then
_builder_opts+=( --deps --no-deps --force-deps )
opts+=( --deps --no-deps --force-deps )
fi
printf '%s ' "${_builder_opts[@]}"
printf '%s ' "${opts[@]}"
}
builder_display_usage() {
@ -1686,6 +1895,7 @@ builder_display_usage() {
fi
done
# See also _builder_reserved_parameters
_builder_pad $width " --verbose, -v" "Verbose logging"
_builder_pad $width " --debug, -d" "Debug build"
_builder_pad $width " --color" "Force colorized output"
@ -1755,6 +1965,8 @@ builder_finish_action() {
builder_echo end "$action_name" error "## $action_name failed with message: $result"
fi
_builder_timing_stop "${_builder_matched_action}"
# Remove $action$target from the array; it is no longer a current action
_builder_current_action=
else
@ -1833,6 +2045,8 @@ _builder_do_build_deps() {
return 0
fi
_builder_timing_start "$1:dependencies"
for dep in "${_builder_deps[@]}"; do
# Don't attempt to build dependencies that don't match the current
# action:target (wildcards supported for matches here)
@ -1861,7 +2075,7 @@ _builder_do_build_deps() {
$_builder_build_deps \
--builder-dep-parent "$THIS_SCRIPT_IDENTIFIER" && (
if $_builder_debug_internal; then
builder_echo success "## Dependency $dep$dep_target for $_builder_matched_action_name successfully"
builder_echo success "## Dependency $dep$dep_target for $_builder_matched_action_name completed successfully"
fi
) || (
result=$?
@ -1869,6 +2083,8 @@ _builder_do_build_deps() {
exit $result
) || exit $? # Required due to above subshell masking exit
done
_builder_timing_stop "$1:dependencies"
}
#
@ -2115,13 +2331,13 @@ builder_describe_platform() {
# For testing, we can override the current platform
if [[ ! -z "${BUILDER_PLATFORM_OVERRIDE+x}" ]]; then
builder_warn "BUILDER_PLATFORM_OVERRIDE variable found. Overriding detected platform '$builder_platform' with '$BUILDER_PLATFORM_OVERRIDE'"
builder_warn "WARNING: BUILDER_PLATFORM_OVERRIDE variable found. Overriding detected platform '$builder_platform' with '$BUILDER_PLATFORM_OVERRIDE'"
builder_platform="$BUILDER_PLATFORM_OVERRIDE"
fi
# For testing, we can override the current tools
if [[ ! -z "${BUILDER_TOOLS_OVERRIDE+x}" ]]; then
builder_warn "BUILDER_TOOLS_OVERRIDE variable found. Overriding detected tools (${builder_installed_tools[@]}) with (${BUILDER_TOOLS_OVERRIDE[@]})"
builder_warn "WARNING: BUILDER_TOOLS_OVERRIDE variable found. Overriding detected tools (${builder_installed_tools[@]}) with (${BUILDER_TOOLS_OVERRIDE[@]})"
builder_installed_tools=("${BUILDER_TOOLS_OVERRIDE[@]}")
fi

View file

@ -1,16 +1,12 @@
import { KeymanEngine } from './keymanEngine.js'
import { SourcemappedWorker } from '@keymanapp/lexical-model-layer/web'
/**
* Determine path and protocol of executing script, setting them as
* construction defaults.
*
* This can only be done during load when the active script will be the
* last script loaded. Otherwise the script must be identified by name.
*/
const scripts = document.getElementsByTagName('script');
const ss = scripts[scripts.length-1].src;
const sPath = ss.substr(0,ss.lastIndexOf('/')+1);
/**
* Determine path and protocol of executing script, setting them as
* construction defaults.
*/
const ss = (document.currentScript as HTMLScriptElement)?.src;
const sPath = ss ? ss.substring(0, ss.lastIndexOf('/') + 1) : './';
// @ts-ignore
window['keyman'] = new KeymanEngine(SourcemappedWorker, sPath);

View file

@ -1,16 +1,12 @@
import { KeymanEngine } from './keymanEngine.js'
import { Worker } from '@keymanapp/lexical-model-layer/web'
/**
* Determine path and protocol of executing script, setting them as
* construction defaults.
*
* This can only be done during load when the active script will be the
* last script loaded. Otherwise the script must be identified by name.
*/
const scripts = document.getElementsByTagName('script');
const ss = scripts[scripts.length-1].src;
const sPath = ss.substr(0,ss.lastIndexOf('/')+1);
/**
* Determine path and protocol of executing script, setting them as
* construction defaults.
*/
const ss = (document.currentScript as HTMLScriptElement)?.src;
const sPath = ss ? ss.substring(0, ss.lastIndexOf('/') + 1) : './';
// @ts-ignore
window['keyman'] = new KeymanEngine(Worker, sPath);

View file

@ -78,7 +78,7 @@ compile_and_copy() {
node map-polyfill-bundler.js
# For dependent test pages.
"$KEYMAN_ROOT/web/src/test/manual/embed/android-harness/build.sh"
builder_launch /web/src/test/manual/embed/android-harness/build.sh configure,build
}
builder_run_action configure node_select_version_and_npm_ci

View file

@ -1,16 +1,12 @@
import { KeymanEngine } from './keymanEngine.js'
import { SourcemappedWorker } from '@keymanapp/lexical-model-layer/web'
/**
* Determine path and protocol of executing script, setting them as
* construction defaults.
*
* This can only be done during load when the active script will be the
* last script loaded. Otherwise the script must be identified by name.
*/
const scripts = document.getElementsByTagName('script');
const ss = scripts[scripts.length-1].src;
const sPath = ss.substring(0,ss.lastIndexOf('/')+1);
/**
* Determine path and protocol of executing script, setting them as
* construction defaults.
*/
const ss = (document.currentScript as HTMLScriptElement)?.src;
const sPath = ss ? ss.substring(0, ss.lastIndexOf('/') + 1) : './';
// @ts-ignore
window['keyman'] = new KeymanEngine(SourcemappedWorker, sPath);

View file

@ -1,16 +1,12 @@
import { KeymanEngine } from './keymanEngine.js'
import { Worker } from '@keymanapp/lexical-model-layer/web'
/**
* Determine path and protocol of executing script, setting them as
* construction defaults.
*
* This can only be done during load when the active script will be the
* last script loaded. Otherwise the script must be identified by name.
*/
const scripts = document.getElementsByTagName('script');
const ss = scripts[scripts.length-1].src;
const sPath = ss.substring(0,ss.lastIndexOf('/')+1);
/**
* Determine path and protocol of executing script, setting them as
* construction defaults.
*/
const ss = (document.currentScript as HTMLScriptElement)?.src;
const sPath = ss ? ss.substring(0, ss.lastIndexOf('/') + 1) : './';
// @ts-ignore
window['keyman'] = new KeymanEngine(Worker, sPath);

View file

@ -36,7 +36,12 @@ do_build () {
--format esm
}
do_test() {
test-headless "${SUBPROJECT_NAME}" ""
test-headless-typescript "${SUBPROJECT_NAME}"
}
builder_run_action configure node_select_version_and_npm_ci
builder_run_action clean rm -rf "${KEYMAN_ROOT}/web/build/${SUBPROJECT_NAME}"
builder_run_action build do_build
builder_run_action test test-headless "${SUBPROJECT_NAME}" ""
builder_run_action test do_test

View file

@ -1,6 +1,6 @@
// Defines the base Deadkey-tracking object.
export class Deadkey {
p: number; // Position of deadkey
p: number; // Position of deadkey in the textstore (deadkey comes before the character at this position)
d: number; // Numerical id of the deadkey
o: number; // Ordinal value of the deadkey (resolves same-place conflicts)
matched: number;
@ -11,10 +11,11 @@ export class Deadkey {
this.p = pos;
this.d = id;
this.o = Deadkey.ordinalSeed++;
this.matched = 0;
}
match(p: number, d: number): boolean {
const result:boolean = (this.p == p && this.d == d);
match(pos: number, deadkey: number): boolean {
const result:boolean = (this.p == pos && this.d == deadkey);
return result;
}
@ -39,7 +40,7 @@ export class Deadkey {
}
equal(other: Deadkey) {
return this.d == other.d && this.p == other.d && this.o == other.o;
return this.d == other.d && this.p == other.p && this.o == other.o;
}
/**
@ -81,22 +82,25 @@ export class DeadkeyTracker {
* Function isMatch
* Scope Public
* @param {number} caretPos current cursor position
* @param {number} n expected offset of deadkey from cursor
* @param {number} d deadkey
* @return {boolean} True if deadkey found selected context matches val
* @param {number} offset expected offset to add to deadkey position to get to caretPos
* @param {number} deadkey deadkey to match
* @return {boolean} True if deadkey found at offset position
* Description Match deadkey at current cursor position
*/
isMatch(caretPos: number, n: number, d: number): boolean {
isMatch(caretPos: number, offset: number, deadkey: number): boolean {
if(this.dks.length == 0) {
return false; // I3318
}
const sp=caretPos;
n = sp - n;
// Calculate position of deadkey from beginning of context.
// Caret position is always after the deadkey position (although this
// doesn't really matter for the calculation).
const dkPosition = caretPos - offset;
for(let i = 0; i < this.dks.length; i++) {
// Don't re-match an already-matched deadkey. It's possible to have two identical
// entries, and they should be kept separately.
if(this.dks[i].match(n, d) && !this.dks[i].matched) {
if (this.dks[i].match(dkPosition, deadkey) && !this.dks[i].matched) {
this.dks[i].set();
// Assumption: since we match the first possible entry in the array, we
// match the entry with the lower ordinal - the 'first' deadkey in the position.
@ -115,7 +119,11 @@ export class DeadkeyTracker {
remove(dk: Deadkey) {
const index = this.dks.indexOf(dk);
this.dks.splice(index, 1);
if (index > -1) {
this.dks.splice(index, 1);
} else {
console.warn(`Deadkey ${JSON.stringify(dk)} to remove not found in DeadkeyTracker.`);
}
}
clear() {
@ -160,17 +168,15 @@ export class DeadkeyTracker {
return false;
}
const otherDks = other.dks;
const matchedDks: Deadkey[] = [];
for(const dk of this.dks) {
const match = otherDks.find((otherDk) => dk.equal(otherDk));
if(!match) {
const sortedDeadkeys = this.toSortedArray();
const otherDks = other.toSortedArray();
for(let i = 0; i < sortedDeadkeys.length; i++) {
if(!sortedDeadkeys[i].equal(otherDks[i])) {
return false;
}
}
return matchedDks.length == otherDks.length;
return true;
}
count(): number {

View file

@ -9,3 +9,12 @@ export { default as OutputTarget } from "./outputTarget.js";
export * from "./outputTarget.js";
export { Mock } from "./mock.js";
export * from "./stringDivergence.js";
import { DeadkeyTracker } from './deadkeys.js';
/**
* these are exported only for unit tests, do not use
*/
export const unitTestEndpoints = {
DeadkeyTracker,
};

View file

@ -613,6 +613,8 @@ export default class KeyboardInterface extends KeyboardHarness {
* @param {number} d deadkey
* @return {boolean} True if deadkey found selected context matches val
* Description Match deadkey at current cursor position
*
* Deprecated: use `fullContextMatch` instead.
*/
deadkeyMatch(n: number, outputTarget: OutputTarget, d: number): boolean {
return outputTarget.hasDeadkeyMatch(n, d);

View file

@ -0,0 +1,373 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
import { assert } from 'chai';
import { Deadkey, unitTestEndpoints } from 'keyman/engine/js-processor';
const DeadkeyTracker = unitTestEndpoints.DeadkeyTracker;
describe('DeadkeyTracker', function() {
it('can add deadkeys and return count', function() {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(5, 2));
tracker.add(new Deadkey(10, 3));
assert.equal(tracker.dks.length, 3);
assert.equal(tracker.count(), 3);
});
it('sorts deadkeys by position, latest first', function() {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(5, 2));
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(10, 3));
const sortedDeadkeys = tracker.toSortedArray();
assert.equal(sortedDeadkeys[0].p, 10);
assert.equal(sortedDeadkeys[1].p, 5);
assert.equal(sortedDeadkeys[2].p, 3);
});
it('can adjust positions', function () {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(5, 2));
tracker.add(new Deadkey(10, 3));
tracker.adjustPositions(4, 2); // adjust deadkeys after position 4 by +2
const sortedDeadkeys = tracker.toSortedArray();
assert.equal(sortedDeadkeys[0].p, 12); // 10 + 2
assert.equal(sortedDeadkeys[1].p, 7); // 5 + 2
assert.equal(sortedDeadkeys[2].p, 3); // unchanged
});
describe('equal', function () {
it('treats cloned tracker as equal', function () {
const tracker1 = new DeadkeyTracker();
tracker1.add(new Deadkey(3, 1));
tracker1.add(new Deadkey(5, 2));
tracker1.add(new Deadkey(10, 3));
const tracker2 = tracker1.clone();
assert.isTrue(tracker1.equal(tracker2));
});
it('treats two trackers with identical deadkeys as equal', function () {
const dk1 = new Deadkey(3, 1);
const dk2 = new Deadkey(5, 2);
const dk3 = new Deadkey(10, 3);
const tracker1 = new DeadkeyTracker();
tracker1.add(dk1);
tracker1.add(dk2);
tracker1.add(dk3);
const tracker2 = new DeadkeyTracker();
tracker2.add(dk1);
tracker2.add(dk2);
tracker2.add(dk3);
assert.isTrue(tracker1.equal(tracker2));
});
it('treats two trackers with same deadkeys create separately not as equal', function () {
const tracker1 = new DeadkeyTracker();
tracker1.add(new Deadkey(3, 1));
tracker1.add(new Deadkey(5, 2));
tracker1.add(new Deadkey(10, 3));
const tracker2 = new DeadkeyTracker();
tracker2.add(new Deadkey(3, 1));
tracker2.add(new Deadkey(5, 2));
tracker2.add(new Deadkey(10, 3));
assert.isFalse(tracker1.equal(tracker2));
});
it('treats two trackers with same deadkeys in different order not as equal', function () {
const tracker1 = new DeadkeyTracker();
tracker1.add(new Deadkey(5, 2));
tracker1.add(new Deadkey(3, 1));
tracker1.add(new Deadkey(10, 3));
const tracker2 = new DeadkeyTracker();
tracker2.add(new Deadkey(10, 3));
tracker2.add(new Deadkey(5, 2));
tracker2.add(new Deadkey(3, 1));
assert.isFalse(tracker1.equal(tracker2));
});
it('treats two trackers with same deadkeys in different places not as equal', function () {
const tracker1 = new DeadkeyTracker();
tracker1.add(new Deadkey(7, 2));
tracker1.add(new Deadkey(3, 1));
tracker1.add(new Deadkey(12, 3));
const tracker2 = new DeadkeyTracker();
tracker2.add(new Deadkey(10, 3));
tracker2.add(new Deadkey(5, 2));
tracker2.add(new Deadkey(3, 1));
assert.isFalse(tracker1.equal(tracker2));
});
it('treats a tracker with additional deadkeys not as equal', function () {
const tracker1 = new DeadkeyTracker();
tracker1.add(new Deadkey(3, 1));
tracker1.add(new Deadkey(5, 2));
tracker1.add(new Deadkey(10, 3));
const tracker2 = tracker1.clone();
tracker2.add(new Deadkey(15, 4));
assert.isFalse(tracker1.equal(tracker2));
});
it('treats non-indentical trackers with duplicate deadkeys not as equal', function () {
const dk1 = new Deadkey(3, 1);
const dk2 = new Deadkey(5, 2);
const dk3 = new Deadkey(10, 3);
const tracker1 = new DeadkeyTracker();
tracker1.add(dk1);
tracker1.add(dk1); // add same again
tracker1.add(dk2);
const tracker2 = new DeadkeyTracker();
tracker2.add(dk1);
tracker2.add(dk2);
tracker2.add(dk3);
assert.isFalse(tracker1.equal(tracker2));
});
it('treats indentical trackers with duplicate deadkeys as equal', function () {
const dk1 = new Deadkey(3, 1);
const dk2 = new Deadkey(5, 2);
const dk3 = new Deadkey(10, 3);
const tracker1 = new DeadkeyTracker();
tracker1.add(dk1);
tracker1.add(dk1); // add same again
tracker1.add(dk2);
tracker1.add(dk3);
const tracker2 = new DeadkeyTracker();
tracker2.add(dk1);
tracker2.add(dk1); // add same again
tracker2.add(dk2);
tracker2.add(dk3);
assert.isTrue(tracker1.equal(tracker2));
});
});
it('can clear deadkeys', function() {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(5, 2));
tracker.add(new Deadkey(10, 3));
tracker.clear();
assert.equal(tracker.count(), 0);
});
describe('remove', function () {
it('can remove existing deadkey', function () {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
const dk = new Deadkey(5, 2);
tracker.add(dk);
tracker.add(new Deadkey(10, 3));
tracker.remove(dk);
assert.equal(tracker.count(), 2);
assert.equal(tracker.dks[0].p, 3);
assert.equal(tracker.dks[1].p, 10);
});
it('does not remove similar deadkey', function () {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(5, 2));
tracker.add(new Deadkey(10, 3));
tracker.remove(new Deadkey(5, 2));
assert.equal(tracker.count(), 3);
assert.equal(tracker.dks[0].p, 3);
assert.equal(tracker.dks[1].p, 5);
assert.equal(tracker.dks[2].p, 10);
});
});
it('sorts deadkeys in cloned tracker', function () {
const tracker1 = new DeadkeyTracker();
tracker1.add(new Deadkey(5, 1));
tracker1.add(new Deadkey(3, 2));
tracker1.add(new Deadkey(10, 3));
const tracker2 = tracker1.clone();
assert.equal(tracker2.count(), 3);
assert.equal(tracker2.dks[0].p, 10);
assert.equal(tracker2.dks[1].p, 5);
assert.equal(tracker2.dks[2].p, 3);
});
it('can delete matched deadkeys', function () {
const dk1 = new Deadkey(5, 1);
const dk2 = new Deadkey(3, 2);
const dk3 = new Deadkey(10, 3);
const tracker = new DeadkeyTracker();
tracker.add(dk1);
tracker.add(dk2);
tracker.add(dk3);
dk2.set();
dk3.set();
tracker.deleteMatched();
assert.equal(tracker.count(), 1);
assert.equal(tracker.dks[0].p, 5);
});
it('can reset matched deadkeys', function () {
const dk1 = new Deadkey(5, 1);
const dk2 = new Deadkey(3, 2);
const dk3 = new Deadkey(10, 3);
const tracker = new DeadkeyTracker();
tracker.add(dk1);
tracker.add(dk2);
tracker.add(dk3);
dk2.set();
dk3.set();
tracker.resetMatched();
assert.equal(tracker.count(), 3);
assert.equal(tracker.dks[0].matched, 0);
assert.equal(tracker.dks[1].matched, 0);
assert.equal(tracker.dks[2].matched, 0);
});
describe('isMatch', function () {
it('returns false without having any deadkeys', function () {
const tracker = new DeadkeyTracker();
assert.isFalse(tracker.isMatch(0, 1, 2));
});
it('returns false for non-matching deadkey', function () {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(5, 2));
tracker.add(new Deadkey(10, 3));
assert.isFalse(tracker.isMatch(0, 88, 99));
assert.equal(tracker.dks[0].matched, 0);
assert.equal(tracker.dks[1].matched, 0);
assert.equal(tracker.dks[2].matched, 0);
});
it('returns false if offset is wrong', function () {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(5, 2));
tracker.add(new Deadkey(10, 3));
assert.isFalse(tracker.isMatch(0, 3, 1));
assert.equal(tracker.dks[0].matched, 0);
assert.equal(tracker.dks[1].matched, 0);
assert.equal(tracker.dks[2].matched, 0);
});
it('returns true if offset is correct and we have multiple deadkeys with same id', function () {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(5, 1));
tracker.add(new Deadkey(10, 3));
assert.isTrue(tracker.isMatch(7, 2, 1));
assert.equal(tracker.dks[0].matched, 0);
assert.equal(tracker.dks[1].matched, 1);
assert.equal(tracker.dks[2].matched, 0);
});
it('matches entry with lowest ordinal value', function () {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(10, 3));
assert.isTrue(tracker.isMatch(0, -3, 1));
assert.equal(tracker.dks[0].matched, 1);
assert.equal(tracker.dks[1].matched, 0);
assert.equal(tracker.dks[2].matched, 0);
});
it('matches entry with lowest ordinal value if caret is after deadkey', function () {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(10, 3));
assert.isTrue(tracker.isMatch(5, 2, 1));
assert.equal(tracker.dks[0].matched, 1);
assert.equal(tracker.dks[1].matched, 0);
assert.equal(tracker.dks[2].matched, 0);
});
it('returns true for matching deadkey and sets matched flag on deadkey', function () {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(5, 2));
tracker.add(new Deadkey(10, 3));
assert.isTrue(tracker.isMatch(0, -3, 1));
assert.equal(tracker.dks[0].matched, 1);
assert.equal(tracker.dks[1].matched, 0);
assert.equal(tracker.dks[2].matched, 0);
});
it('returns true for matching deadkey if caret on dk', function () {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(5, 2));
tracker.add(new Deadkey(10, 3));
assert.isTrue(tracker.isMatch(3, 0, 1));
assert.equal(tracker.dks[0].matched, 1);
assert.equal(tracker.dks[1].matched, 0);
assert.equal(tracker.dks[2].matched, 0);
});
it('returns false for matching deadkey if already matched and resets matched flag', function () {
const tracker = new DeadkeyTracker();
tracker.add(new Deadkey(3, 1));
tracker.add(new Deadkey(5, 2));
tracker.add(new Deadkey(10, 3));
tracker.dks[0].matched = 1;
tracker.dks[1].matched = 1;
tracker.dks[2].matched = 1;
assert.isFalse(tracker.isMatch(0, -3, 1));
assert.equal(tracker.dks[0].matched, 0);
assert.equal(tracker.dks[1].matched, 0);
assert.equal(tracker.dks[2].matched, 0);
});
});
});

View file

@ -90,10 +90,6 @@ function do_publish() {
rm -f setup.inf
cat "$WINDOWS_PROGRAM_APP/setup-redist.exe" keymandesktop.zip > keymandesktop.exe
rm -f keymandesktop.zip
#
# Sign the installer
#
wrap-signcode //d "Keyman for Windows" keymandesktop.exe
builder_if_release_build_level copy-installer

View file

@ -29,11 +29,10 @@ function do_build() {
# TODO: why no manifest?
# build_manifest.res
delphi_msbuild insthelp.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" insthelp.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/insthelp.map" "$WIN32_TARGET"
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/insthelp.dbg" "$WINDOWS_DEBUGPATH_APP/insthelp.dbg"
cp_if_exists "$WIN32_TARGET_PATH/insthelp.pdb" "$WINDOWS_DEBUGPATH_APP"
}
function do_publish() {
@ -43,7 +42,7 @@ function do_publish() {
wrap-signcode //d "Keyman for Windows Install Helper" "$WINDOWS_PROGRAM_APP/insthelp.exe"
wrap-symstore "$WINDOWS_PROGRAM_APP/insthelp.exe" //t keyman-windows
wrap-symstore "$WINDOWS_DEBUGPATH_APP/insthelp.dbg" //t keyman-windows
wrap-symstore "$WINDOWS_DEBUGPATH_APP/insthelp.pdb" //t keyman-windows
}
builder_run_action clean:project clean_windows_project_files

View file

@ -28,11 +28,10 @@ function do_build() {
build_version.res
build_manifest.res
delphi_msbuild kmbrowserhost.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" kmbrowserhost.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/kmbrowserhost.map" "$WIN32_TARGET"
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmbrowserhost.dbg" "$WINDOWS_DEBUGPATH_APP/kmbrowserhost.dbg"
cp_if_exists "$WIN32_TARGET_PATH/kmbrowserhost.pdb" "$WINDOWS_DEBUGPATH_APP"
}
function do_publish() {
@ -41,7 +40,12 @@ function do_publish() {
wrap-signcode //d "Keyman for Windows" "$WINDOWS_PROGRAM_APP/kmbrowserhost.exe"
wrap-symstore "$WINDOWS_PROGRAM_APP/kmbrowserhost.exe" //t keyman-windows
wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmbrowserhost.dbg" //t keyman-windows
wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmbrowserhost.pdb" //t keyman-windows
}
function do_install() {
cp "$WINDOWS_PROGRAM_APP/kmbrowserhost.exe" "$INSTALLPATH_KEYMANAPP/kmbrowserhost.exe"
cp_if_exists "$WINDOWS_DEBUGPATH_APP/kmbrowserhost.pdb" "$INSTALLPATH_KEYMANAPP/kmbrowserhost.pdb"
}
builder_run_action clean:project clean_windows_project_files
@ -49,5 +53,5 @@ builder_run_action configure:project configure_windows_build_environment
builder_run_action build:project do_build
# builder_run_action test:project do_test
builder_run_action publish:project do_publish
builder_run_action install:project cp "$WINDOWS_PROGRAM_APP/kmbrowserhost.exe" "$INSTALLPATH_KEYMANAPP/kmbrowserhost.exe"
builder_run_action install:project do_install
builder_run_action edit:project start kmbrowserhost.dproj

View file

@ -28,11 +28,10 @@ function do_build() {
build_version.res
build_manifest.res
delphi_msbuild kmconfig.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" kmconfig.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/kmconfig.map" "$WIN32_TARGET"
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmconfig.dbg" "$WINDOWS_DEBUGPATH_APP/kmconfig.dbg"
cp_if_exists "$WIN32_TARGET_PATH/kmconfig.pdb" "$WINDOWS_DEBUGPATH_APP"
}
function do_publish() {
@ -41,7 +40,12 @@ function do_publish() {
wrap-signcode //d "Keyman for Windows" "$WINDOWS_PROGRAM_APP/kmconfig.exe"
wrap-symstore "$WINDOWS_PROGRAM_APP/kmconfig.exe" //t keyman-windows
wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmconfig.dbg" //t keyman-windows
wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmconfig.pdb" //t keyman-windows
}
function do_install() {
cp "$WINDOWS_PROGRAM_APP/kmconfig.exe" "$INSTALLPATH_KEYMANAPP/kmconfig.exe"
cp_if_exists "$WINDOWS_DEBUGPATH_APP/kmconfig.pdb" "$INSTALLPATH_KEYMANAPP/kmconfig.pdb"
}
builder_run_action clean:project clean_windows_project_files
@ -49,5 +53,5 @@ builder_run_action configure:project configure_windows_build_environment
builder_run_action build:project do_build
# builder_run_action test:project do_test
builder_run_action publish:project do_publish
builder_run_action install:project cp "$WINDOWS_PROGRAM_APP/kmconfig.exe" "$INSTALLPATH_KEYMANAPP/kmconfig.exe"
builder_run_action install:project do_install
builder_run_action edit:project start kmconfig.dproj

View file

@ -35,11 +35,10 @@ function do_build() {
build_manifest.res
run_in_vs_env rc icons.rc
delphi_msbuild kmshell.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" kmshell.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/kmshell.map" "$WIN32_TARGET"
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmshell.dbg" "$WINDOWS_DEBUGPATH_APP/kmshell.dbg"
cp_if_exists "$WIN32_TARGET_PATH/kmshell.pdb" "$WINDOWS_DEBUGPATH_APP"
do_build_data
}
@ -73,11 +72,13 @@ function do_publish() {
wrap-signcode //d "Keyman for Windows" "$WINDOWS_PROGRAM_APP/kmshell.exe"
wrap-symstore "$WINDOWS_PROGRAM_APP/kmshell.exe" //t keyman-windows
wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmshell.dbg" //t keyman-windows
wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmshell.pdb" //t keyman-windows
}
function do_install() {
cp "$WINDOWS_PROGRAM_APP/kmshell.exe" "$INSTALLPATH_KEYMANAPP/kmshell.exe"
cp_if_exists "$WINDOWS_DEBUGPATH_APP/kmshell.pdb" "$INSTALLPATH_KEYMANAPP/kmshell.pdb"
rm -rf "$INSTALLPATH_KEYMANAPP/xml"
mkdir -p "$INSTALLPATH_KEYMANAPP/xml"
cp -r "$WINDOWS_PROGRAM_APP/xml"/* "$INSTALLPATH_KEYMANAPP/xml/"

View file

@ -37,13 +37,32 @@ function do_build() {
"$DEVTOOLS" -buildsetupstrings locale\\ .\\
delphi_msbuild setup.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" setup.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/setup.map" "$WIN32_TARGET"
#
# The installer needs to be a multiple of 512 bytes in order for the SFX code
# to find the ZIP header. After map2pdb appends its header, the file may not
# be the right size. So take the file size of setup.exe and generate a nul
# file that would expand it to nearest 512 bytes, and append it to the
# executable. This will make the file 512 bytes larger if its size is already
# on a 512 byte boundary.
#
local file_size expansion_size expansion_file
file_size=$(stat -c %s "$WIN32_TARGET")
expansion_size=$(( 512 - ( $file_size % 512 ) ))
expansion_file="$(mktemp)"
dd if=/dev/zero of="${expansion_file}" bs=1 count="${expansion_size}"
cat "$WIN32_TARGET" "$expansion_file" > "$WIN32_TARGET.out"
mv -f "$WIN32_TARGET.out" "$WIN32_TARGET"
rm -f "${expansion_file}"
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP"
cp_if_exists "$WIN32_TARGET_PATH/setup.pdb" "$WINDOWS_DEBUGPATH_APP"
# setup-redist.exe does not get signed as it is intended for a bundled installer
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP/setup-redist.exe"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/setup.dbg" "$WINDOWS_DEBUGPATH_APP/setup.dbg"
}
function do_build_debug_manifest() {
@ -61,7 +80,7 @@ function do_publish() {
wrap-signcode //d "Keyman for Windows Setup" "$WINDOWS_PROGRAM_APP/setup.exe"
wrap-symstore "$WINDOWS_PROGRAM_APP/setup.exe" //t keyman-windows
wrap-symstore "$WINDOWS_DEBUGPATH_APP/setup.dbg" //t keyman-windows
wrap-symstore "$WINDOWS_DEBUGPATH_APP/setup.pdb" //t keyman-windows
}
builder_run_action clean:project do_clean

View file

@ -27,18 +27,17 @@ function do_build() {
create-windows-output-folders
build_version.res
delphi_msbuild insthelper.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" insthelper.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/insthelper.map" "$WIN32_TARGET"
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/insthelper.dbg" "$WINDOWS_DEBUGPATH_ENGINE/insthelper.dbg"
cp_if_exists "$WIN32_TARGET_PATH/insthelper.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
}
function do_publish() {
wrap-signcode //d "Keyman Engine for Windows" "$WINDOWS_PROGRAM_ENGINE/insthelper.dll"
wrap-symstore "$WINDOWS_PROGRAM_ENGINE/insthelper.dll" //t keyman-engine-windows
wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/insthelper.dbg" //t keyman-engine-windows
wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/insthelper.pdb" //t keyman-engine-windows
}
builder_run_action clean:project clean_windows_project_files

View file

@ -38,11 +38,10 @@ function do_build() {
run_in_vs_env rc osktoolbar.rc
run_in_vs_env rc langswitch/langswitchmanager.rc
delphi_msbuild keyman.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" keyman.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/keyman.map" "$WIN32_TARGET"
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/keyman.dbg" "$WINDOWS_DEBUGPATH_ENGINE/keyman.dbg"
cp_if_exists "$WIN32_TARGET_PATH/keyman.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
# Also copy sentry files here
cp "$KEYMAN_ROOT/common/windows/delphi/ext/sentry/sentry.dll" \
@ -65,7 +64,7 @@ function do_publish() {
wrap-signcode //d "Keyman Engine for Windows" "$WINDOWS_PROGRAM_ENGINE/crashpad_handler.exe"
wrap-symstore "$WINDOWS_PROGRAM_ENGINE/keyman.exe" //t keyman-engine-windows
wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/keyman.dbg" //t keyman-engine-windows
wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/keyman.pdb" //t keyman-engine-windows
}
function do_build_debug_manifest() {
@ -77,12 +76,17 @@ function do_build_debug_manifest() {
mv -f std-manifest.tmp manifest.in
}
function do_install() {
cp "$WINDOWS_PROGRAM_ENGINE/keyman.exe" "$INSTALLPATH_KEYMANENGINE/keyman.exe"
cp_if_exists "$WINDOWS_DEBUGPATH_ENGINE/keyman.pdb" "$INSTALLPATH_KEYMANENGINE/keyman.pdb"
}
builder_run_action clean:project do_clean
builder_run_action configure:project configure_windows_build_environment
builder_run_action build:project do_build
# builder_run_action test:project do_test
builder_run_action publish:project do_publish
builder_run_action install:project cp "$WINDOWS_PROGRAM_ENGINE/keyman.exe" "$INSTALLPATH_KEYMANENGINE/keyman.exe"
builder_run_action install:project do_install
builder_run_action debug-manifest:project do_build_debug_manifest
builder_run_action edit:project start keyman.dproj

View file

@ -133,18 +133,14 @@ PKEYMAN64THREADDATA Globals_InitThread()
void Globals_UninitThread()
{
OutputThreadDebugString("Globals_UninitThread");
// OutputThreadDebugString("Globals_UninitThread");
if (!Globals_ProcessInitialised()) {
OutputThreadDebugString("Globals_UninitThread aborted without cleanup");
// OutputThreadDebugString("Globals_UninitThread aborted without cleanup");
return;
}
ISerialKeyEventClient::Shutdown();
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (_td) {
delete _td->pSerialKeyEventClient;
}
CloseThreadSharedBufferManager();
EnterCriticalSection(&csGlobals);

View file

@ -119,27 +119,20 @@ BOOL __stdcall DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID reserved)
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
//OutputThreadDebugString("DLL_PROCESS_ATTACH");
if(!Globals_InitProcess()) return FALSE;
break;
case DLL_PROCESS_DETACH:
if (reserved == NULL) {
// If reserved == NULL, that means the library is being unloaded, but
// the process is not terminating.
//OutputThreadDebugString("DLL_PROCESS_DETACH not terminating");
UninitialiseProcess(FALSE);
Globals_UninitProcess();
}
else {
//OutputThreadDebugString("DLL_PROCESS_DETACH terminating");
}
break;
case DLL_THREAD_ATTACH:
//OutputThreadDebugString("DLL_THREAD_ATTACH");
Globals_InitThread();
break;
case DLL_THREAD_DETACH:
//OutputThreadDebugString("DLL_THREAD_DETACH");
UninitialiseProcess(FALSE);
Globals_UninitThread();
break;
@ -838,7 +831,7 @@ void RefreshKeyboards(BOOL Initialising)
void ReleaseKeyboards(BOOL Lock)
{
OutputThreadDebugString("ReleaseKeyboards");
// OutputThreadDebugString("ReleaseKeyboards");
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td || !_td->lpKeyboards) return;

View file

@ -22,25 +22,25 @@ public:
m_hKeyEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, GLOBAL_KEY_EVENT_NAME);
if (m_hKeyEvent == 0) {
DebugLastError("OpenEvent");
// DebugLastError("OpenEvent");
return;
}
m_hKeyMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, GLOBAL_KEY_MUTEX_NAME);
if (m_hKeyMutex == 0) {
DebugLastError("OpenMutex");
// DebugLastError("OpenMutex");
return;
}
m_hMMF = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, GLOBAL_FILE_MAPPING_NAME);
if (m_hMMF == 0) {
DebugLastError("OpenFileMapping");
// DebugLastError("OpenFileMapping");
return;
}
m_pSharedData = (SerialKeyEventSharedData *)MapViewOfFile(m_hMMF, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SerialKeyEventSharedData));
if (!m_pSharedData) {
DebugLastError("MapViewOfFile");
// DebugLastError("MapViewOfFile");
return;
}
}
@ -48,25 +48,25 @@ public:
SerialKeyEventClient::~SerialKeyEventClient() {
if (m_pSharedData != NULL) {
if (!UnmapViewOfFile(m_pSharedData)) {
DebugLastError("UnmapViewOfFile");
// DebugLastError("UnmapViewOfFile");
}
}
if (m_hMMF != NULL) {
if (!CloseHandle(m_hMMF)) {
DebugLastError("CloseHandle(m_hMMF)");
// DebugLastError("CloseHandle(m_hMMF)");
}
}
if (m_hKeyMutex != NULL) {
if (!CloseHandle(m_hKeyMutex)) {
DebugLastError("CloseHandle(m_hKeyMutex)");
// DebugLastError("CloseHandle(m_hKeyMutex)");
}
}
if (m_hKeyEvent != NULL) {
if (!CloseHandle(m_hKeyEvent)) {
DebugLastError("CloseHandle(m_hKeyEvent)");
// DebugLastError("CloseHandle(m_hKeyEvent)");
}
}
}
@ -149,7 +149,7 @@ public:
};
void ISerialKeyEventClient::Startup() {
OutputThreadDebugString("ISerialKeyEventClient::Startup");
// OutputThreadDebugString("ISerialKeyEventClient::Startup");
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (_td) {
_td->pSerialKeyEventClient = new SerialKeyEventClient();
@ -157,7 +157,7 @@ void ISerialKeyEventClient::Startup() {
}
void ISerialKeyEventClient::Shutdown() {
OutputThreadDebugString("ISerialKeyEventClient::Shutdown");
// OutputThreadDebugString("ISerialKeyEventClient::Shutdown");
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (_td && _td->pSerialKeyEventClient) {
delete _td->pSerialKeyEventClient;

View file

@ -30,10 +30,10 @@ function do_build() {
function do_install() {
cp "$WINDOWS_PROGRAM_ENGINE/keymanmc.dll" "$INSTALLPATH_KEYMANENGINE/keymanmc.dll"
reg add 'HKLM\\SYSTEM\\CurrentControlSet\\services\\eventlog\\Application\\Keyman' //v CategoryCount //t REG_DWORD //d 1 //f
reg add 'HKLM\\SYSTEM\\CurrentControlSet\\services\\eventlog\\Application\\Keyman' //v CategoryMessageFile //t REG_SZ //d "$INSTALLPATH_KEYMANENGINE\\keymanmc.dll" //f
reg add 'HKLM\\SYSTEM\\CurrentControlSet\\services\\eventlog\\Application\\Keyman' //v EventMessageFile //t REG_SZ //d "$INSTALLPATH_KEYMANENGINE\\keymanmc.dll" //f
reg add 'HKLM\\SYSTEM\\CurrentControlSet\\services\\eventlog\\Application\\Keyman' //v TypesSupported //t REG_DWORD //d 7 //f
reg add 'HKLM\SYSTEM\CurrentControlSet\services\eventlog\Application\Keyman' //v CategoryCount //t REG_DWORD //d 1 //f
reg add 'HKLM\SYSTEM\CurrentControlSet\services\eventlog\Application\Keyman' //v CategoryMessageFile //t REG_SZ //d "$INSTALLPATH_KEYMANENGINE\\keymanmc.dll" //f
reg add 'HKLM\SYSTEM\CurrentControlSet\services\eventlog\Application\Keyman' //v EventMessageFile //t REG_SZ //d "$INSTALLPATH_KEYMANENGINE\\keymanmc.dll" //f
reg add 'HKLM\SYSTEM\CurrentControlSet\services\eventlog\Application\Keyman' //v TypesSupported //t REG_DWORD //d 7 //f
}
builder_run_action clean:project rm -f keymanmc.dll keymanmc.h keymanmc.rc keymanmc.res MSG00409.bin

View file

@ -36,15 +36,16 @@ function do_build() {
run_in_vs_env gentlb -Tkmcomapi.tlb kmcomapi.ridl
delphi_msbuild kmcomapi.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" kmcomapi.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/kmcomapi.map" "$WIN32_TARGET"
# TODO: map2pdb for .x64
delphi_msbuild kmcomapi.dproj "//p:Platform=Win64"
# Delphi does not allow us to build to a different target filename so we rename after build
mv -f "$WIN64_TARGET_PATH/kmcomapi.dll" "$WIN64_TARGET_PATH/kmcomapi.x64.dll"
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmcomapi.dbg" "$WINDOWS_DEBUGPATH_ENGINE/kmcomapi.dbg"
cp_if_exists "$WIN32_TARGET_PATH/kmcomapi.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
cp "$WIN64_TARGET_PATH/kmcomapi.x64.dll" "$WINDOWS_PROGRAM_ENGINE/kmcomapi.x64.dll"
# x64 Delphi symbols not supported: cp "$WIN64_TARGET_PATH/kmcomapi.dbg" "$WINDOWS_PROGRAM_ENGINE/kmcomapi.x64.dbg"
@ -56,7 +57,7 @@ function do_publish() {
wrap-symstore "$WINDOWS_PROGRAM_ENGINE/kmcomapi.dll" //t keyman-engine-windows
wrap-symstore "$WINDOWS_PROGRAM_ENGINE/kmcomapi.x64.dll" //t keyman-engine-windows
wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/kmcomapi.dbg" //t keyman-engine-windows
wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/kmcomapi.pdb" //t keyman-engine-windows
# No support for 64-bit delphi symbols
}
@ -65,6 +66,8 @@ function do_install() {
cp "$WINDOWS_PROGRAM_ENGINE/kmcomapi.dll" "$INSTALLPATH_KEYMANENGINE/kmcomapi.dll"
regsvr32 //s "$INSTALLPATH_KEYMANENGINE/kmcomapi.dll"
cp_if_exists "$WINDOWS_DEBUGPATH_ENGINE/kmcomapi.pdb" "$INSTALLPATH_KEYMANENGINE/kmcomapi.pdb"
regsvr32 //s //u "$INSTALLPATH_KEYMANENGINE/kmcomapi.x64.dll"
cp "$WINDOWS_PROGRAM_ENGINE/kmcomapi.x64.dll" "$INSTALLPATH_KEYMANENGINE/kmcomapi.x64.dll"
regsvr32 //s "$INSTALLPATH_KEYMANENGINE/kmcomapi.x64.dll"

View file

@ -42,12 +42,11 @@ function do_build() {
rm -f tsysinfox64.bin
delphi_msbuild tsysinfo.dproj "//p:Platform=Win32"
sentrytool_delphiprep "$WIN32_TARGET" tsysinfo.dpr
tds2dbg "$WIN32_TARGET"
do_map2pdb "$WIN32_TARGET_PATH/tsysinfo.map" "$WIN32_TARGET"
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
cp *.xslt "$WINDOWS_PROGRAM_ENGINE/"
builder_if_release_build_level cp "$WIN32_TARGET_PATH/tsysinfo.dbg" "$WINDOWS_DEBUGPATH_ENGINE/tsysinfo.dbg"
cp_if_exists "$WIN32_TARGET_PATH/tsysinfo.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
}
function do_publish() {
@ -57,11 +56,12 @@ function do_publish() {
wrap-signcode //d "Keyman Engine for Windows" "$WINDOWS_PROGRAM_ENGINE/tsysinfo.exe"
wrap-symstore "$WINDOWS_PROGRAM_ENGINE/tsysinfo.exe" //t keyman-engine-windows
wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/tsysinfo.dbg" //t keyman-engine-windows
wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/tsysinfo.pdb" //t keyman-engine-windows
}
function do_install() {
cp "$WINDOWS_PROGRAM_ENGINE/tsysinfo.exe" "$INSTALLPATH_KEYMANENGINE/tsysinfo.exe"
cp_if_exists "$WINDOWS_DEBUGPATH_ENGINE/tsysinfo.pdb" "$INSTALLPATH_KEYMANENGINE/tsysinfo.pdb"
cp *.xslt "$INSTALLPATH_KEYMANENGINE/"
}

View file

@ -39,15 +39,10 @@ function do_publish() {
wrap-signcode //d "Keyman Engine for Windows" "$WINDOWS_PROGRAM_ENGINE/tsysinfox64.exe"
}
function do_install() {
cp "$WINDOWS_PROGRAM_ENGINE/tsysinfox64.exe" "$INSTALLPATH_KEYMANENGINE/tsysinfox64.exe"
cp *.xslt "$INSTALLPATH_KEYMANENGINE/"
}
builder_run_action clean:project clean_windows_project_files
builder_run_action configure:project configure_windows_build_environment
builder_run_action build:project do_build
# builder_run_action test:project do_test
# builder_run_action test:project (no current tests)
builder_run_action publish:project do_publish
builder_run_action install:project do_install
# builder_run_action install:project (installed as part of tsysinfo)
builder_run_action edit:project start tsysinfox64.dproj

View file

@ -149,7 +149,7 @@ begin
pSessionProperties.Wnode.Flags := WNODE_FLAG_TRACED_GUID;
pSessionProperties.Wnode.ClientContext := 1; //QPC clock resolution
pSessionProperties.Wnode.Guid := SessionGuid;
pSessionProperties.LogFileMode := EVENT_TRACE_FILE_MODE_SEQUENTIAL;
pSessionProperties.LogFileMode := EVENT_TRACE_FILE_MODE_CIRCULAR;
pSessionProperties.MaximumFileSize := 256; // 256 MB
pSessionProperties.LoggerNameOffset := sizeof(EVENT_TRACE_PROPERTIES);
pSessionProperties.LogFileNameOffset := sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME);