// file : libbuild2/types.hxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #ifndef LIBBUILD2_TYPES_HXX #define LIBBUILD2_TYPES_HXX // Include unprocessed file during bootstrap. See // for details. // #ifdef BUILD2_BOOTSTRAP # include #else # include #endif #include #include #include #include #include #include #include #include // unique_ptr, shared_ptr #include // pair, move() #include // size_t, nullptr_t #include // uint{8,16,32,64}_t, *_MIN, *_MAX #include #include #include // hash, function, reference_wrapper #include #include #include #include #include #include #if defined(__cpp_lib_shared_mutex) || defined(__cpp_lib_shared_timed_mutex) # include #endif #include // ios_base::failure #include // exception #include // logic_error, invalid_argument, runtime_error #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace build2 { // Commonly-used types. // using std::uint8_t; using std::uint16_t; using std::uint32_t; using std::int64_t; using std::uint64_t; using std::uintptr_t; using int64s = std::vector; using uint64s = std::vector; using std::size_t; using std::nullptr_t; using std::pair; using std::tuple; using std::string; using std::function; using std::reference_wrapper; using strings = std::vector; using cstrings = std::vector; using std::hash; using std::initializer_list; using std::unique_ptr; using std::shared_ptr; using std::weak_ptr; using std::map; using std::multimap; using std::set; using std::multiset; using std::array; using std::vector; using butl::vector_view; // using butl::small_vector; // using std::istream; using std::ostream; using std::endl; using std::streamsize; // C++'s ssize_t. // Regex. // // Note that includes an ostream insertion operator for // regex_error which prints cleaned up message, if any. // using std::regex; using std::regex_error; using regex_match_results = std::match_results; // Concurrency. // using std::atomic; using std::memory_order; using std::memory_order_relaxed; using std::memory_order_consume; using std::memory_order_acquire; using std::memory_order_release; using std::memory_order_acq_rel; using std::memory_order_seq_cst; using atomic_count = atomic; // Matches scheduler::atomic_count. // Like std::atomic except implicit conversion and assignment use relaxed // memory ordering. // template struct relaxed_atomic: atomic { using atomic::atomic; // Delegate. relaxed_atomic (const relaxed_atomic& a) noexcept : atomic (a.load (memory_order_relaxed)) {} operator T () const noexcept {return this->load (memory_order_relaxed);} T operator= (T v) noexcept { this->store (v, memory_order_relaxed); return v;} T operator= (const relaxed_atomic& a) noexcept { return *this = a.load (memory_order_relaxed);} }; template struct relaxed_atomic: atomic { using atomic::atomic; // Delegate. relaxed_atomic (const relaxed_atomic& a) noexcept : atomic (a.load (memory_order_relaxed)) {} operator T* () const noexcept {return this->load (memory_order_relaxed);} T& operator* () const noexcept {return *this->load (memory_order_relaxed);} T* operator-> () const noexcept {return this->load (memory_order_relaxed);} T* operator= (T* v) noexcept { this->store (v, memory_order_relaxed); return v;} T* operator= (const relaxed_atomic& a) noexcept { return *this = a.load (memory_order_relaxed);} }; // VC 14 has issues. // #if defined(_MSC_VER) && _MSC_VER <= 1900 template inline bool operator== (const relaxed_atomic& x, const P& y) { return static_cast (x) == y; } template inline bool operator!= (const relaxed_atomic& x, const P& y) { return static_cast (x) != y; } #endif using std::mutex; using mlock = std::unique_lock; using std::condition_variable; #if defined(__cpp_lib_shared_mutex) using shared_mutex = std::shared_mutex; using ulock = std::unique_lock; using slock = std::shared_lock; #elif defined(__cpp_lib_shared_timed_mutex) using shared_mutex = std::shared_timed_mutex; using ulock = std::unique_lock; using slock = std::shared_lock; #else // Because we have this fallback, we need to be careful not to create // multiple shared locks in the same thread. // struct shared_mutex: mutex { using mutex::mutex; void lock_shared () { lock (); } void try_lock_shared () { try_lock (); } void unlock_shared () { unlock (); } }; using ulock = std::unique_lock; using slock = ulock; #endif using std::defer_lock; using std::adopt_lock; using std::thread; namespace this_thread = std::this_thread; // Global, MT-safe information cache. Normally used for caching information // (versions, target triplets, search paths, etc) extracted from other // programs (compilers, etc). // // The key is normally a hash of all the inputs that can affect the output. // // Note that insertion is racy and it's possible the cache entry already // exists, in which case we ignore our value assuming it is the same. // template class global_cache { public: const T* find (const K& k) const { mlock l (mutex_); auto i (cache_.find (k)); return i != cache_.end () ? &i->second : nullptr; } const T& insert (K k, T v) { mlock l (mutex_); return cache_.insert (std::make_pair (std::move (k), std::move (v))).first->second; } private: map cache_; mutable mutex mutex_; }; // Exceptions. // // While is included, there is no using for std::exception -- // use qualified. // using std::logic_error; using std::invalid_argument; using std::runtime_error; using std::system_error; using io_error = std::ios_base::failure; // // using butl::optional; using butl::nullopt; // // using butl::const_ptr; // // // using butl::path; using path_traits = path::traits_type; using butl::path_name; using butl::path_name_view; using butl::path_name_value; using butl::dir_path; using butl::dir_name_view; using butl::path_cast; using butl::basic_path; using butl::invalid_path; using butl::path_abnormality; using butl::path_map; using butl::dir_path_map; using butl::path_multimap; using butl::dir_path_multimap; // Absolute directory path. Note that for now we don't do any checking that // the path is in fact absolute. // // The idea is to have a different type that we automatically complete when // a (variable) value of this type gets initialized from untyped names. See // value_type for details. // // Note that currently we also normalize and actualize the path. And we // leave empty path as is. // struct abs_dir_path: dir_path { using dir_path::dir_path; explicit abs_dir_path (dir_path d): dir_path (std::move (d)) {} abs_dir_path () = default; }; using paths = std::vector; using dir_paths = std::vector; // // using butl::system_clock; using butl::timestamp; using butl::duration; using butl::timestamp_unknown; using butl::timestamp_unknown_rep; using butl::timestamp_nonexistent; using butl::timestamp_unreal; using butl::to_string; using butl::operator<<; // // using butl::sha256; // using butl::process; using butl::process_env; using butl::process_path; using butl::process_error; // Extended process_path with additional information. // // See also {import,export}.metadata. // // Note that the environment checksum is calculated in the (potentially // hermetic) project environment which makes instances of process_path_ex // project-specific. (We could potentially store the original list of // environment variables and calculate the checksum on the fly in the // current context thus making it project-independent. But this will // complicate things without, currently, much real benefit since all our // use-cases fit well with the project-specific restriction. We could // probably even support both variants if desirable.) // struct process_path_ex: process_path { optional name; // Stable name for diagnostics. optional checksum; // Executable checksum for change tracking. optional env_checksum; // Environment checksum for change tracking. using process_path::process_path; process_path_ex (const process_path& p, string n, optional c = {}, optional ec = {}) : process_path (p, false /* init */), name (std::move (n)), checksum (std::move (c)), env_checksum (std::move (ec)) {} process_path_ex (process_path&& p, string n, optional c = {}, optional ec = {}) : process_path (std::move (p)), name (std::move (n)), checksum (std::move (c)), env_checksum (std::move (ec)) {} process_path_ex () = default; }; // // using butl::auto_fd; using butl::fdpipe; using butl::ifdstream; using butl::ofdstream; using butl::fdopen_mode; using butl::fdstream_mode; using butl::fdselect_state; using butl::fdselect_set; // // using butl::target_triplet; // // using butl::semantic_version; using butl::parse_semantic_version; // // using butl::standard_version; using butl::standard_version_constraint; // // using butl::project_name; // Diagnostics location. // // Note that location maintains a shallow reference to path/path_name (use // location_value if you need the deep copy semantics). Zero lines or // columns are not printed. // class location { public: path_name_view file; uint64_t line; uint64_t column; location (): line (0), column (0) {} explicit location (const path& f, uint64_t l = 0, uint64_t c = 0) : file (&f, nullptr /* name */), line (l), column (c) {} explicit location (path&&, uint64_t = 0, uint64_t = 0) = delete; explicit location (const path_name_view& f, uint64_t l = 0, uint64_t c = 0) : file (f), line (l), column (c) {} explicit location (path_name_view&&, uint64_t = 0, uint64_t = 0) = delete; bool empty () const {return file.null () || file.empty ();} protected: location (uint64_t l, uint64_t c): line (l), column (c) {} }; // Print in the :: form with 0 lines/columns not // printed. Nothing is printed for an empty location. // ostream& operator<< (ostream&, const location&); // Similar (and implicit-convertible) to the above but stores a copy of the // path. // class location_value: public location { public: path_name_value file; location_value (); location_value (const location&); location_value (location_value&&); location_value (const location_value&); location_value& operator= (location_value&&); location_value& operator= (const location_value&); }; // See context. // enum class run_phase {load, match, execute}; LIBBUILD2_SYMEXPORT ostream& operator<< (ostream&, run_phase); // utility.cxx } // In order to be found (via ADL) these have to be either in std:: or in // butl::. The latter is a bad idea since libbutl includes the default // implementation. They are defined in utility.cxx. // namespace std { // Path printing potentially relative with trailing slash for directories. // LIBBUILD2_SYMEXPORT ostream& operator<< (ostream&, const ::butl::path&); LIBBUILD2_SYMEXPORT ostream& operator<< (ostream&, const ::butl::path_name_view&); // Print as recall[@effect]. // LIBBUILD2_SYMEXPORT ostream& operator<< (ostream&, const ::butl::process_path&); } // // #include #include #endif // LIBBUILD2_TYPES_HXX