From df1ef68cd8e8582724ce1192bfc202e0b9aeaf0c Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Tue, 28 Sep 2021 19:24:31 +0300 Subject: Get rid of C++ modules related code and rename *.mxx files to *.hxx --- libbutl/builtin.hxx | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 libbutl/builtin.hxx (limited to 'libbutl/builtin.hxx') diff --git a/libbutl/builtin.hxx b/libbutl/builtin.hxx new file mode 100644 index 0000000..2398c84 --- /dev/null +++ b/libbutl/builtin.hxx @@ -0,0 +1,220 @@ +// file : libbutl/builtin.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#pragma once + +#include +#include +#include +#include +#include +#include +#include // unique_ptr +#include // size_t +#include // move() +#include // uint8_t +#include +#include + +#include +#include +#include + +#include + +namespace butl +{ + // A process/thread-like object representing a running builtin. + // + // For now, instead of allocating the result storage dynamically, we expect + // it to be provided by the caller (allocating it dynamically would be + // wasteful for synchronous builtins). + // + class LIBBUTL_SYMEXPORT builtin + { + public: + // Wait for the builtin to complete and return its exit code. This + // function can be called multiple times. + // + std::uint8_t + wait (); + + // Return the same result as wait() if the builtin has already completed + // and nullopt otherwise. + // + optional + try_wait (); + + // Wait for the builtin to complete for up to the specified time duration. + // Return the same result as wait() if the builtin has completed in this + // timeframe and nullopt otherwise. + // + template + optional + timed_wait (const std::chrono::duration&); + + ~builtin () {if (state_ != nullptr) state_->thread.join ();} + + public: + struct async_state + { + bool finished = false; + std::mutex mutex; + std::condition_variable condv; + std::thread thread; + + // Note that we can't use std::function as an argument type to get rid + // of the template since std::function can only be instantiated with a + // copy-constructible function and that's too restrictive for us (won't + // be able to capture auto_fd by value in a lambda, etc). + // + template + explicit + async_state (F); + }; + + builtin (std::uint8_t& r, std::unique_ptr&& s = nullptr) + : result_ (r), state_ (move (s)) {} + + builtin (builtin&&) = default; + + private: + std::uint8_t& result_; + std::unique_ptr state_; + }; + + // Builtin execution callbacks that can be used for checking/handling the + // filesystem entries being acted upon (enforcing that they are sub-entries + // of some "working" directory, registering cleanups for new entries, etc) + // and for providing custom implementations for some functions used by + // builtins. + // + // Note that the filesystem paths passed to the callbacks are absolute and + // normalized with directories distinguished from non-directories based on + // the lexical representation (presence of the trailing directory separator; + // use path::to_directory() to check). + // + // Also note that builtins catch any exceptions that may be thrown by the + // callbacks and, if that's the case, issue diagnostics and exit with the + // non-zero status. + // + struct builtin_callbacks + { + // If specified, called before (pre is true) and after (pre is false) a + // new filesystem entry is created or an existing one is re-created or + // updated. + // + using create_hook = void (const path&, bool pre); + + std::function create; + + // If specified, called before (pre is true) and after (pre is false) a + // filesystem entry is moved. The force argument is true if the builtin is + // executed with the --force option. + // + using move_hook = void (const path& from, + const path& to, + bool force, + bool pre); + + std::function move; + + // If specified, called before (pre is true) and after (pre is false) a + // filesystem entry is removed. The force argument is true if the builtin + // is executed with the --force option. + // + using remove_hook = void (const path&, bool force, bool pre); + + std::function remove; + + // If specified, called on encountering an unknown option passing the + // argument list and the position of the option in question. Return the + // number of parsed arguments. + // + using parse_option_function = + std::size_t (const std::vector&, std::size_t); + + std::function parse_option; + + // If specified, called by the sleep builtin instead of the default + // implementation. + // + using sleep_function = void (const duration&); + + std::function sleep; + + explicit + builtin_callbacks (std::function c = {}, + std::function m = {}, + std::function r = {}, + std::function p = {}, + std::function s = {}) + : create (std::move (c)), + move (std::move (m)), + remove (std::move (r)), + parse_option (std::move (p)), + sleep (std::move (s)) {} + + explicit + builtin_callbacks (std::function sl) + : sleep (std::move (sl)) {} + }; + + // Start a builtin command. Use the current process' standard streams for + // the unopened in, out, and err file descriptors. Use the process' current + // working directory unless an alternative is specified. Throw + // std::system_error on failure. + // + // Note that unlike argc/argv, args don't include the program name. + // + using builtin_function = builtin (std::uint8_t& result, + const std::vector& args, + auto_fd in, auto_fd out, auto_fd err, + const dir_path& cwd, + const builtin_callbacks&); + + // Builtin function and weight. + // + // The weight between 0 and 2 reflects the builtin's contribution to the + // containing script semantics with 0 being the lowest/ignore. Current + // mapping is as follows: + // + // 0 - non-contributing (true, false) + // 1 - non-creative (rm, rmdir, sleep, test) + // 2 - creative (any builtin that may produce output) + // + // If the function is NULL, then the builtin has an external implementation + // and should be executed by running the program with this name. + // + struct builtin_info + { + builtin_function* function; + uint8_t weight; + }; + + class builtin_map: public std::map + { + public: + using base = std::map; + using base::base; + + // Return NULL if not a builtin. + // + const builtin_info* + find (const std::string&) const; + }; + + // Asynchronously run a function as if it was a builtin. The function must + // have the std::uint8_t() signature and not throw exceptions. + // + // Note that using std::function as an argument type would be too + // restrictive (see above). + // + template + builtin + pseudo_builtin (std::uint8_t&, F); + + LIBBUTL_SYMEXPORT extern const builtin_map builtins; +} + +#include -- cgit v1.1