From 5fa111c2a3a2b1ae1f9774cc381aa75fd0c8e746 Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 27 Dec 2024 21:33:55 +0100 Subject: [PATCH] Hello libcode-build --- .editorconfig | 16 ++ .gitattributes | 1 + .gitea/workflows/on-push.yaml | 24 ++ .gitignore | 31 ++ LICENSE | 31 ++ README.md | 21 ++ build/.gitignore | 4 + build/bootstrap.build | 7 + build/export.build | 6 + build/root.build | 16 ++ buildfile | 5 + code/build/.gitignore | 9 + code/build/abstraction.hxx | 399 ++++++++++++++++++++++++++ code/build/application.hxx | 55 ++++ code/build/application.txx | 163 +++++++++++ code/build/buildfile | 72 +++++ code/build/diagnostics.cxx | 244 ++++++++++++++++ code/build/diagnostics.hxx | 202 +++++++++++++ code/build/except.hxx | 58 ++++ code/build/initialization-context.hxx | 67 +++++ code/build/traits.hxx | 175 +++++++++++ code/build/types.hxx | 99 +++++++ code/build/version.hxx.in | 37 +++ code/build/visitor.hxx | 38 +++ code/build/web/augmentation.cxx | 105 +++++++ code/build/web/augmentation.hxx | 40 +++ code/build/web/configurator.cxx | 62 ++++ code/build/web/configurator.hxx | 63 ++++ code/build/web/diagnostics.cxx | 20 ++ code/build/web/diagnostics.hxx | 18 ++ code/build/web/except.cxx | 19 ++ code/build/web/except.hxx | 28 ++ code/build/web/http-server.cxx | 85 ++++++ code/build/web/http-server.hxx | 55 ++++ manifest | 20 ++ repositories.manifest | 39 +++ tests/.gitignore | 8 + tests/build/.gitignore | 4 + tests/build/bootstrap.build | 5 + tests/build/root.build | 16 ++ tests/buildfile | 1 + 41 files changed, 2368 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitea/workflows/on-push.yaml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build/.gitignore create mode 100644 build/bootstrap.build create mode 100644 build/export.build create mode 100644 build/root.build create mode 100644 buildfile create mode 100644 code/build/.gitignore create mode 100644 code/build/abstraction.hxx create mode 100644 code/build/application.hxx create mode 100644 code/build/application.txx create mode 100644 code/build/buildfile create mode 100644 code/build/diagnostics.cxx create mode 100644 code/build/diagnostics.hxx create mode 100644 code/build/except.hxx create mode 100644 code/build/initialization-context.hxx create mode 100644 code/build/traits.hxx create mode 100644 code/build/types.hxx create mode 100644 code/build/version.hxx.in create mode 100644 code/build/visitor.hxx create mode 100644 code/build/web/augmentation.cxx create mode 100644 code/build/web/augmentation.hxx create mode 100644 code/build/web/configurator.cxx create mode 100644 code/build/web/configurator.hxx create mode 100644 code/build/web/diagnostics.cxx create mode 100644 code/build/web/diagnostics.hxx create mode 100644 code/build/web/except.cxx create mode 100644 code/build/web/except.hxx create mode 100644 code/build/web/http-server.cxx create mode 100644 code/build/web/http-server.hxx create mode 100644 manifest create mode 100644 repositories.manifest create mode 100644 tests/.gitignore create mode 100644 tests/build/.gitignore create mode 100644 tests/build/bootstrap.build create mode 100644 tests/build/root.build create mode 100644 tests/buildfile diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0928329 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf + +[*.md] +indent_style = space +indent_size = 4 +max_line_length = off +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitea/workflows/on-push.yaml b/.gitea/workflows/on-push.yaml new file mode 100644 index 0000000..23a9e11 --- /dev/null +++ b/.gitea/workflows/on-push.yaml @@ -0,0 +1,24 @@ +name: on-push +on: [push] + +jobs: + build-and-test: + runs-on: linux + container: code.helloryan.se/infra/buildenv/cxx-amd64-fedora-40:latest + volumes: + - /build + steps: + - name: Clone repository + uses: actions/checkout@v3 + - name: Authenticate + run: | + git config unset http.https://code.helloryan.se/.extraheader + echo "${{ secrets.NETRC }}" >> ~/.netrc + - name: Initialize + run: | + bpkg create -d /build cc config.cc.coptions="-Wall -Werror" + bdep init -A /build + - name: Build + run: b + - name: Test + run: b test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c96e1ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +.bdep/ + +# Local default options files. +# +.build2/local/ + +# Compiler/linker output. +# +*.d +*.t +*.i +*.i.* +*.ii +*.ii.* +*.o +*.obj +*.gcm +*.pcm +*.ifc +*.so +*.dylib +*.dll +*.a +*.lib +*.exp +*.pdb +*.ilk +*.exe +*.exe.dlls/ +*.exe.manifest +*.pc diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dfc745b --- /dev/null +++ b/LICENSE @@ -0,0 +1,31 @@ +Copyright © 2024 Ryan. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. All advertising materials mentioning features or use of this software must + display the following acknowledgement: + + This product includes software developed by Ryan, http://helloryan.se/. + +4. Neither the name(s) of the copyright holder(s) nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..adca885 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# libcode-build + +![Build status](https://code.helloryan.se/code/libcode-build/actions/workflows/on-push.yaml/badge.svg) + +## Requirements + +None, other than a modern C++-compiler. + +## Building + +See the wiki, https://code.helloryan.se/code/wiki/wiki/Build-Instructions, for +build instructions. + +## Contact + +Please report bugs and issues by sending an e-mail to: ryan@helloryan.se. + +## Contributing + +Please send an e-mail to ryan@helloryan.se to request an account and +write-access to the libcode-build repository. diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..974e01d --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,4 @@ +/config.build +/root/ +/bootstrap/ +build/ diff --git a/build/bootstrap.build b/build/bootstrap.build new file mode 100644 index 0000000..9a4ae1f --- /dev/null +++ b/build/bootstrap.build @@ -0,0 +1,7 @@ +project = libcode-build + +using version +using config +using test +using install +using dist diff --git a/build/export.build b/build/export.build new file mode 100644 index 0000000..863f380 --- /dev/null +++ b/build/export.build @@ -0,0 +1,6 @@ +$out_root/ +{ + include code/build/ +} + +export $out_root/code/build/$import.target diff --git a/build/root.build b/build/root.build new file mode 100644 index 0000000..21e0a2e --- /dev/null +++ b/build/root.build @@ -0,0 +1,16 @@ +# Uncomment to suppress warnings coming from external libraries. +# +#cxx.internal.scope = current + +cxx.std = latest + +using cxx + +hxx{*}: extension = hxx +ixx{*}: extension = ixx +txx{*}: extension = txx +cxx{*}: extension = cxx + +# The test target for cross-testing (running tests under Wine, etc). +# +test.target = $cxx.target diff --git a/buildfile b/buildfile new file mode 100644 index 0000000..bbe185e --- /dev/null +++ b/buildfile @@ -0,0 +1,5 @@ +./: {code/ tests/} doc{README.md} legal{LICENSE} manifest + +# Don't install tests. +# +tests/: install = false diff --git a/code/build/.gitignore b/code/build/.gitignore new file mode 100644 index 0000000..b1ed0e0 --- /dev/null +++ b/code/build/.gitignore @@ -0,0 +1,9 @@ +# Generated version header. +# +version.hxx + +# Unit test executables and Testscript output directories +# (can be symlinks). +# +*.test +test-*.test diff --git a/code/build/abstraction.hxx b/code/build/abstraction.hxx new file mode 100644 index 0000000..e9a6b19 --- /dev/null +++ b/code/build/abstraction.hxx @@ -0,0 +1,399 @@ +#ifndef code__build__abstraction_hxx_ +#define code__build__abstraction_hxx_ + +#include +#include + +namespace code::build +{ + + class initialization_context_t; + + class worker_t + { + public: + template + worker_t(std::in_place_type_t, Args&&... args) + : _worker{new instance_t{forward(args)...}} + {} + + void + join() + { + _worker->join(); + } + + private: + struct abstraction_t + { + virtual + ~abstraction_t() noexcept = default; + + virtual + void + join() = 0; + + }; + + template + struct instance_t + : abstraction_t + { + template + instance_t(Args&&... args) + : worker{forward(args)...} + {} + + void + join() override + { + worker.join(); + } + + W worker; + }; + + unique_ptr _worker; + + }; + + template + worker_t + make_worker(Args&&... args) + { + return worker_t{std::in_place_type, forward(args)...}; + } + + class augmentation_t + { + public: + template + augmentation_t(std::in_place_type_t, Args&&... args) + : _augmentation{new instance_t{forward(args)...}} + {} + + type_index + get_type() const + { + return _augmentation->get_type(); + } + + bool + can_init() + { + return _augmentation->can_init(); + } + + void + init(initialization_context_t& context) + { + _augmentation->init(context); + } + + bool + can_finalize() + { + return _augmentation->can_finalize(); + } + + void + finalize(initialization_context_t& context) + { + _augmentation->finalize(context); + } + + bool + can_start() + { + return _augmentation->can_start(); + } + + worker_t + start(initialization_context_t& context) + { + return _augmentation->start(context); + } + + template + A& + cast() + { + if (auto ptr = dynamic_cast*>(_augmentation.get()); ptr) { + return ptr->augmentation; + } + + throw logic_error{"invalid augmentation type"}; + } + + private: + struct abstraction_t + { + virtual + ~abstraction_t() = default; + + virtual + type_index + get_type() const = 0; + + virtual + bool + can_init() = 0; + + virtual + void + init(initialization_context_t&) = 0; + + virtual + bool + can_finalize() = 0; + + virtual + void + finalize(initialization_context_t&) = 0; + + virtual + bool + can_start() = 0; + + virtual + worker_t + start(initialization_context_t&) = 0; + + }; + + template + struct instance_t : abstraction_t + { + template + instance_t(Args&&... args) + : augmentation{forward(args)...} + {} + + type_index + get_type() const override + { + return typeid(A); + } + + bool + can_init() override + { + return traits::augmentation_traits::is_initializable; + } + + void + init(initialization_context_t& context) override + { + if constexpr (traits::augmentation_traits::is_initializable) { + augmentation.init(context); + return; + } + + throw logic_error{"attempt to initialize non-intializable augmentation"}; + } + + bool + can_finalize() override + { + return traits::augmentation_traits::is_finalizable; + } + + void + finalize(initialization_context_t& context) override + { + if constexpr (traits::augmentation_traits::is_finalizable) { + augmentation.finalize(context); + return; + } + + throw logic_error{"attempt to finalize non-finalizable augmentation"}; + } + + bool + can_start() override + { + return traits::augmentation_traits::is_startable; + } + + worker_t + start(initialization_context_t& context) override + { + if constexpr (traits::augmentation_traits::is_startable) { + return augmentation.start(context); + } + + throw logic_error{"attempt to start non-startable augmentation"}; + } + + A augmentation; + + }; + + unique_ptr _augmentation; + + }; + + class initializer_t + { + public: + template + initializer_t(std::in_place_type_t, Args&&... args) + : initializer_{new instance_t{forward(args)...}} + {} + + type_index + get_type() const + { + return initializer_->get_type(); + } + + augmentation_t + make() const + { + return initializer_->make(); + } + + private: + struct abstraction_t + { + virtual + ~abstraction_t() noexcept = default; + + virtual + type_index + get_type() const = 0; + + virtual + augmentation_t + make() const = 0; + + }; + + template + struct instance_t : abstraction_t + { + template + instance_t(Args&&... args) + : initializer{forward(args)...} + {} + + type_index + get_type() const override + { + return typeid(T); + } + + virtual + augmentation_t + make() const override + { + return initializer.make(); + } + + T initializer; + }; + + unique_ptr initializer_; + + }; + + class configurator_t + { + public: + template + configurator_t(std::in_place_type_t, std::in_place_type_t, Args&&... args) + : configurator_{new instance_t{forward(args)...}} + {} + + type_index + get_interface_type() const + { + return configurator_->get_interface_type(); + } + + template + C& + cast() + { + if (auto ptr = dynamic_cast*>(configurator_.get()); ptr) { + return ptr->cast_interface(); + } + + if (auto ptr = dynamic_cast*>(configurator_.get()); ptr) { + return ptr->cast(); + } + + throw logic_error{"invalid configurator type"}; + } + + private: + struct abstraction_t + { + virtual + ~abstraction_t() noexcept = default; + + virtual + type_index + get_interface_type() const = 0; + + }; + + template + struct interface_t : abstraction_t + { + virtual + I& + cast_interface() = 0; + + }; + + template + struct implementation_t + { + virtual + ~implementation_t() noexcept = default; + + virtual + C& + cast() = 0; + + }; + + template + struct instance_t : interface_t, + implementation_t + { + template + instance_t(Args&&... args) + : configurator{forward(args)...} + {} + + type_index + get_interface_type() const override + { + return typeid(I); + } + + I& + cast_interface() override + { + return configurator; + } + + C& + cast() override + { + return configurator; + } + + C configurator; + + }; + + unique_ptr configurator_; + + }; + +} // namespace code::build + +#endif diff --git a/code/build/application.hxx b/code/build/application.hxx new file mode 100644 index 0000000..9983893 --- /dev/null +++ b/code/build/application.hxx @@ -0,0 +1,55 @@ +#ifndef code__build__application_hxx_ +#define code__build__application_hxx_ + +#include +#include +#include +#include + +namespace code::build +{ + + template + class application_t + { + public: + template + application_t(Args&&...); + + ~application_t() noexcept; + + void + join(); + + template + A& + find_augmentation() + { + return do_find_augmentation(typeid(A)).template cast(); + } + + private: + class initialization_context_t; + + template + struct factory_t; + + template + static + vector + make_initializers(Args&&...); + + augmentation_t& + do_find_augmentation(type_index); + + private: + vector augmentations_; + vector workers_; + + }; + +} // namespace code::build + +#include + +#endif diff --git a/code/build/application.txx b/code/build/application.txx new file mode 100644 index 0000000..54cb6a2 --- /dev/null +++ b/code/build/application.txx @@ -0,0 +1,163 @@ +namespace code::build +{ + + template + class application_t::initialization_context_t + : public build::initialization_context_t + { + public: + application_t& application; + + initialization_context_t(application_t& app) + : application{app} + {} + + augmentation_t& + do_find_augmentation(type_index type) override + { + return application.do_find_augmentation(type); + } + + configurator_t& + do_register_configurator(configurator_t configurator) override + { + // fixme: ensure interface has not already been configured. + // + + auto type = configurator.get_interface_type(); + return configurators_.emplace(type, move(configurator)).first->second; + } + + configurator_t& + do_find_configurator(type_index type) override + { + if (auto it = configurators_.find(type); it != configurators_.end()) { + return it->second; + } + + throw invalid_configurator_t{}; + } + + private: + map configurators_; + + }; + + template + template + struct application_t::factory_t + { + std::tuple args; + + factory_t(Args&&... args) + : args{forward(args)...} + {} + + augmentation_t + make() const + { + auto construct = [&](auto&&... args) + { + return augmentation_t{std::in_place_type, forward(args)...}; + }; + + return std::apply(construct, args); + } + + }; + + template + template + application_t:: + application_t(Args&&... args) + { + auto initializers = make_initializers(std::forward(args)...); + + vector augmentations; + + for (auto& j : initializers) { + augmentations.emplace_back(j.make()); + } + + initialization_context_t context{*this}; + + for (auto& j : augmentations) { + if (j.can_init()) { + j.init(context); + } + } + + for (auto it = augmentations.rbegin(); it != augmentations.rend(); ++it) { + if (it->can_finalize()) { + it->finalize(context); + } + } + + vector workers; + + for (auto& j : augmentations) { + if (j.can_start()) { + workers.emplace_back(j.start(context)); + } + } + + augmentations_ = move(augmentations); + workers_ = move(workers); + } + + template + application_t:: + ~application_t() noexcept + { + try { + join(); + } + catch (...) { + } + } + + template + void + application_t:: + join() + { + for (auto& j : workers_) { + j.join(); + } + } + + template + template + vector + application_t:: + make_initializers(Args&&... args) + { + vector initializers; + + (( + initializers.emplace_back(std::in_place_type>) + ), ...); + + initializers.emplace_back( + std::in_place_type>, + forward(args)... + ); + + return initializers; + } + + template + augmentation_t& + application_t:: + do_find_augmentation(type_index type) + { + for (auto& j : augmentations_) { + if (j.get_type() == type) { + return j; + } + } + + throw invalid_augmentation_t{}; + } + +} // namespace code::build diff --git a/code/build/buildfile b/code/build/buildfile new file mode 100644 index 0000000..4893e5f --- /dev/null +++ b/code/build/buildfile @@ -0,0 +1,72 @@ +intf_libs = # Interface dependencies. +impl_libs = # Implementation dependencies. +test_libs = # Test dependencies. + +import intf_libs =+ libcode-uri%lib{code-uri} +import intf_libs =+ libcode-json%lib{code-json} +import intf_libs =+ libcode-seafire-common%lib{code-seafire-common} +import intf_libs =+ libcode-seafire-protocol%lib{code-seafire-protocol} +import intf_libs =+ libcode-seafire-server%lib{code-seafire-server} +import intf_libs =+ libcode-seafire-routing%lib{code-seafire-routing} +import intf_libs =+ libcode-seafire-representation%lib{code-seafire-representation} +import intf_libs =+ libcode-seafire-resources%lib{code-seafire-resources} + +./: lib{code-build}: libul{code-build} + +libul{code-build}: {hxx ixx txx cxx}{** -**.test... -version} \ + {hxx }{ version} + +libul{code-build}: $impl_libs $intf_libs + +# Unit tests. +# +exe{*.test}: +{ + test = true + install = false +} + +for t: cxx{**.test...} +{ + d = $directory($t) + n = $name($t)... + + ./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n} $test_libs + $d/exe{$n}: libul{code-build}: bin.whole = false +} + +hxx{version}: in{version} $src_root/manifest +{ + dist = true + clean = ($src_root != $out_root) +} + +# Build options. +# +cxx.poptions =+ "-I$out_root" "-I$src_root" + +# Export options. +# +lib{code-build}: +{ + cxx.export.poptions = "-I$out_root" "-I$src_root" + cxx.export.libs = $intf_libs +} + +# For pre-releases use the complete version to make sure they cannot +# be used in place of another pre-release or the final version. See +# the version module for details on the version.* variable values. +# +if $version.pre_release + lib{code-build}: bin.lib.version = "-$version.project_id" +else + lib{code-build}: bin.lib.version = "-$version.major.$version.minor" + +# Install into the code/build/ subdirectory of, say, /usr/include/ +# recreating subdirectories. +# +{hxx ixx txx}{*}: +{ + install = include/code/build/ + install.subdirs = true +} diff --git a/code/build/diagnostics.cxx b/code/build/diagnostics.cxx new file mode 100644 index 0000000..d179d8b --- /dev/null +++ b/code/build/diagnostics.cxx @@ -0,0 +1,244 @@ +#include + +#include + +namespace code::build +{ + + static + diagnostics_t* + _activated{}; + + category_t:: + category_t(string prefix) + : _prefix{move(prefix)} + {} + + string const& + category_t:: + prefix() const + { + return _prefix; + } + + level_t:: + level_t(string prefix) + : _prefix{move(prefix)} + {} + + string const& + level_t:: + prefix() const + { + return _prefix; + } + + void + diagnostics_t:: + activate() + { + _activated = this; + } + + void + diagnostics_t:: + show_origin() + { + _show_origin = true; + } + + void + diagnostics_t:: + hide_origin() + { + _show_origin = false; + } + + void + diagnostics_t:: + enable(category_t const& category) + { + _categories.emplace(&category); + } + + void + diagnostics_t:: + disable(category_t const& category) + { + _categories.erase(&category); + } + + bool + diagnostics_t:: + is_enabled(category_t const& category) + { + return _categories.contains(&category); + } + + void + diagnostics_t:: + enable(level_t const& level) + { + _severities.emplace(&level); + } + + void + diagnostics_t:: + disable(level_t const& level) + { + _severities.erase(&level); + } + + bool + diagnostics_t:: + is_enabled(level_t const& level) + { + return _severities.contains(&level); + } + + diagnostics_t:: + diagnostics_t() = default; + + diagnostics_t:: + ~diagnostics_t() + { + if (_activated == this) { + _activated = nullptr; + } + } + + string + diagnostics_t:: + do_format(record_t const& record) + { + std::size_t c_length{0}; + + for (auto const& j : _categories) { + if (j->prefix().size() > c_length) { + c_length = j->prefix().size(); + } + } + + std::size_t s_length{0}; + + for (auto const& j : _severities) { + if (j->prefix().size() > s_length) { + s_length = j->prefix().size(); + } + } + + stringstream what{record.what()}; + stringstream str; + + for (string line; getline(what, line);) { + str << std::setw(s_length) << std::left << record.level().prefix() << ": " + << std::setw(c_length) << std::left << record.category().prefix() << ": " + << line + << "\n" + ; + } + + return str.str(); + } + + void + diagnostics_t:: + report(record_t const& record) + { + if (_activated) { + if ( + _activated->is_enabled(record.category()) && + _activated->is_enabled(record.level()) + ) { + _activated->do_report(_activated->do_format(record)); + } + } + } + + diagnostics_t::record_t:: + record_t(category_t const& category, + level_t const& level, + source_location const& origin) + : _category{category}, + _level{level}, + _origin{origin} + {} + + diagnostics_t::record_t:: + ~record_t() noexcept + { + diagnostics_t::report(*this); + } + + category_t const& + diagnostics_t::record_t:: + category() const + { + return _category; + } + + level_t const& + diagnostics_t::record_t:: + level() const + { + return _level; + } + + source_location const& + diagnostics_t::record_t:: + origin() const + { + return _origin; + } + + string + diagnostics_t::record_t:: + what() const + { + return _what.str(); + } + + ostream_diagnostics_t:: + ostream_diagnostics_t(ostream& out) + : _out{out} + {} + + void + ostream_diagnostics_t:: + do_report(string const& what) + { + static mutex lock; + + std::lock_guard guard{lock}; + _out << what; + } + + level_t const& + info() + { + static level_t level{"info"}; + return level; + } + + level_t const& + warning() + { + static level_t level{"warning"}; + return level; + } + + level_t const& + error() + { + static level_t level{"error"}; + return level; + } + + level_t const& + debug() + { + static level_t level{"debug"}; + return level; + } + +} // namespace code::build diff --git a/code/build/diagnostics.hxx b/code/build/diagnostics.hxx new file mode 100644 index 0000000..56b2641 --- /dev/null +++ b/code/build/diagnostics.hxx @@ -0,0 +1,202 @@ +#ifndef code__build__diagnostics_hxx_ +#define code__build__diagnostics_hxx_ + +#include + +namespace code::build +{ + + class category_t + { + public: + explicit + category_t(string); + + category_t(category_t const&) = delete; + category_t(category_t&&) = delete; + + string const& + prefix() const; + + category_t& operator=(category_t const&) = delete; + category_t& operator=(category_t&&) = delete; + + private: + string _prefix; + + }; + + class level_t + { + public: + explicit + level_t(string); + + level_t(level_t const&) = delete; + level_t(level_t&&) = delete; + + string const& + prefix() const; + + level_t& operator=(level_t const&) = delete; + level_t& operator=(level_t&&) = delete; + + private: + string _prefix; + + }; + + class diagnostics_t + { + public: + class record_t; + friend record_t; + + void + activate(); + + void + show_origin(); + + void + hide_origin(); + + void + enable(category_t const&); + + void + disable(category_t const&); + + bool + is_enabled(category_t const&); + + void + enable(level_t const&); + + void + disable(level_t const&); + + bool + is_enabled(level_t const&); + + protected: + diagnostics_t(); + + diagnostics_t(diagnostics_t const&) = delete; + diagnostics_t(diagnostics_t&&) = delete; + + ~diagnostics_t() noexcept; + + virtual + string + do_format(record_t const&); + + virtual + void + do_report(string const&) = 0; + + static + void + report(record_t const&); + + private: + bool _show_origin{}; + set _categories; + set _severities; + + }; + + class diagnostics_t::record_t + { + friend diagnostics_t; + + public: + record_t(category_t const&, + level_t const&, + source_location const& = source_location::current()); + + record_t(record_t const&) = delete; + record_t(record_t&&) = delete; + + ~record_t() noexcept; + + category_t const& + category() const; + + level_t const& + level() const; + + source_location const& + origin() const; + + string + what() const; + + record_t& operator=(record_t const&) = delete; + record_t& operator=(record_t&&) = delete; + + template + record_t& + operator<<(T const& what) + { + _what << what; + return *this; + } + + private: + category_t const& _category; + level_t const& _level; + source_location _origin; + ostringstream _what; + + }; + + inline + diagnostics_t::record_t + record(category_t const& category, + level_t const& level, + source_location const& = source_location::current()) + { + return diagnostics_t::record_t{ + category, + level + }; + } + + class ostream_diagnostics_t + : public diagnostics_t + { + public: + explicit + ostream_diagnostics_t(ostream&); + + ostream_diagnostics_t(ostream_diagnostics_t const&) = delete; + ostream_diagnostics_t(ostream_diagnostics_t&&) = delete; + + ostream_diagnostics_t& operator=(ostream_diagnostics_t const&) = delete; + ostream_diagnostics_t& operator=(ostream_diagnostics_t&&) = delete; + + protected: + void + do_report(string const&) override; + + private: + ostream& _out; + + }; + + level_t const& + info(); + + level_t const& + warning(); + + level_t const& + error(); + + level_t const& + debug(); + +} // namespace code::build + +#endif diff --git a/code/build/except.hxx b/code/build/except.hxx new file mode 100644 index 0000000..6e0651b --- /dev/null +++ b/code/build/except.hxx @@ -0,0 +1,58 @@ +#ifndef code__build__except_hxx_ +#define code__build__except_hxx_ + +#include + +namespace code::build +{ + + class not_implemented_t + : public runtime_error + { + public: + not_implemented_t(source_location origin = source_location::current()) + : runtime_error{"function not implemented"}, + _origin{move(origin)} + {} + + not_implemented_t(string what, source_location origin = source_location::current()) + : runtime_error{move(what)}, + _origin{move(origin)} + {} + + using runtime_error::runtime_error; + + source_location const& + origin() const + { + return _origin; + } + + private: + source_location _origin; + + }; + + class invalid_augmentation_t + : public runtime_error + { + public: + invalid_augmentation_t() + : runtime_error{"invalid augmentation"} + {} + + }; + + class invalid_configurator_t + : public runtime_error + { + public: + invalid_configurator_t() + : runtime_error{"invalid configurator"} + {} + + }; + +} // namespace code::build + +#endif diff --git a/code/build/initialization-context.hxx b/code/build/initialization-context.hxx new file mode 100644 index 0000000..fcb014b --- /dev/null +++ b/code/build/initialization-context.hxx @@ -0,0 +1,67 @@ +#ifndef code__build__initialization_context_hxx_ +#define code__build__initialization_context_hxx_ + +#include +#include + +namespace code::build +{ + + class initialization_context_t + { + public: + template + A& + find_augmentation() + { + return do_find_augmentation(typeid(A)).cast(); + } + + template + C& + register_configurator(Args&&... args) + { + configurator_t c{ + std::in_place_type, + std::in_place_type, + forward(args)... + }; + + return do_register_configurator(move(c)).cast(); + } + + template + C& + find_configurator() + { + return do_find_configurator(typeid(I)).cast(); + } + + protected: + initialization_context_t() = default; + + ~initialization_context_t() noexcept = default; + + initialization_context_t(initialization_context_t const&) = delete; + initialization_context_t(initialization_context_t&&) = delete; + initialization_context_t& operator=(initialization_context_t const&) = delete; + initialization_context_t& operator=(initialization_context_t&&) = delete; + + private: + virtual + augmentation_t& + do_find_augmentation(type_index) = 0; + + virtual + configurator_t& + do_register_configurator(configurator_t) = 0; + + virtual + configurator_t& + do_find_configurator(type_index) = 0; + + }; + +} // namespace code::build + +#endif diff --git a/code/build/traits.hxx b/code/build/traits.hxx new file mode 100644 index 0000000..3970bee --- /dev/null +++ b/code/build/traits.hxx @@ -0,0 +1,175 @@ +#ifndef code__build__traits_hxx_ +#define code__build__traits_hxx_ + +namespace code::build +{ + + class initialization_context_t; + class worker_t; + +} // namespace code::build + +namespace code::build::traits +{ + template + struct function_traits; + + template + struct function_traits + { + static constexpr std::size_t arity = sizeof...(Args); + using return_type = std::decay_t; + using argument_tuple = std::tuple...>; + }; + + template + struct function_traits + { + static constexpr std::size_t arity = sizeof...(Args); + using return_type = std::decay_t; + using class_type = Class; + using argument_tuple = std::tuple...>; + }; + + template + struct function_traits + { + static constexpr std::size_t arity = sizeof...(Args); + using return_type = std::decay_t; + using class_type = Class; + using argument_tuple = std::tuple...>; + }; + + template + struct function_traits + { + static constexpr std::size_t arity = sizeof...(Args); + using return_type = std::decay_t; + using argument_tuple = std::tuple...>; + }; + + // function_arg_n + // + + template< + typename F, + std::size_t arg, + std::size_t arity = function_traits::arity + > + struct function_arg_n; + + template< + typename F, + std::size_t arg + > + struct function_arg_n + { + using function_traits = traits::function_traits; + using type = std::tuple_element_t; + }; + + template< + typename F, + std::size_t arg + > + struct function_arg_n + { + using function_traits = traits::function_traits; + using type = std::tuple_element_t; + }; + + template< + typename F, + std::size_t arg + > + using function_arg_n_t = function_arg_n::type; + + template> + struct is_initializable_augmentation + : std::false_type + {}; + + template + struct is_initializable_augmentation< + T, + std::void_t< + decltype( + std::declval().init(std::declval()) + ) + > + > : std::true_type + {}; + + template + inline constexpr bool is_initializable_augmentation_v{ + is_initializable_augmentation::value + }; + + template> + struct is_finalizable_augmentation + : std::false_type + {}; + + template + struct is_finalizable_augmentation< + T, + std::void_t< + decltype( + std::declval().finalize(std::declval()) + ) + > + > : std::true_type + {}; + + template + inline constexpr bool is_finalizable_augmentation_v{ + is_finalizable_augmentation::value + }; + + template> + struct is_startable_augmentation + : std::false_type + {}; + + template + struct is_startable_augmentation< + T, + std::void_t< + decltype(&T::start) + > + > : std::bool_constant< + std::is_convertible_v< + typename function_traits::return_type, + worker_t + > + > + {}; + + template + inline constexpr bool is_startable_augmentation_v{ + is_startable_augmentation::value + }; + + template + struct augmentation_traits + { + static + constexpr + bool + is_initializable{is_initializable_augmentation_v}; + + static + constexpr + bool + is_finalizable{is_finalizable_augmentation_v}; + + static + constexpr + bool + is_startable{is_startable_augmentation_v}; + + }; + +} // namespace code::build::traits + +#endif diff --git a/code/build/types.hxx b/code/build/types.hxx new file mode 100644 index 0000000..d4b91c5 --- /dev/null +++ b/code/build/types.hxx @@ -0,0 +1,99 @@ +#ifndef code__build__types_hxx_ +#define code__build__types_hxx_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace code::build +{ + + using std::source_location; + + using std::forward; + using std::move; + using std::swap; + + using std::size_t; + + using std::int8_t; + using std::int16_t; + using std::int32_t; + using std::int64_t; + using std::uint8_t; + using std::uint16_t; + using std::uint32_t; + using std::uint64_t; + + using std::function; + + using std::istream; + using std::ostream; + using std::iostream; + + using std::ifstream; + using std::ofstream; + using std::fstream; + + using std::istringstream; + using std::ostringstream; + using std::stringstream; + + using std::error_code; + + using std::exception; + using std::invalid_argument; + using std::logic_error; + using std::runtime_error; + + using std::shared_ptr; + using std::unique_ptr; + + using std::make_shared; + using std::make_unique; + + using std::deque; + using std::list; + using std::map; + using std::queue; + using std::set; + using std::tuple; + using std::variant; + using std::vector; + + using std::optional; + using std::nullopt; + + using std::string; + using strings = vector; + + using std::type_index; + + using std::thread; + using std::mutex; + using std::recursive_mutex; + + using std::chrono::system_clock; + using time_point = system_clock::time_point; + +} // namespace code::build + +#endif diff --git a/code/build/version.hxx.in b/code/build/version.hxx.in new file mode 100644 index 0000000..68731a1 --- /dev/null +++ b/code/build/version.hxx.in @@ -0,0 +1,37 @@ +#ifndef code__build__version_hxx_ +#define code__build__version_hxx_ + +// The numeric version format is AAAAABBBBBCCCCCDDDE where: +// +// AAAAA - major version number +// BBBBB - minor version number +// CCCCC - bugfix version number +// DDD - alpha / beta (DDD + 500) version number +// E - final (0) / snapshot (1) +// +// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example: +// +// Version AAAAABBBBBCCCCCDDDE +// +// 0.1.0 0000000001000000000 +// 0.1.2 0000000001000020000 +// 1.2.3 0000100002000030000 +// 2.2.0-a.1 0000200001999990010 +// 3.0.0-b.2 0000299999999995020 +// 2.2.0-a.1.z 0000200001999990011 +// +#define LIBCODE_BUILD_VERSION $libcode_build.version.project_number$ULL +#define LIBCODE_BUILD_VERSION_STR "$libcode_build.version.project$" +#define LIBCODE_BUILD_VERSION_ID "$libcode_build.version.project_id$" +#define LIBCODE_BUILD_VERSION_FULL "$libcode_build.version$" + +#define LIBCODE_BUILD_VERSION_MAJOR $libcode_build.version.major$ +#define LIBCODE_BUILD_VERSION_MINOR $libcode_build.version.minor$ +#define LIBCODE_BUILD_VERSION_PATCH $libcode_build.version.patch$ + +#define LIBCODE_BUILD_PRE_RELEASE $libcode_build.version.pre_release$ + +#define LIBCODE_BUILD_SNAPSHOT_SN $libcode_build.version.snapshot_sn$ULL +#define LIBCODE_BUILD_SNAPSHOT_ID "$libcode_build.version.snapshot_id$" + +#endif diff --git a/code/build/visitor.hxx b/code/build/visitor.hxx new file mode 100644 index 0000000..42a1578 --- /dev/null +++ b/code/build/visitor.hxx @@ -0,0 +1,38 @@ +#ifndef code__build__visitor_hxx_ +#define code__build__visitor_hxx_ + +namespace code::build +{ + + class visitor_t + { + public: + virtual + ~visitor_t() noexcept = default; + + virtual + void + visit_default() = 0; + + protected: + visitor_t() = default; + + }; + + template + class basic_visitor_t + { + public: + virtual + void + visit(V const&) = 0; + + protected: + basic_visitor_t() = default; + + ~basic_visitor_t() noexcept = default; + }; + +} // namespace code::build + +#endif diff --git a/code/build/web/augmentation.cxx b/code/build/web/augmentation.cxx new file mode 100644 index 0000000..8be0a78 --- /dev/null +++ b/code/build/web/augmentation.cxx @@ -0,0 +1,105 @@ +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +namespace code::build::web +{ + + augmentation_t:: + augmentation_t() + : _diagnostics{std::cout} // fixme: make configurable + {} + + void + augmentation_t:: + init(initialization_context_t& context) + { + record(web_category(), info()) << "initializing..."; + + auto& c = context.register_configurator< + configurator_t, + configurator_t + >(); + + // provide some HTTP defaults for each response. + // + c.use( + [] + ( + seafire::server::request_t& req, + seafire::server::response_t& res, + seafire::server::request_handler_t const& next + ) + { + using code::seafire::protocol::rfc7231::server_t; + using code::seafire::protocol::rfc7231::product_t; + using code::seafire::protocol::rfc7231::products_t; + + code::seafire::server::set(res, products_t{ + product_t{"Code-Build", LIBCODE_BUILD_VERSION_STR} + }); + + next.invoke(req, res); + } + ); + + } + + worker_t + augmentation_t:: + start(initialization_context_t& context) + { + auto& c = context.find_configurator(); + + code::seafire::server::server_t::acceptor_set_t acceptors; + + for (auto const& j : c.endpoints()) { + acceptors.emplace( + make_unique(_io_context, j) + ); + } + + if (acceptors.empty()) { + record( + web_category(), + warning() + ) << "acceptor set empty, application will not accept any requests"; + } + + record(web_category(), info()) << "routing table:\n" << c.root(); + + auto routing_table = c.build_routing_table(); + + { + diagnostics_t::record_t r{web_category(), info()}; + + for (auto const& j : routing_table.endpoints()) { + r << "configured endpoint: " << j << "\n"; + } + } + + record( + web_category(), + info() + ) << "starting web server..."; + + return make_worker( + _io_context, + _diagnostics, + code::seafire::server::configuration_t{}, // fixme: make configurable. + move(acceptors), + code::seafire::routing::router_t{_diagnostics, routing_table} + ); + } + +} // namespace code::build::web diff --git a/code/build/web/augmentation.hxx b/code/build/web/augmentation.hxx new file mode 100644 index 0000000..ca64c34 --- /dev/null +++ b/code/build/web/augmentation.hxx @@ -0,0 +1,40 @@ +#ifndef code__build__web__augmentation_hxx_ +#define code__build__web__augmentation_hxx_ + +#include +#include +#include + +#include + +#include + +namespace code::build::web +{ + + class augmentation_t + { + public: + augmentation_t(); + + augmentation_t(augmentation_t const&) = delete; + augmentation_t(augmentation_t&&) = delete; + + void + init(initialization_context_t&); + + worker_t + start(initialization_context_t&); + + augmentation_t& operator=(augmentation_t const&) = delete; + augmentation_t& operator=(augmentation_t&&) = delete; + + private: + asio::io_context _io_context; + seafire::common::ostream_diagnostics_t _diagnostics; + + }; + +} // namespace code::build::web + +#endif diff --git a/code/build/web/configurator.cxx b/code/build/web/configurator.cxx new file mode 100644 index 0000000..5eb6655 --- /dev/null +++ b/code/build/web/configurator.cxx @@ -0,0 +1,62 @@ +#include + +namespace code::build::web +{ + + configurator_t:: + configurator_t() = default; + + void + configurator_t:: + add_endpoint(asio::ip::tcp::endpoint endpoint) + { + _endpoints.emplace_back(endpoint); + } + + seafire::routing::route_t& + configurator_t:: + add_route() + { + return _root.add_route(); + } + + seafire::routing::route_t& + configurator_t:: + add_route(seafire::routing::route_t route) + { + return _root.add_route(move(route)); + } + + void + configurator_t:: + use(seafire::server::middleware_t middleware) + { + _root.use(move(middleware)); + } + + vector const& + configurator_t:: + endpoints() const + { + return _endpoints; + } + + seafire::routing::route_t const& + configurator_t:: + root() const + { + return _root; + } + + seafire::routing::routing_table_t + configurator_t:: + build_routing_table() const + { + seafire::routing::routing_table_t::builder_t builder; + builder.add_route(root()); + + return builder.build(); + } + + +} // namespace code::build::web diff --git a/code/build/web/configurator.hxx b/code/build/web/configurator.hxx new file mode 100644 index 0000000..7dc3f9b --- /dev/null +++ b/code/build/web/configurator.hxx @@ -0,0 +1,63 @@ +#ifndef code__build__web__configurator_hxx_ +#define code__build__web__configurator_hxx_ + +#include + +#include + +#include +#include + +#include + +namespace code::build::web +{ + + class augmentation_t; + + class configurator_t + { + friend augmentation_t; + + public: + class endpoint_t; + + configurator_t(); + + configurator_t(configurator_t const&) = delete; + configurator_t(configurator_t&&) = delete; + + void + add_endpoint(asio::ip::tcp::endpoint); + + seafire::routing::route_t& + add_route(); + + seafire::routing::route_t& + add_route(seafire::routing::route_t); + + void + use(seafire::server::middleware_t); + + configurator_t& operator=(configurator_t const&) = delete; + configurator_t& operator=(configurator_t&&) = delete; + + protected: + vector const& + endpoints() const; + + seafire::routing::route_t const& + root() const; + + seafire::routing::routing_table_t + build_routing_table() const; + + private: + vector _endpoints; + seafire::routing::route_t _root; + + }; + +} // namespace code::build::web + +#endif diff --git a/code/build/web/diagnostics.cxx b/code/build/web/diagnostics.cxx new file mode 100644 index 0000000..3508386 --- /dev/null +++ b/code/build/web/diagnostics.cxx @@ -0,0 +1,20 @@ +#include + +namespace code::build::web +{ + + category_t const& + http_category() + { + static category_t const category{"http"}; + return category; + } + + category_t const& + web_category() + { + static category_t const category{"web"}; + return category; + } + +} // namespace code::build::web diff --git a/code/build/web/diagnostics.hxx b/code/build/web/diagnostics.hxx new file mode 100644 index 0000000..ca340e3 --- /dev/null +++ b/code/build/web/diagnostics.hxx @@ -0,0 +1,18 @@ +#ifndef code__build__web__diagnostics_hxx_ +#define code__build__web__diagnostics_hxx_ + +#include +#include + +namespace code::build::web +{ + + category_t const& + http_category(); + + category_t const& + web_category(); + +} // namespace code::build::web + +#endif diff --git a/code/build/web/except.cxx b/code/build/web/except.cxx new file mode 100644 index 0000000..6c204e6 --- /dev/null +++ b/code/build/web/except.cxx @@ -0,0 +1,19 @@ +#include + +namespace code::build::web +{ + + web_exception_t:: + web_exception_t(seafire::server::common_error_t error, string what) + : runtime_error{move(what)}, + _error{error} + {} + + seafire::server::common_error_t + web_exception_t:: + error() const + { + return _error; + } + +} // namespace code::build::web diff --git a/code/build/web/except.hxx b/code/build/web/except.hxx new file mode 100644 index 0000000..281a386 --- /dev/null +++ b/code/build/web/except.hxx @@ -0,0 +1,28 @@ +#ifndef code__build__web__except_hxx_ +#define code__build__web__except_hxx_ + +#include + +#include + +namespace code::build::web +{ + + class web_exception_t + : public runtime_error + { + public: + web_exception_t(seafire::server::common_error_t, string); + + seafire::server::common_error_t + error() const; + + private: + seafire::server::common_error_t _error; + + + }; + +} // namespace code::build::web + +#endif diff --git a/code/build/web/http-server.cxx b/code/build/web/http-server.cxx new file mode 100644 index 0000000..ba0c6b4 --- /dev/null +++ b/code/build/web/http-server.cxx @@ -0,0 +1,85 @@ +#include +#include +#include + +namespace code::build::web +{ + + http_server_t:: + http_server_t(asio::io_context& io_context, + seafire::common::diagnostics_t& d, + seafire::server::configuration_t config, + seafire::server::server_t::acceptor_set_t acceptors, + seafire::server::request_handler_t handler) + : seafire::server::server_t{d, config, move(acceptors), handler}, + _io_context{io_context} + { + start(); + + for (int i; i < 8; ++i) { + _threads.emplace_back([this] { run(); }); + } + } + + http_server_t:: + ~http_server_t() noexcept = default; + + void + http_server_t:: + join() + { + for (auto& j : _threads) { + j.join(); + } + } + + void + http_server_t:: + on_error(seafire::server::request_t& req, + seafire::server::response_t& res, + seafire::server::common_error_t error) + { + seafire::server::server_t::on_error(req, res, error); + } + + void + http_server_t:: + on_exception(seafire::server::request_t&, + seafire::server::response_t& res) noexcept + { + try { + throw; + } + catch (web_exception_t const& ex) { + record( + web_category(), + error() + ) << "exception: " << ex.what() << ": " << (int)ex.error(); + + res.send(ex.error()); + return; + } + catch (exception const& ex) { + record( + web_category(), + error() + ) << "exception: " << ex.what(); + } + // catch (...) { + // record( + // web_category(), + // error() + // ) << "exception: unknown exception thrown"; + // } + + res.send(500); + } + + void + http_server_t:: + run() + { + _io_context.run(); + } + +} // namespace code::build::web diff --git a/code/build/web/http-server.hxx b/code/build/web/http-server.hxx new file mode 100644 index 0000000..06d91c8 --- /dev/null +++ b/code/build/web/http-server.hxx @@ -0,0 +1,55 @@ +#ifndef code__build__web__http_server_hxx_ +#define code__build__web__http_server_hxx_ + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace code::build::web +{ + + class http_server_t + : seafire::server::server_t + { + public: + http_server_t(asio::io_context&, + seafire::common::diagnostics_t&, + seafire::server::configuration_t, + seafire::server::server_t::acceptor_set_t, + seafire::server::request_handler_t); + + ~http_server_t() noexcept; + + void + join(); + + protected: + void + on_error(seafire::server::request_t&, + seafire::server::response_t&, + seafire::server::common_error_t) override; + + void + on_exception(seafire::server::request_t&, + seafire::server::response_t&) noexcept override; + + private: + void + run(); + + asio::io_context& _io_context; + vector _threads; + + }; + +} // namespace code::build::web + +#endif diff --git a/manifest b/manifest new file mode 100644 index 0000000..2578679 --- /dev/null +++ b/manifest @@ -0,0 +1,20 @@ +: 1 +name: libcode-build +version: 0.1.0-a.0.z +language: c++ +summary: libcode-build C++ library +license: BSD-4-Clause +description-file: README.md +url: https://helloryan.se/code/ +email: ryan@helloryan.se +depends: * build2 >= 0.17.0 +depends: * bpkg >= 0.17.0 +depends: libasio ^1.29.0 +depends: libcode-uri ^0.1.0- +depends: libcode-json ^0.1.0- +depends: libcode-seafire-common ^0.1.0- +depends: libcode-seafire-protocol ^0.1.0- +depends: libcode-seafire-server ^0.1.0- +depends: libcode-seafire-routing ^0.1.0- +depends: libcode-seafire-representation ^0.1.0- +depends: libcode-seafire-resources ^0.1.0- diff --git a/repositories.manifest b/repositories.manifest new file mode 100644 index 0000000..bd15616 --- /dev/null +++ b/repositories.manifest @@ -0,0 +1,39 @@ +: 1 +summary: libcode-build project repository + +: +role: prerequisite +location: https://pkg.cppget.org/1/beta +trust: 70:64:FE:E4:E0:F3:60:F1:B4:51:E1:FA:12:5C:E0:B3:DB:DF:96:33:39:B9:2E:E5:C2:68:63:4C:A6:47:39:43 + +: +role: prerequisite +location: https://code.helloryan.se/code/libcode-uri.git##HEAD + +: +role: prerequisite +location: https://code.helloryan.se/code/libcode-json.git##HEAD + +: +role: prerequisite +location: https://code.helloryan.se/code/libcode-seafire-common.git##HEAD + +: +role: prerequisite +location: https://code.helloryan.se/code/libcode-seafire-protocol.git##HEAD + +: +role: prerequisite +location: https://code.helloryan.se/code/libcode-seafire-server.git##HEAD + +: +role: prerequisite +location: https://code.helloryan.se/code/libcode-seafire-routing.git##HEAD + +: +role: prerequisite +location: https://code.helloryan.se/code/libcode-seafire-representation.git##HEAD + +: +role: prerequisite +location: https://code.helloryan.se/code/libcode-seafire-resources.git##HEAD diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..662178d --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,8 @@ +# Test executables. +# +driver + +# Testscript output directories (can be symlinks). +# +test +test-* diff --git a/tests/build/.gitignore b/tests/build/.gitignore new file mode 100644 index 0000000..974e01d --- /dev/null +++ b/tests/build/.gitignore @@ -0,0 +1,4 @@ +/config.build +/root/ +/bootstrap/ +build/ diff --git a/tests/build/bootstrap.build b/tests/build/bootstrap.build new file mode 100644 index 0000000..a07b5ea --- /dev/null +++ b/tests/build/bootstrap.build @@ -0,0 +1,5 @@ +project = # Unnamed tests subproject. + +using config +using test +using dist diff --git a/tests/build/root.build b/tests/build/root.build new file mode 100644 index 0000000..a67b2fe --- /dev/null +++ b/tests/build/root.build @@ -0,0 +1,16 @@ +cxx.std = latest + +using cxx + +hxx{*}: extension = hxx +ixx{*}: extension = ixx +txx{*}: extension = txx +cxx{*}: extension = cxx + +# Every exe{} in this subproject is by default a test. +# +exe{*}: test = true + +# The test target for cross-testing (running tests under Wine, etc). +# +test.target = $cxx.target diff --git a/tests/buildfile b/tests/buildfile new file mode 100644 index 0000000..aeeab15 --- /dev/null +++ b/tests/buildfile @@ -0,0 +1 @@ +./: {*/ -build/}