diff --git a/README.md b/README.md index 7d6d43e..a9a7333 100644 --- a/README.md +++ b/README.md @@ -17,33 +17,15 @@ files in there directly if you don't need the API documentation. ## Building -Libostd is built using the supplied C++ build tool. You need to compile the -build tool first using the compiler you will use to build the library itself. - -On a typical Unix-like setup, this will involve: +Libostd is built using Meson. Therefore, you need to install Meson and then +you can compile it as usual. Typically, this will be something like ~~~ -c++ build.cc -o build -std=c++1z -I. -pthread -./build --help +mkdir build && cd build +meson .. +ninja all ~~~ This will typically build using either GCC or Clang with the default standard library. **Keep in mind that it is you need at least Clang 4.0 or GCC 7.1 to build.** - -You can skip `-pthread` on Windows. - -Using the tool should be straightforward. The `./build --help` command lists -the available options. - -It also recognizes the environment variables `CXX` (the C++ compiler used -to build, defaults to `c++`), `AS` (the assembler used to build, defaults to -`c++` as well, as Clang and GCC can compile assembly files), `AR` (the tool -to create static lib archives, `ar` by default) and `STRIP` (the tool used -to strip the library in release mode, `strip` by default). - -Additionally, the `CXXFLAGS`, `LDFLAGS` and `ASFLAGS` environment variables -are also used. The `CXXFLAGS` are passed when compiling C++ source files as -well as when linking (the compiler is used to link). The `LDFLAGS` are passed -additionally to `CXXFLAGS` only when linking. The `ASFLAGS` are passed to -the assembler (`CXXFLAGS` are not, even when Clang/GCC is used). diff --git a/build.cc b/build.cc deleted file mode 100644 index cd6f2ca..0000000 --- a/build.cc +++ /dev/null @@ -1,572 +0,0 @@ -/* A simple build system to build libostd. - * - * This file is a part of the libostd project. Libostd is licensed under the - * University of Illinois/NCSA Open Source License, as is this file. See the - * COPYING.md file further information. - */ - -/* just glibc bein awful */ -#define _FILE_OFFSET_BITS 64 - -#include -#include -#include -#include - -#include "ostd/io.hh" -#include "ostd/range.hh" -#include "ostd/format.hh" -#include "ostd/string.hh" -#include "ostd/path.hh" -#include "ostd/thread_pool.hh" -#include "ostd/channel.hh" -#include "ostd/argparse.hh" - -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/thread_pool.cc" -#include "src/channel.cc" -#include "src/string.cc" -#include "src/argparse.cc" - -#define OSTD_GEN_UNICODE_INCLUDE -#include "gen_unicode.cc" - -using strvec = std::vector; -using pathvec = std::vector; - -/* THESE VARIABLES CAN BE ALTERED */ - -/* all examples in the directory are built */ -static path EXAMPLES_DIR = "examples"; - -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 path CXX_SOURCE_DIR = "src"; - -static path TEST_DIR = "tests"; -static pathvec TEST_CASES = { - "algorithm", "range" -}; - -static path OSTD_UNICODE_DATA = "data/UnicodeData-11.0.txt"; -static path OSTD_UNICODE_SRC = CXX_SOURCE_DIR / "string_utf.hh"; - -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 " - "-D_FILE_OFFSET_BITS=64"; -static std::string DEFAULT_LDFLAGS = "-pthread"; -static std::string DEFAULT_ASFLAGS = "-fPIC"; - -static std::string DEBUG_CXXFLAGS = "-g"; - -static std::string SHARED_CXXFLAGS = ""; -static std::string SHARED_LDFLAGS = "-shared"; -static std::string SHARED_ASFLAGS = ""; - -/* DO NOT CHANGE PAST THIS POINT */ - -static std::string from_env_or(char const *evar, char const *defval) { - char const *varv = std::getenv(evar); - if (!varv || !varv[0]) { - return defval; - } - return varv; -} - -static void add_cross(std::string &arg) { - char const *cross = std::getenv("CROSS"); - if (!cross || !cross[0]) { - return; - } - arg.insert(0, cross); -} - -static void add_env(std::string &val, char const *evar) { - char const *varv = std::getenv(evar); - if (!varv || !varv[0]) { - return; - } - val += ' '; - val += varv; -} - -static void exec_command(strvec const &args) { - if (int ret = ostd::subprocess{nullptr, ostd::iter(args)}.close(); ret) { - throw std::runtime_error{ostd::format( - ostd::appender(), "command failed with code %d", ret - ).get()}; - } -} - -static std::string get_command(strvec const &args) { - std::string ret; - for (auto &s: args) { - if (!ret.empty()) { - ret += ' '; - } - ret += s; - } - return ret; -} - -static void add_args(strvec &args, std::string const &cmdl) { - ostd::split_args([&args](ostd::string_range arg) { - args.emplace_back(arg); - }, cmdl); -} - -static void try_remove(path const &path, bool all = false) { - try { - if (all) { - fs::remove_all(path); - } else { - fs::remove(path); - } - } catch (fs::fs_error const &) {} -} - -static bool verbose = false; - -int main(int argc, char **argv) { - bool build_examples = true; - bool build_testsuite = true; - bool build_static = true; - bool build_shared = false; - std::string build_cfg = "debug"; - bool clean = false; - - std::string cxxflags = DEFAULT_CXXFLAGS; - std::string ldflags = DEFAULT_LDFLAGS; - std::string asflags = DEFAULT_ASFLAGS; - - ostd::arg_parser ap; - - auto &help = ap.add_optional("-h", "--help", 0) - .help("print this message and exit") - .action(ostd::arg_print_help(ap)); - - ap.add_optional("--no-examples", 0) - .help("do not build examples") - .action(ostd::arg_store_false(build_examples)); - - ap.add_optional("--no-test-suite", 0) - .help("do not build test suite") - .action(ostd::arg_store_false(build_testsuite)); - - ap.add_optional("--no-static", 0) - .help("do not build static libostd") - .action(ostd::arg_store_false(build_static)); - - ap.add_optional("--shared", 0) - .help("build shared libostd") - .action(ostd::arg_store_true(build_shared)); - - ap.add_optional("--config", 1) - .help("build configuration (debug/release)") - .action(ostd::arg_store_str(build_cfg)); - - ap.add_optional("-v", "--verbose", 0) - .help("print entire commands") - .action(ostd::arg_store_true(verbose)); - - ap.add_positional("target", ostd::arg_value::OPTIONAL) - .help("the action to perform") - .action([&clean](auto vals) { - if (!vals.empty()) { - if (vals.front() == "clean") { - clean = true; - } else if (vals.front() != "build") { - throw ostd::arg_error{"invalid build action"}; - } - } - }); - - try { - ap.parse(argc, argv); - } catch (ostd::arg_error const &e) { - ostd::cerr.writefln("argument parsing failed: %s", e.what()); - ap.print_help(ostd::cerr.iter()); - return 1; - } - - if (help.used()) { - return 0; - } - - std::string default_lib = OSTD_SHARED_LIB; - if (build_static) { - default_lib = OSTD_STATIC_LIB; - } - - auto strip = from_env_or("STRIP", "strip"); - auto cxx = from_env_or("CXX", "c++"); - auto as = from_env_or("AS", "c++"); - auto ar = from_env_or("AR", "ar"); - - add_cross(strip); - add_cross(cxx); - add_cross(as); - add_cross(ar); - - if (build_cfg == "debug") { - cxxflags += ' '; - cxxflags += DEBUG_CXXFLAGS; - } else if (build_cfg != "release") { - ostd::cerr.writefln("invalid build configuration: %s", build_cfg); - ap.print_help(ostd::cerr.iter()); - return 1; - } - - add_env(cxxflags, "CXXFLAGS"); - add_env(ldflags, "LDFLAGS"); - add_env(asflags, "ASFLAGS"); - - auto examples = ostd::appender(); - fs::glob_match(examples, EXAMPLES_DIR / "*.cc"); - for (auto &ex: examples.get()) { - ex.replace_suffix(); - } - - auto cxx_sources = ostd::appender(); - fs::glob_match(cxx_sources, CXX_SOURCE_DIR / "*.cc"); - for (auto &cc: cxx_sources.get()) { - cc.replace_suffix(); - } - - if (clean) { - ostd::writeln("Cleaning..."); - - for (auto &ex: examples.get()) { - auto rp = ex; - try_remove(rp); - rp.replace_suffix(".o"); - try_remove(rp); - } - for (auto &aso: ASM_SOURCES) { - auto rp = (ASM_SOURCE_DIR / aso).with_suffix(".o"); - try_remove(rp); - rp.replace_name((aso + "_dyn.o").string()); - try_remove(rp); - } - for (auto &cso: cxx_sources.get()) { - auto rp = cso.with_suffix(".o"); - try_remove(rp); - rp.replace_name((cso + "_dyn.o").string()); - try_remove(rp); - } - try_remove(OSTD_UNICODE_SRC); - try_remove(OSTD_STATIC_LIB); - try_remove(OSTD_SHARED_LIB); - try_remove("test_runner.o"); - try_remove("test_runner"); - try_remove("tests", true); - - return 0; - } - - /* a queue of stuff to print to stdout */ - ostd::channel io_msgs; - - /* a thread which reads from the queue */ - std::thread io_thread{[&io_msgs]() { - try { - for (;;) { - /* wait for a msg; if closed, throw channel_error */ - ostd::writeln(io_msgs.get()); - } - } catch (ostd::channel_error const &) { - /* the queue is empty and closed, thread is done */ - return; - } - }}; - - auto echo_q = [&io_msgs](ostd::string_range fmt, auto const &...args) { - if (!verbose) { - auto app = ostd::appender(); - ostd::format(app, fmt, args...); - io_msgs.put(std::move(app.get())); - } - }; - - auto exec_v = [&io_msgs](strvec const &args) { - if (verbose) { - io_msgs.put(get_command(args)); - } - exec_command(args); - }; - - auto call_cxx = [&]( - path const &input, path const &output, bool lib, bool shared - ) { - strvec args = { cxx }; - add_args(args, cxxflags); - - auto ifs = input.string(); - auto outp = output; - - if (shared) { - outp.replace_suffix(); - outp += "_dyn.o"; - echo_q("CXX (shared): %s", ifs); - add_args(args, SHARED_CXXFLAGS); - } else { - echo_q("CXX: %s", ifs); - } - - if (lib) { - args.push_back("-DOSTD_BUILD"); - if (shared) { - args.push_back("-DOSTD_DLL"); - } - } - - args.push_back("-c"); - args.push_back("-o"); - args.push_back(outp); - args.push_back(ifs); - - exec_v(args); - - return outp; - }; - - /* mostly unnecessary to separately compile shared, but - * the files may check for __PIC__ (at least mips32 does) - */ - auto call_as = [&]( - path const &input, path const &output, bool, bool shared - ) { - strvec args = { as }; - add_args(args, asflags); - - auto ifs = input.string(); - auto outp = output; - - if (shared) { - outp.replace_suffix(); - outp += "_dyn.o"; - echo_q("AS (shared): %s", ifs); - add_args(args, SHARED_ASFLAGS); - } else { - echo_q("AS: %s", ifs); - } - - args.push_back("-c"); - args.push_back("-o"); - args.push_back(outp); - args.push_back(ifs); - - exec_v(args); - - return outp; - }; - - auto call_ld = [&]( - path const &output, pathvec const &files, strvec const &flags - ) { - echo_q("LD: %s", output); - - strvec args = { cxx }; - add_args(args, cxxflags); - - args.push_back("-o"); - args.push_back(output); - for (auto &p: files) { - args.push_back(p); - } - args.insert(args.cend(), flags.begin(), flags.end()); - - add_args(args, ldflags); - - exec_v(args); - - if (build_cfg == "release") { - args.clear(); - args.push_back(strip); - args.push_back(output); - exec_v(args); - } - }; - - auto call_ldlib = [&]( - path const &output, pathvec const &files, bool shared - ) { - if (shared) { - strvec flags; - add_args(flags, SHARED_CXXFLAGS); - add_args(flags, SHARED_LDFLAGS); - call_ld(output, files, flags); - } else { - strvec args = { ar }; - echo_q("AR: %s", output); - - args.push_back("rcs"); - args.push_back(output); - for (auto &p: files) { - args.push_back(p); - } - - exec_v(args); - } - }; - - auto build_example = [&](path const &name) { - auto base = name; - auto ccf = base.with_suffix(".cc"); - auto obf = base.with_suffix(".o"); - - call_cxx(ccf, obf, false, false); - call_ld(base, { obf }, { default_lib }); - - try_remove(obf); - }; - - auto build_test = [&](path const &name) { - auto base = TEST_DIR / name; - 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}; - if (!f.is_open()) { - auto app = ostd::appender(); - ostd::format(app, "cannot open '%s' for writing", ccf); - throw std::runtime_error{app.get()}; - } - f.writef( - "#define OSTD_BUILD_TESTS libostd_%s\n" - "\n" - "#include \n" - "#include \n" - "#include \n" - "\n" - "int main() {\n" - " auto [ succ, fail ] = ostd::test::run();\n" - " ostd::writeln(succ, \" \", fail);\n" - " return 0;\n" - "}\n", - name, name - ); - f.close(); - - call_cxx(ccf, obf, false, false); - call_ld(base, { obf }, { default_lib }); - try_remove(obf); - }; - - auto build_test_runner = [&]() { - call_cxx("test_runner.cc", "test_runner.o", false, false); - call_ld("test_runner", { "test_runner.o" }, { default_lib }); - try_remove("test_runner.o"); - }; - - ostd::thread_pool tp{}; - tp.start(); - - std::queue> future_obj, future_dynobj; - - /* build object files in static and shared (PIC) variants */ - auto build_all = [&]( - pathvec const &list, path const &spath, - path const &sext, auto &buildf - ) { - 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); - })); - }; - for (auto &sf: list) { - auto sp = spath.empty() ? sf : (spath / sf); - if (build_static) { - build_obj(sp, false); - } - if (build_shared) { - build_obj(sp, true); - } - } - }; - - echo_q("Generating Unicode tables..."); - ostd::unicode_gen::parse_state{}.build_all_from_file( - OSTD_UNICODE_DATA.string(), OSTD_UNICODE_SRC.string() - ); - - echo_q("Building the library..."); - build_all(ASM_SOURCES, ASM_SOURCE_DIR, ".S", call_as); - build_all(cxx_sources.get(), path{}, ".cc", call_cxx); - - if (build_static) { - pathvec objs; - while (!future_obj.empty()) { - objs.push_back(future_obj.front().get()); - future_obj.pop(); - } - call_ldlib(OSTD_STATIC_LIB, objs, false); - } - if (build_shared) { - pathvec objs; - while (!future_dynobj.empty()) { - objs.push_back(future_dynobj.front().get()); - future_dynobj.pop(); - } - call_ldlib(OSTD_SHARED_LIB, objs, true); - } - - std::queue> future_bin; - - if (build_examples) { - echo_q("Building examples..."); - for (auto &ex: examples.get()) { - future_bin.push(tp.push([&build_example, ex]() { - build_example(ex); - })); - } - } - - if (build_testsuite) { - echo_q("Building tests..."); - build_test_runner(); - if (!fs::is_directory(TEST_DIR)) { - if (!fs::create_directory(TEST_DIR)) { - echo_q("Failed creating test directory"); - return 1; - } - } - for (auto &test: TEST_CASES) { - future_bin.push(tp.push([&build_test, test]() { - build_test(test); - })); - } - } - - while (!future_bin.empty()) { - /* wait and propagate possible exception */ - future_bin.front().get(); - future_bin.pop(); - } - - if (build_testsuite) { - exec_v({ "./test_runner", TEST_DIR }); - } - - io_msgs.close(); - io_thread.join(); - - return 0; -} diff --git a/examples/meson.build b/examples/meson.build new file mode 100644 index 0000000..6342166 --- /dev/null +++ b/examples/meson.build @@ -0,0 +1,25 @@ +libostd_examples_src = [ + 'argparse.cc', + 'concurrency.cc', + 'coroutine1.cc', + 'coroutine2.cc', + 'format.cc', + 'glob.cc', + 'listdir.cc', + 'range.cc', + 'range_pipe.cc', + 'signal.cc', + 'stream1.cc', + 'stream2.cc' +] + +thread_dep = dependency('threads') + +foreach example: libostd_examples_src + executable(example.split('.')[0], + [example], + dependencies: [libostd, thread_dep], + include_directories: libostd_includes, + install: false + ) +endforeach diff --git a/gen_unicode.cc b/gen_unicode.cc index cab8cea..af42aec 100644 --- a/gen_unicode.cc +++ b/gen_unicode.cc @@ -19,6 +19,12 @@ #include #include +#ifdef OSTD_GEN_UNICODE_BUILD +#define OSTD_NO_UNICODE_TABLES +#include "src/string.cc" +#include "src/io.cc" +#endif + namespace ostd { namespace unicode_gen { diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..f4ac580 --- /dev/null +++ b/meson.build @@ -0,0 +1,35 @@ +project('libostd', ['cpp'], + version: '0.1.0', + default_options: ['buildtype=plain', 'cpp_std=c++17'], + meson_version: '>=0.46' +) + +dir_prefix = get_option('prefix') +dir_include = join_paths(dir_prefix, get_option('includedir')) +dir_data = join_paths(dir_prefix, get_option('datadir')) +dir_lib = join_paths(dir_prefix, get_option('libdir')) + +dir_package_include = join_paths(dir_include, 'ostd') + +unicode_data = join_paths('data', 'UnicodeData-11.0.txt') + +libostd_includes = [include_directories('.')] + +tgt_compiler_id = meson.get_compiler('cpp').get_id() + +if tgt_compiler_id == 'gcc' or tgt_compiler_id == 'clang' + add_global_arguments( + '-Wextra', '-Wshadow', '-Wold-style-cast', + language: 'cpp' + ) +endif + +subdir('src') + +if get_option('build-tests') + subdir('tests') +endif + +if get_option('build-examples') + subdir('examples') +endif diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..be9eebf --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,11 @@ +option('build-examples', + type: 'boolean', + value: true, + description: 'Build examples' +) + +option('build-tests', + type: 'boolean', + value: true, + description: 'Build tests' +) \ No newline at end of file diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..6997c55 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,81 @@ +libostd_header_src = [ + '../ostd/algorithm.hh', + '../ostd/argparse.hh', + '../ostd/channel.hh', + '../ostd/concurrency.hh', + '../ostd/context_stack.hh', + '../ostd/coroutine.hh', + '../ostd/environ.hh', + '../ostd/event.hh', + '../ostd/format.hh', + '../ostd/generic_condvar.hh', + '../ostd/io.hh', + '../ostd/path.hh', + '../ostd/platform.hh', + '../ostd/process.hh', + '../ostd/range.hh', + '../ostd/stream.hh', + '../ostd/string.hh', + '../ostd/thread_pool.hh', + '../ostd/unit_test.hh', + '../ostd/vecmath.hh', + + '../ostd/build/make.hh', + '../ostd/build/make_coroutine.hh', + + '../ostd/ext/sdl_rwops.hh' +] + +libostd_src = [ + 'argparse.cc', + 'build_make.cc', + 'channel.cc', + 'concurrency.cc', + 'context_stack.cc', + 'environ.cc', + 'io.cc', + 'path.cc', + 'process.cc', + 'string.cc', + 'thread_pool.cc', + + 'asm/jump_all_gas.S', + 'asm/make_all_gas.S', + 'asm/ontop_all_gas.S' +] + +thread_dep = dependency('threads') + +libostd_gen_unicode_exe = executable('gen_unicode', + ['../gen_unicode.cc'], + include_directories: libostd_includes, + cpp_args: '-DOSTD_GEN_UNICODE_BUILD', + install: false +) + +libostd_extra_src = [] + +libostd_extra_src += custom_target('libostd_unicode_tables', + input: join_paths('..', unicode_data), + output: ['string_utf.hh'], + install: false, + command: [ + libostd_gen_unicode_exe, '@INPUT@', + join_paths(meson.current_build_dir(), 'string_utf.hh') + ] +) + +libostd_lib = library('ostd', + libostd_src, libostd_extra_src, + dependencies: thread_dep, + include_directories: libostd_includes + [include_directories('.')], + install: true, + version: meson.project_version() +) + +libostd = declare_dependency( + include_directories: libostd_includes, + link_with: libostd_lib +) + +install_headers(libostd_header_src, install_dir: dir_package_include) \ No newline at end of file diff --git a/src/string.cc b/src/string.cc index 19d4fb5..6221eff 100644 --- a/src/string.cc +++ b/src/string.cc @@ -508,7 +508,7 @@ OSTD_EXPORT bool isupper(char32_t c) noexcept; OSTD_EXPORT char32_t tolower(char32_t c) noexcept; OSTD_EXPORT char32_t toupper(char32_t c) noexcept; -#if __has_include("string_utf.hh") +#ifndef OSTD_NO_UNICODE_TABLES #include "string_utf.hh" #else @@ -558,7 +558,7 @@ OSTD_EXPORT char32_t toupper(char32_t c) noexcept { return c; } -#endif /* __has_include("string_utf.hh") */ +#endif /* !OSTD_NO_UNICODE_TABLES */ namespace detail { template diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..5c7dfd9 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,28 @@ +test_runner_exe = executable('test_runner', + ['test_runner.cc'], + dependencies: libostd, + include_directories: libostd_includes, + install: false +) + +libostd_tests_src = [ + 'algorithm.cc', + 'range.cc' +] + +test_target = [] +foreach test_src: libostd_tests_src + test_target += executable(test_src.split('.')[0], + [test_src], + dependencies: libostd, + include_directories: libostd_includes, + install: false + ) +endforeach + +test('libostd', + test_runner_exe, + args: [meson.current_build_dir()], + workdir: meson.current_build_dir(), + depends: test_target +) diff --git a/test_runner.cc b/tests/test_runner.cc similarity index 100% rename from test_runner.cc rename to tests/test_runner.cc