diff --git a/README.md b/README.md index d4a4ee3..4f68f70 100644 --- a/README.md +++ b/README.md @@ -36,23 +36,6 @@ c++ build.cc -o build -std=c++1z -I. -pthread -stdlib=libc++ CXXFLAGS="-stdlib=libc++" ./build ~~~ -If you get undefined references about filesystem, you will need to add -extra linkage. - -For libstdc++: - -~~~ -c++ build.cc -o build -std=c++1z -I. -pthread -lstdc++fs -LDFLAGS="-lstdc++fs" ./build -~~~ - -For libc++: - -~~~ -c++ build.cc -o build -std=c++1z -I. -pthread -stdlib=libc++ -lc++experimental -CXXFLAGS="-stdlib=libc++" LDFLAGS="-lc++experimental" ./build -~~~ - You can skip `-pthread` on Windows. Using the tool should be straightforward. The `./build --help` command lists diff --git a/build.cc b/build.cc index 1ee5a89..1ad59d5 100644 --- a/build.cc +++ b/build.cc @@ -18,18 +18,17 @@ #include "ostd/format.hh" #include "ostd/string.hh" #include "ostd/path.hh" -#include "ostd/filesystem.hh" #include "ostd/thread_pool.hh" #include "ostd/channel.hh" #include "ostd/argparse.hh" -namespace fs = ostd::filesystem; +namespace fs = ostd::fs; +using path = ostd::path; /* ugly, but do not explicitly compile */ #include "src/io.cc" #include "src/process.cc" #include "src/path.cc" -#include "src/filesystem.cc" #include "src/thread_pool.cc" #include "src/channel.cc" #include "src/string.cc" @@ -39,31 +38,31 @@ namespace fs = ostd::filesystem; #include "gen_unicode.cc" using strvec = std::vector; -using pathvec = std::vector; +using pathvec = std::vector; /* THESE VARIABLES CAN BE ALTERED */ /* all examples in the directory are built */ -static fs::path EXAMPLES_DIR = "examples"; +static path EXAMPLES_DIR = "examples"; -static fs::path ASM_SOURCE_DIR = fs::path{"src"} / "asm"; +static path ASM_SOURCE_DIR = path{"src"} / "asm"; static pathvec ASM_SOURCES = { "jump_all_gas", "make_all_gas", "ontop_all_gas" }; /* all sources in the directory are built */ -static fs::path CXX_SOURCE_DIR = "src"; +static path CXX_SOURCE_DIR = "src"; -static fs::path TEST_DIR = "tests"; +static path TEST_DIR = "tests"; static pathvec TEST_CASES = { "algorithm", "range" }; -static fs::path OSTD_UNICODE_DATA = "data/UnicodeData-10.0.txt"; -static fs::path OSTD_UNICODE_SRC = CXX_SOURCE_DIR / "string_utf.hh"; +static path OSTD_UNICODE_DATA = "data/UnicodeData-10.0.txt"; +static path OSTD_UNICODE_SRC = CXX_SOURCE_DIR / "string_utf.hh"; -static fs::path OSTD_SHARED_LIB = "libostd.so"; -static fs::path OSTD_STATIC_LIB = "libostd.a"; +static path OSTD_SHARED_LIB = "libostd.so"; +static path OSTD_STATIC_LIB = "libostd.a"; static std::string DEFAULT_CXXFLAGS = "-std=c++1z -I. -O2 -Wall -Wextra " "-Wshadow -Wold-style-cast -fPIC " @@ -129,20 +128,14 @@ static void add_args(strvec &args, std::string const &cmdl) { }, cmdl); } -static fs::path path_with_ext(fs::path const &p, fs::path const &ext) { - fs::path rp = p; - rp.replace_extension(ext); - return rp; -} - -static void try_remove(fs::path const &path, bool all = false) { - try { +static void try_remove(path const &path, bool all = false) { + //try { if (all) { fs::remove_all(path); } else { fs::remove(path); } - } catch (fs::filesystem_error const &) {} + //} catch (fs::fs_error const &) {} } static bool verbose = false; @@ -213,9 +206,9 @@ int main(int argc, char **argv) { return 0; } - std::string default_lib = OSTD_SHARED_LIB.string(); + auto default_lib = std::string{OSTD_SHARED_LIB.string()}; if (build_static) { - default_lib = OSTD_STATIC_LIB.string(); + default_lib = std::string{OSTD_STATIC_LIB.string()}; } auto strip = from_env_or("STRIP", "strip"); @@ -242,15 +235,15 @@ int main(int argc, char **argv) { add_env(asflags, "ASFLAGS"); auto examples = ostd::appender(); - ostd::glob_match(examples, EXAMPLES_DIR / "*.cc"); + fs::glob_match(examples, EXAMPLES_DIR / "*.cc"); for (auto &ex: examples.get()) { - ex.replace_extension(); + ex.replace_suffix(); } auto cxx_sources = ostd::appender(); - ostd::glob_match(cxx_sources, CXX_SOURCE_DIR / "*.cc"); + fs::glob_match(cxx_sources, CXX_SOURCE_DIR / "*.cc"); for (auto &cc: cxx_sources.get()) { - cc.replace_extension(); + cc.replace_suffix(); } if (clean) { @@ -259,19 +252,19 @@ int main(int argc, char **argv) { for (auto &ex: examples.get()) { auto rp = ex; try_remove(rp); - rp.replace_extension(".o"); + rp.replace_suffix(".o"); try_remove(rp); } for (auto &aso: ASM_SOURCES) { - auto rp = path_with_ext(ASM_SOURCE_DIR / aso, ".o"); + auto rp = (ASM_SOURCE_DIR / aso).with_suffix(".o"); try_remove(rp); - rp.replace_filename(aso.string() + "_dyn.o"); + rp.replace_name((aso + "_dyn.o").string()); try_remove(rp); } for (auto &cso: cxx_sources.get()) { - auto rp = path_with_ext(cso, ".o"); + auto rp = cso.with_suffix(".o"); try_remove(rp); - rp.replace_filename(cso.string() + "_dyn.o"); + rp.replace_name((cso + "_dyn.o").string()); try_remove(rp); } try_remove(OSTD_UNICODE_SRC); @@ -316,7 +309,7 @@ int main(int argc, char **argv) { }; auto call_cxx = [&]( - fs::path const &input, fs::path const &output, bool lib, bool shared + path const &input, path const &output, bool lib, bool shared ) { strvec args = { cxx }; add_args(args, cxxflags); @@ -325,7 +318,7 @@ int main(int argc, char **argv) { auto outp = output; if (shared) { - outp.replace_extension(); + outp.replace_suffix(); outp += "_dyn.o"; echo_q("CXX (shared): %s", ifs); add_args(args, SHARED_CXXFLAGS); @@ -342,8 +335,8 @@ int main(int argc, char **argv) { args.push_back("-c"); args.push_back("-o"); - args.push_back(outp.string()); - args.push_back(ifs); + args.push_back(std::string{outp.string()}); + args.push_back(std::string{ifs}); exec_v(args); @@ -354,7 +347,7 @@ int main(int argc, char **argv) { * the files may check for __PIC__ (at least mips32 does) */ auto call_as = [&]( - fs::path const &input, fs::path const &output, bool, bool shared + path const &input, path const &output, bool, bool shared ) { strvec args = { as }; add_args(args, asflags); @@ -363,7 +356,7 @@ int main(int argc, char **argv) { auto outp = output; if (shared) { - outp.replace_extension(); + outp.replace_suffix(); outp += "_dyn.o"; echo_q("AS (shared): %s", ifs); add_args(args, SHARED_ASFLAGS); @@ -373,8 +366,8 @@ int main(int argc, char **argv) { args.push_back("-c"); args.push_back("-o"); - args.push_back(outp.string()); - args.push_back(ifs); + args.push_back(std::string{outp.string()}); + args.push_back(std::string{ifs}); exec_v(args); @@ -382,7 +375,7 @@ int main(int argc, char **argv) { }; auto call_ld = [&]( - fs::path const &output, pathvec const &files, strvec const &flags + path const &output, pathvec const &files, strvec const &flags ) { echo_q("LD: %s", output); @@ -390,9 +383,9 @@ int main(int argc, char **argv) { add_args(args, cxxflags); args.push_back("-o"); - args.push_back(output.string()); + args.push_back(std::string{output.string()}); for (auto &p: files) { - args.push_back(p.string()); + args.push_back(std::string{p.string()}); } args.insert(args.cend(), flags.begin(), flags.end()); @@ -403,13 +396,13 @@ int main(int argc, char **argv) { if (build_cfg == "release") { args.clear(); args.push_back(strip); - args.push_back(output.string()); + args.push_back(std::string{output.string()}); exec_v(args); } }; auto call_ldlib = [&]( - fs::path const &output, pathvec const &files, bool shared + path const &output, pathvec const &files, bool shared ) { if (shared) { strvec flags; @@ -421,19 +414,19 @@ int main(int argc, char **argv) { echo_q("AR: %s", output); args.push_back("rcs"); - args.push_back(output.string()); + args.push_back(std::string{output.string()}); for (auto &p: files) { - args.push_back(p.string()); + args.push_back(std::string{p.string()}); } exec_v(args); } }; - auto build_example = [&](fs::path const &name) { + auto build_example = [&](path const &name) { auto base = name; - auto ccf = path_with_ext(base, ".cc"); - auto obf = path_with_ext(base, ".o"); + auto ccf = base.with_suffix(".cc"); + auto obf = base.with_suffix(".o"); call_cxx(ccf, obf, false, false); call_ld(base, { obf }, { default_lib }); @@ -441,10 +434,10 @@ int main(int argc, char **argv) { try_remove(obf); }; - auto build_test = [&](fs::path const &name) { + auto build_test = [&](path const &name) { auto base = TEST_DIR / name; - auto ccf = path_with_ext(base, ".cc"); - auto obf = path_with_ext(base, ".o"); + auto ccf = base.with_suffix(".cc"); + auto obf = base.with_suffix(".o"); try_remove(ccf); ostd::file_stream f{ccf.string(), ostd::stream_mode::WRITE}; @@ -483,16 +476,16 @@ int main(int argc, char **argv) { ostd::thread_pool tp{}; tp.start(); - std::queue> future_obj, future_dynobj; + std::queue> future_obj, future_dynobj; /* build object files in static and shared (PIC) variants */ auto build_all = [&]( - pathvec const &list, fs::path const &spath, - fs::path const &sext, auto &buildf + pathvec const &list, path const &spath, + path const &sext, auto &buildf ) { - auto build_obj = [&](fs::path const &fpath, bool shared) { - auto srcf = path_with_ext(fpath, sext); - auto srco = path_with_ext(srcf, ".o"); + auto build_obj = [&](path const &fpath, bool shared) { + auto srcf = fpath.with_suffix(sext.string()); + auto srco = srcf.with_suffix(".o"); auto &fq = (shared ? future_dynobj : future_obj); fq.push(tp.push([&buildf, srcf, srco, shared]() { return buildf(srcf, srco, true, shared); @@ -516,7 +509,7 @@ int main(int argc, char **argv) { echo_q("Building the library..."); build_all(ASM_SOURCES, ASM_SOURCE_DIR, ".S", call_as); - build_all(cxx_sources.get(), fs::path{}, ".cc", call_cxx); + build_all(cxx_sources.get(), path{}, ".cc", call_cxx); if (build_static) { pathvec objs; @@ -569,7 +562,7 @@ int main(int argc, char **argv) { } if (build_testsuite) { - exec_v({ "./test_runner", TEST_DIR.string() }); + exec_v({ "./test_runner", std::string{TEST_DIR.string()} }); } io_msgs.close(); diff --git a/ostd/filesystem.hh b/ostd/filesystem.hh deleted file mode 100644 index 0a0ced9..0000000 --- a/ostd/filesystem.hh +++ /dev/null @@ -1,204 +0,0 @@ -/** @addtogroup Utilities - * @{ - */ - -/** @file filesystem.hh - * - * @brief Filesystem abstraction module. - * - * This module defines the namespace ostd::filesystem, which is either - * std::experimental::filesystem or std::filesystem depending on which - * is available. For portable applications, only use the subset of the - * module covered by both versions. - * - * It also provides range integration for directory iterators and - * ostd::format_traits specialization for std::filesystem::path. - * - * Additionally, it implements glob matching following POSIX with its - * own extensions (mainly recursive glob matching via `**`). - * - * @include glob.cc - * @include listdir.cc - * - * @copyright See COPYING.md in the project tree for further information. - */ - -#ifndef OSTD_FILESYSTEM_HH -#define OSTD_FILESYSTEM_HH - -#if __has_include() -# include -namespace ostd { - namespace filesystem = std::filesystem; -} -#elif __has_include() -# include -namespace ostd { - namespace filesystem = std::experimental::filesystem; -} -#else -# error "Unsupported platform" -#endif - -#include -#include -#include - -namespace ostd { - -/** @addtogroup Utilities - * @{ - */ - -/** @brief Range integration for std::filesystem::directory_iterator. - * - * Allows directory iterators to be made into ranges via ostd::iter(). - * - * @see ostd::ranged_traits - */ -template<> -struct ranged_traits { - /** @brief The range type for the iterator. */ - using range = iterator_range; - - /** @brief Creates a range for the iterator. */ - static range iter(filesystem::directory_iterator const &r) { - return range{filesystem::begin(r), filesystem::end(r)}; - } -}; - -/** @brief Range integration for std::filesystem::recursive_directory_iterator. - * - * Allows recursive directory iterators to be made into ranges via ostd::iter(). - * - * @see ostd::ranged_traits - */ -template<> -struct ranged_traits { - /** @brief The range type for the iterator. */ - using range = iterator_range; - - /** @brief Creates a range for the iterator. */ - static range iter(filesystem::recursive_directory_iterator const &r) { - return range{filesystem::begin(r), filesystem::end(r)}; - } -}; - -/** @brief ostd::format_traits specialization for std::filesystem::path. - * - * This allows paths to be formatted as strings. The value is formatted - * as if `path.string()` was formatted, using the exact ostd::format_spec. - */ -template<> -struct format_traits { - /** @brief Formats the path's string value. - * - * This calls exactly - * - * ~~~{.cc} - * fs.format_value(writer, p.string()); - * ~~~ - */ - template - static void to_format( - filesystem::path const &p, R &writer, format_spec const &fs - ) { - fs.format_value(writer, p.string()); - } -}; - -namespace detail { - OSTD_EXPORT bool glob_match_filename_impl( - typename filesystem::path::value_type const *fname, - typename filesystem::path::value_type const *wname - ) noexcept; - - OSTD_EXPORT void glob_match_impl( - void (*out)(filesystem::path const &, void *), - typename filesystem::path::iterator beg, - typename filesystem::path::iterator &end, - filesystem::path pre, void *data - ); -} /* namespace detail */ - -/** @brief Checks if the given path matches the given glob pattern. - * - * This matches the given filename against POSIX-style glob patterns. - * The following patterns are supported: - * - * | Pattern | Description | - * |---------|----------------------------------------------------| - * | * | 0 or more characters | - * | ? | any single character | - * | [abc] | one character in the brackets | - * | [a-z] | one character within the range in the brackets | - * | [!abc] | one character not in the brackets | - * | [!a-z] | one character not within the range in the brackets | - * - * The behavior is the same as in POSIX. You can combine ranges and - * individual characters in the `[]` pattern together as well as define - * multiple ranges in one (e.g. `[a-zA-Z_?]` matching alphabetics, - * an underscore and a question mark). The behavior of the range varies - * by locale. If the second character in the range is lower in value - * than the first one, a match will never happen. To match the `]` - * character in the brackets, make it the first one. To match the - * dash character, make it the first or the last. - * - * You can also use the brackets to escape metacharacters. So to - * match a literal `*`, use `[*]`. - * - * Keep in mind that an invalid bracket syntax (unterminated) will - * always cause this to return `false`. - * - * This function is used in ostd::glob_match(). - */ -inline bool glob_match_filename( - filesystem::path const &filename, filesystem::path const &pattern -) noexcept { - return detail::glob_match_filename_impl(filename.c_str(), pattern.c_str()); -} - -/** @brief Expands a path with glob patterns. - * - * Individual expanded paths are put in `out` and are of the standard - * std::filesystem::path type. It supports standard patterns as defined - * in ostd::glob_match_filename(). - * - * So for example, `*.cc` will expand to `one.cc`, `two.cc` and so on. - * A pattern like `foo/[cb]at.txt` will match `foo/cat.txt` and `foo/bat.txt` - * but not `foo/Cat.txt`. The `foo/?at.txt` will match `foo/cat.txt`, - * `foo/Cat.txt`, `foo/pat.txt`, `foo/vat.txt` or any other character - * in the place. - * - * Additionally, a special `**` pattern is also supported which is not - * matched by ostd::glob_match_filename(). It's only allowed if the entire - * filename or directory name is `**`. When used as a directory name, it - * will expand to all directories in the location and all subdirectories - * of those directories. If used as a filename (at the end of the path), - * then it expands to directories and subdirectories aswell as all files - * in the location and in the directories or subdirectories. Keep in mind - * that it is not a regular pattern and a `**` when found in a regular - * context (i.e. not as entire filename/directory name) will be treated - * as two regular `*` patterns. - * - * @throws std::filesystem_error if a filesystem error occurs. - * @returns The forwarded `out`. - */ -template -inline OutputRange &&glob_match( - OutputRange &&out, filesystem::path const &path -) { - auto pend = path.end(); - detail::glob_match_impl([](filesystem::path const &p, void *outp) { - static_cast *>(outp)->put(p); - }, path.begin(), pend, filesystem::path{}, &out); - return std::forward(out); -} - -/** @} */ - -} /* namespace ostd */ - -#endif - -/** @} */ diff --git a/ostd/path.hh b/ostd/path.hh index e1332b4..a6d4bcf 100644 --- a/ostd/path.hh +++ b/ostd/path.hh @@ -296,13 +296,13 @@ struct path { return *this; } - path with_name(string_range name) { + path with_name(string_range name) const { path ret{*this}; ret.replace_name(name); return ret; } - path &replace_suffix(string_range sfx) { + path &replace_suffix(string_range sfx = string_range{}) { auto osfx = suffix(); if (!osfx.empty()) { p_path.erase(p_path.size() - osfx.size(), osfx.size()); @@ -311,7 +311,7 @@ struct path { return *this; } - path &replace_suffixes(string_range sfx) { + path &replace_suffixes(string_range sfx = string_range{}) { auto sfxs = suffixes(); if (!sfxs.empty()) { p_path.erase(p_path.size() - sfxs.size(), sfxs.size()); @@ -320,13 +320,13 @@ struct path { return *this; } - path with_suffix(string_range sfx) { + path with_suffix(string_range sfx = string_range{}) const { path ret{*this}; ret.replace_suffix(sfx); return ret; } - path with_suffixes(string_range sfx) { + path with_suffixes(string_range sfx = string_range{}) const { path ret{*this}; ret.replace_suffixes(sfx); return ret; diff --git a/src/filesystem.cc b/src/filesystem.cc deleted file mode 100644 index 4616c39..0000000 --- a/src/filesystem.cc +++ /dev/null @@ -1,199 +0,0 @@ -/* Filesystem implementation bits. - * - * This file is part of libostd. See COPYING.md for futher information. - */ - -#include - -#include "ostd/filesystem.hh" - -namespace ostd { -namespace detail { - -inline typename filesystem::path::value_type const *glob_match_brackets( - typename filesystem::path::value_type match, - typename filesystem::path::value_type const *wp -) noexcept { - bool neg = (*wp == '!'); - if (neg) { - ++wp; - } - - /* grab the first character as it can be ] */ - auto c = *wp++; - if (!c) { - /* unterminated */ - return nullptr; - } - - /* make sure it's terminated somewhere */ - auto *eb = wp; - for (; *eb != ']'; ++eb) { - if (!*eb) { - return nullptr; - } - } - ++eb; - - /* no need to worry about \0 from now on */ - do { - /* character range */ - if ((*wp == '-') && (*(wp + 1) != ']')) { - auto lc = *(wp + 1); - wp += 2; - if ((match >= c) && (match <= lc)) { - return neg ? nullptr : eb; - } - c = *wp++; - continue; - } - /* single-char match */ - if (match == c) { - return neg ? nullptr : eb; - } - c = *wp++; - } while (c != ']'); - - /* loop ended, so no match */ - return neg ? eb : nullptr; -} - -OSTD_EXPORT bool glob_match_filename_impl( - typename filesystem::path::value_type const *fname, - typename filesystem::path::value_type const *wname -) noexcept { - /* skip any matching prefix if present */ - while (*wname && (*wname != '*')) { - if (!*wname || (*wname == '*')) { - break; - } - if (*fname) { - /* ? wildcard matches any character */ - if (*wname == '?') { - ++wname; - ++fname; - continue; - } - /* [...] wildcard */ - if (*wname == '[') { - wname = glob_match_brackets(*fname, wname + 1); - if (!wname) { - return false; - } - ++fname; - continue; - } - } - if ((*wname == '?') && *fname) { - ++wname; - ++fname; - continue; - } - if (*fname++ != *wname++) { - return false; - } - } - /* skip * wildcards; a wildcard matches 0 or more */ - if (*wname == '*') { - while (*wname == '*') { - ++wname; - } - /* was trailing so everything matches */ - if (!*wname) { - return true; - } - } - /* prefix skipping matched entire filename */ - if (!*fname) { - return true; - } - /* empty pattern and non-empty filename */ - if (!*wname) { - return false; - } - /* keep incrementing filename until it matches */ - while (*fname) { - if (glob_match_filename_impl(fname, wname)) { - return true; - } - ++fname; - } - return false; -} - -OSTD_EXPORT void glob_match_impl( - void (*out)(filesystem::path const &, void *), - typename filesystem::path::iterator beg, - typename filesystem::path::iterator &end, - filesystem::path pre, void *data -) { - while (beg != end) { - auto cur = *beg; - auto *cs = cur.c_str(); - /* this part of the path might contain wildcards */ - for (auto c = *cs; c; c = *++cs) { - /* ** as a name does recursive expansion */ - if ((c == '*') && (*(cs + 1) == '*') && !*(cs + 2)) { - ++beg; - auto ip = pre.empty() ? "." : pre; - if (!filesystem::is_directory(ip)) { - return; - } - filesystem::recursive_directory_iterator it{ip}; - /* include "current" dir in the match */ - if (beg != end) { - glob_match_impl(out, beg, end, pre, data); - } - for (auto &de: it) { - /* followed by more path, only consider dirs */ - auto dp = de.path(); - if ((beg != end) && !filesystem::is_directory(dp)) { - continue; - } - /* otherwise also match files */ - if (pre.empty()) { - /* avoid ugly formatting, sadly we have to do - * with experimental fs api so no better way... - */ - auto dpb = dp.begin(), dpe = dp.end(); - if (*dpb == ".") { - ++dpb; - } - filesystem::path ap; - while (dpb != dpe) { - ap /= *dpb; - ++dpb; - } - glob_match_impl(out, beg, end, ap, data); - } else { - glob_match_impl(out, beg, end, dp, data); - } - } - return; - } - /* wildcards *, ?, [...] */ - if ((c == '*') || (c == '?') || (c == '[')) { - ++beg; - auto ip = pre.empty() ? "." : pre; - if (!filesystem::is_directory(ip)) { - return; - } - filesystem::directory_iterator it{ip}; - for (auto &de: it) { - auto p = de.path().filename(); - if (!glob_match_filename(p, cur)) { - continue; - } - glob_match_impl(out, beg, end, pre / p, data); - } - return; - } - } - pre /= cur; - ++beg; - } - out(pre, data); -} - -} /* namespace detail */ -} /* namespace ostd */ diff --git a/test_runner.cc b/test_runner.cc index e3003e9..50e1a36 100644 --- a/test_runner.cc +++ b/test_runner.cc @@ -6,7 +6,7 @@ #include #include #include -#include +#include using namespace ostd; @@ -52,21 +52,21 @@ int main(int argc, char **argv) { ++nfailed; }; - filesystem::directory_iterator ds{testdir}; - for (auto &v: ds) { - auto p = filesystem::path{v}; - if (!filesystem::is_regular_file(p)) { + fs::directory_range dr{testdir}; + for (auto &v: dr) { + if (!v.is_regular_file()) { continue; } + auto p = v.path(); #ifdef OSTD_PLATFORM_WIN32 - if (p.extension().string() != ".exe") + if (p.suffix().string() != ".exe") #else - if (p.extension().string() != "") + if (p.has_suffix()) #endif { continue; } - auto modname = p.stem().string(); + auto modname = p.stem(); auto exepath = p.string(); auto rf = popen(exepath.data(), "r");