jak-project/goalc/main.cpp

186 lines
6.3 KiB
C++
Raw Normal View History

2020-08-22 22:30:12 -04:00
#include <cstdio>
#include <regex>
#include "common/log/log.h"
#include "common/repl/nrepl/ReplServer.h"
#include "common/repl/repl_wrapper.h"
#include "common/util/FileUtil.h"
#include "common/util/diff.h"
#include "common/util/string_util.h"
#include "common/util/term_util.h"
#include "common/util/unicode_util.h"
#include "common/versions/versions.h"
#include "goalc/compiler/Compiler.h"
#include "fmt/color.h"
#include "fmt/core.h"
#include "third-party/CLI11.hpp"
void setup_logging(const bool disable_ansi_colors) {
lg::set_file_level(lg::level::info);
lg::set_stdout_level(lg::level::info);
lg::set_flush_level(lg::level::info);
if (disable_ansi_colors) {
lg::disable_ansi_colors();
}
lg::set_file("compiler");
lg::initialize();
}
2020-08-22 22:30:12 -04:00
int main(int argc, char** argv) {
ArgumentGuard u8_guard(argc, argv);
bool auto_find_user = false;
std::string cmd = "";
std::string username = "#f";
std::string game = "jak1";
int nrepl_port = -1;
fs::path project_path_override;
Parameterize the iso_data folder for goalc (#3692) I hope this is everything I needed, and nothing I didn't. ## What's Changed This update adds a command-line parameter to goalc, `--iso-path`. Providing a path to a directory like `D:\Files\Repositories\ArchipelaGOAL\iso_data\jak1` will inform the compiler to use that directory instead. ## Why is this useful? When combined with `--proj-path`, the compiler can be pointed to a completely different project folder, given the `(mi)` command, and immediately begin building from that directory, with everything it needs. This eliminates the need to copy `iso_data` to multiple `data` directories. If a subsequent change to the Launcher is made, each mod could be passed an --iso-path pointing to a single shared folder, allowing mods to each run their own REPL _without_ requiring a copy of `iso_data` in a subfolder. ## Independent testing required! My local repositories are a little suspect, with a mod, a fork of mod-base, and a fork of jak-project, all on the same drive. My decompiler_out and iso_data folders are in the mod repo, not mod-base nor jak-project. So what I did was make the change in the mod-base fork, point `--proj-path and --iso-path` to the mod folders, and then ran `(mi)`. The output showed a build starting with no errors. Then I had to create this PR, which my fork of mod-base is unable to do, so I created a patch file, forked jak-project, then applied the patch there. All this is to say that it would be preferable if someone could apply this code to their own installation and see if it works. Even I wouldn't take my own word for this. --------- Co-authored-by: Tyler Wilding <xtvaser@gmail.com>
2024-10-18 00:03:14 -04:00
fs::path iso_path_override;
// TODO - a lot of these flags could be deprecated and moved into `repl-config.json`
CLI::App app{"OpenGOAL Compiler / REPL"};
app.add_option("-c,--cmd", cmd, "Specify a command to run, no REPL is launched in this mode");
app.add_option("-u,--user", username,
"Specify the username to use for your user profile in 'goal_src/user/'");
app.add_option("-p,--port", nrepl_port, "Specify the nREPL port. Defaults to 8181");
app.add_flag("--user-auto", auto_find_user,
"Attempt to automatically deduce the user, overrides '--user'");
app.add_option("-g,--game", game, "The game name: 'jak1' or 'jak2'");
app.add_option("--proj-path", project_path_override,
"Specify the location of the 'data/' folder");
Parameterize the iso_data folder for goalc (#3692) I hope this is everything I needed, and nothing I didn't. ## What's Changed This update adds a command-line parameter to goalc, `--iso-path`. Providing a path to a directory like `D:\Files\Repositories\ArchipelaGOAL\iso_data\jak1` will inform the compiler to use that directory instead. ## Why is this useful? When combined with `--proj-path`, the compiler can be pointed to a completely different project folder, given the `(mi)` command, and immediately begin building from that directory, with everything it needs. This eliminates the need to copy `iso_data` to multiple `data` directories. If a subsequent change to the Launcher is made, each mod could be passed an --iso-path pointing to a single shared folder, allowing mods to each run their own REPL _without_ requiring a copy of `iso_data` in a subfolder. ## Independent testing required! My local repositories are a little suspect, with a mod, a fork of mod-base, and a fork of jak-project, all on the same drive. My decompiler_out and iso_data folders are in the mod repo, not mod-base nor jak-project. So what I did was make the change in the mod-base fork, point `--proj-path and --iso-path` to the mod folders, and then ran `(mi)`. The output showed a build starting with no errors. Then I had to create this PR, which my fork of mod-base is unable to do, so I created a patch file, forked jak-project, then applied the patch there. All this is to say that it would be preferable if someone could apply this code to their own installation and see if it works. Even I wouldn't take my own word for this. --------- Co-authored-by: Tyler Wilding <xtvaser@gmail.com>
2024-10-18 00:03:14 -04:00
app.add_option("--iso-path", iso_path_override, "Specify the location of the 'iso_data/' folder");
define_common_cli_arguments(app);
app.validate_positionals();
CLI11_PARSE(app, argc, argv);
GameVersion game_version = game_name_to_version(game);
if (!project_path_override.empty()) {
if (!fs::exists(project_path_override)) {
lg::error("Error: project path override '{}' does not exist", project_path_override.string());
return 1;
}
if (!file_util::setup_project_path(project_path_override, true)) {
lg::error("Could not setup project path!");
return 1;
}
} else if (!file_util::setup_project_path(std::nullopt, true)) {
return 1;
}
try {
setup_logging(_cli_flag_disable_ansi);
} catch (const std::exception& e) {
lg::error("Failed to setup logging: {}", e.what());
return 1;
}
// Figure out the username
if (auto_find_user) {
username = REPL::find_repl_username();
}
// Load the user's startup file
auto startup_file = REPL::load_user_startup_file(username, game_version);
// Load the user's REPL config
auto repl_config = REPL::load_repl_config(username, game_version, nrepl_port);
Parameterize the iso_data folder for goalc (#3692) I hope this is everything I needed, and nothing I didn't. ## What's Changed This update adds a command-line parameter to goalc, `--iso-path`. Providing a path to a directory like `D:\Files\Repositories\ArchipelaGOAL\iso_data\jak1` will inform the compiler to use that directory instead. ## Why is this useful? When combined with `--proj-path`, the compiler can be pointed to a completely different project folder, given the `(mi)` command, and immediately begin building from that directory, with everything it needs. This eliminates the need to copy `iso_data` to multiple `data` directories. If a subsequent change to the Launcher is made, each mod could be passed an --iso-path pointing to a single shared folder, allowing mods to each run their own REPL _without_ requiring a copy of `iso_data` in a subfolder. ## Independent testing required! My local repositories are a little suspect, with a mod, a fork of mod-base, and a fork of jak-project, all on the same drive. My decompiler_out and iso_data folders are in the mod repo, not mod-base nor jak-project. So what I did was make the change in the mod-base fork, point `--proj-path and --iso-path` to the mod folders, and then ran `(mi)`. The output showed a build starting with no errors. Then I had to create this PR, which my fork of mod-base is unable to do, so I created a patch file, forked jak-project, then applied the patch there. All this is to say that it would be preferable if someone could apply this code to their own installation and see if it works. Even I wouldn't take my own word for this. --------- Co-authored-by: Tyler Wilding <xtvaser@gmail.com>
2024-10-18 00:03:14 -04:00
// Check for a custom ISO path before we instantiate the compiler.
if (!iso_path_override.empty()) {
if (!fs::exists(iso_path_override)) {
lg::error("Error: iso path override '{}' does not exist", iso_path_override.string());
return 1;
}
file_util::set_iso_data_dir(iso_path_override);
repl_config.iso_path = iso_path_override.string();
}
// Init Compiler
std::unique_ptr<Compiler> compiler;
std::mutex compiler_mutex;
// if a command is provided on the command line, no REPL just run the compiler on it
try {
if (!cmd.empty()) {
compiler = std::make_unique<Compiler>(game_version);
compiler->run_front_end_on_string(cmd);
return 0;
}
} catch (std::exception& e) {
lg::error("Compiler Fatal Error: {}", e.what());
docs: Automatically generate documentation from goal_src code (#2214) This automatically generates documentation from goal_src docstrings, think doxygen/java-docs/rust docs/etc. It mostly supports everything already, but here are the following things that aren't yet complete: - file descriptions - high-level documentation to go along with this (think pure markdown docs describing overall systems that would be co-located in goal_src for organizational purposes) - enums - states - std-lib functions (all have empty strings right now for docs anyway) The job of the new `gen-docs` function is solely to generate a bunch of JSON data which should give you everything you need to generate some decent documentation (outputting markdown/html/pdf/etc). It is not it's responsibility to do that nice formatting -- this is by design to intentionally delegate that responsibility elsewhere. Side-note, this is about 12-15MB of minified json for jak 2 so far :) In our normal "goal_src has changed" action -- we will generate this data, and the website can download it -- use the information to generate the documentation at build time -- and it will be included in the site. Likewise, if we wanted to include docs along with releases for offline viewing, we could do so in a similar fashion (just write a formatting script to generate said documentation). Lastly this work somewhat paves the way for doing more interesting things in the LSP like: - whats the docstring for this symbol? - autocompleting function arguments - type checking function arguments - where is this symbol defined? - etc Fixes #2215
2023-02-20 19:49:37 -05:00
return 1;
}
2020-08-22 22:30:12 -04:00
// Otherwise, start the REPL normally
2022-11-30 22:36:09 -05:00
ReplStatus status = ReplStatus::OK;
std::function<void()> repl_startup_func = [&]() {
// Run automatic forms if applicable
std::lock_guard<std::mutex> lock(compiler_mutex);
for (const auto& cmd : startup_file.run_before_listen) {
status = compiler->handle_repl_string(cmd);
}
};
// Initialize nREPL server socket
std::function<bool()> shutdown_callback = [&]() { return status == ReplStatus::WANT_EXIT; };
ReplServer repl_server(shutdown_callback, repl_config.get_nrepl_port());
bool nrepl_server_ok = repl_server.init_server(true);
std::thread nrepl_thread;
// the compiler may throw an exception if it fails to load its standard library.
try {
compiler = std::make_unique<Compiler>(
game_version, std::make_optional(repl_config), username,
std::make_unique<REPL::Wrapper>(username, repl_config, startup_file, nrepl_server_ok));
// Start nREPL Server if it spun up successfully
if (nrepl_server_ok) {
nrepl_thread = std::thread([&]() {
while (!shutdown_callback()) {
auto resp = repl_server.get_msg();
if (resp) {
std::lock_guard<std::mutex> lock(compiler_mutex);
status = compiler->handle_repl_string(resp.value());
// Print out the prompt, just for better UX
compiler->print_to_repl(compiler->get_prompt());
}
std::this_thread::sleep_for(std::chrono::microseconds(50000));
}
});
}
repl_startup_func();
// Poll Terminal
while (status != ReplStatus::WANT_EXIT) {
if (status == ReplStatus::WANT_RELOAD) {
lg::info("Reloading compiler...");
std::lock_guard<std::mutex> lock(compiler_mutex);
if (compiler) {
compiler->save_repl_history();
}
compiler = std::make_unique<Compiler>(
game_version, std::make_optional(repl_config), username,
std::make_unique<REPL::Wrapper>(username, repl_config, startup_file, nrepl_server_ok));
status = ReplStatus::OK;
}
// process user input
std::string input_from_stdin = compiler->get_repl_input();
if (!input_from_stdin.empty()) {
// lock, while we compile
std::lock_guard<std::mutex> lock(compiler_mutex);
status = compiler->handle_repl_string(input_from_stdin);
}
}
} catch (std::exception& e) {
lg::error("Compiler Fatal Error: {}", e.what());
status = ReplStatus::WANT_EXIT;
}
2020-08-22 22:30:12 -04:00
// TODO - investigate why there is such a delay when exitting
// Cleanup
if (nrepl_server_ok) {
repl_server.shutdown_server();
nrepl_thread.join();
}
2020-08-22 22:30:12 -04:00
return 0;
}