diff --git a/.bazelignore b/.bazelignore index acb5aaf..f25c879 100644 --- a/.bazelignore +++ b/.bazelignore @@ -2,3 +2,4 @@ tmp .vscode .github external +tests diff --git a/.vscode/launch.json b/.vscode/launch.json index dc1eb5b..5051038 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,7 +15,8 @@ "--output=${workspaceFolder}/tmp/test_runtime.dll", "--temp_dir=${workspaceFolder}/tmp", "--ecsact_sdk=${workspaceFolder}/../ecsact_sdk/dist", - "--debug" + "--debug", + "--report_format=text" ] }, "args": [ diff --git a/cli/BUILD.bazel b/cli/BUILD.bazel index 84c3c4a..5bda7c3 100644 --- a/cli/BUILD.bazel +++ b/cli/BUILD.bazel @@ -4,7 +4,7 @@ load("//bazel:copts.bzl", "copts") cc_binary( name = "ecsact_rtb", - srcs = ["cli.cc"], + srcs = ["cli.cc", "stdout_text_progress_reporter.hh"], copts = copts, data = ecsact_rtb_runfiles, visibility = ["//visibility:public"], diff --git a/cli/cli.cc b/cli/cli.cc index 2114dc1..8ad0582 100644 --- a/cli/cli.cc +++ b/cli/cli.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include "docopt.h" #include "nlohmann/json.hpp" #include "ecsact/interpret/eval.hh" @@ -19,19 +20,28 @@ #include "find_cpp_compiler/find_cpp_compiler.hh" #include "progress_report/progress_report.hh" +#include "stdout_text_progress_reporter.hh" + namespace fs = std::filesystem; constexpr auto USAGE = R"( Usage: - ecsact_rtb ... --output= [--temp_dir=] - [--compiler_path=] [--ecsact_sdk=] [--debug] - [--wasm=] + ecsact_rtb ... --output= [--report_format=] + [--temp_dir=] [--compiler_path=] + [--ecsact_sdk=] [--debug] [--wasm=] )"; constexpr auto OPTIONS = R"( Options: --output= Output path for runtime library. + --report_format= [default: json] + Which format to report progress of runtime builder. Allowed the following: + json Progress is reported in JSON format. One JSON object per line to + stdout. + text Progress is reported in human readable text to stdout. Format + should not be relied upon. Only useful when running ecsact_rtb + manually. --temp_dir= Optionally supply a temporary directory to write the generated/fetched source files. If one is not provided one will be generated. @@ -104,9 +114,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(subcommand_end_message, id, exit_code) } // namespace ecsact_rtb -class stdout_progress_reporter : public ecsact_rtb::progress_reporter { - std::mutex _mutex; - +class stdout_json_progress_reporter : public ecsact_rtb::progress_reporter { template void _report(MessageT& message) { auto message_json = "{}"_json; @@ -135,8 +143,6 @@ int main(int argc, char* argv[]) { std::ios_base::sync_with_stdio(false); - stdout_progress_reporter reporter; - std::string runfiles_err; auto argv0 = executable_path().string(); if(argv0.empty()) { @@ -144,14 +150,35 @@ int main(int argc, char* argv[]) { } auto runfiles = Runfiles::Create(argv0, &runfiles_err); + auto args = docopt_workaround(argc, argv); + + auto reporter = [&]() -> std::unique_ptr { + auto report_format_str = std::string{}; + if(!args.contains("--report_format")) { + report_format_str = "json"; + } else { + report_format_str = args.at("--report_format").asString(); + } + + if(report_format_str == "json") { + return std::make_unique(); + } + + if(report_format_str == "text") { + return std::make_unique(); + } + + return nullptr; + }(); + + assert(reporter); + if(runfiles == nullptr) { - reporter.report(ecsact_rtb::warning_message{ + reporter->report(ecsact_rtb::warning_message{ .content = "Cannot load runfiles: "s + runfiles_err, }); } - auto args = docopt_workaround(argc, argv); - const auto debug_build = args.at("--debug").asBool(); std::string wasm_support_str = "auto"; if(args.at("--wasm").isString()) { @@ -177,7 +204,7 @@ int main(int argc, char* argv[]) { for(const auto& file : files) { fs::path file_path(file); if(!fs::exists(file_path)) { - reporter.report(ecsact_rtb::error_message{ + reporter->report(ecsact_rtb::error_message{ .content = "File doesn't exist: " + file_path.string(), }); return 1; @@ -197,7 +224,7 @@ int main(int argc, char* argv[]) { err_msg.line = err.line; err_msg.character = err.character; - reporter.report(err_msg); + reporter->report(err_msg); } return 1; @@ -208,7 +235,7 @@ int main(int argc, char* argv[]) { #if defined(_WIN32) if(output_path.extension() != ".dll") { - reporter.report(ecsact_rtb::error_message{ + reporter->report(ecsact_rtb::error_message{ .content = "Cross compilation not supported. Only allowed to build "s + ".dll runtimes.", }); @@ -216,7 +243,7 @@ int main(int argc, char* argv[]) { } #elif defined(__linux__) if(output_path.extension() != ".so") { - reporter.report(ecsact_rtb::error_message{ + reporter->report(ecsact_rtb::error_message{ .content = "Cross compilation not supported. Only allowed to build "s + ".so runtimes.", }); @@ -253,12 +280,12 @@ int main(int argc, char* argv[]) { } auto ecsact_cli_path = find_ecsact_cli({ - .reporter = reporter, + .reporter = *reporter, .esact_sdk_path = esact_sdk_path, }); if(ecsact_cli_path.ecsact_cli_path.empty()) { - reporter.report(ecsact_rtb::error_message{ + reporter->report(ecsact_rtb::error_message{ .content = "Could not find ecsact CLI", }); return 1; @@ -269,30 +296,36 @@ int main(int argc, char* argv[]) { auto wasmer = find_wasmer({ .wasm_support = wasm_support, - .reporter = reporter, + .reporter = *reporter, .path = {}, }); + auto generated_files = generate_files({ + .reporter = *reporter, + .temp_dir = temp_dir, + .ecsact_cli_path = ecsact_cli_path.ecsact_cli_path, + .ecsact_file_paths = ecsact_file_paths, + }); + + auto fetched_sources = fetch_sources({ + .reporter = *reporter, + .temp_dir = temp_dir, + .runfiles = runfiles, + .fetch_wasm_related_sources = !wasmer.wasmer_path.empty(), + }); + + auto cpp_compiler = find_cpp_compiler({ + .reporter = *reporter, + .working_directory = working_directory, + .path = compiler_path, + .runfiles = runfiles, + }); + runtime_compile({ - .reporter = reporter, - .generated_files = generate_files({ - .reporter = reporter, - .temp_dir = temp_dir, - .ecsact_cli_path = ecsact_cli_path.ecsact_cli_path, - .ecsact_file_paths = ecsact_file_paths, - }), - .fetched_sources = fetch_sources({ - .reporter = reporter, - .temp_dir = temp_dir, - .runfiles = runfiles, - .fetch_wasm_related_sources = !wasmer.wasmer_path.empty(), - }), - .cpp_compiler = find_cpp_compiler({ - .reporter = reporter, - .working_directory = working_directory, - .path = compiler_path, - .runfiles = runfiles, - }), + .reporter = *reporter, + .generated_files = generated_files, + .fetched_sources = fetched_sources, + .cpp_compiler = cpp_compiler, .wasmer = wasmer, .output_path = output_path, .working_directory = working_directory, diff --git a/cli/stdout_text_progress_reporter.hh b/cli/stdout_text_progress_reporter.hh new file mode 100644 index 0000000..cb57d7f --- /dev/null +++ b/cli/stdout_text_progress_reporter.hh @@ -0,0 +1,84 @@ +#pragma once + +#include + +#include "progress_report/progress_report.hh" + +namespace ecsact_rtb { +class stdout_text_progress_reporter : public ecsact_rtb::progress_reporter { + void _report(alert_message& msg) { + std::cerr << "[ALERT] " << msg.content << "\n"; + } + + void _report(info_message& msg) { + std::cout << "[INFO] " << msg.content << "\n"; + } + + void _report(error_message& msg) { + std::cerr << "[ERROR] " << msg.content << "\n"; + } + + void _report(ecsact_error_message& msg) { + std::cerr // + << "[Ecsact Error] " << msg.ecsact_source_path << ":" << msg.line << ":" + << msg.character << "\n" + << msg.message << "\n"; + } + + void _report(warning_message& msg) { + std::cout << "[WARNING] " << msg.content << "\n"; + } + + void _report(success_message& msg) { + std::cout << "[SUCCESS] " << msg.content << "\n"; + } + + void _report(module_methods_message& msg) { + std::cout << "[Module Methods for " << msg.module_name << "]\n"; + for(auto& method : msg.methods) { + std::cout // + << " " << (method.available ? "YES " : " NO ") + << method.method_name << "\n"; + } + } + + void _report(subcommand_start_message& msg) { + std::cout // + << "[SUBCOMMAND START id=(" << std::to_string(msg.id) << ")] " + << msg.executable << " "; + for(auto& arg : msg.arguments) { + std::cout << arg << " "; + } + std::cout << "\n"; + } + + void _report(subcommand_stdout_message& msg) { + std::cout // + << "[SUBCOMMAND STDOUT id=(" << std::to_string(msg.id) << ")] " + << msg.line << "\n"; + } + + void _report(subcommand_stderr_message& msg) { + std::cerr // + << "[SUBCOMMAND STDERR id=(" << std::to_string(msg.id) << ")] " + << msg.line << "\n"; + } + + void _report(subcommand_progress_message& msg) { + std::cout // + << "[SUBCOMMAND PROG id=(" << std::to_string(msg.id) << ")] " + << msg.description << "\n"; + } + + void _report(subcommand_end_message& msg) { + std::cout // + << "[SUBCOMMAND END id=(" << std::to_string(msg.id) << ")] " + << "exit code " << msg.exit_code << "\n"; + } + +public: + void report(ecsact_rtb::message_variant_t message) { + std::visit([this](auto& message) { _report(message); }, message); + } +}; +} // namespace ecsact_rtb diff --git a/fetch_sources/BUILD.bazel b/fetch_sources/BUILD.bazel index 47e23b2..49a5e2e 100644 --- a/fetch_sources/BUILD.bazel +++ b/fetch_sources/BUILD.bazel @@ -9,6 +9,7 @@ cc_library( visibility = ["//visibility:public"], deps = [ "//progress_report", + "//util:report_error_code", "@bazel_tools//tools/cpp/runfiles", "@boost//libs/process", "@com_github_biojppm_c4core//:c4core", diff --git a/fetch_sources/fetch_sources.cc b/fetch_sources/fetch_sources.cc index 706363c..acbc229 100644 --- a/fetch_sources/fetch_sources.cc +++ b/fetch_sources/fetch_sources.cc @@ -6,6 +6,8 @@ #include #include +#include "util/report_error_code.hh" + namespace fs = std::filesystem; using namespace ecsact::rtb; @@ -40,6 +42,7 @@ static void load_fetched_sources_yaml( const fs::path& include_dir, const options::fetch_sources& options ) { + auto ec = std::error_code{}; auto config_file_path = options.runfiles->Rlocation(runfile_path); auto config_file_contents = file_get_contents>(config_file_path.c_str()); @@ -49,7 +52,8 @@ static void load_fetched_sources_yaml( for(const auto& entry : root.find_child("include")) { const auto& key = entry.key(); auto dirname = include_dir / fs::path{std::string_view{key.str, key.len}}; - fs::create_directories(dirname); + fs::create_directories(dirname, ec); + util::report_error_code_and_exit(options.reporter, ec); for(const auto& item : entry.children()) { std::string item_str{item.val().str, item.val().len}; @@ -58,8 +62,10 @@ static void load_fetched_sources_yaml( if(fs::exists(inc_runfile_path)) { fs::copy_file( inc_runfile_path, - dirname / fs::path{inc_runfile_path}.filename() + dirname / fs::path{inc_runfile_path}.filename(), + ec ); + util::report_error_code_and_exit(options.reporter, ec); } } } @@ -68,7 +74,8 @@ static void load_fetched_sources_yaml( for(const auto& entry : root.find_child("sources")) { const auto& key = entry.key(); auto dirname = src_dir / fs::path{std::string_view{key.str, key.len}}; - fs::create_directories(dirname); + fs::create_directories(dirname, ec); + util::report_error_code_and_exit(options.reporter, ec); for(const auto& item : entry.children()) { std::string item_str{item.val().str, item.val().len}; @@ -77,8 +84,10 @@ static void load_fetched_sources_yaml( if(fs::exists(inc_runfile_path)) { fs::copy_file( inc_runfile_path, - dirname / fs::path{inc_runfile_path}.filename() + dirname / fs::path{inc_runfile_path}.filename(), + ec ); + util::report_error_code_and_exit(options.reporter, ec); } } } @@ -95,18 +104,23 @@ result::fetch_sources ecsact::rtb::fetch_sources( return {}; } + auto ec = std::error_code{}; auto base_dir = options.temp_dir / "fetched_files"; if(fs::exists(base_dir)) { options.reporter.report(ecsact_rtb::info_message{ .content = "Removing old fetched files...", }); } - fs::remove_all(base_dir); + fs::remove_all(base_dir, ec); + util::report_error_code_and_exit(options.reporter, ec); auto include_dir = base_dir / "include"; auto src_dir = base_dir / "src"; - fs::create_directory(base_dir); - fs::create_directory(include_dir); - fs::create_directory(src_dir); + fs::create_directory(base_dir, ec); + util::report_error_code_and_exit(options.reporter, ec); + fs::create_directory(include_dir, ec); + util::report_error_code_and_exit(options.reporter, ec); + fs::create_directory(src_dir, ec); + util::report_error_code_and_exit(options.reporter, ec); load_fetched_sources_yaml( "ecsact_rtb/config/minimal_fetched_sources.yml", diff --git a/generate_files/BUILD.bazel b/generate_files/BUILD.bazel index 2f1f598..b1078fc 100644 --- a/generate_files/BUILD.bazel +++ b/generate_files/BUILD.bazel @@ -10,6 +10,7 @@ cc_library( deps = [ "//progress_report", "//util:report_subcommand_output", + "//util:report_error_code", "@boost//libs/process", ], ) diff --git a/generate_files/generate_files.cc b/generate_files/generate_files.cc index 6fc67ab..27c4f34 100644 --- a/generate_files/generate_files.cc +++ b/generate_files/generate_files.cc @@ -6,6 +6,7 @@ #include #include "util/report_subcommand_output.hh" +#include "util/report_error_code.hh" namespace fs = std::filesystem; namespace bp = boost::process; @@ -92,18 +93,23 @@ result::generate_files ecsact::rtb::generate_files( ) { using namespace std::string_literals; + auto ec = std::error_code{}; auto base_dir = options.temp_dir / "generated_files"; if(fs::exists(base_dir)) { options.reporter.report(ecsact_rtb::info_message{ .content = "Removing old generated files..."s, }); + fs::remove_all(base_dir, ec); + util::report_error_code_and_exit(options.reporter, ec); } - fs::remove_all(base_dir); auto include_dir = base_dir / "include"; auto src_dir = base_dir / "src"; - fs::create_directories(base_dir); - fs::create_directory(include_dir); - fs::create_directory(src_dir); + fs::create_directories(base_dir, ec); + util::report_error_code_and_exit(options.reporter, ec); + fs::create_directory(include_dir, ec); + util::report_error_code_and_exit(options.reporter, ec); + fs::create_directory(src_dir, ec); + util::report_error_code_and_exit(options.reporter, ec); run_codegen( options, diff --git a/tests/.bazelrc b/tests/.bazelrc new file mode 100644 index 0000000..01e3ed9 --- /dev/null +++ b/tests/.bazelrc @@ -0,0 +1,18 @@ +build --enable_platform_specific_config +build --incompatible_use_platforms_repo_for_constraints +build --incompatible_enable_cc_toolchain_resolution +build --incompatible_strict_action_env +build --enable_runfiles + +build:windows --platforms=@ecsact_rtb//bazel/platforms:windows +build:windows --host_platform=@ecsact_rtb//bazel/platforms:windows + +build:linux --platforms=@ecsact_rtb//bazel/platforms:linux +build:linux --host_platform=@ecsact_rtb//bazel/platforms:linux +build:linux --extra_toolchains=@llvm_toolchain//:cc-toolchain-x86_64-linux + +common:ci --announce_rc +common:ci --disk_cache=~/.cache/bazel-disk-cache +build:ci -c opt + +try-import %workspace%/../user.bazelrc diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..df371bf --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,2 @@ +/bazel-* + diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel new file mode 100644 index 0000000..8796b61 --- /dev/null +++ b/tests/BUILD.bazel @@ -0,0 +1,23 @@ +load("@bazel_skylib//rules:native_binary.bzl", "native_binary") + +native_binary( + name = "test_runtime_build_windows", + src = "@ecsact_rtb//cli:ecsact_rtb", + out = "test_runtime_build_cli_ecsact_rtb.exe", + data = [ + "dummy.ecsact", + "@ecsact_sdk_windows//:bin/ecsact.exe", + "@ecsact_sdk_windows//:all_files", + ], + args = [ + "$(location dummy.ecsact)", + "--report_format=text", + "--output=./_test_runtime_build_windows_temp/test_runtime.dll", + "--temp_dir=./_test_runtime_build_windows_temp", + "--ecsact_sdk=$(location @ecsact_sdk_windows//:bin/ecsact.exe)/../..", + "--debug", + ], + target_compatible_with = [ + "@platforms//os:windows", + ], +) diff --git a/tests/WORKSPACE.bazel b/tests/WORKSPACE.bazel new file mode 100644 index 0000000..4855b65 --- /dev/null +++ b/tests/WORKSPACE.bazel @@ -0,0 +1,56 @@ +workspace(name = "ecsact_rtb_tests") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +local_repository( + name = "ecsact_rtb", + path = "../", +) + +load("@ecsact_rtb//:repositories.bzl", "ecsact_rtb_repositories") + +ecsact_rtb_repositories() + +load("@ecsact_rtb//:workspace.bzl", "ecsact_rtb_workspace") + +ecsact_rtb_workspace() + +_EXPORTS_ALL = """ +package(default_visibility = ["//visibility:public"]) +exports_files(glob(["**/*"])) +filegroup( + name = "all_files", + srcs = glob(["**/*"]), +) +""" + +http_archive( + name = "ecsact_sdk_windows", + sha256 = "b7ff3612159a9cf4f042d46531656580b1616b593bdf395593a6e699f5336bf5", + url = "https://github.com/ecsact-dev/ecsact_sdk/releases/download/0.4.4/ecsact_sdk_0.4.4_windows_x64.zip", + build_file_content = _EXPORTS_ALL, +) + +http_archive( + name = "bazel_skylib", + sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", + ], +) + +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") + +bazel_skylib_workspace() + +http_archive( + name = "hedron_compile_commands", + sha256 = "b0e084fa3fb7cf43d149debedfcc941c3e3607b2b79de272f5e61463c52860c3", + strip_prefix = "bazel-compile-commands-extractor-57046dba8d60f819887ea81933ed90f8e23a458a", + url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/57046dba8d60f819887ea81933ed90f8e23a458a.tar.gz", +) + +load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup") + +hedron_compile_commands_setup() diff --git a/tests/dummy.ecsact b/tests/dummy.ecsact new file mode 100644 index 0000000..efee374 --- /dev/null +++ b/tests/dummy.ecsact @@ -0,0 +1,9 @@ +main package dummy; + +component DummyComponent { + i32 n; +} + +system DummySystem { + readwrite DummyComponent; +} diff --git a/util/BUILD.bazel b/util/BUILD.bazel index 109437a..3c01019 100644 --- a/util/BUILD.bazel +++ b/util/BUILD.bazel @@ -22,3 +22,9 @@ cc_library( "@boost//libs/process", ], ) + +cc_library( + name = "report_error_code", + hdrs = ["report_error_code.hh"], + copts = copts, +) diff --git a/util/report_error_code.hh b/util/report_error_code.hh new file mode 100644 index 0000000..9ca14d4 --- /dev/null +++ b/util/report_error_code.hh @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include "progress_report/progress_report.hh" + +namespace ecsact::rtb::util { +inline auto report_error_code_and_exit( + ecsact_rtb::progress_reporter& reporter, + std::error_code ec +) -> void { + if(ec) { + reporter.report(ecsact_rtb::error_message{ + .content = ec.message(), + }); + reporter.report(ecsact_rtb::info_message{ + .content = "Exiting with error code " + std::to_string(ec.value()), + }); + std::exit(ec.value()); + } +} +} // namespace ecsact::rtb::util