138 lines
5.0 KiB
C++
138 lines
5.0 KiB
C++
// Подставная «java» для тестов. Без Qt, чтобы её копию можно было положить
|
||
// в любую папку и запустить без поиска библиотек Qt.
|
||
//
|
||
// Поведение задаётся файлом fake-java.cfg рядом с исполняемым файлом,
|
||
// по строке «ключ=значение»:
|
||
// version=21.0.2 что печатать на «-version» (в stderr, как настоящая java)
|
||
// exit=0 код выхода обычного запуска
|
||
// sleep_ms=0 сколько «работать» перед выходом
|
||
// args_file=<путь> куда записать аргументы, по одному на строку
|
||
// dump_env=<ИМЯ> дописать в args_file строку «env:ИМЯ=значение» (можно несколько)
|
||
// stdout=<строка> что напечатать в stdout (можно несколько)
|
||
// create=<путь>\t<текст> создать файл с текстом; относительный путь — от рабочей папки
|
||
// crash=1 завершиться аварийно (abort)
|
||
|
||
#include <chrono>
|
||
#include <cstdlib>
|
||
#include <filesystem>
|
||
#include <fstream>
|
||
#include <iostream>
|
||
#include <iterator>
|
||
#include <string>
|
||
#include <thread>
|
||
#include <vector>
|
||
|
||
#if defined(_WIN32)
|
||
#include <windows.h>
|
||
#elif defined(__APPLE__)
|
||
#include <mach-o/dyld.h>
|
||
#endif
|
||
|
||
namespace fs = std::filesystem;
|
||
|
||
namespace {
|
||
|
||
fs::path executablePath(const char *argv0)
|
||
{
|
||
#if defined(_WIN32)
|
||
wchar_t buffer[MAX_PATH * 4];
|
||
const DWORD size = GetModuleFileNameW(nullptr, buffer, DWORD(std::size(buffer)));
|
||
return fs::path(std::wstring(buffer, size));
|
||
#elif defined(__APPLE__)
|
||
char buffer[4096];
|
||
uint32_t size = sizeof(buffer);
|
||
if (_NSGetExecutablePath(buffer, &size) == 0)
|
||
return fs::canonical(buffer);
|
||
return fs::absolute(argv0);
|
||
#else
|
||
std::error_code ec;
|
||
const fs::path self = fs::read_symlink("/proc/self/exe", ec);
|
||
return ec ? fs::absolute(argv0) : self;
|
||
#endif
|
||
}
|
||
|
||
} // namespace
|
||
|
||
int main(int argc, char *argv[])
|
||
{
|
||
std::string version = "17.0.8";
|
||
int exitCode = 0;
|
||
int sleepMs = 0;
|
||
bool crash = false;
|
||
std::string argsFile;
|
||
std::vector<std::string> dumpEnv;
|
||
std::vector<std::string> stdoutLines;
|
||
std::vector<std::pair<std::string, std::string>> creates;
|
||
|
||
std::ifstream config(executablePath(argv[0]).parent_path() / "fake-java.cfg");
|
||
for (std::string line; std::getline(config, line);) {
|
||
if (!line.empty() && line.back() == '\r')
|
||
line.pop_back();
|
||
const auto eq = line.find('=');
|
||
if (eq == std::string::npos)
|
||
continue;
|
||
const std::string key = line.substr(0, eq);
|
||
const std::string value = line.substr(eq + 1);
|
||
if (key == "version")
|
||
version = value;
|
||
else if (key == "exit")
|
||
exitCode = std::atoi(value.c_str());
|
||
else if (key == "sleep_ms")
|
||
sleepMs = std::atoi(value.c_str());
|
||
else if (key == "crash")
|
||
crash = value == "1";
|
||
else if (key == "args_file")
|
||
argsFile = value;
|
||
else if (key == "dump_env")
|
||
dumpEnv.push_back(value);
|
||
else if (key == "stdout")
|
||
stdoutLines.push_back(value);
|
||
else if (key == "create") {
|
||
const auto tab = value.find('\t');
|
||
creates.emplace_back(value.substr(0, tab),
|
||
tab == std::string::npos ? std::string() : value.substr(tab + 1));
|
||
}
|
||
}
|
||
|
||
for (int i = 1; i < argc; ++i) {
|
||
if (std::string(argv[i]) == "-version") {
|
||
std::cerr << "openjdk version \"" << version << "\" 2024-01-16\n"
|
||
<< "OpenJDK Runtime Environment (fake)\n";
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
if (!argsFile.empty()) {
|
||
std::ofstream out(argsFile, std::ios::trunc);
|
||
for (int i = 1; i < argc; ++i)
|
||
out << argv[i] << '\n';
|
||
for (const std::string &name : dumpEnv) {
|
||
const char *value = std::getenv(name.c_str());
|
||
out << "env:" << name << '=' << (value ? value : "") << '\n';
|
||
}
|
||
}
|
||
|
||
for (const auto &[path, text] : creates) {
|
||
const fs::path target(path);
|
||
std::error_code ec;
|
||
if (target.has_parent_path())
|
||
fs::create_directories(target.parent_path(), ec);
|
||
std::ofstream(target, std::ios::trunc | std::ios::binary) << text;
|
||
}
|
||
|
||
for (const std::string &line : stdoutLines)
|
||
std::cout << line << '\n';
|
||
std::cout.flush();
|
||
|
||
if (sleepMs > 0)
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(sleepMs));
|
||
if (crash) {
|
||
#if defined(_WIN32)
|
||
// QProcess считает аварией коды 0x8xxxxxxx–0xCxxxxxxx; abort() дал бы код 3.
|
||
TerminateProcess(GetCurrentProcess(), 0xC0000005);
|
||
#endif
|
||
std::abort();
|
||
}
|
||
return exitCode;
|
||
}
|