From eac4c92b8491e19458060ae7778b66e1b0ee9869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Kov=C3=A1cs?= Date: Mon, 10 Feb 2025 18:56:30 +0100 Subject: [PATCH] Support emscripten build with PBRST support, also via cmake --- CMakeLists.txt | 39 ++++++++++++++++++++++++++++----------- Makefile | 31 +++++++++++++++++++++---------- cli.cpp | 20 ++------------------ qt/CMakeLists.txt | 9 +-------- qt/statementwindow.cpp | 9 --------- statements/pbrst.y | 20 -------------------- 6 files changed, 52 insertions(+), 76 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1df6acd..539a27f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,15 @@ +# This cmake configuration file has been successfully tested on the following platforms: +# Linux, macOS, MSYS2/MINGW64, emscripten. + +# When building for emscripten, build a native Linux version first, create the addbooks cache, +# and just then start the build for emscripten, providing the bibref-addbooks-cache folder name +# as an option when starting cmake. See the variable BIBREF_ADDBOOKS_CACHE_FOLDER below. + cmake_minimum_required(VERSION 3.16) project(bibref) -option(PBRST "Include the brst parser" OFF) +set(BIBREF_ADDBOOKS_CACHE_FOLDER ../bibref-addbooks-cache CACHE STRING + "Set the previously created bibref-addbooks-cache folder for the Emscripten build") set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "-pedantic-errors") @@ -9,12 +17,16 @@ set(CMAKE_CXX_FLAGS "-pedantic-errors") find_package(PkgConfig REQUIRED) pkg_check_modules(sword REQUIRED sword) +if(EMSCRIPTEN) +find_library(SWORD_LIB NAMES libsword.a) +endif(EMSCRIPTEN) + set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME OFF) find_package(Boost CONFIG COMPONENTS system filesystem) -if(APPLE) # readline seems to have trouble on MacOS +if(APPLE OR EMSCRIPTEN) # readline seems to have trouble on macOS, unsupported on emscripten else() # One needs readline >= 8.0 to make this work: pkg_check_modules (readline REQUIRED readline) @@ -22,9 +34,6 @@ endif() include_directories(${sword_INCLUDE_DIRS}) -if(PBRST) -add_compile_definitions(WITH_PBRST) - set(PARSER_DIR "${CMAKE_CURRENT_BINARY_DIR}") find_package(FLEX 2.6 REQUIRED) find_package(BISON 3.0 REQUIRED) @@ -38,7 +47,7 @@ bison_target(PARSER "${CMAKE_CURRENT_SOURCE_DIR}/statements/pbrst.y" "${PARSER_O add_flex_bison_dependency(LEXER PARSER) include_directories(${PARSER_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DYYDEBUG=1 -DIN_BIBREF") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DYYDEBUG=1") #add_executable(pbrst-cli statements/pbrst-cli.c ${PARSER_DIR}/pbrst.tab.h "${LEXER_OUT}" "${PARSER_OUT}") @@ -47,7 +56,6 @@ SET(PBRST_SRCS fingerprint-wrapper.cpp fingerprint-wrapper.h ${PARSER_DIR}/pbrst.tab.h "${LEXER_OUT}" "${PARSER_OUT}" ) -endif(PBRST) add_executable(bibref main.cpp book.cpp book.h books.cpp books.h fingerprint.cpp fingerprint.h cli.cpp cli.h psalmsinfo.cpp psalmsinfo.h ${PBRST_SRCS} @@ -63,16 +71,25 @@ if(APPLE) set(sword_LIBRARIES_folder "-L/usr/local/lib") # this is hardcoded (via configure/make install), FIXME endif() -target_link_libraries(bibref - ${sword_LIBRARIES} ${sword_LIBRARIES_folder} ${Boost_LIBRARIES} ${readline_LIBRARIES} -) - +if(EMSCRIPTEN) +target_link_libraries(bibref ${SWORD_LIB} ${Boost_LIBRARIES}) +target_link_options(bibref PUBLIC -sSINGLE_FILE -sUSE_BOOST_HEADERS -sALLOW_MEMORY_GROWTH -O3 -fexceptions) +target_link_options(bibref PUBLIC "SHELL: + --preload-file '$ENV{HOME}/.sword@/'") +target_link_options(bibref PUBLIC "SHELL: + --preload-file '${BIBREF_ADDBOOKS_CACHE_FOLDER}@/bibref-addbooks-cache'") +target_link_options(bibref PUBLIC -sEXTRA_EXPORTED_RUNTIME_METHODS=['cwrap'] -sMODULARIZE -sEXPORT_NAME=bibref) +else() +target_link_libraries(bibref ${sword_LIBRARIES} ${sword_LIBRARIES_folder} ${Boost_LIBRARIES} ${readline_LIBRARIES}) +endif(EMSCRIPTEN) install( CODE " set(ENV{SWORD_PATH} \"$ENV{HOME}/.sword\") " # Download and install LXX and SBLGNT via CrossWire... ## Now we disable this because LXX was updated to version 3.0 and its versification ("LXX") differs from version 2.5 ("KJV"), ## so we want to avoid installing the new version for the moment. Instead, we use the bundled versions... +## In fact, now (February 2025) the bundled version contains LXX version 3.0, but +## it is simpler to maintain a ZIP bundle than managing the modules via installmgr. ## CODE " execute_process (COMMAND bash -c \"test -d \\\"$SWORD_PATH/mods.d\\\" || (mkdir -p \\\"$SWORD_PATH/mods.d\\\"; echo yes | installmgr -init)\") " ## CODE " execute_process (COMMAND bash -c \"installmgr -s | grep \\\"\\\\[CrossWire\\\\]\\\" || echo yes | installmgr -sc\") " ## CODE " execute_process (COMMAND bash -c \"echo yes | installmgr -r CrossWire\") " diff --git a/Makefile b/Makefile index d20bec4..6f3886d 100644 --- a/Makefile +++ b/Makefile @@ -23,13 +23,16 @@ # in the cmake configuration are important, otherwise the SWORD utilities # and examples will also be built, but some of them will eventually fail # and the library will not be copied to the official emsdk folder. -# (In that case you need to do that manually.) +# (In that case you need to do that manually.) There will be no shared library created. # -# 3. Step 2 will make sure that libsword.a is visible for the rest of the process, -# but the first LDFLAGS setting makes sure that one can extend the search path -# by the scope of the ".." folder. +# 3. Step 2 will make sure that libsword.a is visible for the rest of the process. +# Unfortunately, since static libraries are not available published properly by +# the SWORD library setup, we still have to work around manually that +# the first LDFLAGS setting extends the scope to find the library also under the ".." folder. # -# 4. Issue "emmake make" in the current directory. +# 4. Create symlinks in this folder to the files statements/{pbrst.c,pbrst.tab.c,pbrst.tab.h}. +# These files will be present only if you successfully compile the native program above. +# Issue then "emmake make" in the current directory. # # See also the GitHub actions in the .github/ folder for more information on the # steps above. They may be somewhat different, but hopefully still working. @@ -67,12 +70,16 @@ TARGET_HTML ?= bibref.html BUILD_DIR ?= ./wasm-build -SRCS := book.cpp books.cpp cli.cpp fingerprint.cpp main.cpp psalmsinfo.cpp -OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) +CXX_SRCS := book.cpp books.cpp cli.cpp fingerprint.cpp main.cpp psalmsinfo.cpp \ + books-wrapper.cpp fingerprint-wrapper.cpp +CXX_OBJS := $(CXX_SRCS:%=$(BUILD_DIR)/%.o) +C_SRCS := pbrst.tab.c pbrst.c +C_OBJS := $(C_SRCS:%=$(BUILD_DIR)/%.o) DOXS := book.dox books.dox cli.dox fingerprint.dox main.dox psalmsinfo.dox -CPPFLAGS += -I/usr/include/sword -s USE_BOOST_HEADERS=1 +CPPFLAGS += -I/usr/include/sword -s USE_BOOST_HEADERS=1 -DYYDEBUG=1 +CFLAGS := -s EXPORTED_FUNCTIONS=[_bibref_wasm,_bibref_wasm_vocabulary] -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap'] -fexceptions CXXFLAGS := -s EXPORTED_FUNCTIONS=[_bibref_wasm,_bibref_wasm_vocabulary] -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap'] -fexceptions BIBREF_NATIVE_FOLDER := $(shell find . -name bibref -executable -printf "%h\n" | sort | head -1) @@ -87,15 +94,19 @@ ifeq ($(TARGET_HTML),bibref.js) LDFLAGS += -s MODULARIZE=1 -s EXPORT_NAME=bibref endif -$(BUILD_DIR)/$(TARGET_HTML): $(OBJS) +$(BUILD_DIR)/$(TARGET_HTML): $(CXX_OBJS) $(C_OBJS) $(MKDIR_P) $(dir $@) echo addbooks | $(BIBREF_NATIVE_FOLDER)/bibref # Make sure bibref generates its cache. - $(CXX) $(OBJS) -o $@ $(LDFLAGS) + $(LD) $(CXX_OBJS) $(C_OBJS) -o $@ $(LDFLAGS) $(BUILD_DIR)/%.cpp.o: %.cpp $(MKDIR_P) $(dir $@) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ +$(BUILD_DIR)/%.c.o: %.c + $(MKDIR_P) $(dir $@) + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + ####################################################################################################### # Documentation related code diff --git a/cli.cpp b/cli.cpp index d99bb86..983882b 100644 --- a/cli.cpp +++ b/cli.cpp @@ -7,12 +7,9 @@ #include #include -#ifdef WITH_PBRST extern "C" char* brst_scan_string(char *string, int correct_raw, int correct_differ, int correct_cover, int correct_versification, int show_dump); -// #include "statements/pbrst.tab.h" // use flex/bison parser for bibref statements (brst) #include "pbrst.tab.h" // the statements folder must be included among the folders -#endif using namespace std; @@ -75,10 +72,8 @@ string tokensCmd = "tokens"; string searchCmd = "search"; string quitCmd = "quit"; string helpCmd = "help"; -#ifdef WITH_PBRST string statementCmd = "statement"; string statementCmd2 = "Statement"; -#endif string errorNotRecognized = "Sorry, the command you entered was not recognized or its syntax is invalid."; string errorTextIncomplete = "Either " + textCmd + "1 or " + textCmd + "2 must be used."; @@ -111,11 +106,8 @@ vector commands {addbooksCmd, compareCmd + "12", jaccardCmd + "12", minuniqueCmd + "1", latintextCmd + "1", latintextCmd + "2", extendCmd, getrefsCmd, lookupCmd, maxresultsCmd, sqlCmd, psalminfoCmd, rawCmd, rawCmd + "1", rawCmd + "2", - printCmd + "1", printCmd + "2", colorsCmd, tokensCmd, searchCmd -#ifdef WITH_PBRST - , statementCmd -#endif - }; + printCmd + "1", printCmd + "2", colorsCmd, tokensCmd, searchCmd, statementCmd +}; vector vocabulary = commands; void add_vocabulary_item(string item) { @@ -261,10 +253,8 @@ string getHelp(const string &key) "Tokenization is done via Strong's numbers.", "* `help` *command*: Show some hints on usage of *command*, or get general help if no " "parameter is given.", -#ifdef WITH_PBRST "* `statement` ...: Analyze the given statement, see https://matek.hu/zoltan/blog-20250102.php " "for some explanations.", -#endif "* `quit`: Exit program."}; string retval; for (int i = 0; i < helpStr.size(); i++) { @@ -851,7 +841,6 @@ void processGetrefsCmd(string input) { info("Finished"); // Success! } -#ifdef WITH_PBRST void processStatementCmd(string input) { char *output = brst_scan_string((char*)input.c_str(), 0, 0, 0, 0, 0); string output_s(output); @@ -865,7 +854,6 @@ void processStatementCmd(string input) { } info("Finished"); // Success! } -#endif string cli_process(char *buf) { string rawinput(buf); // Convert the input to string. @@ -925,10 +913,8 @@ string cli_process(char *buf) { processExtendCmd(input); else if (boost::starts_with(input, getrefsCmd)) processGetrefsCmd(input); -#ifdef WITH_PBRST else if (boost::starts_with(input, statementCmd) || boost::starts_with(input, statementCmd2)) processStatementCmd(input); -#endif // If the input is not recognized, we show an error... else if (input.length() != 0) // unless there was no input at all error(errorNotRecognized); @@ -999,14 +985,12 @@ void cli(const char *input_prepend, const char *output_prepend, bool addbooks, b line = string(bufline); // Handle multiline inputs (for the statement command)... boost::algorithm::trim(line); // Trim the input. -#ifdef WITH_PBRST if (!multiline && (boost::starts_with(line, statementCmd) || boost::starts_with(line, statementCmd2))) { multiline = true; } if (multiline && boost::ends_with(line, ".")) { multiline = false; } -#endif // WITH_PBRST strcat(buf, bufline); if (!multiline) { cli_process(buf); // Process the input. diff --git a/qt/CMakeLists.txt b/qt/CMakeLists.txt index 13f5f5b..7214582 100644 --- a/qt/CMakeLists.txt +++ b/qt/CMakeLists.txt @@ -1,8 +1,6 @@ cmake_minimum_required(VERSION 3.16) project(bibref-qt LANGUAGES CXX C) -option(PBRST "Include the brst parser" OFF) - find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Concurrent SvgWidgets) find_package(PkgConfig REQUIRED) @@ -50,9 +48,6 @@ set(CMAKE_AUTOUIC ON) add_definitions(-DPROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}" -DINSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}") -if(PBRST) -add_compile_definitions(WITH_PBRST) - set(PARSER_DIR "${CMAKE_CURRENT_BINARY_DIR}") find_package(FLEX 2.6 REQUIRED) find_package(BISON 3.0 REQUIRED) @@ -66,7 +61,7 @@ bison_target(PARSER "${CMAKE_CURRENT_SOURCE_DIR}/../statements/pbrst.y" "${PARSE add_flex_bison_dependency(LEXER PARSER) include_directories(${PARSER_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/..") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DYYDEBUG=1 -DIN_BIBREF") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DYYDEBUG=1") SET(PBRST_SRCS books-wrapper.cpp books-wrapper.h @@ -74,8 +69,6 @@ SET(PBRST_SRCS "${LEXER_OUT}" "${PARSER_OUT}" ) -endif(PBRST) - # set the Application icon, the first line is the property added to Info.plist set(MACOSX_BUNDLE_ICON_FILE bibref-qt.icns) set(bibref_qt_ICON ${CMAKE_CURRENT_SOURCE_DIR}/logo-Psalm40-256.icns) diff --git a/qt/statementwindow.cpp b/qt/statementwindow.cpp index d7c1415..a531fe7 100644 --- a/qt/statementwindow.cpp +++ b/qt/statementwindow.cpp @@ -4,7 +4,6 @@ #include #include -#ifdef WITH_PBRST #include #include #include @@ -15,7 +14,6 @@ extern "C" char* brst_scan_string(char *string, int correct_raw, int correct_dif #include "pbrst.tab.h" // the statements folder must be included among the folders using namespace std; -#endif StatementWindow::StatementWindow(QWidget *parent) : QMainWindow(parent) @@ -123,7 +121,6 @@ void StatementWindow::setupFileMenu() void StatementWindow::parse() { -#ifdef WITH_PBRST char* output = brst_scan_string((char*)editor->toPlainText().toStdString().c_str(), 0, 0, 0, 0, 0); string output_s(output); vector statementAnalysis; @@ -195,30 +192,24 @@ void StatementWindow::parse() link += QUrl::toPercentEncoding(QString::fromStdString(graphviz_input)); QDesktopServices::openUrl(QUrl(link)); } - -#endif } void StatementWindow::showSvg() { -#ifdef WITH_PBRST auto vwindow = new VisualizeWindow(this, graphviz_input); // vwindow->resize(600, 400); vwindow->show(); vwindow->setWindowIcon(QIcon::fromTheme("emblem-photos")); vwindow->setWindowTitle(tr("Visualize")); -#endif // WITH_PBRST } void StatementWindow::setupProveMenu() { -#ifdef WITH_PBRST QMenu *proveMenu = new QMenu(tr("&Prove"), this); menuBar()->addMenu(proveMenu); proveMenu->addAction(QIcon::fromTheme("tools-check-spelling"), tr("&Parse"), QKeySequence::Forward, this, &StatementWindow::parse); -#endif // WITH_PBRST } void StatementWindow::setupHelpMenu() diff --git a/statements/pbrst.y b/statements/pbrst.y index f64c84f..4dfbcef 100644 --- a/statements/pbrst.y +++ b/statements/pbrst.y @@ -7,11 +7,9 @@ #include #include -#ifdef IN_BIBREF #include "books-wrapper.h" #include "fingerprint-wrapper.h" #define YY_USER_INIT ( init_addbooks(); ) -#endif // IN_BIBREF char *stmt_identifier; double stored_cover = 0.0; @@ -84,13 +82,11 @@ int correct_cover = 0; // fix covering percents if possible int correct_versification = 0; // fix versification related issues if possible int show_dump = 0; // if requested, print internal dump in BRST format -#ifdef IN_BIBREF void init_addbooks() { if (!addbooks_done) { addBibles1(); addbooks_done = true; } } -#endif /* shortcut to concatenate a, " " and b, and put the result in c */ #define _CONCAT(a,b,c) \ @@ -383,7 +379,6 @@ check_rawposition_length(char *s) { extern int yylineno; extern int yycolumn; -#ifdef IN_BIBREF int from, to, length=-1; if (strstr(s, "length") == NULL) { add_parseinfo("%d,%d: warning: no length is given, consider adding it\n", yylineno, yycolumn); @@ -405,7 +400,6 @@ check_rawposition_length(char *s) intervals[iv_counter][2] = UNCLASSIFIED; // unclassified add_parseinfo("%d,%d: debug: interval %d [%d,%d] stored\n", yylineno, yycolumn, iv_counter, from, to); iv_counter++; -#endif // IN_BIBREF } void @@ -413,7 +407,6 @@ save_string_in_introduction(char *s, int t) // t == 0: declares, t == 1: identif { extern int yylineno; extern int yycolumn; -#ifdef IN_BIBREF char *l; l = greekToLatin1(s); strcpy(introduction_substrings[substrings++], l); @@ -421,7 +414,6 @@ save_string_in_introduction(char *s, int t) // t == 0: declares, t == 1: identif if (t==1) strcpy(identifies[iv_counter-1], s); add_parseinfo("%d,%d: debug: found %s in input\n", yylineno, yycolumn, l); free(l); -#endif // IN_BIBREF } @@ -430,7 +422,6 @@ check_nt_passage(char *book, char *info, char *verse) { extern int yylineno; extern int yycolumn; -#ifdef IN_BIBREF init_addbooks(); char *l; bool err = false; // be optimistic @@ -484,7 +475,6 @@ check_nt_passage(char *book, char *info, char *verse) // strcpy(infos_s[iv_counter-1], nt_info); // strcpy(books_s[iv_counter-1], nt_book); add_parseinfo("%d,%d: debug: interval %d %s %s is the headline NT passage\n", yylineno, yycolumn, iv_counter-1, nt_book, nt_info); -#endif // IN_BIBREF } int detect_ot_book(char *book, char *info) { @@ -616,7 +606,6 @@ check_ot_passage(char *book, char *info, char *verse) { extern int yylineno; extern int yycolumn; -#ifdef IN_BIBREF char *l; bool err = false; // be optimistic char *verse_fixed = NULL; @@ -690,7 +679,6 @@ check_ot_passage(char *book, char *info, char *verse) strcpy(infos_s[iv_counter-1], ot_info); strcpy(books_s[iv_counter-1], ot_book); strcpy(verses_s[iv_counter-1], ot_verse); // for the dump -#endif // IN_BIBREF } void @@ -698,7 +686,6 @@ check_introduction_passage(char *passage, char *ay) { extern int yylineno; extern int yycolumn; -#ifdef IN_BIBREF char *l; bool err = false; // be optimistic l = lookupVerse1(nt_info, nt_book, passage); @@ -772,14 +759,12 @@ check_introduction_passage(char *passage, char *ay) intervals[iv_counter-1][2] = NT_INTRODUCTION; // NT (introduction) add_parseinfo("%d,%d: debug: interval %d is an introductory NT passage\n", yylineno, yycolumn, iv_counter-1); if (nt_intros_start == -1) nt_intros_start = iv_counter-1; -#endif // IN_BIBREF } void check_fragment(char *passage, char *ay_nt, char *ay_ot) { extern int yylineno; extern int yycolumn; -#ifdef IN_BIBREF // At this point, we have not yet checked if the raw position of the NT part matches the verse. // This is a technical issue, so we need to handle the situation here. if (ot_book == NULL || ot_info == NULL || ot_verse == NULL) { @@ -889,21 +874,17 @@ check_fragment(char *passage, char *ay_nt, char *ay_ot) { intervals_data[iv_counter-2] = difference; // save data for the diagram intervals_data[iv_counter-1] = count; // save data for the diagram (TODO: consider showing this only if unique_prep) if (fragments_start == -1) fragments_start = iv_counter-2; -#endif // IN_BIBREF } void check_unique_prepare() { extern int yylineno; extern int yycolumn; -#ifdef IN_BIBREF unique_prep = true; -#endif // IN_BIBREF } void check_cover(double cover) { extern int yylineno; extern int yycolumn; -#ifdef IN_BIBREF if (ot_book == NULL || ot_info == NULL || ot_verse == NULL) { add_parseinfo("%d,%d: debug: cannot identify OT passage, skipping cover check %4.2f%%\n", yylineno, yycolumn, cover); fatal = true; @@ -1100,7 +1081,6 @@ void check_cover(double cover) { yylineno, yycolumn, i); } // end of for checking OT headlines -#endif // IN_BIBREF } void