// file : build2/test/script/script -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #ifndef BUILD2_TEST_SCRIPT_SCRIPT #define BUILD2_TEST_SCRIPT_SCRIPT #include #include #include #include #include // replay_tokens namespace build2 { class target; namespace test { namespace script { // Pre-parse representation. // enum class line_type {variable, test}; struct line { line_type type; replay_tokens tokens; }; // Parse object model. // enum class redirect_type { none, null, here_string, // Value is the string. here_document // Value is the document. }; struct redirect { redirect_type type = redirect_type::none; string value; string here_end; // Only for here-documents. }; struct command { path program; strings arguments; redirect in; redirect out; redirect err; }; enum class command_to_stream: uint16_t { header = 0x01, here_doc = 0x02, // Note: printed on a new line. all = header | here_doc }; void to_stream (ostream&, const command&, command_to_stream); ostream& operator<< (ostream&, const command&); enum class exit_comparison {eq, ne}; struct command_exit { // C/C++ don't apply constraints on program exit code other than it // being of type int. // // POSIX specifies that only the least significant 8 bits shall be // available from wait() and waitpid(); the full value shall be // available from waitid() (read more at _Exit, _exit Open Group // spec). // // While the Linux man page for waitid() doesn't mention any // deviations from the standard, the FreeBSD implementation (as of // version 11.0) only returns 8 bits like the other wait*() calls. // // Windows supports 32-bit exit codes. // // Note that in shells some exit values can have special meaning so // using them can be a source of confusion. For bash values in the // [126, 255] range are such a special ones (see Appendix E, "Exit // Codes With Special Meanings" in the Advanced Bash-Scripting Guide). // exit_comparison comparison; uint8_t status; }; struct test: command { command_exit exit {exit_comparison::eq, 0}; }; ostream& operator<< (ostream&, const test&); class scope { public: scope* parent; // NULL for the root (script) scope. scope (scope& p): parent (&p) {} protected: scope (): parent (nullptr) {} // For the root (script) scope. // Variables. // public: // Note that if we pass the variable name as a string, then it will // be looked up in the wrong pool. // variable_map vars; // Lookup the variable starting from this scope, continuing with outer // scopes, then the target being tested, then the testscript target, // and then outer buildfile scopes (including testscript-type/pattern // specific). // lookup find (const variable&) const; // Return a value suitable for assignment. If the variable does not // exist in this scope's map, then a new one with the NULL value is // added and returned. Otherwise the existing value is returned. // value& assign (const variable& var) {return vars.assign (var);} // Return a value suitable for append/prepend. If the variable does // not exist in this scope's map, then outer scopes are searched for // the same variable. If found then a new variable with the found // value is added to this scope and returned. Otherwise this function // proceeds as assign() above. // value& append (const variable&); // Pre-parse. // public: vector lines; }; class script: public scope { public: script (target& test_target, testscript& script_target); public: target& test_target; // Target we are testing. testscript& script_target; // Target of the testscript file. public: variable_pool var_pool; const variable& test_var; // test const variable& opts_var; // test.options const variable& args_var; // test.arguments const variable& cmd_var; // $* const variable& cwd_var; // $~ }; } } } #include #endif // BUILD2_TEST_SCRIPT_SCRIPT