From 1cd666d0262aa92d31308de1290a7b6a4826fc8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Tue, 15 Nov 2022 18:51:26 +0100 Subject: [PATCH] waforthc: Clean up dependencies --- .github/workflows/build-waforthc.yml | 16 +-- .vscode/settings.json | 7 +- src/waforthc/Makefile | 27 ++--- src/waforthc/waforthc.cpp | 173 ++++++++++++++++++++------- 4 files changed, 149 insertions(+), 74 deletions(-) diff --git a/.github/workflows/build-waforthc.yml b/.github/workflows/build-waforthc.yml index 1617878..fde889c 100644 --- a/.github/workflows/build-waforthc.yml +++ b/.github/workflows/build-waforthc.yml @@ -10,21 +10,21 @@ jobs: build-waforthc: strategy: matrix: - os: [macos-latest, ubuntu-18.04, windows-latest] + os: [ubuntu-18.04] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - uses: ./.github/actions/setup - - if: runner.os == 'macOS' - run: brew install boost + - if: runner.os == 'macOS' || runner.os == 'Windows' + run: make -C src/waforthc package shell: bash - if: runner.os == 'Linux' - run: sudo apt-get install libboost-dev libboost-filesystem-dev libboost-program-options-dev + run: | + make -C src/waforthc package \ + WABT_INCLUDE_DIR=/usr/local/include \ + WABT_LIB_DIR=/usr/local/lib \ + WABT_DATA_DIR=/usr/local/share/wabt shell: bash - - if: runner.os == 'Windows' - run: C:\msys64\usr\bin\pacman -S --noconfirm mingw-w64-x86_64-boost - shell: cmd - - run: make -C src/waforthc package - run: make -C src/waforthc check diff --git a/.vscode/settings.json b/.vscode/settings.json index 8823f56..608d8ff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,12 +10,7 @@ "**/node_modules": true }, "files.associations": { - "*.fr": "forth", - "__locale": "c", - "ios": "c", - "vector": "cpp", - "*.ipp": "cpp", - "filesystem": "cpp" + "*.fr": "forth" }, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode", diff --git a/src/waforthc/Makefile b/src/waforthc/Makefile index b58860a..2e4e817 100644 --- a/src/waforthc/Makefile +++ b/src/waforthc/Makefile @@ -5,8 +5,9 @@ VERSION?=$(shell cat ../../package.json | grep '"version"' | sed -e 's/.*:.*"\(. ifeq ($(OS),Windows_NT) CXX = g++ WABT_DIR = /c/tools/wabt -BOOST_LIB_DIR = /mingw64/lib -BOOST_INCLUDE_DIR = /mingw64/include +WABT_INCLUDE_DIR ?= $(WABT_DIR)/include +WABT_LIB_DIR ?= $(WABT_DIR)/lib +WABT_DATA_DIR ?= $(WABT_DIR)/share/wabt package: waforthc 7z a -tzip waforthc-v$(VERSION)-windows.zip waforthc.exe else # $(OS) @@ -27,30 +28,26 @@ endif HOMEBREW_DIR = $(shell brew --prefix) # FIXME: When new WABT 1.0.31 is released, we can update this to standard paths WABT_DIR = /usr/local/wabt -BOOST_INCLUDE_DIR := $(HOMEBREW_DIR)/include -BOOST_LIB_DIR := $(HOMEBREW_DIR)/lib +WABT_INCLUDE_DIR = $(WABT_DIR)/include +WABT_LIB_DIR = $(WABT_DIR)/lib +WABT_DATA_DIR = $(WABT_DIR)/share/wabt LIBS := -mmacosx-version-min=13.0 CXXFLAGS := -std=c++17 else # $(UNAME_S) -WABT_DIR = /usr/local/wabt -BOOST_INCLUDE_DIR = /usr/include -BOOST_LIB_DIR := /usr/lib/$(shell gcc -dumpmachine) +GCC_TARGET_MACHINE = $(shell gcc -dumpmachine) +WABT_INCLUDE_DIR ?= /usr/lib/include +WABT_LIB_DIR ?= /usr/lib/$(GCC_TARGET_MACHINE) +WABT_DATA_DIR ?= /usr/share/wabt PACKAGE_SUFFIX=x86_64-linux endif # $(UNAME_S) package: waforthc $(TAR) czf waforthc-v$(VERSION)-$(PACKAGE_SUFFIX).tgz waforthc endif # $(OS) -WABT_INCLUDE_DIR := $(WABT_DIR)/include -WABT_LIB_DIR := $(WABT_DIR)/lib -WABT_DATA_DIR = $(WABT_DIR)/share/wabt - LIBS := \ $(WABT_LIB_DIR)/libwabt.a \ - $(BOOST_LIB_DIR)/libboost_filesystem.a \ - $(BOOST_LIB_DIR)/libboost_program_options.a \ $(LIBS) -CPPFLAGS := -I$(WABT_INCLUDE_DIR) -I$(BOOST_INCLUDE_DIR) +CPPFLAGS := -I$(WABT_INCLUDE_DIR) ifeq ($(DEBUG),1) CXXFLAGS := $(CXXFLAGS) -g @@ -99,7 +96,7 @@ waforth_core.wasm: ../waforth.wat .PHONY: check check: -rm -f test test.out - ./waforthc -o test --init=SAY_HELLO ../examples/hello.fs | tee test.out + ./waforthc --output=test --init=SAY_HELLO ../examples/hello.fs | tee test.out grep -q "Hello, Forth" test.out ./test | tee test.out grep -q "Hello, Forth" test.out diff --git a/src/waforthc/waforthc.cpp b/src/waforthc/waforthc.cpp index 89d8366..959ff51 100644 --- a/src/waforthc/waforthc.cpp +++ b/src/waforthc/waforthc.cpp @@ -4,11 +4,14 @@ #include #include #include +#include #include +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) +#include +#else +#include +#endif -#include -#include -#include #include #include #include @@ -30,8 +33,6 @@ #include "waforth_wabt_wasm-rt-impl_h.h" #include "waforth_wabt_wasm-rt_h.h" -namespace bp = boost::process; -namespace bpo = boost::program_options; namespace fs = std::filesystem; namespace wabti = wabt::interp; @@ -46,6 +47,66 @@ static std::unique_ptr stderrStream; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool endsWith(const std::string &x, const std::string &y) { + return x.size() >= y.size() && x.compare(x.size() - y.size(), std::string::npos, y) == 0; +} + +int runChild(const std::vector &cmd) { +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) + std::ostringstream cmdss; + for (const i = 0; i < cmd.size(); ++i) { + if (i > 0) { + cmdss << " "; + } + auto arg = cmd[i]; + // TODO: Escape + cmds << arg; + } + auto cmdss = cmds.str(); + + STARTUPINFO si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + if (!CreateProcess(NULL, cmds.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { + std::cerr << "CreateProcess failed: %d" << GetLastError() << std::endl; + return -1; + } + WaitForSingleObject(pi.hProcess, INFINITE); + DWORD ret; + GetExitCodeProcess(pi.hProcess, &ret); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return ret; +} +#else + std::vector argsp; + for (const auto &arg : cmd) { + argsp.push_back(arg.c_str()); + } + argsp.push_back(nullptr); + auto pid = fork(); + if (pid == 0) { + if (execvp(argsp[0], (char **)&argsp[0]) == -1) { + std::cerr << "execvp() error" << std::endl; + return -1; + } + return 0; + } + int status; + waitpid(pid, &status, 0); + if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } else { + return -1; + } + return 0; +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** * Compiles a WASM module to a native file named `outfile`. */ @@ -88,16 +149,12 @@ wabt::Result compileToNative(wabt::Module &mod, const std::string &init, const s wabt::FileStream((wd / "wasm-rt.h").string()).WriteData(waforth_wabt_wasm_rt_h, sizeof(waforth_wabt_wasm_rt_h)); } - std::ostringstream cmd; - bp::child c(bp::search_path(cc), "-o", outfile, (wd / "_waforth_rt.c").string(), (wd / "_waforth.c").string(), (wd / "wasm-rt-impl.c").string(), - bp::args(cflags)); - c.wait(); - int result = c.exit_code(); - if (result != 0) { + std::vector cmd = {cc, "-o", outfile, (wd / "_waforth_rt.c").string(), (wd / "_waforth.c").string(), (wd / "wasm-rt-impl.c").string()}; + cmd.insert(cmd.end(), cflags.begin(), cflags.end()); + if (runChild(cmd) != 0) { std::cerr << "error compiling"; return wabt::Result::Error; } - return wabt::Result::Ok; } @@ -351,7 +408,7 @@ wabt::Result main_(const std::string &infile, const std::string &outfile, const wabt::Module compiled; CHECK_RESULT(compileToModule(words, rresult.data, rresult.dataOffset, rresult.latest, compiled, errors)); - if (boost::ends_with(outfile, ".wasm")) { + if (endsWith(outfile, ".wasm")) { CHECK_RESULT(writeModule(outfile, compiled)); } else { CHECK_RESULT(compileToNative(compiled, init, outfile, cc, cflags)); @@ -360,44 +417,70 @@ wabt::Result main_(const std::string &infile, const std::string &outfile, const return wabt::Result::Ok; } +const char help[] = "waforthc " VERSION R"( + +Usage: waforthc [OPTION]... INPUT_FILE + +Options: + --help Show this help message + --output=FILE Output file (default: "out") + If `arg` ends with .wasm, the result will be a + WebAssembly module. Otherwise, the result will be + a native executable. + --cc=CC C compiler (default: "gcc") + --ccflag=FLAG C compiler flag + --init=PROGRAM Initialization program + If specified, PROGRAM will be executed when the + resulting executable is run. Otherwise, the + resulting executable will start an interactive + session. +)"; + +std::pair splitOption(const std::string &s) { + auto i = s.find("="); + if (i == std::string::npos) { + return std::make_pair(s, ""); + } + return std::make_pair(s.substr(0, i), s.substr(i + 1)); +} + int main(int argc, char *argv[]) { - std::string outfile; + std::string outfile("out"); std::string infile; std::string init; - std::string cc; + std::string cc("gcc"); std::vector ccflags; - - bpo::options_description odesc("Options"); - odesc.add_options()("help", "Show this help message")( - "output,o", bpo::value(&outfile)->default_value("out"), - "Output file\nIf `arg` ends with .wasm, the result will be a WebAssembly module. Otherwise, the result will be a native executable.")( - "cc", bpo::value(&cc)->default_value("gcc"), "C compiler")("ccflag", bpo::value>(&ccflags), - "C compiler flag")( - "init", bpo::value(&init), - "Initialization program\nIf specified, PROGRAM will be executed when the resulting executable is run. Otherwise, the resulting executable will " - "start an interactive session."); - bpo::options_description idesc("Options"); - idesc.add_options()("input", bpo::value(&infile)->required(), "Input file"); - bpo::options_description desc; - desc.add(odesc).add(idesc); - - bpo::positional_options_description p; - p.add("input", 1); - bpo::variables_map vm; - try { - bpo::store(bpo::command_line_parser(argc, argv).options(desc).positional(p).run(), vm); - if (vm.count("help")) { - std::cout << "waforthc " << VERSION << std::endl << std::endl; - std::cout << "Usage: " << argv[0] << " [OPTION]... INPUT_FILE" << std::endl << std::endl; - std::cout << odesc << std::endl; - return 0; + for (int i = 1; i < argc; ++i) { + std::string arg(argv[i]); + if (arg.size() >= 0 && arg[0] == '-') { + auto opt = splitOption(arg); + if (opt.first == "--help") { + std::cout << help; + return 0; + } else if (opt.first == "--output") { + outfile = opt.second; + } else if (opt.first == "--init") { + init = opt.second; + } else if (opt.first == "--cc") { + cc = opt.second; + } else if (opt.first == "--ccflag") { + ccflags.push_back(opt.second); + } else { + std::cerr << "unrecognized option: " << arg << std::endl; + return -1; + } + } else { + if (!infile.empty()) { + std::cerr << "only 1 input file supported" << std::endl; + return -1; + } + infile = arg; } - bpo::notify(vm); - } catch (const bpo::error &e) { - std::cout << e.what(); - return -1; } - + if (infile.empty()) { + std::cout << help; + return 0; + } ccflags.push_back("-lm"); wabt::Errors errors;