From 2c60a9483f0806e0799b8c1e9321775cfb031ada Mon Sep 17 00:00:00 2001 From: SheetJS Date: Thu, 28 Sep 2023 13:13:57 -0400 Subject: [PATCH] hermes-windows-x64 --- docz/docs/03-demos/42-engines/09-hermes.md | 188 ++++++++++++++++++++- docz/docs/03-demos/42-engines/index.md | 2 +- docz/static/hermes/sheetjs-hermesw.cpp | 117 +++++++++++++ 3 files changed, 304 insertions(+), 3 deletions(-) create mode 100644 docz/static/hermes/sheetjs-hermesw.cpp diff --git a/docz/docs/03-demos/42-engines/09-hermes.md b/docz/docs/03-demos/42-engines/09-hermes.md index 884fbd5d..e8434ad5 100644 --- a/docz/docs/03-demos/42-engines/09-hermes.md +++ b/docz/docs/03-demos/42-engines/09-hermes.md @@ -135,6 +135,30 @@ static char *read_file(const char *filename, size_t *sz) { size_t sz; char *xlsx_full_min_js = read_file("xlsx.full.min.js", &sz); ``` +:::caution pass + +For Windows applications, the string must be null-terminated: + +```cpp +/* Hermes-Windows requires the null terminator */ +static char *read_file_null(const char *filename, size_t *sz) { + FILE *f = fopen(filename, "rb"); + if(!f) return NULL; + long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f) + 1; fseek(f, 0, SEEK_SET); } + char *buf = (char *)malloc(fsize * sizeof(char)); + *sz = fread((void *) buf, 1, fsize, f); + buf[fsize - 1] = 0; + fclose(f); + return buf; +} + +// ... + /* read SheetJS library from filesystem */ + size_t sz; char *xlsx_full_min_js = read_file_null("xlsx.full.min.js", &sz); +``` + +::: + _Hermes Wrapper_ Hermes does not provide a friendly way to prepare JavaScript code stored in a @@ -343,13 +367,20 @@ This demo was tested in the following deployments: | `linux-x64` | `49e1930` | 2023-09-22 | | `linux-arm` | `70af78b` | 2023-08-27 | +The main Hermes source tree does not have Windows support. The `hermes-windows` +fork, which powers React Native for Windows, does have built-in support[^5] + +| Architecture | Git Commit | Date | +|:-------------|:-----------|:-----------| +| `win10-x64` | `c7a4a82` | 2023-09-27 | + ::: 0) Install [dependencies](https://hermesengine.dev/docs/building-and-running/#dependencies)
Installation Notes (click to show) -The official guidance[^5] has been verified in macOS and HoloOS (Linux). +The official guidance[^6] has been verified in macOS and HoloOS (Linux). On macOS: @@ -424,6 +455,158 @@ curl -LO https://sheetjs.com/pres.numbers`} If successful, the program will print the library version number and the contents of the first sheet as CSV rows. +### Windows Example + +0) Install dependencies. + +
Installation Notes (click to show) + +CMake and Visual Studio with "Desktop development with C++" workload must be +installed. In addition, the following Spectre-mitigated libs must be added: + +- MSVC C++ Spectre-mitigated libs (Latest) +- C++ ATL for latest build tools with Spectre Mitigations +- C++ MFC for latest build tools with Spectre Mitigations + +The easiest way to install is to select "Individual components" and search for +"spectre latest" (no quotation marks). Pick each option for the relevant CPU. + +
+ +1) Set up `depot_tools`. + +[`depot_tools.zip`](https://storage.googleapis.com/chrome-infra/depot_tools.zip) +must be downloaded and extracted to `c:\src\depot_tools\`. + +:::note pass + +This ZIP has a number of hidden files and folders (including `.git`) which +should be extracted along with the normal files. + +::: + +Add the path `c:\src\depot_tools\` to the User `PATH` environment variable + +
Environment Variable Setup (click to show) + +Type `env` in the search bar and select "Edit the system environment variables". + +In the new window, click the "Environment Variables..." button. + +In the new window, look for the "User variables" section. Select "Path" in the +list and click "Edit". + +In the new window, click "New" and type `c:\src\depot_tools` and press Enter. + +Select the row and repeatedly click "Move Up" until it is the first entry. + +Click "OK" in each window (3 windows) and restart your computer. + +
+ +2) Delete `c:\src\depot_tools\ninja` if it exists, then download the +[official Windows release](https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-win.zip) +and move the `ninja.exe` into `c:\src\depot_tools`. If a `ninja.exe` exists in +the folder, replace the existing program. + +3) Make a project directory: + +```bash +mkdir sheetjs-hermes +cd sheetjs-hermes +``` + +4) Clone the `hermes-windows` repo: + +```bash +git clone https://github.com/microsoft/hermes-windows +cd hermes-windows +git checkout c7a4a82 +cd .. +``` + +:::note pass + +If there are errors related to SSL or certificates or `CApath`, temporarily +disable SSL in Git: + +```bash +git config --global http.sslVerify false +git clone https://github.com/microsoft/hermes-windows +git config --global http.sslVerify true +``` + +::: + +5) Build the library: + +```bash +cd hermes-windows +.\.ado\scripts\cibuild.ps1 -AppPlatform win32 -Platform x64 -ToolsPlatform x64 +cd .. +``` + +:::note pass + +The script may fail with the message: + +> cannot be loaded because running scripts is disabled on this system + +In a "Run as Administrator" powershell window, run the following command: + +``` +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned +``` + +::: + +6) Copy every generated `.lib` and `.dll` file into the main folder: + +```powershell +dir -r -Path .\hermes-windows\workspace\build\win32-x64-debug\ -Filter "*.dll" | Copy-Item -Destination .\ +dir -r -Path .\hermes-windows\workspace\build\win32-x64-debug\ -Filter "*.lib" | Copy-Item -Destination .\ +``` + +7) Download [`sheetjs-hermes.cpp`](pathname:///hermes/sheetjs-hermesw.cpp): + +```bash +curl -LO https://docs.sheetjs.com/hermes/sheetjs-hermesw.cpp +``` + +8) Build the application: + +```powershell +cl /MDd sheetjs-hermesw.cpp DbgHelp.lib *.lib /I hermes-windows\API /I hermes-windows\include /I hermes-windows\public\ /I hermes-windows\API\jsi +``` + +:::caution pass + +If `cl` is not found, run the command in the "Native Tools Command Prompt" + +::: + +9) Download the SheetJS Standalone script and the test file. Save both files in +the project directory: + + + +{`\ +curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js +curl -LO https://sheetjs.com/pres.numbers`} + + +10) Run the application: + +```bash +.\sheetjs-hermesw.exe pres.numbers +``` + +If successful, the program will print the library version number and the +contents of the first sheet as CSV rows. + ### CLI Test :::note @@ -493,4 +676,5 @@ If successful, the script will print CSV data from the test file. [^2]: See ["Workbook Object"](/docs/csf/book) [^3]: See ["Workbook Object"](/docs/csf/book) [^4]: See [`sheet_to_csv` in "Utilities"](/docs/api/utilities/csv#csv-output) -[^5]: See ["Dependencies" in "Building and Running"](https://hermesengine.dev/docs/building-and-running/#dependencies) in the Hermes Documentation \ No newline at end of file +[^5]: See [`microsoft/hermes-windows`](https://github.com/microsoft/hermes-windows) on GitHub +[^6]: See ["Dependencies" in "Building and Running"](https://hermesengine.dev/docs/building-and-running/#dependencies) in the Hermes Documentation \ No newline at end of file diff --git a/docz/docs/03-demos/42-engines/index.md b/docz/docs/03-demos/42-engines/index.md index 00148aa6..bc2095e0 100644 --- a/docz/docs/03-demos/42-engines/index.md +++ b/docz/docs/03-demos/42-engines/index.md @@ -55,7 +55,7 @@ only be used when there is no safe way to pass `ArrayBuffer` or `Uint8Array`. **Byte Conventions** -Java has no native concept of unsigned bytes. Values in a `byte[]` are clamped +Java has no native concept of unsigned bytes. Values in a `byte[]` are limited to the range `-128 .. 127`. They need to be fixed within the JS engine. Some engines support typed arrays. The `Uint8Array` constructor will fix values: diff --git a/docz/static/hermes/sheetjs-hermesw.cpp b/docz/static/hermes/sheetjs-hermesw.cpp new file mode 100644 index 00000000..99bf95af --- /dev/null +++ b/docz/static/hermes/sheetjs-hermesw.cpp @@ -0,0 +1,117 @@ +/* sheetjs-hermesw.cpp Copyright (c) SheetJS LLC. */ +#include +#include "hermes/hermes.h" + +static char *read_file(const char *filename, size_t *sz) { + FILE *f = fopen(filename, "rb"); + if(!f) return NULL; + long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); } + char *buf = (char *)malloc(fsize * sizeof(char)); + *sz = fread((void *) buf, 1, fsize, f); + fclose(f); + return buf; +} + +/* Hermes-Windows requires the null terminator */ +static char *read_file_null(const char *filename, size_t *sz) { + FILE *f = fopen(filename, "rb"); + if(!f) return NULL; + long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f) + 1; fseek(f, 0, SEEK_SET); } + char *buf = (char *)malloc(fsize * sizeof(char)); + *sz = fread((void *) buf, 1, fsize, f); + buf[fsize - 1] = 0; + fclose(f); + return buf; +} + +/* Unfortunately the library provides no C-friendly Buffer classes */ +class CBuffer : public facebook::jsi::Buffer { + public: + CBuffer(const uint8_t *data, size_t size) : buf(data), sz(size) {} + size_t size() const override { return sz; } + const uint8_t *data() const override { return buf; } + + private: + const uint8_t *buf; + size_t sz; +}; +/* ArrayBuffer constructor expects MutableBuffer*/ +class CMutableBuffer : public facebook::jsi::MutableBuffer { + public: + CMutableBuffer(uint8_t *data, size_t size) : buf(data), sz(size) {} + size_t size() const override { return sz; } + uint8_t *data() override { return buf; } + + private: + uint8_t *buf; + size_t sz; +}; + +int main(int argc, char **argv) { + std::unique_ptr rt(facebook::hermes::makeHermesRuntime()); + + /* setup */ + try { + auto src = std::make_shared( + "var global = (function(){ return this; }).call(null);" + "var console = { log: function(x) { print(x); } };" + ); + auto js = rt->prepareJavaScript(src, std::string("")); + rt->evaluatePreparedJavaScript(js); + } catch (const facebook::jsi::JSIException &e) { + std::cerr << "JavaScript terminated via uncaught exception: " << e.what() << '\n'; + return 1; + } + + /* load SheetJS library */ + try { + size_t sz; char *xlsx_full_min_js = read_file_null("xlsx.full.min.js", &sz); + auto src = std::make_shared(CBuffer((uint8_t *)xlsx_full_min_js, sz)); + auto js = rt->prepareJavaScript(src, std::string("xlsx.full.min.js")); + rt->evaluatePreparedJavaScript(js); + } catch (const facebook::jsi::JSIException &e) { + std::cerr << "JavaScript terminated via uncaught exception: " << e.what() << '\n'; + return 1; + } + + /* print library version */ + try { + auto src = std::make_shared( + "console.log('SheetJS Library Version: ' + XLSX.version)" + ); + auto js = rt->prepareJavaScript(src, std::string("")); + rt->evaluatePreparedJavaScript(js); + } catch (const facebook::jsi::JSIException &e) { + std::cerr << "JavaScript terminated via uncaught exception: " << e.what() << '\n'; + return 1; + } + + try { + /* load payload as ArrayBuffer */ + size_t sz; char *data = read_file(argv[1], &sz); + auto payload = std::make_shared(CMutableBuffer((uint8_t *)data, sz)); + auto ab = facebook::jsi::ArrayBuffer(*rt, payload); + + /* define stub function to read and convert first sheet to CSV */ + auto src = std::make_shared( + "(function(buf) {" + "var wb = XLSX.read(buf);" + "return XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);" + "})" + ); + auto js = rt->prepareJavaScript(src, std::string("")); + auto func = rt->evaluatePreparedJavaScript(js); + + /* call stub function and capture result */ + auto csv = func.asObject(*rt).asFunction(*rt).call(*rt, ab); + + /* interpret as utf8 and print to stdout */ + std::string str = csv.getString(*rt).utf8(*rt); + std::cout << str << std::endl; + } catch (const facebook::jsi::JSIException &e) { + std::cerr << "JavaScript terminated via uncaught exception: " << e.what() << std::endl; + return 1; + } + + return 0; +} \ No newline at end of file