1 /* 2 * yosys -- Yosys Open SYnthesis Suite 3 * 4 * Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.com> 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 */ 19 20 #include "kernel/register.h" 21 #include "kernel/log.h" 22 #include <cstdio> 23 24 #if defined(_WIN32) 25 # include <csignal> 26 # define WIFEXITED(x) 1 27 # define WIFSIGNALED(x) 0 28 # define WIFSTOPPED(x) 0 29 # define WEXITSTATUS(x) ((x) & 0xff) 30 # define WTERMSIG(x) SIGTERM 31 # define WSTOPSIG(x) 0 32 #else 33 # include <sys/wait.h> 34 #endif 35 36 USING_YOSYS_NAMESPACE 37 PRIVATE_NAMESPACE_BEGIN 38 39 struct ExecPass : public Pass { ExecPassExecPass40 ExecPass() : Pass("exec", "execute commands in the operating system shell") { } helpExecPass41 void help() override 42 { 43 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| 44 log("\n"); 45 log(" exec [options] -- [command]\n"); 46 log("\n"); 47 log("Execute a command in the operating system shell. All supplied arguments are\n"); 48 log("concatenated and passed as a command to popen(3). Whitespace is not guaranteed\n"); 49 log("to be preserved, even if quoted. stdin and stderr are not connected, while stdout is\n"); 50 log("logged unless the \"-q\" option is specified.\n"); 51 log("\n"); 52 log("\n"); 53 log(" -q\n"); 54 log(" Suppress stdout and stderr from subprocess\n"); 55 log("\n"); 56 log(" -expect-return <int>\n"); 57 log(" Generate an error if popen() does not return specified value.\n"); 58 log(" May only be specified once; the final specified value is controlling\n"); 59 log(" if specified multiple times.\n"); 60 log("\n"); 61 log(" -expect-stdout <regex>\n"); 62 log(" Generate an error if the specified regex does not match any line\n"); 63 log(" in subprocess's stdout. May be specified multiple times.\n"); 64 log("\n"); 65 log(" -not-expect-stdout <regex>\n"); 66 log(" Generate an error if the specified regex matches any line\n"); 67 log(" in subprocess's stdout. May be specified multiple times.\n"); 68 log("\n"); 69 log("\n"); 70 log(" Example: exec -q -expect-return 0 -- echo \"bananapie\" | grep \"nana\"\n"); 71 log("\n"); 72 log("\n"); 73 } executeExecPass74 void execute(std::vector<std::string> args, RTLIL::Design *design) override 75 { 76 std::string cmd = ""; 77 char buf[1024] = {}; 78 std::string linebuf = ""; 79 bool flag_cmd = false; 80 bool flag_quiet = false; 81 bool flag_expect_return = false; 82 int expect_return_value = 0; 83 bool flag_expect_stdout = false; 84 struct expect_stdout_elem { 85 bool matched; 86 bool polarity; //true: this regex must match at least one line 87 //false: this regex must not match any line 88 std::string str; 89 YS_REGEX_TYPE re; 90 91 expect_stdout_elem() : matched(false), polarity(true), str(), re(){}; 92 }; 93 std::vector<expect_stdout_elem> expect_stdout; 94 95 if(args.size() == 0) 96 log_cmd_error("No command provided.\n"); 97 98 for(size_t argidx = 1; argidx < args.size(); ++argidx) { 99 if (flag_cmd) { 100 cmd += args[argidx] + (argidx != (args.size() - 1)? " " : ""); 101 } else { 102 if (args[argidx] == "--") 103 flag_cmd = true; 104 else if (args[argidx] == "-q") 105 flag_quiet = true; 106 else if (args[argidx] == "-expect-return") { 107 flag_expect_return = true; 108 ++argidx; 109 if (argidx >= args.size()) 110 log_cmd_error("No expected return value specified.\n"); 111 112 expect_return_value = atoi(args[argidx].c_str()); 113 } else if (args[argidx] == "-expect-stdout") { 114 flag_expect_stdout = true; 115 ++argidx; 116 if (argidx >= args.size()) 117 log_cmd_error("No expected regular expression specified.\n"); 118 119 try{ 120 expect_stdout_elem x; 121 x.str = args[argidx]; 122 x.re = YS_REGEX_COMPILE(args[argidx]); 123 expect_stdout.push_back(x); 124 } catch (const YS_REGEX_NS::regex_error& e) { 125 log_cmd_error("Error in regex expression '%s' !\n", args[argidx].c_str()); 126 } 127 } else if (args[argidx] == "-not-expect-stdout") { 128 flag_expect_stdout = true; 129 ++argidx; 130 if (argidx >= args.size()) 131 log_cmd_error("No expected regular expression specified.\n"); 132 133 try{ 134 expect_stdout_elem x; 135 x.str = args[argidx]; 136 x.re = YS_REGEX_COMPILE(args[argidx]); 137 x.polarity = false; 138 expect_stdout.push_back(x); 139 } catch (const YS_REGEX_NS::regex_error& e) { 140 log_cmd_error("Error in regex expression '%s' !\n", args[argidx].c_str()); 141 } 142 143 } else 144 log_cmd_error("Unknown option \"%s\" or \"--\" doesn\'t precede command.", args[argidx].c_str()); 145 } 146 } 147 148 log_header(design, "Executing command \"%s\".\n", cmd.c_str()); 149 log_push(); 150 151 fflush(stdout); 152 bool keep_reading = true; 153 int status = 0; 154 int retval = 0; 155 156 #ifndef EMSCRIPTEN 157 FILE *f = popen(cmd.c_str(), "r"); 158 if (f == nullptr) 159 log_cmd_error("errno %d after popen() returned NULL.\n", errno); 160 while (keep_reading) { 161 keep_reading = (fgets(buf, sizeof(buf), f) != nullptr); 162 linebuf += buf; 163 memset(buf, 0, sizeof(buf)); 164 165 auto pos = linebuf.find('\n'); 166 while (pos != std::string::npos) { 167 std::string line = linebuf.substr(0, pos); 168 linebuf.erase(0, pos + 1); 169 if (!flag_quiet) 170 log("%s\n", line.c_str()); 171 172 if (flag_expect_stdout) 173 for(auto &x : expect_stdout) 174 if (YS_REGEX_NS::regex_search(line, x.re)) 175 x.matched = true; 176 177 pos = linebuf.find('\n'); 178 } 179 } 180 status = pclose(f); 181 #endif 182 183 if(WIFEXITED(status)) { 184 retval = WEXITSTATUS(status); 185 } 186 else if(WIFSIGNALED(status)) { 187 retval = WTERMSIG(status); 188 } 189 else if(WIFSTOPPED(status)) { 190 retval = WSTOPSIG(status); 191 } 192 193 if (flag_expect_return && retval != expect_return_value) 194 log_cmd_error("Return value %d did not match expected return value %d.\n", retval, expect_return_value); 195 196 if (flag_expect_stdout) 197 for (auto &x : expect_stdout) 198 if (x.polarity ^ x.matched) 199 log_cmd_error("Command stdout did%s have a line matching given regex \"%s\".\n", (x.polarity? " not" : ""), x.str.c_str()); 200 201 log_pop(); 202 } 203 } ExecPass; 204 205 PRIVATE_NAMESPACE_END 206