diff --git a/.buckconfig b/.buckconfig
index 5274f36..8debfd6 100644
--- a/.buckconfig
+++ b/.buckconfig
@@ -1,6 +1,14 @@
[project]
ignore = .git, .buckd
+[parser]
+ default_build_file_syntax = SKYLARK
+
[cxx]
+ should_remap_host_platform = true
+
+[cxx#linux-x86_64]
+ cxxflags = -std=c++14
+
+[cxx#macosx-x86_64]
cxxflags = -std=c++14
- gtest_dep = google.gtest//:gtest
diff --git a/.gitignore b/.gitignore
index 6fb2d9d..dc2d1d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,6 @@ buckaroo
buck-out
.buckd
.buckconfig.local
+.buckconfig.d
BUCKAROO_DEPS
+buckaroo_macros.bzl
diff --git a/.travis.yml b/.travis.yml
index d7e3caa..1a51c48 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,19 +7,27 @@ addons:
sources:
- ubuntu-toolchain-r-test
packages:
- - g++-5
+ - g++-6
+ - gcc-6
+ homebrew:
+ taps:
+ - facebook/fb
+ packages:
+ - buck
+
+os:
+ - linux
+ - osx
+
+env:
+ - BUCKAROO_VERSION=buckaroo-redux-alpha-8
+
+osx_image: xcode9.3
before_install:
- - cd /usr/bin/ && sudo rm g++ && sudo ln -s g++-5 g++ && cd -
- - g++ --version
- - sudo apt-get install default-jdk
- - wget -O buck.deb https://github.com/facebook/buck/releases/download/v2017.09.04.02/buck-2017.09.04.02_all.deb
- - sudo dpkg -i buck.deb
- - wget -O buckaroo.deb https://github.com/LoopPerfect/buckaroo/releases/download/v1.3.1/buckaroo_1.3.1_amd64.deb
- - sudo dpkg -i buckaroo.deb
+ - ./travis/before-install-$TRAVIS_OS_NAME.sh
script:
- - buckaroo install
- - buck build //:neither
- - buck build //:test#linux-x86_64
- - buck run //:test#linux-x86_64
+ - ./buckaroo-client install
+ - buck build -c ui.superconsole=DISABLED :neither
+ - buck test -c ui.superconsole=DISABLED //...
diff --git a/BUCK b/BUCK
index 43f2bb3..7f16be3 100644
--- a/BUCK
+++ b/BUCK
@@ -1,3 +1,6 @@
+load('//:subdir_glob.bzl', 'subdir_glob')
+load('//:buckaroo_macros.bzl', 'buckaroo_deps_from_package')
+
prebuilt_cxx_library(
name = 'neither',
header_only = True,
@@ -18,11 +21,8 @@ cxx_test(
srcs = glob([
'neither/tests/**/*.cpp',
]),
- platform_linker_flags = [
- ('^linux.*', [ '-lpthread', ]),
- ],
- link_style = 'shared',
- deps = [
- ':neither',
- ],
+ deps = buckaroo_deps_from_package('github.com/buckaroo-pm/google-googletest') + \
+ [
+ ':neither',
+ ],
)
diff --git a/README.md b/README.md
index 3ca1c4a..8c07885 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,3 @@
-
-
# neither
A functional implementation of Either in C++14.
@@ -7,7 +5,7 @@ A functional implementation of Either in C++14.
[](https://travis-ci.org/LoopPerfect/neither) [](https://raw.githubusercontent.com/LoopPerfect/neither/master/license)
```
-buckaroo install loopperfect/neither
+buckaroo add github.com/loopperfect/neither
```
## Examples
@@ -26,15 +24,15 @@ auto unsafe = [] { // a function that throws, sometimes we can't avoid it...
Either e = Try(unsafe); // let's lift the exception into the typesystem
e.left()
- .map([](auto const& e) {
- return std::cerr << e.what() << std::endl;
+ .map([](auto const& e) {
+ return std::cerr << e.what() << std::endl;
}); // print error if available
int result = e
.leftMap([](auto) { return 42; }) // do nothing with exception and map to 42
.rightMap([](auto x) { return x * 2; }) // do further computation if value available
.join() // join both sides of either
-
+
ASSERT_TRUE(result == 42);
```
@@ -53,7 +51,7 @@ std::string resultString = compute(5)
[](auto errorStr) { return "compute said: " + errorStr; }, // error-case
[](auto x) { return "compute said: " + std::to_string(x); } // success-case
);
-
+
std::cout << resultString << std::endl;
```
@@ -93,11 +91,11 @@ Maybe compute(float x) {
Maybe x = compute(-4)
.map([](auto x){ return x*x;})
.map([](auto x){ return x+1 });
-
+
if(!x.hasValue) {
std::cerr << "error occured" << std::endl;
}
-
+
```
### Monadic Lifting
@@ -112,12 +110,12 @@ auto monadicSum = lift(sum); // transforms sum to: Maybe MonadicSum(Maybe don't use output parameters
+ - => don't use output parameters
- Exceptions are 2-3 orders of magnitude slower if exceptions are thrown
- => avoid throwing exceptions - not always possible
- Overhead of exceptions grows linear with the callstack
- => catch exceptions early
- Exceptions are not part of the type-system
- - annotating function signatures with `throw` and `noexcept` is not helpful;
+ - annotating function signatures with `throw` and `noexcept` is not helpful;
contract breaches are not detected in compile-time but call `std::terminate` in run-time
- handling exceptions is error prone and requires documentation
- - => encode errors in the types to enforce propper handling by the API consumer
+ - => encode errors in the types to enforce proper handling by the API consumer
## Installation
This library requires a C++ 14 compiler.
-Install with [Buckaroo](https://buckaroo.pm):
+Install with [Buckaroo](https://buckaroo.pm):
+
```
-buckaroo install loopperfect/neither
+buckaroo add github.com/loopperfect/neither
```
-The [Buck](https://www.buckbuild.com) target is `:neither`
+The [Buck](https://www.buckbuild.com) target is `:neither`
+
+Alternatively you can copy & paste the headers to your include path:
-Alternatively you can copy & paste the headers to your include path:
```
cp neither/include/*.hpp $InstallPath/include/neither
```
-
-
diff --git a/buckaroo.json b/buckaroo.json
deleted file mode 100644
index c9b34eb..0000000
--- a/buckaroo.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "neither",
- "dependencies": {
- "google/gtest": "1.8.0"
- }
-}
diff --git a/buckaroo.lock.json b/buckaroo.lock.json
deleted file mode 100644
index 5ae8232..0000000
--- a/buckaroo.lock.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "google/gtest": {
- "source": {
- "url": "https://github.com/google/googletest/archive/ec44c6c1675c25b9827aacd08c02433cccde7780.zip",
- "sha256": "bc258fff04a6511e7106a1575bb514a185935041b2c16affb799e0567393ec30",
- "subPath": "googletest-ec44c6c1675c25b9827aacd08c02433cccde7780"
- },
- "buck": {
- "url": "https://raw.githubusercontent.com/nikhedonia/googletest/665327f0141d4a4fc4f2496e781dce436d742645/BUCK",
- "sha256": "d976069f5b47fd8fc57201f47026a70dee4e47b3141ac23567b8a0c56bf9288c"
- }
- }
-}
\ No newline at end of file
diff --git a/buckaroo.lock.toml b/buckaroo.lock.toml
new file mode 100644
index 0000000..0b3cb74
--- /dev/null
+++ b/buckaroo.lock.toml
@@ -0,0 +1,9 @@
+manifest = "eaaa6562a921b2d40d31aed0f4b442994cb5a3135397b66bbff0d1fd868b4abc"
+
+[[dependency]]
+package = "github.com/buckaroo-pm/google-googletest"
+target = "//:googletest"
+
+[lock."github.com/buckaroo-pm/google-googletest"]
+versions = [ "branch=master" ]
+revision = "cb8d2c1a2fcd344953e6c129a1a699a2c230551d"
diff --git a/buckaroo.toml b/buckaroo.toml
new file mode 100644
index 0000000..2a1effa
--- /dev/null
+++ b/buckaroo.toml
@@ -0,0 +1,6 @@
+targets = [ "//:neither" ]
+
+[[dependency]]
+package = "github.com/buckaroo-pm/google-googletest"
+version = "branch=master"
+private = true
diff --git a/neither/include/either.hpp b/neither/include/either.hpp
index 1ea7c1a..a6d28f6 100644
--- a/neither/include/either.hpp
+++ b/neither/include/either.hpp
@@ -58,22 +58,22 @@ struct Either {
bool const isLeft = 0;
constexpr Either( Left const& l )
- : leftValue{l.value}
+ : leftValue(l.value)
, isLeft(1)
{}
constexpr Either( Right const& r )
- : rightValue{r.value}
+ : rightValue(r.value)
, isLeft(0)
{}
Either(Left && l )
- : leftValue{std::move(l.value)}
+ : leftValue(std::move(l.value))
, isLeft(1)
{}
Either( Right && r )
- : rightValue{std::move(r.value)}
+ : rightValue(std::move(r.value))
, isLeft(0)
{}
@@ -116,19 +116,19 @@ struct Either {
}
static constexpr auto leftOf( L const& l ) {
- return Either{ neither::left(l) };
+ return Either( neither::left(l) );
}
static constexpr auto rightOf( R const& r ) {
- return Either{ neither::right(r) };
+ return Either( neither::right(r) );
}
static constexpr auto leftOf( L && l ) {
- return Either{ neither::left(std::move(l)) };
+ return Either( neither::left(std::move(l)) );
}
static constexpr auto rightOf( R && r ) {
- return Either{ neither::right(std::move(r)) };
+ return Either( neither::right(std::move(r)) );
}
template<
@@ -150,12 +150,30 @@ struct Either {
return isLeft ? std::move(leftValue) : std::move(rightValue);
}
+ template<
+ class L2 = L,
+ class R2 = R,
+ typename std::enable_if::value && std::is_copy_constructible::value), int>::type = 0
+ >
+ auto join()&
+ -> std::common_type_t {
+ return isLeft ? std::move(leftValue) : std::move(rightValue);
+ }
+
template
constexpr auto join(LeftF const& leftCase, RightF const& rightCase) const
-> decltype( isLeft? leftCase( leftValue ) : rightCase( rightValue ) ) {
return isLeft ? leftCase( leftValue ) : rightCase( rightValue );
}
+ template::value && std::is_copy_constructible::value), int>::type = 0
+ >
+ constexpr auto join(LeftF const& leftCase, RightF const& rightCase)&
+ -> decltype( isLeft? leftCase(std::move(leftValue)) : rightCase(std::move(rightValue)) ){
+ return isLeft ? leftCase(std::move(leftValue)) : rightCase(std::move(rightValue));
+ }
+
template
constexpr auto leftMap(F const& leftCase) const&
-> Either {
@@ -173,6 +191,16 @@ struct Either {
NextEither::rightOf( std::move(rightValue) );
}
+
+ template::value && std::is_copy_constructible::value), int>::type = 0>
+ auto leftMap(F const& leftCase)& -> Either {
+ using NextEither = Either;
+ return isLeft ?
+ NextEither::leftOf(leftCase(std::move(leftValue))) :
+ NextEither::rightOf( std::move(rightValue) );
+ }
+
template
constexpr auto rightMap(F const& rightCase) const& -> Either {
using NextEither = Either;
@@ -182,13 +210,22 @@ struct Either {
}
template
- auto rightMap(F const& rightCase)&& -> Either {
+ auto rightMap(F const& rightCase)&& -> Either {
using NextEither = Either;
return isLeft ?
NextEither::leftOf( std::move(leftValue) ) :
NextEither::rightOf( rightCase( std::move(rightValue) ) );
}
+ template::value && std::is_copy_constructible::value), int>::type = 0>
+ auto rightMap(F const& rightCase)& -> Either {
+ using NextEither = Either;
+ return isLeft ?
+ NextEither::leftOf( std::move(leftValue) ) :
+ NextEither::rightOf( rightCase( std::move(rightValue) ) );
+ }
+
template
constexpr auto leftFlatMap(LeftCase const& leftCase) const&
-> decltype( ensureEitherRight(leftCase(isCopyable((L2)leftValue)), isCopyable((R2)rightValue))) {
@@ -225,6 +262,19 @@ struct Either {
return NextEither::rightOf(std::move(rightValue));
}
+ template::value && std::is_copy_constructible::value), int>::type = 0>
+ auto leftFlatMap(LeftCase const& leftCase)&
+ -> decltype( ensureEitherRight(leftCase(std::move(leftValue)), std::move(rightValue))) {
+ using NextEither = decltype(leftCase(std::move(leftValue)));
+
+ if (!*this) {
+ return leftCase( std::move(leftValue) );
+ }
+
+ return NextEither::rightOf(std::move(rightValue));
+ }
+
template
auto rightFlatMap(RightCase const& rightCase)&&
-> decltype( ensureEitherLeft(rightCase(std::move(rightValue)), std::move(leftValue))) {
@@ -237,6 +287,19 @@ struct Either {
return NextEither::leftOf(std::move(leftValue));
}
+ template::value && std::is_copy_constructible::value), int>::type = 0>
+ auto rightFlatMap(RightCase const& rightCase)&
+ -> decltype( ensureEitherLeft(rightCase(std::move(rightValue)), std::move(leftValue))) {
+ using NextEither = decltype(rightCase(std::move(rightValue)));
+
+ if (*this) {
+ return rightCase(std::move(rightValue));
+ }
+
+ return NextEither::leftOf(std::move(leftValue));
+ }
+
constexpr operator bool()const { return !isLeft; }
};
diff --git a/neither/include/maybe.hpp b/neither/include/maybe.hpp
index a4c347b..9078e14 100644
--- a/neither/include/maybe.hpp
+++ b/neither/include/maybe.hpp
@@ -3,6 +3,8 @@
#include
#include
+#include
+#include
#include
namespace neither {
@@ -13,18 +15,20 @@ template <> struct Maybe {};
template struct Maybe {
+ using size_type = std::size_t;
+
union {
T value;
};
- bool const hasValue = 0;
+ bool const hasValue;
- constexpr Maybe() : hasValue{0} {}
+ constexpr Maybe() : hasValue{false} {}
- constexpr Maybe(T const& value) : value{value}, hasValue{1} {}
- constexpr Maybe(T&& value) : value{std::move(value)}, hasValue{1} {}
+ constexpr Maybe(T const& value) : value{value}, hasValue{true} {}
+ constexpr Maybe(T&& value) : value{std::move(value)}, hasValue{true} {}
- constexpr Maybe(Maybe) : hasValue{0} {}
+ constexpr Maybe(Maybe) : hasValue{false} {}
constexpr Maybe(Maybe const &o) : hasValue{o.hasValue} {
if (o.hasValue) {
@@ -47,6 +51,10 @@ template struct Maybe {
return value;
}
+ constexpr size_type size() const noexcept { return hasValue ? 1: 0; }
+
+ constexpr bool empty() const noexcept { return !hasValue; }
+
template
constexpr auto map(F const &f) const&
-> Maybe {
@@ -99,12 +107,25 @@ auto maybe(T value) -> Maybe { return {value}; }
template
auto maybe() -> Maybe { return {}; }
+namespace {
+
+ inline
+ bool equal(Maybe const&, Maybe const&) {
+ return true;
+ }
+
+ template
+ bool equal(Maybe const &a, Maybe const &b) {
+ if (a.hasValue) {
+ return b.hasValue && a.value == b.value;
+ }
+ return !b.hasValue;
+ }
+}
+
template
bool operator == (Maybe const& a, Maybe const& b) {
- if (a.hasValue) {
- return b.hasValue && a.value == b.value;
- }
- return !b.hasValue;
+ return equal(a, b);
}
template
@@ -112,6 +133,8 @@ bool operator != (Maybe const& a, Maybe const& b) {
return !(a == b);
}
+static const auto none = maybe();
+
}
#endif
diff --git a/neither/tests/either.cpp b/neither/tests/either.cpp
index 105c09e..07461e2 100644
--- a/neither/tests/either.cpp
+++ b/neither/tests/either.cpp
@@ -12,6 +12,7 @@ using namespace std::literals::string_literals;
using IntOrStr = Either;
using StrOrInt = Either;
using StrOrStr = Either;
+using PtrOrPtr = neither::Either, std::unique_ptr>;
TEST(neither, either_join_left) {
@@ -97,7 +98,7 @@ TEST(neither, either_flatMapToUnique) {
}
-TEST(neither, either_mapToUnique) {
+TEST(neither, either_leftMapToUnique) {
neither::Either e = left(1);
auto u = e.leftFlatMap([](auto x) { return
@@ -112,3 +113,137 @@ TEST(neither, either_mapToUnique) {
ASSERT_TRUE(*u == 1);
}
+
+
+TEST(neither, either_rightMapToUnique) {
+ neither::Either e = right(1);
+
+ auto u = e.rightFlatMap([](auto x) { return
+ Either>::rightOf(
+ std::make_unique(x));
+ }).rightMap([](auto&& x) {
+ return std::move(x);
+ }).leftFlatMap([](auto&& x) {
+ return Either, std::unique_ptr>::leftOf(
+ std::make_unique(2));
+ }).join();
+
+ ASSERT_TRUE(*u == 1);
+}
+
+TEST(neither, either_leftMapFromStoredUnique)
+{
+ PtrOrPtr e = left(std::make_unique(1));
+ auto u = e.leftMap([](auto x){
+ return(std::move(x));
+ }).join();
+
+ ASSERT_EQ(*u, 1);
+}
+
+TEST(neither, either_rightMapFromStoredUnique)
+{
+ PtrOrPtr e = right(std::make_unique(1));
+ auto u = e.rightMap([](auto x){
+ return(std::move(x));
+ }).join();
+
+ ASSERT_EQ(*u, 1);
+}
+
+TEST(neither, either_leftMapOfConst)
+{
+ const auto e = IntOrStr::leftOf(1);
+ const auto f = e.leftMap([](auto x){return x;});
+ const auto ret = f.rightMap([](auto x){return 0;}).join();
+
+ ASSERT_EQ(ret, 1);
+}
+
+TEST(neither, either_rightMapOfConst)
+{
+ const auto e = IntOrStr::rightOf("Hello World!");
+ const auto f = e.leftMap([](auto x){return std::string("Bye World!");});
+ const auto ret = f.rightMap([](auto x){return x;}).join();
+
+ ASSERT_EQ(ret, "Hello World!");
+}
+
+TEST(neither, either_leftMapNotAltered)
+{
+ auto e = IntOrStr::leftOf(1);
+ e.leftMap([](auto x){return 3;});
+ const auto ret = e.rightMap([](auto right){return 0;}).join();
+
+ ASSERT_EQ(ret, 1);
+}
+
+TEST(neither, either_leftMapOfConstNotAltered)
+{
+ const auto e = IntOrStr::leftOf(1);
+ e.leftMap([](auto x){return 3;});
+ const auto ret = e.rightMap([](auto right){return 0;}).join();
+
+ ASSERT_EQ(ret, 1);
+}
+
+TEST(neither, either_rightMapNotAltered)
+{
+ auto e = IntOrStr::rightOf("Hello World!");
+ e.leftMap([](auto left){return "";});
+ const auto ret = e.leftMap([](auto x){return std::string("Bye World!");}).join();
+
+ ASSERT_EQ(ret, "Hello World!");
+}
+
+TEST(neither, either_rightMapOfConstNotAltered)
+{
+ const auto e = IntOrStr::rightOf("Hello World!");
+ e.leftMap([](auto left){return "";});
+ const auto ret = e.leftMap([](auto x){return std::string("Bye World!");}).join();
+
+ ASSERT_EQ(ret, "Hello World!");
+}
+
+TEST(neither, either_leftFlatMapFromStoredUnique)
+{
+ PtrOrPtr e = left(std::make_unique(1));
+ auto u = e.leftFlatMap([](auto x){
+ return PtrOrPtr::leftOf(std::make_unique(0));
+ }).join();
+
+ ASSERT_EQ(*u, 0);
+}
+
+TEST(neither, either_rightFlatMapFromStoredUnique)
+{
+ PtrOrPtr e = right(std::make_unique(1));
+ auto u = e.rightFlatMap([](auto x){
+ return PtrOrPtr::leftOf(std::make_unique(0));
+ }).join();
+
+ ASSERT_EQ(*u, 0);
+}
+
+TEST(neither, either_joinFromStoredUnique)
+{
+ PtrOrPtr e = right(std::make_unique(1));
+ auto u = e.join(
+ [](auto left){
+ return left;
+ },
+ [](auto right){
+ return right;
+ }
+ );
+
+ ASSERT_EQ(*u, 1);
+}
+
+TEST(neither, either_joinFromStoredUniqueNoFunction)
+{
+ PtrOrPtr e = right(std::make_unique(1));
+ auto u = e.join();
+
+ ASSERT_EQ(*u, 1);
+}
diff --git a/neither/tests/maybe.cpp b/neither/tests/maybe.cpp
index 10456b8..54b5120 100644
--- a/neither/tests/maybe.cpp
+++ b/neither/tests/maybe.cpp
@@ -53,3 +53,29 @@ TEST(neither, maybe_comparison) {
EXPECT_TRUE(d == d);
EXPECT_TRUE(d == e);
}
+
+TEST(neither, maybe_size) {
+
+ auto none = maybe();
+ auto some = maybe(1);
+
+ EXPECT_EQ(none.size(), 0);
+ EXPECT_EQ(some.size(), 1);
+}
+
+TEST(neither, maybe_empty) {
+
+ auto none = maybe();
+ auto some = maybe(1);
+
+ EXPECT_TRUE(none.empty());
+ EXPECT_FALSE(some.empty());
+}
+
+TEST(neither, maybe_none) {
+ auto none1 = none;
+ auto none2 = none;
+
+ EXPECT_TRUE(none1 == none2);
+ EXPECT_FALSE(none1 != none2);
+}
diff --git a/subdir_glob.bzl b/subdir_glob.bzl
new file mode 100644
index 0000000..97950bf
--- /dev/null
+++ b/subdir_glob.bzl
@@ -0,0 +1,82 @@
+"""Provides utility macros for working with globs."""
+
+def _paths_join(path, *others):
+ """Joins one or more path components."""
+ result = path
+
+ for p in others:
+ if p.startswith("/"): # absolute
+ result = p
+ elif not result or result.endswith("/"):
+ result += p
+ else:
+ result += "/" + p
+
+ return result
+
+def subdir_glob(glob_specs, exclude = None, prefix = ""):
+ """Returns a dict of sub-directory relative paths to full paths.
+
+ The subdir_glob() function is useful for defining header maps for C/C++
+ libraries which should be relative the given sub-directory.
+ Given a list of tuples, the form of (relative-sub-directory, glob-pattern),
+ it returns a dict of sub-directory relative paths to full paths.
+
+ Please refer to native.glob() for explanations and examples of the pattern.
+
+ Args:
+ glob_specs: The array of tuples in form of
+ (relative-sub-directory, glob-pattern inside relative-sub-directory).
+ type: List[Tuple[str, str]]
+ exclude: A list of patterns to identify files that should be removed
+ from the set specified by the first argument. Defaults to [].
+ type: Optional[List[str]]
+ prefix: If is not None, prepends it to each key in the dictionary.
+ Defaults to None.
+ type: Optional[str]
+
+ Returns:
+ A dict of sub-directory relative paths to full paths.
+ """
+ if exclude == None:
+ exclude = []
+
+ results = []
+
+ for dirpath, glob_pattern in glob_specs:
+ results.append(
+ _single_subdir_glob(dirpath, glob_pattern, exclude, prefix),
+ )
+
+ return _merge_maps(*results)
+
+def _merge_maps(*file_maps):
+ result = {}
+ for file_map in file_maps:
+ for key in file_map:
+ if key in result and result[key] != file_map[key]:
+ fail(
+ "Conflicting files in file search paths. " +
+ "\"%s\" maps to both \"%s\" and \"%s\"." %
+ (key, result[key], file_map[key]),
+ )
+
+ result[key] = file_map[key]
+
+ return result
+
+def _single_subdir_glob(dirpath, glob_pattern, exclude = None, prefix = None):
+ if exclude == None:
+ exclude = []
+ results = {}
+ files = native.glob([_paths_join(dirpath, glob_pattern)], exclude = exclude)
+ for f in files:
+ if dirpath:
+ key = f[len(dirpath) + 1:]
+ else:
+ key = f
+ if prefix:
+ key = _paths_join(prefix, key)
+ results[key] = f
+
+ return results
diff --git a/travis/before-install-linux.sh b/travis/before-install-linux.sh
new file mode 100755
index 0000000..e9fbbd7
--- /dev/null
+++ b/travis/before-install-linux.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-6 90
+sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 90
+
+c++ --version
+g++ --version
+gcc --version
+
+sudo apt-get install -y equivs openjdk-8-jdk
+
+wget -O buck.deb https://github.com/facebook/buck/releases/download/v2018.10.29.01/buck.2018.10.29.01_all.deb
+sudo dpkg -i buck.deb
+buck --version
+
+wget https://github.com/LoopPerfect/buckaroo/releases/download/$BUCKAROO_VERSION/buckaroo-linux -O buckaroo-client
+chmod +x ./buckaroo-client
+./buckaroo-client version
diff --git a/travis/before-install-osx.sh b/travis/before-install-osx.sh
new file mode 100755
index 0000000..2e77b82
--- /dev/null
+++ b/travis/before-install-osx.sh
@@ -0,0 +1,6 @@
+brew tap facebook/fb
+brew install buck
+
+wget https://github.com/LoopPerfect/buckaroo/releases/download/$BUCKAROO_VERSION/buckaroo-macos -O buckaroo-client
+chmod +x ./buckaroo-client
+./buckaroo-client version