Hello libcode-build

This commit is contained in:
G.H.O.S.T 2024-12-27 21:33:55 +01:00
commit 5fa111c2a3
Signed by: G.H.O.S.T
GPG Key ID: 3BD93EABD1407B82
41 changed files with 2368 additions and 0 deletions

16
.editorconfig Normal file
View File

@ -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

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto

View File

@ -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

31
.gitignore vendored Normal file
View File

@ -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

31
LICENSE Normal file
View File

@ -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.

21
README.md Normal file
View File

@ -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.

4
build/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/

7
build/bootstrap.build Normal file
View File

@ -0,0 +1,7 @@
project = libcode-build
using version
using config
using test
using install
using dist

6
build/export.build Normal file
View File

@ -0,0 +1,6 @@
$out_root/
{
include code/build/
}
export $out_root/code/build/$import.target

16
build/root.build Normal file
View File

@ -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

5
buildfile Normal file
View File

@ -0,0 +1,5 @@
./: {code/ tests/} doc{README.md} legal{LICENSE} manifest
# Don't install tests.
#
tests/: install = false

9
code/build/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
# Generated version header.
#
version.hxx
# Unit test executables and Testscript output directories
# (can be symlinks).
#
*.test
test-*.test

399
code/build/abstraction.hxx Normal file
View File

@ -0,0 +1,399 @@
#ifndef code__build__abstraction_hxx_
#define code__build__abstraction_hxx_
#include <code/build/types.hxx>
#include <code/build/traits.hxx>
namespace code::build
{
class initialization_context_t;
class worker_t
{
public:
template<typename T, typename... Args>
worker_t(std::in_place_type_t<T>, Args&&... args)
: _worker{new instance_t<T>{forward<Args>(args)...}}
{}
void
join()
{
_worker->join();
}
private:
struct abstraction_t
{
virtual
~abstraction_t() noexcept = default;
virtual
void
join() = 0;
};
template<typename W>
struct instance_t
: abstraction_t
{
template<typename... Args>
instance_t(Args&&... args)
: worker{forward<Args>(args)...}
{}
void
join() override
{
worker.join();
}
W worker;
};
unique_ptr<abstraction_t> _worker;
};
template<typename W, typename... Args>
worker_t
make_worker(Args&&... args)
{
return worker_t{std::in_place_type<W>, forward<Args>(args)...};
}
class augmentation_t
{
public:
template<typename T, typename... Args>
augmentation_t(std::in_place_type_t<T>, Args&&... args)
: _augmentation{new instance_t<T>{forward<Args>(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<typename A>
A&
cast()
{
if (auto ptr = dynamic_cast<instance_t<A>*>(_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<typename A>
struct instance_t : abstraction_t
{
template<typename... Args>
instance_t(Args&&... args)
: augmentation{forward<Args>(args)...}
{}
type_index
get_type() const override
{
return typeid(A);
}
bool
can_init() override
{
return traits::augmentation_traits<A>::is_initializable;
}
void
init(initialization_context_t& context) override
{
if constexpr (traits::augmentation_traits<A>::is_initializable) {
augmentation.init(context);
return;
}
throw logic_error{"attempt to initialize non-intializable augmentation"};
}
bool
can_finalize() override
{
return traits::augmentation_traits<A>::is_finalizable;
}
void
finalize(initialization_context_t& context) override
{
if constexpr (traits::augmentation_traits<A>::is_finalizable) {
augmentation.finalize(context);
return;
}
throw logic_error{"attempt to finalize non-finalizable augmentation"};
}
bool
can_start() override
{
return traits::augmentation_traits<A>::is_startable;
}
worker_t
start(initialization_context_t& context) override
{
if constexpr (traits::augmentation_traits<A>::is_startable) {
return augmentation.start(context);
}
throw logic_error{"attempt to start non-startable augmentation"};
}
A augmentation;
};
unique_ptr<abstraction_t> _augmentation;
};
class initializer_t
{
public:
template<typename T, typename... Args>
initializer_t(std::in_place_type_t<T>, Args&&... args)
: initializer_{new instance_t<T>{forward<Args>(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<typename T>
struct instance_t : abstraction_t
{
template<typename... Args>
instance_t(Args&&... args)
: initializer{forward<Args>(args)...}
{}
type_index
get_type() const override
{
return typeid(T);
}
virtual
augmentation_t
make() const override
{
return initializer.make();
}
T initializer;
};
unique_ptr<abstraction_t> initializer_;
};
class configurator_t
{
public:
template<typename I, typename C, typename... Args>
configurator_t(std::in_place_type_t<I>, std::in_place_type_t<C>, Args&&... args)
: configurator_{new instance_t<I, C>{forward<Args>(args)...}}
{}
type_index
get_interface_type() const
{
return configurator_->get_interface_type();
}
template<typename C>
C&
cast()
{
if (auto ptr = dynamic_cast<interface_t<C>*>(configurator_.get()); ptr) {
return ptr->cast_interface();
}
if (auto ptr = dynamic_cast<implementation_t<C>*>(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<typename I>
struct interface_t : abstraction_t
{
virtual
I&
cast_interface() = 0;
};
template<typename C>
struct implementation_t
{
virtual
~implementation_t() noexcept = default;
virtual
C&
cast() = 0;
};
template<typename I, typename C>
struct instance_t : interface_t<I>,
implementation_t<C>
{
template<typename... Args>
instance_t(Args&&... args)
: configurator{forward<Args>(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<abstraction_t> configurator_;
};
} // namespace code::build
#endif

View File

@ -0,0 +1,55 @@
#ifndef code__build__application_hxx_
#define code__build__application_hxx_
#include <code/build/abstraction.hxx>
#include <code/build/except.hxx>
#include <code/build/initialization-context.hxx>
#include <code/build/types.hxx>
namespace code::build
{
template<typename M, typename... Augmentations>
class application_t
{
public:
template<typename... Args>
application_t(Args&&...);
~application_t() noexcept;
void
join();
template<typename A>
A&
find_augmentation()
{
return do_find_augmentation(typeid(A)).template cast<A>();
}
private:
class initialization_context_t;
template<typename A, typename... Args>
struct factory_t;
template<typename... Args>
static
vector<initializer_t>
make_initializers(Args&&...);
augmentation_t&
do_find_augmentation(type_index);
private:
vector<augmentation_t> augmentations_;
vector<worker_t> workers_;
};
} // namespace code::build
#include <code/build/application.txx>
#endif

163
code/build/application.txx Normal file
View File

@ -0,0 +1,163 @@
namespace code::build
{
template<typename M, typename... Augmentations>
class application_t<M, Augmentations...>::initialization_context_t
: public build::initialization_context_t
{
public:
application_t<M, Augmentations...>& application;
initialization_context_t(application_t<M, Augmentations...>& 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<type_index, configurator_t> configurators_;
};
template<typename M, typename... Augmentations>
template<typename A, typename... Args>
struct application_t<M, Augmentations...>::factory_t
{
std::tuple<Args...> args;
factory_t(Args&&... args)
: args{forward<Args>(args)...}
{}
augmentation_t
make() const
{
auto construct = [&](auto&&... args)
{
return augmentation_t{std::in_place_type<A>, forward<decltype(args)>(args)...};
};
return std::apply(construct, args);
}
};
template<typename M, typename... Augmentations>
template<typename... Args>
application_t<M, Augmentations...>::
application_t(Args&&... args)
{
auto initializers = make_initializers(std::forward<Args>(args)...);
vector<augmentation_t> 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<worker_t> workers;
for (auto& j : augmentations) {
if (j.can_start()) {
workers.emplace_back(j.start(context));
}
}
augmentations_ = move(augmentations);
workers_ = move(workers);
}
template<typename M, typename... Augmentations>
application_t<M, Augmentations...>::
~application_t() noexcept
{
try {
join();
}
catch (...) {
}
}
template<typename M, typename... Augmentations>
void
application_t<M, Augmentations...>::
join()
{
for (auto& j : workers_) {
j.join();
}
}
template<typename M, typename... Augmentations>
template<typename... Args>
vector<initializer_t>
application_t<M, Augmentations...>::
make_initializers(Args&&... args)
{
vector<initializer_t> initializers;
((
initializers.emplace_back(std::in_place_type<factory_t<Augmentations>>)
), ...);
initializers.emplace_back(
std::in_place_type<factory_t<M, Args...>>,
forward<Args>(args)...
);
return initializers;
}
template<typename M, typename... Augmentations>
augmentation_t&
application_t<M, Augmentations...>::
do_find_augmentation(type_index type)
{
for (auto& j : augmentations_) {
if (j.get_type() == type) {
return j;
}
}
throw invalid_augmentation_t{};
}
} // namespace code::build

72
code/build/buildfile Normal file
View File

@ -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
}

244
code/build/diagnostics.cxx Normal file
View File

@ -0,0 +1,244 @@
#include <code/build/diagnostics.hxx>
#include <iomanip>
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<mutex> 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

202
code/build/diagnostics.hxx Normal file
View File

@ -0,0 +1,202 @@
#ifndef code__build__diagnostics_hxx_
#define code__build__diagnostics_hxx_
#include <code/build/types.hxx>
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<category_t const*> _categories;
set<level_t const*> _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<typename T>
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

58
code/build/except.hxx Normal file
View File

@ -0,0 +1,58 @@
#ifndef code__build__except_hxx_
#define code__build__except_hxx_
#include <code/build/types.hxx>
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

View File

@ -0,0 +1,67 @@
#ifndef code__build__initialization_context_hxx_
#define code__build__initialization_context_hxx_
#include <code/build/abstraction.hxx>
#include <code/build/types.hxx>
namespace code::build
{
class initialization_context_t
{
public:
template<typename A>
A&
find_augmentation()
{
return do_find_augmentation(typeid(A)).cast<A>();
}
template<typename I, typename C, typename... Args>
C&
register_configurator(Args&&... args)
{
configurator_t c{
std::in_place_type<I>,
std::in_place_type<C>,
forward<Args>(args)...
};
return do_register_configurator(move(c)).cast<C>();
}
template<typename C, typename I = C>
C&
find_configurator()
{
return do_find_configurator(typeid(I)).cast<C>();
}
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

175
code/build/traits.hxx Normal file
View File

@ -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<typename>
struct function_traits;
template<typename Ret, typename... Args>
struct function_traits<Ret(Args...)>
{
static constexpr std::size_t arity = sizeof...(Args);
using return_type = std::decay_t<Ret>;
using argument_tuple = std::tuple<std::decay_t<Args>...>;
};
template<typename Ret, typename Class, typename... Args>
struct function_traits<Ret (Class::*)(Args...)>
{
static constexpr std::size_t arity = sizeof...(Args);
using return_type = std::decay_t<Ret>;
using class_type = Class;
using argument_tuple = std::tuple<std::decay_t<Args>...>;
};
template<typename Ret, typename Class, typename... Args>
struct function_traits<Ret (Class::*)(Args...) const>
{
static constexpr std::size_t arity = sizeof...(Args);
using return_type = std::decay_t<Ret>;
using class_type = Class;
using argument_tuple = std::tuple<std::decay_t<Args>...>;
};
template<typename Ret, typename... Args>
struct function_traits<Ret(*)(Args...)>
{
static constexpr std::size_t arity = sizeof...(Args);
using return_type = std::decay_t<Ret>;
using argument_tuple = std::tuple<std::decay_t<Args>...>;
};
// function_arg_n
//
template<
typename F,
std::size_t arg,
std::size_t arity = function_traits<F>::arity
>
struct function_arg_n;
template<
typename F,
std::size_t arg
>
struct function_arg_n<F, arg, 1>
{
using function_traits = traits::function_traits<F>;
using type = std::tuple_element_t<arg, typename function_traits::argument_tuple>;
};
template<
typename F,
std::size_t arg
>
struct function_arg_n<F, arg, 2>
{
using function_traits = traits::function_traits<F>;
using type = std::tuple_element_t<arg, typename function_traits::argument_tuple>;
};
template<
typename F,
std::size_t arg
>
using function_arg_n_t = function_arg_n<F, arg>::type;
template<typename, typename = std::void_t<>>
struct is_initializable_augmentation
: std::false_type
{};
template<typename T>
struct is_initializable_augmentation<
T,
std::void_t<
decltype(
std::declval<T>().init(std::declval<initialization_context_t&>())
)
>
> : std::true_type
{};
template<typename T>
inline constexpr bool is_initializable_augmentation_v{
is_initializable_augmentation<T>::value
};
template<typename, typename = std::void_t<>>
struct is_finalizable_augmentation
: std::false_type
{};
template<typename T>
struct is_finalizable_augmentation<
T,
std::void_t<
decltype(
std::declval<T>().finalize(std::declval<initialization_context_t&>())
)
>
> : std::true_type
{};
template<typename T>
inline constexpr bool is_finalizable_augmentation_v{
is_finalizable_augmentation<T>::value
};
template<typename, typename = std::void_t<>>
struct is_startable_augmentation
: std::false_type
{};
template<typename T>
struct is_startable_augmentation<
T,
std::void_t<
decltype(&T::start)
>
> : std::bool_constant<
std::is_convertible_v<
typename function_traits<decltype(&T::start)>::return_type,
worker_t
>
>
{};
template<typename T>
inline constexpr bool is_startable_augmentation_v{
is_startable_augmentation<T>::value
};
template<typename A>
struct augmentation_traits
{
static
constexpr
bool
is_initializable{is_initializable_augmentation_v<A>};
static
constexpr
bool
is_finalizable{is_finalizable_augmentation_v<A>};
static
constexpr
bool
is_startable{is_startable_augmentation_v<A>};
};
} // namespace code::build::traits
#endif

99
code/build/types.hxx Normal file
View File

@ -0,0 +1,99 @@
#ifndef code__build__types_hxx_
#define code__build__types_hxx_
#include <chrono>
#include <cstdint>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <queue>
#include <set>
#include <source_location>
#include <sstream>
#include <stdexcept>
#include <thread>
#include <typeindex>
#include <utility>
#include <vector>
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<string>;
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

37
code/build/version.hxx.in Normal file
View File

@ -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

38
code/build/visitor.hxx Normal file
View File

@ -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<typename V>
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

View File

@ -0,0 +1,105 @@
#include <code/build/diagnostics.hxx>
#include <code/build/version.hxx>
#include <code/build/web/augmentation.hxx>
#include <code/build/web/configurator.hxx>
#include <code/build/web/diagnostics.hxx>
#include <code/build/web/http-server.hxx>
#include <code/seafire/protocol/rfc7231/server.hxx>
#include <code/seafire/common/io/tcp-acceptor.hxx>
#include <code/seafire/routing/router.hxx>
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<server_t>(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<configurator_t>();
code::seafire::server::server_t::acceptor_set_t acceptors;
for (auto const& j : c.endpoints()) {
acceptors.emplace(
make_unique<code::seafire::common::io::tcp_acceptor_t>(_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<http_server_t>(
_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

View File

@ -0,0 +1,40 @@
#ifndef code__build__web__augmentation_hxx_
#define code__build__web__augmentation_hxx_
#include <code/build/abstraction.hxx>
#include <code/build/initialization-context.hxx>
#include <code/build/types.hxx>
#include <code/seafire/common/diagnostics.hxx>
#include <asio.hpp>
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

View File

@ -0,0 +1,62 @@
#include <code/build/web/configurator.hxx>
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<asio::ip::tcp::endpoint> 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

View File

@ -0,0 +1,63 @@
#ifndef code__build__web__configurator_hxx_
#define code__build__web__configurator_hxx_
#include <code/build/types.hxx>
#include <code/seafire/server/middleware.hxx>
#include <code/seafire/routing/route.hxx>
#include <code/seafire/routing/routing-table.hxx>
#include <asio.hpp>
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<asio::ip::tcp::endpoint> const&
endpoints() const;
seafire::routing::route_t const&
root() const;
seafire::routing::routing_table_t
build_routing_table() const;
private:
vector<asio::ip::tcp::endpoint> _endpoints;
seafire::routing::route_t _root;
};
} // namespace code::build::web
#endif

View File

@ -0,0 +1,20 @@
#include <code/build/web/diagnostics.hxx>
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

View File

@ -0,0 +1,18 @@
#ifndef code__build__web__diagnostics_hxx_
#define code__build__web__diagnostics_hxx_
#include <code/build/types.hxx>
#include <code/build/diagnostics.hxx>
namespace code::build::web
{
category_t const&
http_category();
category_t const&
web_category();
} // namespace code::build::web
#endif

19
code/build/web/except.cxx Normal file
View File

@ -0,0 +1,19 @@
#include <code/build/web/except.hxx>
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

28
code/build/web/except.hxx Normal file
View File

@ -0,0 +1,28 @@
#ifndef code__build__web__except_hxx_
#define code__build__web__except_hxx_
#include <code/build/types.hxx>
#include <code/seafire/server/common-error.hxx>
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

View File

@ -0,0 +1,85 @@
#include <code/build/web/http-server.hxx>
#include <code/build/web/diagnostics.hxx>
#include <code/build/web/except.hxx>
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

View File

@ -0,0 +1,55 @@
#ifndef code__build__web__http_server_hxx_
#define code__build__web__http_server_hxx_
#include <code/build/types.hxx>
#include <code/seafire/common/diagnostics.hxx>
#include <code/seafire/server/configuration.hxx>
#include <code/seafire/server/request-handler.hxx>
#include <code/seafire/server/request.hxx>
#include <code/seafire/server/response.hxx>
#include <code/seafire/server/server.hxx>
#include <asio.hpp>
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<thread> _threads;
};
} // namespace code::build::web
#endif

20
manifest Normal file
View File

@ -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-

39
repositories.manifest Normal file
View File

@ -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

8
tests/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Test executables.
#
driver
# Testscript output directories (can be symlinks).
#
test
test-*

4
tests/build/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/

View File

@ -0,0 +1,5 @@
project = # Unnamed tests subproject.
using config
using test
using dist

16
tests/build/root.build Normal file
View File

@ -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

1
tests/buildfile Normal file
View File

@ -0,0 +1 @@
./: {*/ -build/}