1
1
Fork 0
mirror of https://github.com/oxen-io/lokinet synced 2023-12-14 06:53:00 +01:00

manual merge of jeff's ios liblokinet changes

This commit is contained in:
Thomas Winget 2022-12-12 18:19:30 -05:00
parent 411148d515
commit 735c2183ae
15 changed files with 423 additions and 28 deletions

View file

@ -315,6 +315,33 @@ local mac_builder(name,
],
};
// iphone builder
local iphone_builder(name,
build_type='Release',
werror=true,
cmake_extra='',
extra_cmds=[],
jobs=6,
allow_fail=false) = {
kind: 'pipeline',
type: 'exec',
name: name,
platform: { os: 'darwin', arch: 'amd64' },
steps: [
{ name: 'submodules', commands: submodule_commands },
{
name: 'build',
environment: { SSH_KEY: { from_secret: 'SSH_KEY' } },
commands: [
'echo "Building on ${DRONE_STAGE_MACHINE}"',
'export CMAKE_BUILD_PARALLEL_LEVEL=6',
'ulimit -n 1024', // because macos sets ulimit to 256 for some reason yeah idk
'./contrib/ios.sh ' + ci_mirror_opts,
] + extra_cmds,
},
],
};
local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
kind: 'pipeline',
type: 'docker',
@ -438,6 +465,11 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
deb_builder(docker_base + 'ubuntu-jammy-builder', 'jammy', 'ubuntu/jammy'),
deb_builder(docker_base + 'debian-sid-builder', 'sid', 'debian/sid', arch='arm64'),
// iphone builds:
iphone_builder('iOS (embedded lokinet)', build_type='Debug', extra_cmds=[
'UPLOAD_OS=iphone ./contrib/ci/drone-static-upload.sh',
]),
// Macos builds:
mac_builder('macOS (Release)', extra_cmds=[
'./contrib/ci/drone-check-static-libs.sh',

3
.gitmodules vendored
View file

@ -39,3 +39,6 @@
[submodule "external/CLI11"]
path = external/CLI11
url = https://github.com/CLIUtils/CLI11.git
[submodule "external/ios-cmake"]
path = external/ios-cmake
url = https://github.com/leetal/ios-cmake.git

View file

@ -50,6 +50,7 @@ option(USE_AVX2 "enable avx2 code" OFF)
option(USE_NETNS "enable networking namespace support. Linux only" OFF)
option(NATIVE_BUILD "optimise for host system and FPU" ON)
option(WITH_EMBEDDED_LOKINET "build liblokinet.so for embedded lokinet" OFF)
option(BUILD_EMBEDDED_LOKINET "build embedded lokinet" OFF)
option(LIBLOKINET_TEST_UTILS "build test utils in contrib/liblokinet" OFF)
option(XSAN "use sanitiser, if your system has it (requires -DCMAKE_BUILD_TYPE=Debug)" OFF)
option(USE_JEMALLOC "Link to jemalloc for memory allocations, if found" ON)
@ -78,6 +79,7 @@ if(BUILD_STATIC_DEPS AND NOT STATIC_LINK)
endif()
if(BUILD_STATIC_DEPS)
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE)
include(cmake/combine_archives.cmake)
include(StaticBuild)
endif()
@ -214,20 +216,28 @@ if (WOW64_CROSS_COMPILE OR WIN64_CROSS_COMPILE)
include(cmake/cross_compile.cmake)
endif()
if(NOT APPLE)
if(NATIVE_BUILD)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL ppc64le)
add_compile_options(-mcpu=native -mtune=native)
else()
add_compile_options(-march=native -mtune=native)
endif()
elseif(NOT NON_PC_TARGET)
if (USE_AVX2)
add_compile_options(-march=haswell -mtune=haswell -mfpmath=sse)
else()
# Public binary releases
add_compile_options(-march=nocona -mtune=haswell -mfpmath=sse)
endif()
if(APPLE)
if(IOS AND ARCH STREQUAL "arm64")
message(STATUS "IOS: Changing arch from arm64 to armv8")
add_compile_options(-march=armv8)
elseif(IOS AND ARCH STREQUAL "x86_64")
message(STATUS "IOS: Changing arch from x86_64 to x86-64")
add_compile_options(-march=x86-64)
endif()
endif()
if(NATIVE_BUILD)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL ppc64le)
add_compile_options(-mcpu=native -mtune=native)
else()
add_compile_options(-march=native -mtune=native)
endif()
elseif(NOT NON_PC_TARGET)
if (USE_AVX2)
add_compile_options(-march=haswell -mtune=haswell -mfpmath=sse)
else()
# Public binary releases
add_compile_options(-march=nocona -mtune=haswell -mfpmath=sse)
endif()
endif()

View file

@ -109,9 +109,18 @@ endfunction()
set(cross_host "")
set(cross_rc "")
if(CMAKE_CROSSCOMPILING)
set(cross_host "--host=${ARCH_TRIPLET}")
if (ARCH_TRIPLET MATCHES mingw AND CMAKE_RC_COMPILER)
set(cross_rc "WINDRES=${CMAKE_RC_COMPILER}")
if(APPLE_TARGET_TRIPLE)
if(PLATFORM MATCHES "OS64" OR PLATFORM MATCHES "SIMULATORARM64")
set(APPLE_TARGET_TRIPLE aarch64-apple-ios)
elseif(PLATFORM MATCHES "SIMULATOR64")
set(APPLE_TARGET_TRIPLE x86_64-apple-ios)
endif()
set(cross_host "--host=${APPLE_TARGET_TRIPLE}")
else()
set(cross_host "--host=${ARCH_TRIPLET}")
if (ARCH_TRIPLET MATCHES mingw AND CMAKE_RC_COMPILER)
set(cross_rc "WINDRES=${CMAKE_RC_COMPILER}")
endif()
endif()
endif()
if(ANDROID)
@ -162,11 +171,6 @@ if(WITH_LTO)
set(deps_CFLAGS "${deps_CFLAGS} -flto")
endif()
if(APPLE)
set(deps_CFLAGS "${deps_CFLAGS} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
set(deps_CXXFLAGS "${deps_CXXFLAGS} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
endif()
if(_winver)
set(deps_CFLAGS "${deps_CFLAGS} -D_WIN32_WINNT=${_winver}")
set(deps_CXXFLAGS "${deps_CXXFLAGS} -D_WIN32_WINNT=${_winver}")
@ -179,6 +183,15 @@ else()
set(_make make)
endif()
if(APPLE)
foreach(lang C CXX)
string(APPEND deps_${lang}FLAGS " ${CMAKE_${lang}_SYSROOT_FLAG} ${CMAKE_OSX_SYSROOT} ${CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG}${CMAKE_OSX_DEPLOYMENT_TARGET}")
set(deps_noarch_${lang}FLAGS "${deps_${lang}FLAGS}")
foreach(arch ${CMAKE_OSX_ARCHITECTURES})
string(APPEND deps_${lang}FLAGS " -arch ${arch}")
endforeach()
endforeach()
endif()
# Builds a target; takes the target name (e.g. "readline") and builds it in an external project with
# target name suffixed with `_external`. Its upper-case value is used to get the download details
@ -254,6 +267,16 @@ if(CMAKE_CROSSCOMPILING)
set(openssl_system_env LD=${deps_ld} RANLIB=${deps_ranlib} AR=${deps_ar} ANDROID_NDK_ROOT=${CMAKE_ANDROID_NDK} "PATH=${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin:$ENV{PATH}")
list(APPEND openssl_flags "CPPFLAGS=-D__ANDROID_API__=${ANDROID_API}")
set(openssl_extra_opts no-asm)
elseif(IOS)
get_filename_component(apple_toolchain "${CMAKE_C_COMPILER}" DIRECTORY)
get_filename_component(apple_sdk "${CMAKE_OSX_SYSROOT}" NAME)
if(NOT ${apple_toolchain} MATCHES Xcode OR NOT ${apple_sdk} MATCHES "iPhone(OS|Simulator)")
message(FATAL_ERROR "didn't find your toolchain and sdk correctly from ${CMAKE_C_COMPILER}/${CMAKE_OSX_SYSROOT}: found toolchain=${apple_toolchain}, sdk=${apple_sdk}")
endif()
set(openssl_system_env CROSS_COMPILE=${apple_toolchain}/ CROSS_TOP=${CMAKE_DEVELOPER_ROOT}/ CROSS_SDK=${apple_sdk}/)
set(openssl_configure_command ./Configure iphoneos-cross)
# FIXME: is this needed?
#set(openssl_cc "clang")
elseif(ARCH_TRIPLET STREQUAL mips64-linux-gnuabi64)
set(openssl_arch linux-mips64)
elseif(ARCH_TRIPLET STREQUAL mips-linux-gnu)
@ -307,6 +330,12 @@ build_external(expat
add_static_target(expat expat_external libexpat.a)
set(unbound_extra)
if(APPLE AND IOS)
set(unbound_extra CPP=cpp)
endif()
if(WIN32)
set(unbound_patch
PATCH_COMMAND ${PROJECT_SOURCE_DIR}/contrib/apply-patches.sh
@ -316,10 +345,10 @@ build_external(unbound
DEPENDS openssl_external expat_external
${unbound_patch}
CONFIGURE_COMMAND ./configure ${cross_host} ${cross_rc} --prefix=${DEPS_DESTDIR} --disable-shared
--enable-static --with-libunbound-only --with-pic
--enable-static --with-libunbound-only --with-pic --disable-gost
--$<IF:$<BOOL:${WITH_LTO}>,enable,disable>-flto --with-ssl=${DEPS_DESTDIR}
--with-libexpat=${DEPS_DESTDIR}
"CC=${deps_cc}" "CFLAGS=${deps_CFLAGS}"
"CC=${deps_cc}" "CFLAGS=${deps_CFLAGS}" ${unbound_extra}
)
add_static_target(libunbound unbound_external libunbound.a)
if(NOT WIN32)
@ -336,7 +365,9 @@ add_static_target(sodium sodium_external libsodium.a)
if(WITH_PEERSTATS_BACKEND)
build_external(sqlite3)
build_external(sqlite3 CONFIGURE_COMMAND ./configure ${cross_host} ${cross_rc} --prefix=${DEPS_DESTDIR} --enable-static --disable-shared --disable-readline --with-pic "CC=${deps_cc}" "CFLAGS=${deps_CFLAGS}"
BUILD_COMMAND true
INSTALL_COMMAND make install-includeHEADERS install-libLTLIBRARIES)
add_static_target(sqlite3 sqlite3_external libsqlite3.a)
endif()
@ -355,10 +386,16 @@ if(CMAKE_CROSSCOMPILING AND ARCH_TRIPLET MATCHES mingw)
${PROJECT_SOURCE_DIR}/contrib/patches/libzmq-mingw-unistd.patch)
endif()
set(zmq_cross_host "${cross_host}")
if(IOS AND cross_host MATCHES "-ios$")
# zmq doesn't like "-ios" for the host, so replace it with -darwin
string(REGEX REPLACE "-ios$" "-darwin" zmq_cross_host ${cross_host})
endif()
build_external(zmq
DEPENDS sodium_external
${zmq_patch}
CONFIGURE_COMMAND ./configure ${cross_host} --prefix=${DEPS_DESTDIR} --enable-static --disable-shared
CONFIGURE_COMMAND ./configure ${zmq_cross_host} --prefix=${DEPS_DESTDIR} --enable-static --disable-shared
--disable-curve-keygen --enable-curve --disable-drafts --disable-libunwind --with-libsodium
--without-pgm --without-norm --without-vmci --without-docs --with-pic --disable-Werror --disable-libbsd ${zmq_extra}
"CC=${deps_cc}" "CXX=${deps_cxx}" "CFLAGS=${deps_CFLAGS} -fstack-protector" "CXXFLAGS=${deps_CXXFLAGS} -fstack-protector"

View file

@ -0,0 +1,30 @@
function(combine_archives output_archive)
set(FULL_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/lib${output_archive}.a)
set(output_archive_dummy_file ${CMAKE_CURRENT_BINARY_DIR}/${output_archive}.dummy.cpp)
add_custom_command(OUTPUT ${output_archive_dummy_file}
COMMAND touch ${output_archive_dummy_file}
DEPENDS ${ARGN})
add_library(${output_archive} STATIC EXCLUDE_FROM_ALL ${output_archive_dummy_file})
if(NOT APPLE)
set(mri_file ${CMAKE_CURRENT_BINARY_DIR}/${output_archive}.mri)
set(mri_content "create ${FULL_OUTPUT_PATH}\n")
foreach(in_archive ${ARGN})
string(APPEND mri_content "addlib $<TARGET_FILE:${in_archive}>\n")
endforeach()
string(APPEND mri_content "save\nend\n")
file(GENERATE OUTPUT ${mri_file} CONTENT "${mri_content}")
add_custom_command(TARGET ${output_archive}
POST_BUILD
COMMAND ar -M < ${mri_file})
else()
set(merge_libs)
foreach(in_archive ${ARGN})
list(APPEND merge_libs $<TARGET_FILE:${in_archive}>)
endforeach()
add_custom_command(TARGET ${output_archive}
POST_BUILD
COMMAND /usr/bin/libtool -static -o ${FULL_OUTPUT_PATH} ${merge_libs})
endif()
endfunction(combine_archives)

View file

@ -60,6 +60,9 @@ elif [ -e build-mac ]; then
archive="$base.tar.xz"
mv build-mac/Lokinet*/ "$base"
tar cJvf "$archive" "$base"
elif [ -e build/iphone/ ]; then
archive="$base.tar.xz"
mv build/iphone/*.tar.xz "$archive"
else
cp -av build/daemon/lokinet{,-vpn} "$base"
cp -av contrib/bootstrap/mainnet.signed "$base/bootstrap.signed"

85
contrib/ios.sh Normal file
View file

@ -0,0 +1,85 @@
#!/bin/bash
#
# Builds embeded lokinet for iphone
set -e
set -x
if ! [ -f LICENSE ] || ! [ -d llarp ]; then
echo "You need to run this as ./contrib/ios.sh from the top-level lokinet project directory"
fi
root="$(readlink -f $(dirname $0)/../)"
build_dir="$root/build/iphone"
deviceArchs=(arm64)
devicePlat=(OS64)
simArchs=(arm64 x86_64)
simPlat=(SIMULATORARM64 SIMULATOR64)
for i in "${!deviceArchs[@]}"; do
./contrib/ios/ios-configure.sh "$build_dir/device/${deviceArchs[i]}" ${devicePlat[i]} $@
done
for i in "${!simArchs[@]}"; do
./contrib/ios/ios-configure.sh "$build_dir/sim/${simArchs[i]}" ${simPlat[i]} $@
done
for targ in ${deviceArchs[@]} ; do
./contrib/ios/ios-build.sh "$build_dir/device/$targ"
done
for targ in ${simArchs[@]} ; do
./contrib/ios/ios-build.sh "$build_dir/sim/$targ"
done
pkg_name="iphone_lokinet_embedded_$(date +%s)"
pkg_dir="$build_dir/$pkg_name"
# Combine and device/simulator libraries into a single file so XCode doesn't complain
function combineArchsIfNeeded() {
local group=$1
local destination=$2
shift; shift
local archs=("$@")
if [ ${#archs[@]} -gt 1 ]; then
local dirs=("${archs[@]/#/$build_dir/${group}/}")
local libs=("${dirs[@]/%//llarp/liblokinet-embedded.a}")
mkdir -p "$build_dir/$group/$destination/llarp"
lipo -create "${libs[@]}" -output "$build_dir/$group/${destination}/llarp/liblokinet-embedded.a"
fi
}
deviceArchsString="${deviceArchs[*]}"
joinedDeviceArchs="${deviceArchsString// /_}"
simArchsString="${simArchs[*]}"
joinedSimArchs="${simArchsString// /_}"
combineArchsIfNeeded "device" $joinedDeviceArchs ${deviceArchs[*]}
combineArchsIfNeeded "sim" $joinedSimArchs ${simArchs[*]}
# Create a '.xcframework' so XCode can deal with the different architectures
xcodebuild -create-xcframework \
-library "$build_dir/device/$joinedDeviceArchs/llarp/liblokinet-embedded.a" \
-library "$build_dir/sim/$joinedSimArchs/llarp/liblokinet-embedded.a" \
-output "$pkg_dir/libLokinet.xcframework"
# Copy the headers over
mkdir -p "$pkg_dir/libLokinet.xcframework/lokinet"
cp -a "$root"/include/lokinet{,/*}.h "$pkg_dir/libLokinet.xcframework/lokinet"
mv "$pkg_dir/libLokinet.xcframework/lokinet/lokinet.h" "$pkg_dir/libLokinet.xcframework/lokinet.h"
# The 'module.modulemap' is needed for XCode to be able to find the headers
echo -e 'module Lokinet {' > "$pkg_dir/libLokinet.xcframework/module.modulemap"
echo -e ' umbrella header "lokinet.h"' >> "$pkg_dir/libLokinet.xcframework/module.modulemap"
echo -e ' export *' >> "$pkg_dir/libLokinet.xcframework/module.modulemap"
echo -e '}' >> "$pkg_dir/libLokinet.xcframework/module.modulemap"
# Archive the result
cd "$build_dir"
tar cfv "$pkg_name.tar" $pkg_name
cd -
xz -T 0 "$build_dir/$pkg_name.tar"

7
contrib/ios/ios-build.sh Normal file
View file

@ -0,0 +1,7 @@
#!/bin/bash
#
# Build the shit for iphone
test -e $1 || ( echo "run ios-configure.sh first" ; exit 1 )
cmake --build $1 --target lokinet-embedded

View file

@ -0,0 +1,42 @@
#!/bin/bash
#
# configure step for ios
set -x
root=$(readlink -f "$( dirname $0 )/../../")
unset SDKROOT
export SDKROOT="$(xcrun --sdk iphoneos --show-sdk-path)"
targ=$1
plat=$2
shift
shift
mkdir -p $targ
cmake \
-G Ninja \
-DCMAKE_TOOLCHAIN_FILE="$root/external/ios-cmake/ios.toolchain.cmake" -DPLATFORM=$plat -DDEPLOYMENT_TARGET=13 -DENABLE_VISIBILITY=ON -DENABLE_BITCODE=OFF \
-DBUILD_STATIC_DEPS=ON \
-DBUILD_PACKAGE=OFF \
-DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \
-DWITH_EMBEDDED_LOKINET=ON \
-DWITH_TESTS=OFF \
-DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \
-DWITH_SYSTEMD=OFF \
-DWITH_BOOTSTRAP=OFF \
-DBUILD_DAEMON=OFF \
-DFORCE_OXENMQ_SUBMODULE=ON \
-DFORCE_OXENC_SUBMODULE=ON \
-DFORCE_NLOHMANN_SUBMODULE=ON \
-DFORCE_LIBUV_SUBMODULE=ON \
-DSUBMODULE_CHECK=ON \
-DWITH_LTO=OFF \
-DCMAKE_CXX_FLAGS='-Oz' -DCMAKE_C_FLAGS='-Oz' \
-DCMAKE_BUILD_TYPE=Release \
-S "$root" -B $targ \
$@

10
contrib/ios/readme.txt Normal file
View file

@ -0,0 +1,10 @@
our scientists have yet to reverse engineer the correct way to use the apple build process as dictated on the official apple documentation so we have a made a hack to make it work until that occurs.
the build process for embedded lokinet on iphone is as follows:
* obtain holy water, sprinkle onto keyboard and single button trackpad accordingly.
* run ./contrib/ios.sh
* after it runs and the proper number of goats have been offered to the slaughter you will get an xz's tarball in the build/iphone/ directory
additional cmake flags can be passed to ./contrib/ios.sh as command line arguments like so:
$ ./contrib/ios.sh -DYOLO_SWAG=ON

View file

@ -47,7 +47,7 @@ set(NTRU_AVX_SRC
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-mavx2 COMPILER_SUPPORTS_AVX2)
check_cxx_compiler_flag(-mfma COMPILER_SUPPORTS_FMA)
if(COMPILER_SUPPORTS_AVX2 AND COMPILER_SUPPORTS_FMA AND (NOT ANDROID))
if(COMPILER_SUPPORTS_AVX2 AND COMPILER_SUPPORTS_FMA AND (NOT (ANDROID OR IOS)))
target_sources(lokinet-cryptography PRIVATE ${NTRU_AVX_SRC})
set_property(SOURCE ${NTRU_AVX_SRC} APPEND PROPERTY COMPILE_FLAGS "-mavx2 -mfma")
message(STATUS "Building libntrup with runtime AVX2/FMA support")

1
external/ios-cmake vendored Submodule

@ -0,0 +1 @@
Subproject commit ab8607a7f57970d99d590d420ecd8e2d7f9a9c77

View file

@ -534,5 +534,36 @@ if(WITH_EMBEDDED_LOKINET)
endif()
endif()
if(WITH_EMBEDDED_LOKINET)
add_library(lokinet-embedded-api STATIC lokinet_shared.cpp)
target_link_libraries(lokinet-embedded-api PUBLIC liblokinet)
add_log_tag(lokinet-embedded-api)
combine_archives(lokinet-embedded
lokinet-embedded-api
lokinet-platform lokinet-util lokinet-cryptography sqlite3 ngtcp2_static
liblokinet
libuv
sodium
zlib
OpenSSL::SSL OpenSSL::Crypto
expat
libunbound
libzmq
oxenmq)
if(IOS)
set(lib_folder lib-${ARCH})
else()
set(lib_folder lib)
endif()
install(TARGETS lokinet-embedded
ARCHIVE DESTINATION ${lib_folder}
EXCLUDE_FROM_ALL)
endif()
if(APPLE)
add_subdirectory(apple)
target_sources(lokinet-platform PRIVATE util/nop_service_manager.cpp)
endif()
file(GLOB_RECURSE docs_SRC */*.hpp *.hpp)
set(DOCS_SRC ${docs_SRC} PARENT_SCOPE)

98
llarp/vpn/null.hpp Normal file
View file

@ -0,0 +1,98 @@
#pragma once
#include <llarp/ev/vpn.hpp>
#include <unistd.h>
namespace llarp::vpn
{
class NullInterface : public NetworkInterface
{
/// we use a pipe because it isnt going to poll itself
int m_pipe[2];
public:
NullInterface()
{
::pipe(m_pipe);
}
virtual ~NullInterface()
{
::close(m_pipe[1]);
::close(m_pipe[0]);
}
int
PollFD() const override
{
return m_pipe[0];
}
/// the interface's name
std::string
IfName() const override
{
return "";
}
net::IPPacket
ReadNextPacket() override
{
return net::IPPacket{};
}
/// write a packet to the interface
/// returns false if we dropped it
bool
WritePacket(net::IPPacket) override
{
return true;
}
};
class NopRouteManager : public IRouteManager
{
public:
void AddRoute(IPVariant_t, IPVariant_t) override{};
void DelRoute(IPVariant_t, IPVariant_t) override{};
void AddDefaultRouteViaInterface(std::string) override{};
void DelDefaultRouteViaInterface(std::string) override{};
void
AddRouteViaInterface(NetworkInterface&, IPRange) override{};
void
DelRouteViaInterface(NetworkInterface&, IPRange) override{};
std::vector<IPVariant_t>
GetGatewaysNotOnInterface(std::string) override
{
return {};
}
};
class NullPlatform : public Platform
{
NopRouteManager _routes;
public:
NullPlatform() : Platform{}, _routes{}
{}
virtual ~NullPlatform() = default;
std::shared_ptr<NetworkInterface>
ObtainInterface(InterfaceInfo, AbstractRouter*)
{
return std::static_pointer_cast<NetworkInterface>(std::make_shared<NullInterface>());
}
IRouteManager&
RouteManager() override
{
return _routes;
}
};
} // namespace llarp::vpn

View file

@ -11,6 +11,9 @@
#include "linux.hpp"
#endif
#endif
#ifdef __APPLE__
#include "null.hpp"
#endif
#include <exception>
@ -38,8 +41,11 @@ namespace llarp::vpn
#endif
#endif
#ifdef __APPLE__
throw std::runtime_error{"not supported"};
plat = std::make_shared<NullPlatform>();
#endif
if (not plat)
throw std::runtime_error{"not supported"};
return plat;
}