// file : build2/utility -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #ifndef BUILD2_UTILITY #define BUILD2_UTILITY #include // make_tuple() #include // make_shared() #include // to_string() #include // move(), forward(), declval(), make_pair() #include // assert() #include // make_move_iterator() #include // combine_hash(), reverse_iterate(), casecmp(), // lcase() #include // uncaught_exception() #include #include namespace build2 { using std::move; using std::forward; using std::declval; using std::make_pair; using std::make_tuple; using std::make_shared; using std::make_move_iterator; using std::to_string; // // using butl::combine_hash; using butl::reverse_iterate; using butl::casecmp; using butl::case_compare_string; using butl::case_compare_c_string; using butl::lcase; using butl::alpha; using butl::alnum; // Basic string utilities. // // Trim leading/trailing whitespacec, including '\r'. // string& trim (string&); // Find the beginning and end poistions of the next word. Return the size // of the word or 0 and set b = e = n if there are no more words. For // example: // // for (size_t b (0), e (0); next_word (s, b, e); ) // { // string w (s, b, e - b); // } // // Or: // // for (size_t b (0), e (0), n; n = next_word (s, b, e, ' ', ','); ) // { // string w (s, b, n); // } // // The second version examines up to the n'th character in the string. // size_t next_word (const string&, size_t& b, size_t& e, char d1 = ' ', char d2 = '\0'); size_t next_word (const string&, size_t n, size_t& b, size_t& e, char d1 = ' ', char d2 = '\0'); // Basic process utilities. // // Start a process with the specified arguments printing the command at // verbosity level 3 and higher. Redirect STDOUT to a pipe. If error is // false, then redirecting STDERR to STDOUT (this can be used to suppress // diagnostics from the child process). Issue diagnostics and throw failed // in case of an error. // process_path run_search (const char*& args0); process_path run_search (const path&, bool init, const dir_path& fallback = dir_path ()); process run_start (const process_path&, const char* args[], bool error); inline process run_start (const char* args[], bool error) { return run_start (run_search (args[0]), args, error); } bool run_finish (const char* args[], bool error, process&, const string&); // Start the process as above and then call the specified function on each // trimmed line of the output until it returns a non-empty object T (tested // with T::empty()) which is then returned to the caller. // // The predicate can move the value out of the passed string but, if error // is false, only in case of a "content match" (so that any diagnostics // lines are left intact). // // If ignore_exit is true, then the program's exist status is ignored (if it // is false and the program exits with the non-zero status, then an empty T // instance is returned). // // If checksum is not NULL, then feed it the content of each line. // template T run (const process_path&, const char* args[], T (*) (string&), bool error = true, bool ignore_exit = false, sha256* checksum = nullptr); template inline T run (const char* args[], T (*f) (string&), bool error = true, bool ignore_exit = false, sha256* checksum = nullptr) { return run (run_search (args[0]), args, f, error, ignore_exit, checksum); } // run // template inline T run (const path& prog, T (*f) (string&), bool error = true, bool ignore_exit = false, sha256* checksum = nullptr) { const char* args[] = {prog.string ().c_str (), nullptr}; return run (args, f, error, ignore_exit, checksum); } template inline T run (const process_path& pp, T (*f) (string&), bool error = true, bool ignore_exit = false, sha256* checksum = nullptr) { const char* args[] = {pp.recall_string (), nullptr}; return run (pp, args, f, error, ignore_exit, checksum); } // run // template inline T run (const path& prog, const char* arg, T (*f) (string&), bool error = true, bool ignore_exit = false, sha256* checksum = nullptr) { const char* args[] = {prog.string ().c_str (), arg, nullptr}; return run (args, f, error, ignore_exit, checksum); } template inline T run (const process_path& pp, const char* arg, T (*f) (string&), bool error = true, bool ignore_exit = false, sha256* checksum = nullptr) { const char* args[] = {pp.recall_string (), arg, nullptr}; return run (pp, args, f, error, ignore_exit, checksum); } // Empty string and path. // extern const std::string empty_string; extern const path empty_path; extern const dir_path empty_dir_path; // Append all the values from a variable to the C-string list. T is either // target or scope. The variable is expected to be of type strings. // struct variable; template void append_options (cstrings&, T&, const variable&); template void append_options (cstrings&, T&, const char*); template void append_options (strings&, T&, const variable&); template void append_options (strings&, T&, const char*); template void hash_options (sha256&, T&, const variable&); template void hash_options (sha256&, T&, const char*); // As above but from the strings value directly. // class value; struct lookup; void append_options (cstrings&, const lookup&); void append_options (strings&, const lookup&); void hash_options (sha256&, const lookup&); void append_options (cstrings&, const strings&); void append_options (strings&, const strings&); void hash_options (sha256&, const strings&); // Check if a specified option is present in the variable or value. T is // either target or scope. // template bool find_option (const char* option, T&, const variable&, bool ignore_case = false); template bool find_option (const char* option, T&, const char* variable, bool ignore_case = false); bool find_option (const char* option, const lookup&, bool ignore_case = false); bool find_option (const char* option, const strings&, bool ignore_case = false); bool find_option (const char* option, const cstrings&, bool ignore_case = false); // As above but look for several options. // template bool find_options (initializer_list, T&, const variable&, bool = false); template bool find_options (initializer_list, T&, const char*, bool = false); bool find_options (initializer_list, const lookup&, bool = false); bool find_options (initializer_list, const strings&, bool = false); bool find_options (initializer_list, const cstrings&, bool = false); // As above but look for an option that has the specified prefix. // template bool find_option_prefix (const char* prefix, T&, const variable&, bool = false); template bool find_option_prefix (const char* prefix, T&, const char*, bool = false); bool find_option_prefix (const char* prefix, const lookup&, bool = false); bool find_option_prefix (const char* prefix, const strings&, bool = false); bool find_option_prefix (const char* prefix, const cstrings&, bool = false); // As above but look for several option prefixes. // template bool find_option_prefixes (initializer_list, T&, const variable&, bool = false); template bool find_option_prefixes (initializer_list, T&, const char*, bool = false); bool find_option_prefixes (initializer_list, const lookup&, bool = false); bool find_option_prefixes (initializer_list, const strings&, bool = false); bool find_option_prefixes (initializer_list, const cstrings&, bool = false); // Apply the specified substitution (stem) to a '*'-pattern. If pattern // is NULL, then return the stem itself. Assume the pattern is valid, // i.e., contains a single '*' character. // string apply_pattern (const char* stem, const string* pattern); // Parse version string in the X.Y.Z[-{a|b}N] to a version integer in the // AABBCCDD form, where: // // AA - major version number // BB - minor version number // CC - bugfix version number // DD - alpha / beta (DD + 50) version number // // When DD is not 00, 1 is subtracted from AABBCC. For example: // // Version AABBCCDD // 2.0.0 02000000 // 2.1.0 02010000 // 2.1.1 02010100 // 2.2.0-a1 02019901 // 3.0.0-b2 02999952 // // For a version in the 1.2.3- form, it returns (AABBCC-1)01, which is the // lowest possible version in the 1.2.3 release set. For example: // // Version AABBCCDD // 2.2.0- 02019901 // 1.2.3- 01020201 // // In fact versions 1.2.3- and 1.2.3-a1 are equivalent. // // Throw invalid_argument if the passed string is not a valid version. // unsigned int to_version (const string&); // Call a function if there is an exception. // // Means we are in the body of a destructor that is being called // as part of the exception stack unwindining. Used to compensate // for the deficiencies of uncaught_exception() until C++17 // uncaught_exceptions() becomes available. // // @@ MT: will have to be TLS. // extern bool exception_unwinding_dtor; template struct exception_guard; template inline exception_guard make_exception_guard (F f) { return exception_guard (move (f)); } template struct exception_guard { exception_guard (F f): f_ (move (f)) {} ~exception_guard () { if (std::uncaught_exception ()) { exception_unwinding_dtor = true; f_ (); exception_unwinding_dtor = false; } } private: F f_; }; // Pools (@@ perhaps move into a separate header). // struct string_pool: std::unordered_set { const std::string& find (const char* s) {return *emplace (s).first;} const std::string& find (const std::string& s) {return *emplace (s).first;} }; } #include #include #endif // BUILD2_UTILITY