diff --git a/CMakeLists.txt b/CMakeLists.txt index 39185e6..76530f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,16 +78,20 @@ if (${IS_GNU} OR ${IS_CLANG}) else() set(IS_HOST_NOT_ANDROID 0) endif() - set(CMAKE_EXE_LINKER_FLAGS "--closure 1 -sWASM=0 --memory-init-file 0 -sEXPORTED_FUNCTIONS=_main -sEXIT_RUNTIME=1 -sSINGLE_FILE=1 -sSAFE_HEAP=${IS_HOST_NOT_ANDROID} -sABORTING_MALLOC=0 -sJS_MATH=1 -sENVIRONMENT='web,webview,worker,node,shell' -sNODEJS_CATCH_EXIT=0 -sSTRICT=1 -sMINIMAL_RUNTIME=0 -sDISABLE_EXCEPTION_CATCHING=0 -sALLOW_MEMORY_GROWTH=1 -Wno-pthreads-mem-growth -sMEMORY_GROWTH_GEOMETRIC_STEP=0 -sDECLARE_ASM_MODULE_EXPORTS=0 ${CMAKE_EXE_LINKER_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "--closure 1 --closure-args=\"--compilation_level SIMPLE\" -lnodefs.js -lembind -sDYNAMIC_EXECUTION=0 -sFORCE_FILESYSTEM=1 -sWASM=0 --memory-init-file 0 -sEXPORTED_FUNCTIONS=_main -sEXIT_RUNTIME=1 -sSINGLE_FILE=1 -sSAFE_HEAP=${IS_HOST_NOT_ANDROID} -sMALLOC='emmalloc' -sABORTING_MALLOC=0 -sJS_MATH=1 -sENVIRONMENT='web,webview,worker,node,shell' -sNODEJS_CATCH_EXIT=0 -sSTRICT=1 -sMINIMAL_RUNTIME=0 -sDISABLE_EXCEPTION_CATCHING=0 -sSTACK_SIZE=8MB -sALLOW_MEMORY_GROWTH=1 -Wno-pthreads-mem-growth -sMEMORY_GROWTH_GEOMETRIC_STEP=0 -sDECLARE_ASM_MODULE_EXPORTS=1 -sDEMANGLE_SUPPORT=1 ${CMAKE_EXE_LINKER_FLAGS}") if (CMAKE_USE_PTHREADS_INIT) set(CMAKE_CXX_FLAGS "-pthread ${CMAKE_CXX_FLAGS}") # Emscripten requires the -pthread flag - set(CMAKE_EXE_LINKER_FLAGS "--closure-args=\"--compilation_level SIMPLE\" -sPROXY_TO_PTHREAD=1 ${CMAKE_EXE_LINKER_FLAGS}") # See https://github.com/emscripten-core/emscripten/issues/16706 + set(CMAKE_EXE_LINKER_FLAGS "-sPROXY_TO_PTHREAD=0 -sPTHREAD_POOL_SIZE=3 ${CMAKE_EXE_LINKER_FLAGS}") # See https://github.com/emscripten-core/emscripten/issues/16706 endif() endif() - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-s") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-Wl,--gc-sections -s") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstrict-enums -pipe -funwind-tables -fasynchronous-unwind-tables -frtti -fexceptions") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffunction-sections -fdata-sections -fmerge-all-constants -ftree-vectorize -fno-math-errno") include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("-Oz" Z_OPTIMIZATION_SUPPORTED) + if (Z_OPTIMIZATION_SUPPORTED OR EMSCRIPTEN) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Oz") + endif() set(CF_PROTECTION "-fcf-protection") check_cxx_compiler_flag("${CF_PROTECTION}" CF_PROTECTION_SUPPORTED) if (CF_PROTECTION_SUPPORTED) diff --git a/Jenkinsfile b/Jenkinsfile index 8066c55..c1e6b99 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -7,8 +7,7 @@ boolean use_yarn_or_npm() { return fileExists('/usr/bin/yarn') || fileExists('/u - Sidebar Link - Workspace Cleanup Required Compilers: - - G++ cross-compiler for riscv64 - - Musl cross-compiling toolchains (archives ending in "-cross" from https://musl.cc/#binaries) for x86_64, i686, armel, armhf and aarch64, unzipped into /usr/, along with soft links from /usr/bin/ to the binaries + - Musl cross-compiling toolchains (archives ending in "-cross" from https://musl.cc/#binaries) for x86_64, i686, armel, armhf, aarch64 and riscv64, unzipped into /usr/, along with soft links from /usr/bin/ to the binaries - Emscripten Clang (https://github.com/emscripten-core/emsdk) in /usr/share/ - MinGW32 G++/Clang (https://github.com/mstorsjo/llvm-mingw) for x86_64, i686, armhf and aarch64, (unzip the content of the folder in the tar.xz file) in /usr/, along with soft links from /usr/bin/ to the binaries Optional Tools: @@ -44,7 +43,7 @@ pipeline { 'armel-linux-musleabi (RPM=armv4l, DEB=armel)', 'armv7l-linux-musleabihf (RPM=armv7hl, DEB=armhf)', 'aarch64-linux-musl (RPM=aarch64, DEB=arm64)', - 'riscv64-linux-gnu (RPM=riscv64, DEB=riscv64)', + 'riscv64-linux-musl (RPM=riscv64, DEB=riscv64)', 'x86_64-w64-mingw32 (RPM=x86_64, DEB=amd64)', 'i686-w64-mingw32 (RPM=i386, DEB=i386)', 'armv7-w64-mingw32 (RPM=armv7hl, DEB=armhf)', diff --git a/src/headers/Yerbacon.hpp b/src/headers/Yerbacon.hpp index 801deed..38139a2 100644 --- a/src/headers/Yerbacon.hpp +++ b/src/headers/Yerbacon.hpp @@ -32,6 +32,35 @@ #include #include #include +#ifdef EMSCRIPTEN +#include +static bool is_node; +void emscripten_exit(int status) { + MAIN_THREAD_EM_ASM("yerbacon_output = Module.get_cout();"); + if (not is_node) emscripten_exit_with_live_runtime(); + std::exit(status); +} +#define exit(status) emscripten_exit(status) +#include +static std::ostringstream string_cout; +#define cout (is_node ? std::cout : string_cout) +#define cerr (is_node ? std::cerr : string_cout) +std::string get_cout() { + static size_t previous_cout_size = 0; + std::string output = string_cout.str(); + const size_t current_size = output.size(); + output = output.substr(previous_cout_size, current_size); + previous_cout_size = current_size; + return output; +} +#include +EMSCRIPTEN_BINDINGS() { + emscripten::function("get_cout", &get_cout); +} +#else +using std::exit; +using std::cout; +#endif namespace Yerbacon { consteval const char* getVersion() noexcept { return YBCON_VERSION; } @@ -43,10 +72,10 @@ namespace Yerbacon { } static void fail(const std::initializer_list reason) { std::for_each(reason.begin(), reason.end(), [](const char* current_string){ - std::cout << current_string; + cout << current_string; }); - std::cout << std::endl; - std::exit(EXIT_FAILURE); + cout << std::endl; + exit(EXIT_FAILURE); } inline void fail(const char* reason) { fail({reason}); } class Exception: public std::exception { diff --git a/src/headers/parsing/ReservedIdentifiers.hpp b/src/headers/parsing/ReservedIdentifiers.hpp index 3bc29d1..fed4b5c 100644 --- a/src/headers/parsing/ReservedIdentifiers.hpp +++ b/src/headers/parsing/ReservedIdentifiers.hpp @@ -19,7 +19,9 @@ inline const char* ID(const ReservedIdentifier& identifier) { } bool reserved(const string_view words) { - return find(identifiers.cbegin(), identifiers.cend(), words) != identifiers.cend(); + return any_of(identifiers.cbegin(), identifiers.cend(), [&words](const auto& identifier) { + return words == identifier; + }); } #endif //YERBACON_RESERVEDIDENTIFIERS_HPP diff --git a/src/main.cpp b/src/main.cpp index b5993ee..11580e5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,6 +19,9 @@ int main(int argc, char* argv[]) { using unit_result = pair>; using unit = future; map Units; + #ifdef EMSCRIPTEN + is_node = MAIN_THREAD_EM_ASM_INT({ return ENVIRONMENT_IS_NODE; }); + #endif for (signed int i = 1; i < argc; ++i) { const string_view currentArgument (argv[i]); @@ -42,24 +45,41 @@ int main(int argc, char* argv[]) { #ifdef _OPENMP if (not parallel) omp_set_num_threads(1); #endif - Units.insert_or_assign(currentArgument, async(not parallel ? launch::deferred : launch::async, [currentArgument, &text_provided, &target, &newLines]() { + optional file_path = make_optional(); + if (not text_provided) { + file_path = filesystem::path(currentArgument); + #ifdef EMSCRIPTEN + if (currentArgument.size() >= 2 and currentArgument[1] == ':') { + auto path = string(currentArgument).erase(0, 2); + replace(path.begin(), path.end(), '\\', '/'); + file_path = path; + } + const string directory = filesystem::path(file_path.value()).remove_filename(); + for (unsigned int character = 1; character < directory.size(); ++character) { + const string sub_directory = directory.substr(0, character); + if (is_node and (directory[character] == '/') and not filesystem::is_directory(sub_directory)) { + MAIN_THREAD_EM_ASM({ + const sub_directory = UTF8ToString($0); + if (!FS.isMountpoint(sub_directory)) { + try { + FS.mkdir(sub_directory); + FS.mount(NODEFS, {root: sub_directory}, sub_directory); + } catch (exception) {} + }; + }, sub_directory.c_str()); + } + } + #endif + } + Units.insert_or_assign(currentArgument, async(not parallel ? launch::deferred : launch::async, [&, currentArgument, file_path]() { unit_result resultingPair; try { resultingPair.first = Target::forName(target, newLines)->transpileWithTree( - parseString(text_provided ? currentArgument : getFileContent(currentArgument.data())) + parseString(text_provided ? currentArgument : getFileContent(file_path->string())) ); - if (not text_provided) outputFileContent(string(currentArgument.substr(0, currentArgument.size() - 6)) + '.' + target, resultingPair.first); + if (not text_provided) outputFileContent(filesystem::path(file_path.value()).replace_extension(target).string(), resultingPair.first); } catch (const Yerbacon::Exception& error) { - size_t lastSlash = 0; - const size_t position1 = currentArgument.find_last_of('/'); - if (cmp_not_equal(position1, string_view::npos)) lastSlash = position1; - if constexpr(filesystem::path::preferred_separator != '/') { - const size_t position2 = currentArgument.find_last_of(filesystem::path::preferred_separator); - if (cmp_not_equal(position2, string_view::npos)) { - lastSlash = max(lastSlash, position2); - } - } - resultingPair.first = currentArgument.substr(lastSlash + 1); + resultingPair.first = file_path.has_value() ? file_path->filename().string() : string(); resultingPair.second.emplace(error); } return resultingPair; @@ -89,9 +109,9 @@ int main(int argc, char* argv[]) { } else { cout << "Compilation"; if (not text_provided) cout << " of " << result.first; cout << " has failed with the following error:" << endl; cerr << result.second.value().what() << '\n'; - return EXIT_FAILURE; + exit(EXIT_FAILURE); } } } else cout << (!text_provided ? "No valid file provided" : "No text was provided") << ".\n"; - return EXIT_SUCCESS; + exit(EXIT_SUCCESS); } \ No newline at end of file