diff --git a/MODULE.bazel b/MODULE.bazel index 8c4fd6f..9cdb487 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -28,6 +28,8 @@ bazel_dep(name = "hedron_compile_commands", dev_dependency = True) bazel_dep(name = "xxhash", version = "0.8.2") bazel_dep(name = "boringssl", version = "0.0.0-20240530-2db0eb3") +local_path_override(module_name = "ecsact_codegen", path = "../ecsact_codegen") + git_override( module_name = "hedron_compile_commands", commit = "204aa593e002cbd177d30f11f54cff3559110bb9", diff --git a/ecsact/cli/commands/codegen.cc b/ecsact/cli/commands/codegen.cc index 59bb80e..284578b 100644 --- a/ecsact/cli/commands/codegen.cc +++ b/ecsact/cli/commands/codegen.cc @@ -8,6 +8,7 @@ #include #include #include +#include "codegen/codegen.hh" #include "docopt.h" #include "ecsact/interpret/eval.hh" #include "ecsact/cli/commands/common.hh" @@ -37,36 +38,12 @@ constexpr auto USAGE = R"(Ecsact Codegen Command --report_filter= Filtering out report logs [default: none] )"; -static void stdout_write_fn(const char* str, int32_t str_len) { - std::cout << std::string_view(str, str_len); -} - -static bool received_fatal_codegen_report = false; - -static auto codegen_report_fn( - ecsact_codegen_report_message_type type, - const char* str, - int32_t str_len +static auto stdout_write_fn( + int32_t filename_index, + const char* str, + int32_t str_len ) -> void { - auto msg = std::string{str, static_cast(str_len)}; - switch(type) { - default: - case ECSACT_CODEGEN_REPORT_INFO: - return report(ecsact::cli::info_message{msg}); - case ECSACT_CODEGEN_REPORT_WARNING: - return report(ecsact::cli::warning_message{msg}); - case ECSACT_CODEGEN_REPORT_ERROR: - return report(ecsact::cli::error_message{msg}); - case ECSACT_CODEGEN_REPORT_FATAL: - received_fatal_codegen_report = true; - return report(ecsact::cli::error_message{msg}); - } -} - -static thread_local std::ofstream file_write_stream; - -static void file_write_fn(const char* str, int32_t str_len) { - file_write_stream << std::string_view(str, str_len); + std::cout << std::string_view(str, str_len); } int ecsact::cli::detail::codegen_command(int argc, const char* argv[]) { @@ -161,135 +138,37 @@ int ecsact::cli::detail::codegen_command(int argc, const char* argv[]) { return 1; } - std::vector package_ids; - package_ids.resize(static_cast(ecsact_meta_count_packages())); - ecsact_meta_get_package_ids( - static_cast(package_ids.size()), - package_ids.data(), - nullptr - ); - - std::unordered_set output_paths; - bool has_plugin_error = false; - - for(auto& plugin : plugins) { - // precondition: these methods should've been checked in validation - assert(plugin.has("ecsact_codegen_plugin")); - assert(plugin.has("ecsact_codegen_plugin_name")); - assert(plugin.has("ecsact_dylib_set_fn_addr")); - - auto plugin_fn = - plugin.get("ecsact_codegen_plugin"); - auto plugin_name_fn = plugin.get( - "ecsact_codegen_plugin_name" - ); - std::string plugin_name = plugin_name_fn(); - - decltype(&ecsact_dylib_has_fn) dylib_has_fn = nullptr; - - if(plugin.has("ecsact_dylib_has_fn")) { - dylib_has_fn = - plugin.get("ecsact_dylib_has_fn"); - } - - auto dylib_set_fn_addr = - plugin.get("ecsact_dylib_set_fn_addr" - ); - - auto set_meta_fn_ptr = [&](const char* fn_name, auto fn_ptr) { - if(dylib_has_fn && !dylib_has_fn(fn_name)) { - return; - } - dylib_set_fn_addr(fn_name, reinterpret_cast(fn_ptr)); - }; - -#define CALL_SET_META_FN_PTR(fn_name, unused) \ - set_meta_fn_ptr(#fn_name, &::fn_name) - FOR_EACH_ECSACT_META_API_FN(CALL_SET_META_FN_PTR); -#undef CALL_SET_META_FN_PTR - - std::optional outdir; - if(args.at("--outdir").isString()) { - outdir = fs::path(args.at("--outdir").asString()); - if(!fs::exists(*outdir)) { - std::error_code ec; - fs::create_directories(*outdir, ec); - if(ec) { - report_error( - "Failed to create out directory {}: {}", - outdir->string(), - ec.message() - ); - return 3; - } - } - } - - if(args.at("--stdout").asBool()) { - plugin_fn(*package_ids.begin(), &stdout_write_fn, &codegen_report_fn); - std::cout.flush(); - if(received_fatal_codegen_report) { - received_fatal_codegen_report = false; - has_plugin_error = true; - report_error("Codegen plugin '{}' reported fatal error", plugin_name); - } - } else { - for(auto package_id : package_ids) { - fs::path output_file_path = ecsact_meta_package_file_path(package_id); - if(output_file_path.empty()) { - report_error( - "Could not find package source file path from " - "'ecsact_meta_package_file_path'" - ); - continue; - } - - output_file_path.replace_extension( - output_file_path.extension().string() + "." + plugin_name + std::optional outdir; + if(args.at("--outdir").isString()) { + outdir = fs::path(args.at("--outdir").asString()); + if(!fs::exists(*outdir)) { + std::error_code ec; + fs::create_directories(*outdir, ec); + if(ec) { + report_error( + "Failed to create out directory {}: {}", + outdir->string(), + ec.message() ); - - if(outdir) { - output_file_path = *outdir / output_file_path.filename(); - } - - if(output_paths.contains(output_file_path.string())) { - has_plugin_error = true; - report_error( - "Plugin '{}' has conflicts with another plugin output file '{}'", - plugin.location().filename().string(), - output_file_path.string() - ); - continue; - } - - output_paths.emplace(output_file_path.string()); - if(fs::exists(output_file_path)) { - fs::permissions(output_file_path, fs::perms::all); - } - file_write_stream.open(output_file_path); - plugin_fn(package_id, &file_write_fn, &codegen_report_fn); - file_write_stream.flush(); - file_write_stream.close(); - fs::permissions(output_file_path, file_readonly_perms); - if(received_fatal_codegen_report) { - received_fatal_codegen_report = false; - report_error( - "Codegen plugin '{}' reported fatal error while processing package " - "'{}'", - plugin_name, - ecsact::meta::package_name(package_id) - ); - has_plugin_error = true; - } + return 3; } } + } - plugin.unload(); + auto codegen_options = ecsact::cli::codegen_options{ + .plugin_paths = plugin_paths, + .outdir = outdir, + }; + + if(args.at("--stdout").asBool()) { + codegen_options.write_fn = &stdout_write_fn; } - if(has_plugin_error) { - return 2; + auto exit_code = ecsact::cli::codegen(codegen_options); + + if(args.at("--stdout").asBool()) { + std::cout.flush(); } - return 0; + return exit_code; } diff --git a/ecsact/cli/commands/codegen/codegen.cc b/ecsact/cli/commands/codegen/codegen.cc index 5d5837b..1a0c748 100644 --- a/ecsact/cli/commands/codegen/codegen.cc +++ b/ecsact/cli/commands/codegen/codegen.cc @@ -1,6 +1,8 @@ #include "ecsact/cli/commands/codegen/codegen.hh" +#include #include +#include #include #include #include "ecsact/cli/report.hh" @@ -13,15 +15,20 @@ namespace fs = std::filesystem; using namespace std::string_literals; -static thread_local std::ofstream file_write_stream; +static thread_local std::vector file_write_streams; -static void file_write_fn(const char* str, int32_t str_len) { - file_write_stream << std::string_view(str, str_len); +static void file_write_fn( + int32_t filename_index, + const char* str, + int32_t str_len +) { + file_write_streams.at(filename_index) << std::string_view(str, str_len); } static bool received_fatal_codegen_report = false; static auto codegen_report_fn( + int32_t filename_index, ecsact_codegen_report_message_type type, const char* str, int32_t str_len @@ -127,13 +134,13 @@ auto ecsact::cli::codegen(codegen_options options) -> int { FOR_EACH_ECSACT_META_API_FN(CALL_SET_META_FN_PTR); #undef CALL_SET_META_FN_PTR - if(!fs::exists(options.outdir)) { + if(options.outdir && !fs::exists(*options.outdir)) { auto ec = std::error_code{}; - fs::create_directories(options.outdir, ec); + fs::create_directories(*options.outdir, ec); if(ec) { ecsact::cli::report_error( "Failed to create out directory {}: {}", - options.outdir.string(), + options.outdir->string(), ec.message() ); unload_plugins(); @@ -142,8 +149,10 @@ auto ecsact::cli::codegen(codegen_options options) -> int { } for(auto package_id : package_ids) { - fs::path output_file_path = ecsact_meta_package_file_path(package_id); - if(output_file_path.empty()) { + auto package_file_path = + fs::path{ecsact_meta_package_file_path(package_id)}; + + if(package_file_path.empty()) { has_plugin_error = true; ecsact::cli::report_error( // "Could not find package source file path from " @@ -152,41 +161,107 @@ auto ecsact::cli::codegen(codegen_options options) -> int { continue; } - output_file_path.replace_extension( - output_file_path.extension().string() + "." + plugin_name - ); + auto plugin_output_paths = std::vector{}; + if(plugin.has("ecsact_codegen_output_filenames")) { + auto plugin_outputs_fn = + plugin.get( + "ecsact_codegen_output_filenames" + ); - if(output_paths.contains(output_file_path.string())) { - has_plugin_error = true; - auto conflicting_plugin_name = output_paths[output_file_path.string()]; + auto filenames_count = int32_t{}; + plugin_outputs_fn(package_id, nullptr, 0, 0, &filenames_count); + if(filenames_count <= 0) { + has_plugin_error = true; + ecsact::cli::report_error( + "Plugin '{}' ({}) ecsact_codegen_output_filenames returned {} " + "filenames. Expected 1 or more.", + plugin_name, + plugin.location().filename().string(), + filenames_count + ); + continue; + } - ecsact::cli::report_error( - "Plugin '{}' ({}) has conflicts with plugin '{}' output file " - "{}", - plugin_name, - plugin.location().filename().string(), - conflicting_plugin_name, - output_file_path.string() - ); + auto filenames = std::array{}; + auto filenames_ = + std::array, filenames.size()>{}; + for(auto i = 0; filenames_.size() > i; ++i) { + filenames[i] = filenames_[i].data(); + } - continue; + plugin_outputs_fn(package_id, filenames.data(), 16, 1024, nullptr); + + for(auto i = 0; filenames_count > i; ++i) { + auto filename = + std::string_view{filenames[i], strnlen_s(filenames[i], 1023)}; + plugin_output_paths.emplace_back( + fs::path{package_file_path}.parent_path() / filename + ); + } + } else { + plugin_output_paths.emplace_back( + fs::path{package_file_path}.replace_extension( + package_file_path.extension().string() + "." + plugin_name + ) + ); } - output_paths.emplace(output_file_path.string(), plugin_name); - if(fs::exists(output_file_path)) { - fs::permissions(output_file_path, fs::perms::all); + + auto has_plugin_output_conflict_error = false; + for(auto filename_index = 0; plugin_output_paths.size() > filename_index; + ++filename_index) { + auto output_file_path = plugin_output_paths.at(filename_index); + if(output_paths.contains(output_file_path.string())) { + has_plugin_error = true; + has_plugin_output_conflict_error = true; + auto conflicting_plugin_name = + output_paths[output_file_path.string()]; + + ecsact::cli::report_error( + "Plugin '{}' ({}) has conflicts with plugin '{}' output file " + "{}", + plugin_name, + plugin.location().filename().string(), + conflicting_plugin_name, + output_file_path.string() + ); + } } - output_file_path = options.outdir / output_file_path.filename(); + if(!has_plugin_output_conflict_error) { + // We're filling this in the for loop. We shouldn't have any in here. + assert(file_write_streams.empty()); - file_write_stream.open(output_file_path); - plugin_fn(package_id, &file_write_fn, &codegen_report_fn); - if(received_fatal_codegen_report) { - received_fatal_codegen_report = false; - has_plugin_error = true; - report_error("Codegen plugin '{}' reported fatal error", plugin_name); + for(auto filename_index = 0; + plugin_output_paths.size() > filename_index; + ++filename_index) { + auto output_file_path = plugin_output_paths.at(filename_index); + output_paths.emplace(output_file_path.string(), plugin_name); + if(fs::exists(output_file_path)) { + fs::permissions(output_file_path, fs::perms::all); + } + + if(options.outdir) { + output_file_path = *options.outdir / output_file_path.filename(); + } + + auto& file_write_stream = + file_write_streams.emplace_back(output_file_path); + + file_write_stream.open(output_file_path); + plugin_fn(package_id, &file_write_fn, &codegen_report_fn); + if(received_fatal_codegen_report) { + received_fatal_codegen_report = false; + has_plugin_error = true; + report_error( + "Codegen plugin '{}' reported fatal error", + plugin_name + ); + } + file_write_stream.flush(); + file_write_stream.close(); + } + file_write_streams.clear(); } - file_write_stream.flush(); - file_write_stream.close(); } plugin.unload(); } diff --git a/ecsact/cli/commands/codegen/codegen.hh b/ecsact/cli/commands/codegen/codegen.hh index f4f2ada..3874377 100644 --- a/ecsact/cli/commands/codegen/codegen.hh +++ b/ecsact/cli/commands/codegen/codegen.hh @@ -1,17 +1,15 @@ #pragma once #include -#include #include -#include +#include "ecsact/codegen/plugin.h" namespace ecsact::cli { -using codegen_output_write_fn_t = void (*)(const char* str, int32_t str_len); - struct codegen_options { - std::vector plugin_paths; - std::filesystem::path outdir; + std::vector plugin_paths; + std::optional outdir; + std::optional write_fn; }; auto codegen(codegen_options options) -> int;