driver: switch to cxxopts, replace -B

This commit is contained in:
Emil J. Tywoniak 2024-10-09 15:07:56 +02:00
parent 038e262332
commit 575415ade2
3 changed files with 219 additions and 323 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "abc"]
path = abc
url = https://github.com/YosysHQ/abc
[submodule "libs/cxxopts"]
path = libs/cxxopts
url = git@github.com:jarro2783/cxxopts.git

View File

@ -19,6 +19,8 @@
#include "kernel/yosys.h"
#include "libs/sha1/sha1.h"
#include "libs/cxxopts/include/cxxopts.hpp"
#include <iostream>
#ifdef YOSYS_ENABLE_READLINE
# include <readline/readline.h>
@ -55,55 +57,6 @@
USING_YOSYS_NAMESPACE
char *optarg;
int optind = 1, optcur = 1, optopt = 0;
int getopt(int argc, char **argv, const char *optstring)
{
if (optind >= argc)
return -1;
if (argv[optind][0] != '-' || argv[optind][1] == 0) {
optopt = 1;
optarg = argv[optind++];
return optopt;
}
bool takes_arg = false;
optopt = argv[optind][optcur];
if (optopt == '-') {
++optind;
return -1;
}
for (int i = 0; optstring[i]; i++)
if (optopt == optstring[i] && optstring[i + 1] == ':')
takes_arg = true;
if (!takes_arg) {
if (argv[optind][++optcur] == 0)
optind++, optcur = 1;
return optopt;
}
if (argv[optind][++optcur]) {
optarg = argv[optind++] + optcur;
optcur = 1;
return optopt;
}
if (++optind >= argc) {
fprintf(stderr, "%s: option '-%c' expects an argument\n", argv[0], optopt);
optopt = '?';
return optopt;
}
optarg = argv[optind];
optind++, optcur = 1;
return optopt;
}
#ifdef EMSCRIPTEN
# include <sys/stat.h>
# include <sys/types.h>
@ -235,6 +188,7 @@ int main(int argc, char **argv)
std::vector<std::string> passes_commands;
std::vector<std::string> frontend_files;
std::vector<std::string> plugin_filenames;
std::vector<std::string> special_args;
std::string output_filename = "";
std::string scriptfile = "";
std::string depsfile = "";
@ -251,296 +205,227 @@ int main(int argc, char **argv)
bool mode_v = false;
bool mode_q = false;
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help")))
{
printf("\n");
printf("Usage: %s [options] [<infile> [..]]\n", argv[0]);
printf("\n");
printf(" -Q\n");
printf(" suppress printing of banner (copyright, disclaimer, version)\n");
printf("\n");
printf(" -T\n");
printf(" suppress printing of footer (log hash, version, timing statistics)\n");
printf("\n");
printf(" -q\n");
printf(" quiet operation. only write warnings and error messages to console\n");
printf(" use this option twice to also quiet warning messages\n");
printf("\n");
printf(" -v <level>\n");
printf(" print log headers up to level <level> to the console. (this\n");
printf(" implies -q for everything except the 'End of script.' message.)\n");
printf("\n");
printf(" -t\n");
printf(" annotate all log messages with a time stamp\n");
printf("\n");
printf(" -d\n");
printf(" print more detailed timing stats at exit\n");
printf("\n");
printf(" -l logfile\n");
printf(" write log messages to the specified file\n");
printf("\n");
printf(" -L logfile\n");
printf(" like -l but open log file in line buffered mode\n");
printf("\n");
printf(" -o outfile\n");
printf(" write the design to the specified file on exit\n");
printf("\n");
printf(" -b backend\n");
printf(" use this backend for the output file specified on the command line\n");
printf("\n");
printf(" -f frontend\n");
printf(" use the specified frontend for the input files on the command line\n");
printf("\n");
printf(" -H\n");
printf(" print the command list\n");
printf("\n");
printf(" -h command\n");
printf(" print the help message for the specified command\n");
printf("\n");
printf(" -s scriptfile\n");
printf(" execute the commands in the script file\n");
cxxopts::Options options(argv[0], "Yosys Open SYnthesis Suite");
options.set_width(SIZE_MAX);
options.add_options("operation")
("b,backend", "use <backend> for the output file specified on the command line",
cxxopts::value<std::string>(), "<backend>")
("f,frontend", "use <frontend> for the input files on the command line",
cxxopts::value<std::string>(), "<frontend>")
("s,scriptfile", "execute the commands in <scriptfile>",
cxxopts::value<std::string>(), "<scriptfile>")
#ifdef YOSYS_ENABLE_TCL
printf("\n");
printf(" -c tcl_scriptfile\n");
printf(" execute the commands in the tcl script file (see 'help tcl' for details)\n");
printf("\n");
printf(" -C\n");
printf(" enters TCL interactive shell mode\n");
#endif
("c,tcl-scriptfile", "execute the commands in the TCL <tcl_scriptfile> (see 'help tcl' for details)",
cxxopts::value<std::string>(),"<tcl_scriptfile>")
("C,tcl-interactive", "enters TCL interactive shell mode")
#endif // YOSYS_ENABLE_TCL
#ifdef WITH_PYTHON
printf("\n");
printf(" -y python_scriptfile\n");
printf(" execute a python script with libyosys available as a built-in module\n");
#endif
printf("\n");
printf(" -p command\n");
printf(" execute the commands (to chain commands, separate them with semicolon + whitespace: 'cmd1; cmd2')\n");
printf("\n");
printf(" -m module_file\n");
printf(" load the specified module (aka plugin)\n");
printf("\n");
printf(" -X\n");
printf(" enable tracing of core data structure changes. for debugging\n");
printf("\n");
printf(" -M\n");
printf(" will slightly randomize allocated pointer addresses. for debugging\n");
printf("\n");
printf(" -A\n");
printf(" will call abort() at the end of the script. for debugging\n");
printf("\n");
printf(" -r <module_name>\n");
printf(" elaborate command line arguments using the specified top module\n");
printf("\n");
printf(" -D <macro>[=<value>]\n");
printf(" set the specified Verilog define (via \"read -define\")\n");
printf("\n");
printf(" -P <header_id>[:<filename>]\n");
printf(" dump the design when printing the specified log header to a file.\n");
printf(" yosys_dump_<header_id>.il is used as filename if none is specified.\n");
printf(" Use 'ALL' as <header_id> to dump at every header.\n");
printf("\n");
printf(" -W regex\n");
printf(" print a warning for all log messages matching the regex.\n");
printf("\n");
printf(" -w regex\n");
printf(" if a warning message matches the regex, it is printed as regular\n");
printf(" message instead.\n");
printf("\n");
printf(" -e regex\n");
printf(" if a warning message matches the regex, it is printed as error\n");
printf(" message instead and the tool terminates with a nonzero return code.\n");
printf("\n");
printf(" -E <depsfile>\n");
printf(" write a Makefile dependencies file with in- and output file names\n");
printf("\n");
printf(" -x <feature>\n");
printf(" do not print warnings for the specified experimental feature\n");
printf("\n");
printf(" -g\n");
printf(" globally enable debug log messages\n");
printf("\n");
printf(" -V\n");
printf(" print version information and exit\n");
printf("\n");
printf("The option -S is a shortcut for calling the \"synth\" command, a default\n");
printf("script for transforming the Verilog input to a gate-level netlist. For example:\n");
printf("\n");
printf(" yosys -o output.blif -S input.v\n");
printf("\n");
printf("For more complex synthesis jobs it is recommended to use the read_* and write_*\n");
printf("commands in a script file instead of specifying input and output files on the\n");
printf("command line.\n");
printf("\n");
printf("When no commands, script files or input files are specified on the command\n");
printf("line, yosys automatically enters the interactive command mode. Use the 'help'\n");
printf("command to get information on the individual commands.\n");
printf("\n");
("y,py-scriptfile", "execute the Python <script>",
cxxopts::value<std::vector<std::string>>(), "<script>")
#endif // WITH_PYTHON
("p,commands", "execute <commands> (to chain commands, separate them with semicolon + whitespace: 'cmd1; cmd2')",
cxxopts::value<std::vector<std::string>>(), "<commands>")
("r,top", "elaborate the specified HDL <top> module",
cxxopts::value<std::string>(), "<top>")
("m,plugin", "load the specified <plugin> module",
cxxopts::value<std::vector<std::string>>(), "<plugin>")
("D,define", "set the specified Verilog define to <value> if supplied via command \"read -define\"",
cxxopts::value<std::vector<std::string>>(), "<define>[=<value>]")
("S,synth", "shortcut for calling the \"synth\" command, a default script for transforming " \
"the Verilog input to a gate-level netlist. For example: " \
"yosys -o output.blif -S input.v " \
"For more complex synthesis jobs it is recommended to use the read_* and write_* " \
"commands in a script file instead of specifying input and output files on the " \
"command line.")
("H", "print the command list")
("h,help", "print this help message. If given, print help for <command>.",
cxxopts::value<std::string>(), "[<command>]")
("V,version", "print version information and exit")
("infile", "input files", cxxopts::value<std::vector<std::string>>())
;
options.add_options("logging")
("Q", "suppress printing of banner (copyright, disclaimer, version)")
("T", "suppress printing of footer (log hash, version, timing statistics)")
("q,quiet", "quiet operation. Only write warnings and error messages to console. " \
"Use this option twice to also quiet warning messages")
("v,verbose", "print log headers up to <level> to the console. " \
"Implies -q for everything except the 'End of script.' message.",
cxxopts::value<int>(), "<level>")
("t,timestamp", "annotate all log messages with a time stamp")
("d,detailed-timing", "print more detailed timing stats at exit")
("l,logfile", "write log messages to <logfile>",
cxxopts::value<std::vector<std::string>>(), "<logfile>")
("L,line-buffered-logfile", "like -l but open <logfile> in line buffered mode",
cxxopts::value<std::vector<std::string>>(), "<logfile>")
("o,outfile", "write the design to <outfile> on exit",
cxxopts::value<std::string>(), "<outfile>")
("P,dump-design", "dump the design when printing the specified log header to a file. " \
"yosys_dump_<header_id>.il is used as filename if none is specified. " \
"Use 'ALL' as <header_id> to dump at every header.",
cxxopts::value<std::vector<std::string>>(), "<header_id>[:<filename>]")
("W,warning-as-warning", "print a warning for all log messages matching <regex>",
cxxopts::value<std::vector<std::string>>(), "<regex>")
("w,warning-as-message", "if a warning message matches <regex>, it is printed as regular message instead",
cxxopts::value<std::vector<std::string>>(), "<regex>")
("e,warning-as-error", "if a warning message matches <regex>, it is printed as error message instead",
cxxopts::value<std::vector<std::string>>(), "<regex>")
("E,deps-file", "write a Makefile dependencies file <depsfile> with input and output file names",
cxxopts::value<std::string>(), "<depsfile>")
;
options.add_options("developer")
("X,trace", "enable tracing of core data structure changes. for debugging")
("M,randomize-pointers", "will slightly randomize allocated pointer addresses. for debugging")
("A,abort", "will call abort() at the end of the script. for debugging")
("x,experimental", "do not print warnings for the experimental <feature>",
cxxopts::value<std::vector<std::string>>(), "<feature>")
("g,debug", "globally enable debug log messages")
("perffile", "write a JSON performance log to <perffile>", cxxopts::value<std::string>(), "<perffile>")
;
options.parse_positional({"infile"});
options.positional_help("[<infile> [..]]");
// We can't have -h optionally require an argument
// cxxopts does have an implit argument concept but that doesn't work for us
// cxxopts is therefore instructed allowed to only handle the command help case
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help"))) {
std::cout << options.help() << std::endl;
exit(0);
}
if (argc == 2 && (!strcmp(argv[1], "-V") || !strcmp(argv[1], "-version") || !strcmp(argv[1], "--version")))
{
printf("%s\n", yosys_version_str);
exit(0);
try {
// Check for "--" in arguments
auto it = std::find(argv, argv + argc, std::string("--"));
if (it != argv + argc) {
special_args.assign(it + 1, argv + argc);
// Remove these arguments from cxxopts parsing
argc = std::distance(argv, it);
}
int opt;
while ((opt = getopt(argc, argv, "MXAQTVCSgm:f:Hh:b:o:p:l:L:qv:tds:c:y:W:w:e:r:D:P:E:x:B:")) != -1)
{
switch (opt)
{
case 'M':
memhasher_on();
break;
case 'X':
yosys_xtrace++;
break;
case 'A':
call_abort = true;
break;
case 'Q':
print_banner = false;
break;
case 'T':
print_stats = false;
break;
case 'V':
printf("%s\n", yosys_version_str);
auto result = options.parse(argc, argv);
if (result.count("M")) memhasher_on();
if (result.count("X")) yosys_xtrace++;
if (result.count("A")) call_abort = true;
if (result.count("Q")) print_banner = false;
if (result.count("T")) print_stats = false;
if (result.count("V")) {
std::cout << yosys_version_str << std::endl;
exit(0);
case 'S':
}
if (result.count("S")) {
passes_commands.push_back("synth");
run_shell = false;
break;
case 'g':
log_force_debug++;
break;
case 'm':
plugin_filenames.push_back(optarg);
break;
case 'f':
frontend_command = optarg;
break;
case 'H':
}
if (result.count("C")) run_tcl_shell = true;
if (result.count("g")) log_force_debug++;
if (result.count("m")) plugin_filenames = result["m"].as<std::vector<std::string>>();
if (result.count("f")) frontend_command = result["f"].as<std::string>();
if (result.count("H")) {
passes_commands.push_back("help");
run_shell = false;
break;
case 'h':
passes_commands.push_back(stringf("help %s", optarg));
}
if (result.count("h")) {
std::string res = result["h"].as<std::string>();
passes_commands.push_back("help " + res);
run_shell = false;
break;
case 'b':
backend_command = optarg;
}
if (result.count("b")) {
backend_command = result["b"].as<std::string>();
run_shell = false;
break;
case 'p':
passes_commands.push_back(optarg);
}
if (result.count("p")) {
auto cmds = result["p"].as<std::vector<std::string>>();
passes_commands.insert(passes_commands.end(), cmds.begin(), cmds.end());
run_shell = false;
break;
case 'o':
output_filename = optarg;
}
if (result.count("o")) {
output_filename = result["o"].as<std::string>();
run_shell = false;
break;
case 'l':
case 'L':
log_files.push_back(fopen(optarg, "wt"));
if (log_files.back() == NULL) {
fprintf(stderr, "Can't open log file `%s' for writing!\n", optarg);
}
for (const auto& key : {"l", "L"}) {
if (result.count(key)) {
for (const auto& filename : result[key].as<std::vector<std::string>>()) {
if (FILE* f = fopen(filename.c_str(), "wt")) {
log_files.push_back(f);
if (key[0] == 'L') setvbuf(f, NULL, _IOLBF, 0);
} else {
std::cerr << "Can't open log file `" << filename << "' for writing!\n";
exit(1);
}
if (opt == 'L')
setvbuf(log_files.back(), NULL, _IOLBF, 0);
break;
case 'q':
}
}
}
if (result.count("q")) {
mode_q = true;
if (log_errfile == stderr)
log_quiet_warnings = true;
if (log_errfile == stderr) log_quiet_warnings = true;
log_errfile = stderr;
break;
case 'v':
}
if (result.count("v")) {
mode_v = true;
log_errfile = stderr;
log_verbose_level = atoi(optarg);
break;
case 't':
log_time = true;
break;
case 'd':
timing_details = true;
break;
case 's':
scriptfile = optarg;
scriptfile_tcl = false;
scriptfile_python = false;
log_verbose_level = result["v"].as<int>();
}
if (result.count("t")) log_time = true;
if (result.count("d")) timing_details = true;
for (const auto& key : {"s", "c"}) {
if (result.count(key)) {
scriptfile = result[key].as<std::string>();
scriptfile_tcl = std::string(key) == "c";
run_shell = false;
break;
case 'c':
scriptfile = optarg;
scriptfile_tcl = true;
scriptfile_python = false;
run_shell = false;
break;
case 'y':
scriptfile = optarg;
scriptfile_tcl = false;
}
}
if (result.count("y")) {
scriptfile = result["y"].as<std::string>();
scriptfile_python = true;
run_shell = false;
break;
case 'W':
log_warn_regexes.push_back(YS_REGEX_COMPILE(optarg));
break;
case 'w':
log_nowarn_regexes.push_back(YS_REGEX_COMPILE(optarg));
break;
case 'e':
log_werror_regexes.push_back(YS_REGEX_COMPILE(optarg));
break;
case 'r':
topmodule = optarg;
break;
case 'D':
vlog_defines.push_back(optarg);
break;
case 'P':
{
auto args = split_tokens(optarg, ":");
if (!args.empty() && args[0] == "ALL") {
if (GetSize(args) != 1) {
fprintf(stderr, "Invalid number of tokens in -D ALL.\n");
}
for (const auto& key : {"W", "w", "e"}) {
if (result.count(key)) {
auto regexes = result[key].as<std::vector<std::string>>();
for (const auto& regex : regexes) {
if (std::string(key) == "W")
log_warn_regexes.push_back(std::regex(regex));
if (std::string(key) == "w")
log_nowarn_regexes.push_back(std::regex(regex));
if (std::string(key) == "e")
log_werror_regexes.push_back(std::regex(regex));
}
}
}
if (result.count("r")) topmodule = result["r"].as<std::string>();
if (result.count("D")) vlog_defines = result["D"].as<std::vector<std::string>>();
if (result.count("P")) {
auto dump_args = result["P"].as<std::vector<std::string>>();
for (const auto& arg : dump_args) {
auto tokens = split_tokens(arg, ":");
if (!tokens.empty() && tokens[0] == "ALL") {
if (tokens.size() != 1) {
std::cerr << "Invalid number of tokens in -P ALL." << std::endl;
exit(1);
}
log_hdump_all = true;
} else {
if (!args.empty() && !args[0].empty() && args[0].back() == '.')
args[0].pop_back();
if (GetSize(args) == 1)
args.push_back("yosys_dump_" + args[0] + ".il");
if (GetSize(args) != 2) {
fprintf(stderr, "Invalid number of tokens in -D.\n");
if (!tokens.empty() && !tokens[0].empty() && tokens[0].back() == '.')
tokens[0].pop_back();
if (tokens.size() == 1)
tokens.push_back("yosys_dump_" + tokens[0] + ".il");
if (tokens.size() != 2) {
std::cerr << "Invalid number of tokens in -P." << std::endl;
exit(1);
}
log_hdump[args[0]].insert(args[1]);
log_hdump[tokens[0]].insert(tokens[1]);
}
}
break;
case 'E':
depsfile = optarg;
break;
case 'x':
log_experimentals_ignored.insert(optarg);
break;
case 'B':
perffile = optarg;
break;
case 'C':
run_tcl_shell = true;
break;
case '\001':
frontend_files.push_back(optarg);
break;
default:
fprintf(stderr, "Run '%s -h' for help.\n", argv[0]);
exit(1);
}
if (result.count("E")) depsfile = result["E"].as<std::string>();
if (result.count("x")) {
auto ignores = result["x"].as<std::vector<std::string>>();
log_experimentals_ignored.insert(ignores.begin(), ignores.end());
}
if (result.count("perffile")) perffile = result["perffile"].as<std::string>();
if (result.count("infile")) {
frontend_files = result["infile"].as<std::vector<std::string>>();
}
if (log_errfile == NULL) {
@ -551,6 +436,13 @@ int main(int argc, char **argv)
if (print_banner)
yosys_banner();
}
catch (const cxxopts::exceptions::parsing& e) {
std::cerr << "Error parsing options: " << e.what() << std::endl;
std::cerr << "Run '" << argv[0] << " --help' for help." << std::endl;
exit(1);
}
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
std::string state_dir;
#if defined(_WIN32)
@ -622,10 +514,10 @@ int main(int argc, char **argv)
}
if (scriptfile.empty() || (!scriptfile_tcl && !scriptfile_python)) {
// Without a TCL or Python script, arguments following '--' are also
// treated as frontend files
for (int i = optind; i < argc; ++i)
frontend_files.push_back(argv[i]);
// Without a TCL or Python script, arguments following '--'
// are also treated as frontend files
for (auto special_arg : special_args)
frontend_files.push_back(special_arg);
}
for (auto it = frontend_files.begin(); it != frontend_files.end(); ++it) {

1
libs/cxxopts Submodule

@ -0,0 +1 @@
Subproject commit 4bf61f08697b110d9e3991864650a405b3dd515d