feat(relinker): support explicit CLI help

This commit is contained in:
krishalivaibhav 2026-10-09 14:35:08 +05:30
parent bc40a6b5a0
commit 1fc31ba358
5 changed files with 65 additions and 6 deletions

View File

@ -198,6 +198,8 @@ if(BUILD_TESTING)
find_package(Python3 QUIET COMPONENTS Interpreter)
if(Python3_Interpreter_FOUND)
add_test(NAME cli_help COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/cli/tests/test_help.py $<TARGET_FILE:relinker>)
add_test(NAME elf_offsets COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_elf_offsets.py $<TARGET_FILE:relinker>)
add_test(NAME dynamic_segment COMMAND ${Python3_EXECUTABLE}

View File

@ -8,6 +8,7 @@
namespace Cli {
struct Args {
bool showHelp = false;
bool skipSyscallCheck = false;
bool skipSceModule = false;
bool toIntel = false;
@ -27,6 +28,8 @@ struct Args {
Args ParseArgs(int argc, char* argv[]);
const char* Usage();
int Autorun(const std::string& absPath, bool toWindows);
}

View File

@ -5,13 +5,20 @@
namespace Cli {
const char* Usage() {
return "Usage: relinker [--help] [--windows | --macos] [--windows-diagnostics] [--windows-gui] [--skip-syscall-check] [--skip-sce-module] [--exclude-sce-module <file>]... [--to-intel] [unused-filter=0|1|2] [--registry] [--rpath <path>] [--lazy-binding] [--autorun] <input.elf> <output.elf>\n"
"Example: relinker input.elf output.elf";
}
Args ParseArgs(int argc, char* argv[]) {
Args args;
bool unusedFilterSpecified = false;
for (int i = 1; i < argc; ++i) {
const std::string arg = argv[i];
if (arg == "--skip-syscall-check") {
if (arg == "--help") {
args.showHelp = true;
} else if (arg == "--skip-syscall-check") {
args.skipSyscallCheck = true;
} else if (arg == "--skip-sce-module") {
args.skipSceModule = true;
@ -67,11 +74,9 @@ Args ParseArgs(int argc, char* argv[]) {
if (args.windowsGui && !args.toWindows)
throw std::runtime_error("--windows-gui requires --windows");
if (args.inputPath.empty() || args.outputPath.empty())
throw std::runtime_error(
"Usage: relinker [--windows | --macos] [--windows-diagnostics] [--windows-gui] [--skip-syscall-check] [--skip-sce-module] [--exclude-sce-module <file>]... [--to-intel] [unused-filter=0|1|2] [--registry] [--rpath <path>] [--lazy-binding] [--autorun] <input.elf> <output.elf>\n"
"Example: relinker input.elf output.elf"
);
if (!args.showHelp && (args.inputPath.empty() || args.outputPath.empty()))
throw std::runtime_error(Usage());
return args;
}

View File

@ -0,0 +1,44 @@
from pathlib import Path
import subprocess
import sys
import tempfile
def main():
relinker = Path(sys.argv[1]).resolve()
with tempfile.TemporaryDirectory(prefix="anyps5-help-") as directory:
work = Path(directory)
source = work / "missing.elf"
output = work / "existing.exe"
output.write_bytes(b"preserve this output")
def run(*args):
return subprocess.run([str(relinker), *args], cwd=work,
capture_output=True, text=True, timeout=10)
for args in [("--help",), ("--help", "--registry", "--autorun", str(source), str(output)),
(str(source), str(output), "--help")]:
result = run(*args)
assert result.returncode == 0, result.stderr
assert result.stdout.startswith("Usage: relinker "), result.stdout
assert "--help" in result.stdout and "Example:" in result.stdout
assert result.stderr == "", result.stderr
assert output.read_bytes() == b"preserve this output"
assert sorted(path.name for path in work.iterdir()) == [output.name]
result = run()
assert result.returncode == 1 and "Usage: relinker" in result.stderr
for args in [("--help", "--unknown"), ("--unknown", "--help")]:
result = run(*args)
assert result.returncode == 1 and "unknown option" in result.stderr
result = run("--help", "--windows-gui")
assert result.returncode == 1 and "requires --windows" in result.stderr
for flag in ["--rpath", "--exclude-sce-module"]:
result = run(flag, "--help", str(source), str(output))
assert result.returncode == 2 and "Cannot open file" in result.stderr
assert result.stdout == "", result.stdout
assert output.read_bytes() == b"preserve this output"
if __name__ == "__main__":
main()

View File

@ -37,6 +37,11 @@ int main(const int argc, char* argv[]) {
return 1;
}
if (args.showHelp) {
std::cout << Cli::Usage() << '\n';
return 0;
}
try {
auto extension = std::filesystem::path(args.outputPath).extension().string();
for (auto& character : extension) if (character >= 'A' && character <= 'Z') character = static_cast<char>(character + ('a' - 'A'));