waforthc: Clean up dependencies

This commit is contained in:
Remko Tronçon 2022-11-15 18:51:26 +01:00
parent 0751ed383a
commit 1cd666d026
4 changed files with 149 additions and 74 deletions

View file

@ -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

View file

@ -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",

View file

@ -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

View file

@ -4,11 +4,14 @@
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <boost/algorithm/string/predicate.hpp>
#include <boost/process.hpp>
#include <boost/program_options.hpp>
#include <wabt/apply-names.h>
#include <wabt/binary-reader-ir.h>
#include <wabt/binary-reader.h>
@ -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<wabt::FileStream> 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<std::string> &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<const char *> 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<std::string> 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<std::string, std::string> 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<std::string> ccflags;
bpo::options_description odesc("Options");
odesc.add_options()("help", "Show this help message")(
"output,o", bpo::value<std::string>(&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<std::string>(&cc)->default_value("gcc"), "C compiler")("ccflag", bpo::value<std::vector<std::string>>(&ccflags),
"C compiler flag")(
"init", bpo::value<std::string>(&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<std::string>(&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;