aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/script/script.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/script/script.hxx')
-rw-r--r--libbuild2/script/script.hxx471
1 files changed, 471 insertions, 0 deletions
diff --git a/libbuild2/script/script.hxx b/libbuild2/script/script.hxx
new file mode 100644
index 0000000..f4998b7
--- /dev/null
+++ b/libbuild2/script/script.hxx
@@ -0,0 +1,471 @@
+// file : libbuild2/script/script.hxx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBUILD2_SCRIPT_SCRIPT_HXX
+#define LIBBUILD2_SCRIPT_SCRIPT_HXX
+
+#include <libbuild2/types.hxx>
+#include <libbuild2/forward.hxx>
+#include <libbuild2/utility.hxx>
+
+#include <libbuild2/token.hxx>
+#include <libbuild2/variable.hxx>
+
+namespace build2
+{
+ namespace script
+ {
+ // Pre-parsed representation.
+ //
+
+ enum class line_type
+ {
+ var,
+ cmd,
+ cmd_if,
+ cmd_ifn,
+ cmd_elif,
+ cmd_elifn,
+ cmd_else,
+ cmd_end
+ };
+
+ ostream&
+ operator<< (ostream&, line_type);
+
+ struct line
+ {
+ line_type type;
+ replay_tokens tokens;
+
+ union
+ {
+ const variable* var; // Pre-entered for line_type::var.
+ };
+ };
+
+ // Most of the time we will have just one line (a command).
+ //
+ using lines = small_vector<line, 1>;
+
+ // Print the script lines, trying to reproduce their original (non-
+ // expanded) representation.
+ //
+ // Note that the exact spacing and partial quoting may not be restored due
+ // to the information loss.
+ //
+ void
+ dump (ostream&, const string& ind, const lines&);
+
+ // Parse object model.
+ //
+
+ // redirect
+ //
+ enum class redirect_type
+ {
+ // No data is allowed to be read or written.
+ //
+ // Note that redirect of this type cannot be currently specified on the
+ // script command line and can only be set via the environment object
+ // as a default redirect (see below).
+ //
+ none,
+
+ pass,
+ null,
+ trace,
+ merge,
+ here_str_literal,
+ here_str_regex,
+ here_doc_literal,
+ here_doc_regex,
+ here_doc_ref, // Reference to here_doc literal or regex.
+ file,
+ };
+
+ // Pre-parsed (but not instantiated) regex lines. The idea here is that
+ // we should be able to re-create their (more or less) exact text
+ // representation for diagnostics but also instantiate without any
+ // re-parsing.
+ //
+ struct regex_line
+ {
+ // If regex is true, then value is the regex expression. Otherwise, it
+ // is a literal. Note that special characters can be present in both
+ // cases. For example, //+ is a regex, while /+ is a literal, both
+ // with '+' as a special character. Flags are only valid for regex.
+ // Literals falls apart into textual (has no special characters) and
+ // special (has just special characters instead) ones. For example
+ // foo is a textual literal, while /.+ is a special one. Note that
+ // literal must not have value and special both non-empty.
+ //
+ bool regex;
+
+ string value;
+ string flags;
+ string special;
+
+ uint64_t line;
+ uint64_t column;
+
+ // Create regex with optional special characters.
+ //
+ regex_line (uint64_t l, uint64_t c,
+ string v, string f, string s = string ())
+ : regex (true),
+ value (move (v)),
+ flags (move (f)),
+ special (move (s)),
+ line (l),
+ column (c) {}
+
+ // Create a literal, either text or special.
+ //
+ regex_line (uint64_t l, uint64_t c, string v, bool s)
+ : regex (false),
+ value (s ? string () : move (v)),
+ special (s ? move (v) : string ()),
+ line (l),
+ column (c) {}
+ };
+
+ struct regex_lines
+ {
+ char intro; // Introducer character.
+ string flags; // Global flags (here-document).
+
+ small_vector<regex_line, 8> lines;
+ };
+
+ // Output file redirect mode.
+ //
+ enum class redirect_fmode
+ {
+ compare,
+ overwrite,
+ append
+ };
+
+ struct redirect
+ {
+ redirect_type type;
+
+ struct file_type
+ {
+ using path_type = build2::path;
+ path_type path;
+ redirect_fmode mode; // Meaningless for input redirect.
+ };
+
+ union
+ {
+ int fd; // Merge-to descriptor.
+ string str; // Note: with trailing newline, if requested.
+ regex_lines regex; // Note: with trailing blank, if requested.
+ file_type file;
+ reference_wrapper<const redirect> ref; // Note: no chains.
+ };
+
+ // Modifiers and the original representation (potentially an alias).
+ //
+ build2::token token;
+
+ string end; // Here-document end marker (no regex intro/flags).
+ uint64_t end_line; // Here-document end marker location.
+ uint64_t end_column;
+
+ // Create redirect of a type other than reference.
+ //
+ explicit
+ redirect (redirect_type);
+
+ // Create redirect of the reference type.
+ //
+ redirect (redirect_type t, const redirect& r, build2::token tk)
+ : type (redirect_type::here_doc_ref),
+ ref (r),
+ token (move (tk))
+ {
+ // There is no support (and need) for reference chains.
+ //
+ assert (t == redirect_type::here_doc_ref &&
+ r.type != redirect_type::here_doc_ref);
+ }
+
+ // Create redirect of the merge type.
+ //
+ // Note that it's the caller's responsibility to make sure that the file
+ // descriptor is valid for this redirect (2 for stdout, etc).
+ //
+ redirect (redirect_type t, int f)
+ : type (redirect_type::merge), fd (f)
+ {
+ assert (t == redirect_type::merge && (f == 1 || f == 2));
+ }
+
+ redirect (redirect&&) noexcept;
+ redirect& operator= (redirect&&) noexcept;
+
+ // @@ Defining optional movable-only redirects in the command class make
+ // the older C++ compilers (GCC 4.9, Clang 4, VC 15) fail to compile the
+ // command vector manipulating code. Thus, we make the redirect class
+ // copyable to workaround the issue.
+ //
+ redirect (const redirect&);
+ redirect& operator= (const redirect&);
+
+ ~redirect ();
+
+ const redirect&
+ effective () const noexcept
+ {
+ return type == redirect_type::here_doc_ref ? ref.get () : *this;
+ }
+
+ const string&
+ modifiers () const noexcept
+ {
+ return token.value;
+ }
+ };
+
+ // cleanup
+ //
+ enum class cleanup_type
+ {
+ always, // &foo - cleanup, fail if does not exist.
+ maybe, // &?foo - cleanup, ignore if does not exist.
+ never // &!foo - don’t cleanup, ignore if doesn’t exist.
+ };
+
+ // File or directory to be automatically cleaned up at the end of the
+ // script execution. If the path ends with a trailing slash, then it is
+ // assumed to be a directory, otherwise -- a file. A directory that is
+ // about to be cleaned up must be empty.
+ //
+ // The last component in the path may contain a wildcard that have the
+ // following semantics:
+ //
+ // dir/* - remove all immediate files
+ // dir/*/ - remove all immediate sub-directories (must be empty)
+ // dir/** - remove all files recursively
+ // dir/**/ - remove all sub-directories recursively (must be empty)
+ // dir/*** - remove directory dir with all files and sub-directories
+ // recursively
+ //
+ struct cleanup
+ {
+ cleanup_type type;
+ build2::path path;
+ };
+ using cleanups = vector<cleanup>;
+
+ // command_exit
+ //
+ 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 code;
+ };
+
+ // command
+ //
+ struct command
+ {
+ path program;
+ strings arguments;
+
+ optional<redirect> in;
+ optional<redirect> out;
+ optional<redirect> err;
+
+ script::cleanups cleanups;
+
+ command_exit exit {exit_comparison::eq, 0};
+ };
+
+ 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&);
+
+ // command_pipe
+ //
+ using command_pipe = vector<command>;
+
+ void
+ to_stream (ostream&, const command_pipe&, command_to_stream);
+
+ ostream&
+ operator<< (ostream&, const command_pipe&);
+
+ // command_expr
+ //
+ enum class expr_operator {log_or, log_and};
+
+ struct expr_term
+ {
+ expr_operator op; // OR-ed to an implied false for the first term.
+ command_pipe pipe;
+ };
+
+ using command_expr = vector<expr_term>;
+
+ void
+ to_stream (ostream&, const command_expr&, command_to_stream);
+
+ ostream&
+ operator<< (ostream&, const command_expr&);
+
+ // Script execution environment.
+ //
+ class environment
+ {
+ public:
+ build2::context& context;
+
+ // The platform script programs run on.
+ //
+ const target_triplet& host;
+
+ // The work directory is used as the builtin/process CWD and to complete
+ // relative paths. Any attempt to remove or move this directory (or its
+ // parent directory) using the rm or mv builtins will fail. Must be an
+ // absolute path.
+ //
+ const dir_name_view work_dir;
+
+ // If the sanbox directory is not NULL, then any attempt to remove or
+ // move a filesystem entry outside this directory using an explicit
+ // cleanup or the rm/mv builtins will fail, unless the --force option is
+ // specified for the builtin. Must be an absolute path.
+ //
+ const dir_name_view sandbox_dir;
+
+ // The temporary directory is used by the script running machinery to
+ // create special files. Must be an absolute path, unless empty. Can be
+ // empty until the create_temp_dir() function call, which can be used
+ // for creating this directory on demand.
+ //
+ const dir_path& temp_dir;
+
+ // If true, the temporary directory will not be removed on the script
+ // failure. In particular, this allows the script running machinery to
+ // refer to the special files in diagnostics.
+ //
+ const bool temp_dir_keep;
+
+ // Default process streams redirects.
+ //
+ // If a stream redirect is not specified on the script command line,
+ // then the respective redirect data member will be used as the default.
+ //
+ const redirect in;
+ const redirect out;
+ const redirect err;
+
+ environment (build2::context& ctx,
+ const target_triplet& h,
+ const dir_name_view& wd,
+ const dir_name_view& sd,
+ const dir_path& td, bool tk,
+ redirect&& i = redirect (redirect_type::pass),
+ redirect&& o = redirect (redirect_type::pass),
+ redirect&& e = redirect (redirect_type::pass))
+ : context (ctx), host (h),
+ work_dir (wd), sandbox_dir (sd), temp_dir (td), temp_dir_keep (tk),
+ in (move (i)), out (move (o)), err (move (e))
+ {
+ }
+
+ // Create environment without the sandbox.
+ //
+ environment (build2::context& ctx,
+ const target_triplet& h,
+ const dir_name_view& wd,
+ const dir_path& td, bool tk,
+ redirect&& i = redirect (redirect_type::pass),
+ redirect&& o = redirect (redirect_type::pass),
+ redirect&& e = redirect (redirect_type::pass))
+ : environment (ctx, h,
+ wd, dir_name_view (), td, tk,
+ move (i), move (o), move (e))
+ {
+ }
+
+ // Cleanup.
+ //
+ public:
+ script::cleanups cleanups;
+ paths special_cleanups;
+
+ // Register a cleanup. If the cleanup is explicit, then override the
+ // cleanup type if this path is already registered. Ignore implicit
+ // registration of a path outside root directory (see below).
+ //
+ void
+ clean (cleanup, bool implicit);
+
+ // Register cleanup of a special file. Such files are created to
+ // maintain the script running machinery and must be removed first, not
+ // to interfere with the user-defined wildcard cleanups if the working
+ // and temporary directories are the same.
+ //
+ void
+ clean_special (path);
+
+ public:
+ // Set variable value with optional (non-empty) attributes.
+ //
+ virtual void
+ set_variable (string&& name,
+ names&&,
+ const string& attrs,
+ const location&) = 0;
+
+ // Create the temporary directory and set the temp_dir reference target
+ // to its path. Must only be called if temp_dir is empty.
+ //
+ virtual void
+ create_temp_dir () = 0;
+
+ public:
+ virtual
+ ~environment () = default;
+ };
+ }
+}
+
+#include <libbuild2/script/script.ixx>
+
+#endif // LIBBUILD2_SCRIPT_SCRIPT_HXX