2026-10-12 13:14:08 +03:00
|
|
|
|
// Подставная «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()) {
|
2026-10-17 23:38:49 +03:00
|
|
|
|
// binary: в текстовом режиме Windows превратит '\n' в "\r\n", и тесты увидят «-jar\r».
|
|
|
|
|
|
std::ofstream out(argsFile, std::ios::trunc | std::ios::binary);
|
2026-10-12 13:14:08 +03:00
|
|
|
|
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;
|
|
|
|
|
|
}
|